From: Trevor D. (Twylite) <tw...@cr...> - 2012-08-27 12:01:38
|
Hi, On 2012/08/27 11:24 AM, Colin McCormack wrote: >>> I would like to deal with some of the arguments (Twylite's) for OO as >>> promoting code reuse. > Nothing requires it, I hope nothing precludes it - that's why I'm > entertaining the discussion. Twylite, I think, would like to see it > required. I resist. Nothing precludes it. I would like to see it required. But there is a compromise position. Before I discuss that, there's something I would like to understand: are you against OO as a requirement for implementing datagram protocols, OR against OO as the primary API that script developers will use to work with datagrams, OR both? The compromise entails non-OO implementation of protocols as currently contemplated in the TIP, but with some more pure-Tcl functionality in [datagram] that provides OO wrappers. This _could_ offer a best-of-both-worlds outcome. >> My main argument for OO is that it provides a lot of the basic machinery >> that such an interface would require. I freely admit that I have no idea >> why you would inherit from a UDP endpoint class, > I can't think of any protocol stacked upon another which permits or > could tolerate the arbitrary injection of lower level protocol > elements. As far as I can recall, and imagine, protocols completely > encapsulate lower level protocols. It's less about what you could inherit from or override, and more about what you could mix in. A specific example that comes to mind is mixing in hooks that a Server or Manager object can use to keep track of datagram sockets that it controls. This _can_ be done without OO, but it involves proxy procs and renaming the datagram command and [trace] and being very careful about how callbacks identify their target because its identity can change. >> but then I didn't >> understand why you'd make a subclass of the class of classes for a long >> time either. My idea these days is to not forbid things unless >> absolutely necessary; Tclers are very inventive. :-) > People can be inventive by wrapping datagram commands in OO. That's exactly what we don't want: a glut of MegaDatagram frameworks. Okay, here's the "compromise" suggestion (this follows the well-known form of a Service Provider Interface): 1. Each datagram protocol is implemented as an ensemble in ::tcl::datagram::protocol. 'ensemble' simply means command with subcommands; no specific implementation (namespace ensemble, OO, whatever) is mandated. 2. The list of supported datagram protocols is [info commands ::tcl::datagram::protocol::*] 3. The namespace ::tcl::datagram is used for common functionality. 4. The global command [::datagram] is an ensemble and contains (mostly) protocol-agnostic subcommands. 5. Developers (script writers) will generally call [datagram] subcommands only, and not call protocol implementations directly. 6. Each datagram protocol command (ensemble) must support the following subcommands, and can support other protocol-specific commands: 6.a. [open ?opt val ...?] will open a socket and return a protocol-specific datagramCommand. The options will usually specify a local and/or remote endpoint. 6.a.i. Options that are not recognised by [open] will be passed verbatim to [$d configure] on the datagramCommand $d before it is returned 6.a.ii. The option "-listen val" causes [$d listen $val] to be called on the datagramCommand $d before it is returned. 6.b. [format ?endpoint?] takes an opaque endpoint string and returns a human-readable (but still protocol-specific) representation of the endpoint. It is suggested that [open] and [lookup] should be able to accept an endpoint in this representation. 6.c. (optional) [lookup ?opt val ...?] takes options that specify an endpoint, and returns an opaque endpoint string. 7. The datagramCommand returned by [datagram $protocol open ...] is an ensemble that must support the following subcommands, and can support other protocol-specific commands: 7.a. [close] and [destroy] are equivalent, and will close the socket, free resources and delete the datagramCommand from the interp. {In this hypothetical compromise the destroy] command isn't required, because everything gets wrapped in an object} 7.b. [send payload ?endpoint? ?opt val ...?] sends a payload to the connected endpoint or the given endpoint. The endpoint is protocol-specific ; protocol-agnostic code should treat it as an opaque string. Options are protocol specific. 7.c. [listen callback] starts the socket listening for messages and will cause the callback to be called as [{*}$callback $d $payload $endpoint $metadict] whenever a packet is received. A callback of {} stops the socket listening; omitting the callback argument returns the current callback (or empty if none). The contents of metadict are protocol-specific. 7.d. [configure ?opt val ...?] retrieves and/or sets the configuration options for the socket. Options are protocol-specific. 8. The [datagram] ensemble has the following protocol-agnostic subcommands (all pure-Tcl): 8.a. [datagram create name protocol ?opt val ...?] calls [::tcl::datagram::protocol::$protocol ?opt val ...?], wraps the returned datagramCommand into an object (class: DatagramSocket), and returns the object. 8.a.i. [datagram create] can define and consume options that it will act on. One possibility is "-baseclass" that will allow the datagramCommand to be wrapped in a subclass of DatagramSocket. 8.b. [datagram protocols] returns a list of supported protocols = [info commands ::tcl::datagram::protocol::*] 8.c. [datagram names ?protocol?] returns a list of datagram objects (by protocol). It can do this using [info class instances] or using the datagram object constructor and destructor to manage a list of live objects. This function precludes the need for each datagram protocol to have a [names] subcommand. 8.d. (hat tip: Andreas Leitgeb) [datagram send protocol payload ?opt val ...?] creates a datagramCommand as in [datagram create] (passing the ?opt val ...? to create), calls [send payload] on the datagramCommand, then destroys the datagramCommand. 9. Class DatagramSocket: 9.a. The constructor and destructor are reserved for use by the [datagram] framework. 9.b. Methods [send], [listen] and [configure] are defined as for datagramCommand, and forward to the underlying datagramCommand. 9.b.i. The listener callback is intercepted and the object substituted for the datagramCommand. 9.c. [destroy] works as for an TclOO object. 9.d. Any unknown methods are forwarded to the underlying datagramCommand. 9.e. Useful extensions that can be contemplated immediately: 9.e.i. [get] and [set] methods to access a 'cargo' dict OR define extra properties to be accessed via [configure] 9.e.ii. Method to hook object/socket destruction (without having to subclass) OR flag properties as values or objects (the destructor will free stored objects by calling them with subcommand 'destroy'). So (6) and (7) are very similar to the TIP as it stands, but the protocol is an ensemble, in a slightly different namespace, can defer certain options to the datagramCommand, and has an extra subcommand [format]. The datagramCommand is as discussed: close, send, listen, configure. The difference is that there is an interface layer between the script writer and the protocol writer: [datagram] and [DatagramSocket]. The script writer can - but generally won't - bypass that interface, and the interface is careful to expose all protocol-specific methods and options. The interface permits protocol-agnostic functionality and introspection to be layered on top of the protocol implementations - things like [datagram protocols] and [datagram names] and [datagram send]. The interface also gives script writers a TclOO object to work with, and puts some critical functionality into that object to support extension without wrapping (even without subclassing in most cases, e.g. the 'cargo' dict). Now I think that (6) and (7) will make you happy, and I know (6) - (9) will make me happy ; what I'm wondering is whether (8) and (9) will make you unhappy (and if so, why?). (In case it's relevant: I am quite willing to implement or contribute to the implementation of (8) and (9), once there is an agreement on (6) and (7) ). Regards, Twylite |