TIP #387: UNIFIED YIELD COMMAND SYNTAX ======================================== Version: $Revision: 1.7 $ Author: Lars Hellström State: Withdrawn Type: Project Tcl-Version: 8.6 Vote: Pending Created: Monday, 30 May 2011 URL: https://tip.tcl-lang.org387.html Post-History: Obsoleted-By: TIP #396 ------------------------------------------------------------------------- ABSTRACT ========== This document describes the /syntax/ of a unified *yield* command, which is designed so that it can combine all features of the various yield-related commands proposed during 2010. NOTE ------ This should not be taken as a proposal to necessarily provide all the mentioned features right now, but rather as a roadmap to make a command into which they /could/ all be fitted. The purpose is to avoid choosing a direction that would get really awkward in the future. GENERAL CONSIDERATIONS ======================== It is accepted as given that the present syntax of the *yield* command, namely *yield* ?/value/? should continue to be valid. It is also desirable to control all additional features through options placed before the /value/, since that makes it possible to let an *interp alias* supply them. What is important given those constraints is that one must be able to determine whether the final argument is a /value/ or not solely from looking at the previous arguments, since any option name is also a perfectly valid /value/ for *yield* to return to the coroutine-caller. This can be accomplished through the rule that every option must consist of at least two words (typically -name and value); a word after an option is then known to be the /value/ if it is the very last word of the command, and known to be the first word of another option if it not the very last word of the command. (By contrast, an attempt to get by with a *--* end-of-options option would become very complicated and error-prone.) The overall command syntax would thus be similar to that of *return* (options before an optional value), although there is no need to reproduce any of return's options in *yield*, as all such functionality can be attained by yielding to *return*. POSSIBLE OPTIONS ================== We identify the following options: *-arguments*, *-delivery* and *-to*. These are described below: ARGUMENT SPECIFIERS --------------------- The option *-arguments* /argspec/ would be used to specify a *proc*-style list of arguments for the coroutine-command. As mentioned in [TIP #372], *yieldm* can then be defined as: interp alias {} yieldm {} yield -arguments args whereas the default for *-arguments* is *{arg ""}*, as: yield -arguments {{arg ""}} explicitly says the coroutine-command should have one optional argument /arg/ with default value the empty string. The idea is that if the arguments supplied to the coroutine-command do not match the /argspec/, then the coroutine-command should throw an appropriate error rather than resuming the coroutine. In that case, the coroutine state does not change, and in particular the coroutine-command continues to use the same /argspec/. For the purpose of further discussion below, the elements in the /argspec/ list will be called *formal parameters* of the coroutine-command. YIELDING TO ------------- The *-to* option provides the functionality of the *yieldto* command, and has the syntax: *-to* /cmdname/ ?/arg .../? always absorbing all remaining arguments of the *yield* command; a precedent for this may be found in for example the *-join* option of the *glob* command. In order to meet the "at least two words" requirement for *yield* options, the /cmdname/ argument is mandatory, but this is quite natural since it is anyway to be resolved already by *yield*. Note that behavior like yielding and throwing an error can be performed by yielding to *return*: yield -to return -code error -errorcode DEMO "Just an example" *-code* and *-errorcode* here are not options of *yield*, but of *return*, although the impression is probably close to that of having them options of *yield* directly. ARGUMENT DELIVERY ------------------- An issue where no obvious solution presented itself was that of how arguments supplied to the coroutine-command should be made available in the coroutine. This proposal suggest that a *-delivery* option could be used to control this, and that the word after *-delivery* is a /mode/ keyword which selects whether the arguments should be provided as a list, a dictionary, in variables, or whatever. The following is a list of possible forms of this option. The supported modes will be one of: *dict*, *first*, *flat*, *same* and *vars*. These are described below. (Note that different modes need not have the same number of argument words.) DICTIONARY DELIVERY *-delivery dict* The return value of *yield* is a dictionary, with one entry for each formal parameter, the entries appearing in the same order as in the *-arguments* /argspec/, and the values filled in as they would be for a *proc* call. The latter implies that /default values are filled in for missing optional arguments/; this preserves the invariant for the caller that specifying the default value for an optional argument is the same as omitting that argument. Thus, for the demonstration code: proc demo {delivery} { set last "" foreach argspec { foo {{arg ""}} {{arg ""}} args {foo args} } { set last [yield -arguments $argspec -delivery $delivery $last] } return $last } one can get this interactive session: % coroutine C demo dict % C "a b" foo {a b} % C "a b" arg {a b} % C arg {} % C "a b" args {{a b}} % C "a b" foo {a b} args {} FLAT DELIVERY It is not unreasonable to also expect a mode that delivers just the list of formal parameter values, e.g. *-delivery list* but this can be obtained from dict values [yield -delivery dict ...] since the *dict* mode is specified as putting the entries in the right order. Instead there is another take on returning a list of arguments that provides extra functionality: *-delivery flat* The return value of *yield* is the list of arguments of the coroutine-command, regardless of *-arguments* /argspec/. This is the *yield* counterpart of *info level 0*, providing full introspection into the arguments, but also leaving parsing entirely up to the programmer. With the same sequence of calls as above, one gets % coroutine C demo flat % C "a b" {a b} % C "a b" {a b} % C % C "a b" {a b} % C "a b" {a b} DELIVERY TO LOCAL VARIABLES *-delivery vars* /varlist/ The /varlist/ must be a list with one element for each formal parameter. The values of the formal parameters are assigned, in sequence, to the variables named in the /varlist/. The return value is an empty string. Of course, most of the time one would use the same names as in the /argspec/, so it is reasonable to have a shorthand for this: *-delivery same* The main reason to do otherwise is if both the procedure *yield*ing and the coroutine-command has an *args* argument, since that is a magical name. Hiding implementation details from users can also be a reason to specify a different /varlist/. A further possibility that has been suggested would be: *-delivery array* /arrname/ for dumping the arguments as entries in an array. This can however be accomplished through array set $arrname [yield -delivery dict ...] which is probably easy enough. SIMPLE DELIVERY Finally, there is: *-delivery first* The return value is the value of the first formal parameter, or an empty string if there are no formal parameters; values of additional formal parameters are discarded. /This is the default./ This may seem a strange mode of delivery, and an even stranger thing to have as default, but it is what one must have if the [TIP #372] examples of varying the /argspec/ should work as claimed there. With the same demo as before, one gets: % coroutine C demo first % C "a b" ; # -arguments foo a b % C "a b" ; # -arguments {{arg ""}} a b % C ; # -arguments {{arg ""}} % C "a b" ; # -arguments args {a b} % C "a b" ; # -arguments {foo args} a b An alternative which is perhaps more convenient, but also more complicated, is to have the default *-delivery* depend on the number of formal parameters. It is then likely that one for the case of more than one formal parameter would want Yet Another delivery mode... REFERENCE IMPLEMENTATION ========================== None yet. OTHER COMMANDS ================ If coroutines are going to have *proc*-style argspecs, then it will probably make sense for *info args* and *info default* to operate on coroutine-commands as well as procedures. ACKNOWLEDGEMENTS ================== This TIP is dedicated to Alexandre Ferrieux, who's known to have written that I should prioritize TIPping things over participating in tcl-core discussions. ;-) COPYRIGHT =========== This document has been placed in the public domain. ------------------------------------------------------------------------- TIP AutoGenerator - written by Donal K. Fellows