TIP 387: Unified Yield Command Syntax

Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2017 Conference, Houston/TX, US, Oct 16-20
Send your abstracts to tclconference@googlegroups.com
by Aug 21.
Author:		Lars Hellström <Lars.Hellstrom@residenset.net>
State:		Withdrawn
Type:		Project
Vote:		Pending
Created:	30-May-2011
Tcl-Version:	8.6
Keywords:	Tcl, coroutine
Obsoleted-By:	396


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.


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 [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 yielding 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 [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.


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. ;-)


This document has been placed in the public domain.