Check-in [16ec396728]

Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Updated the tip479 spec to place the new commands for dictargs wrapping in a dedicated namespace and leave room in the spec for handling alternate argument handing schemes
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 16ec396728d2815e014fac117bd96cad827403c141c3f32e6562a4cac49b3eb7
User & Date: hypnotoad 2018-10-27 11:04:48.184
Context
2018-10-27
14:42
Editorial tidy up for TIP 518 check-in: e531f63c61 user: dkf tags: trunk
11:04
Updated the tip479 spec to place the new commands for dictargs wrapping in a dedicated namespace and leave room in the spec for handling alternate argument handing schemes check-in: 16ec396728 user: hypnotoad tags: trunk
11:03
Tip495 revision. check-in: 4304dd5b57 user: hypnotoad tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to tip/479.md.
1
2
3
4
5
6
7
8
# TIP 479: Add Named Procedures as a New Command in Tcl (procx)
	Author:         Sean Woods <[email protected]>
	State:          Draft
	Type:           Project
	Vote:           Pending
	Created:        23-Oct-2017
	Post-History:
	Keywords:       Tcl,procedure,argument handling
|







1
2
3
4
5
6
7
8
# TIP 479: Add Named Procedures as a New Command in Tcl (dictargs::proc)
	Author:         Sean Woods <[email protected]>
	State:          Draft
	Type:           Project
	Vote:           Pending
	Created:        23-Oct-2017
	Post-History:
	Keywords:       Tcl,procedure,argument handling
31
32
33
34
35
36
37
38











39
40

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
option database. Also, those option/value pairs go directly into C data structures,
they at no time feed a script with local variables.

# Specification

Currently if the last parameter named *args* is given a default value, that information
is essentially ignored by the workings inside of tclProc.c. This spec calls for storing
a dict stored where a default would go for the args parameter. Each key in the dict











will be the name of a local variable that the parameter will populate. The values in the
dict wil be a key/value set of configuration option for the named parameter system.


Developers can specify if a named parameter has a default if not given. They can also
specify if the parameter is required. A non-mandatory parameter will not be mapped to
a local variable, but it is understood that developers may want to advertise their
presence for documentation purposes.

		proc foo {{{args {
		   bar {default: baz}
		}}}} {
		   puts $bar
		}
		% foo
		> baz
		% foo bar bing
		> bing

The primary use case for this tip are new functions that take only named parameters. This
TIP also calls for the creation of a new command, provisionally named *procx*. _procx_
will accept the same number of arguments as _proc_, but the format for the argument
spec will be a dict instead of a list. For TclOO methods, we will also create an
methodx as a shortcut to:

		oo::define myclass method namemethod {{{args {
		   bar {default: baz}
		}}}} { ... }

The workings of procx and methodx are essentially:

		proc procx {name argspec body} { proc $name [list [list args $argspec]] $body }

Named parameters are designed to be deliverable in any order. Named parameters
can also be omitted. As such it is necessary to provide more introspection than the
current incarnation of **proc** provides. If the procedure requires
more than the default behavior or populating local variables with the argumemnt given,
it can introspect with **$args** with **\[dict exists\]** to detect if an argument was
given at all. The argument spec that was given by the developer will also be available
as the local variable **argspec**.

		% procx p {a {default: {}}} { return $args }
		% p
		a {}
		% p foo bar
		a {} foo bar

Given these rules, we can get into non-intuitive failures. For instance, what would
happen if a default value was not provided for an argument, and the command call
does not provide one.

		% procx q {a {default: {}} b {}} {
		  # Proc assumes b will be given
		  set result [list $a $b]
		  foreach {f v} $args {
		    if {$item ni [dict keys ${argspec}]} {
		       lappend result $item [dict get $args $item]
		    }
		  }
		}
		% q
		error: No such variable b ; # Stack trace points to first use if $b
		% q b foo bar baz
		a {} b foo bar baz

It would be helpful to have that field (b) treated as a mandatory argument,
and have the error thrown in the procedure's argument parser, not in the procedure
itself. After some play testing with the rules I have worked out what properties will
be tracked for each named parameter, and how those properties are calculated if not
specified directly in the options.

## Configuration Options for Named Parameters

The following general rules apply to all values given in the configuration dict for
a named parameter.

1. If a value for any option is given as an empty string, it is inferred to be null.
2. A variable will be created with the same name as the named parameter.
3. A field that is given, but not otherwise reserved by the spec, is ignored by the parser but made available for introspection via the **argspec** dictionary.







|
>
>
>
>
>
>
>
>
>
>
>
|
|
>






|

|








|


|





|

|









|









|



















|







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
option database. Also, those option/value pairs go directly into C data structures,
they at no time feed a script with local variables.

# Specification

Currently if the last parameter named *args* is given a default value, that information
is essentially ignored by the workings inside of tclProc.c. This spec calls for storing
information where a default would go for the args parameter. This information is a list
with two fields: *namespace* and *spec*.

*namespace* is a Tcl namespace which contains the command *parse*. Parse accepts two
arguments, a specification and list containing the contents of *args*. How that
specification is interpreted is left to the imagination of the developer.

This spec defines a reference implementation called dictargs.

# dictargs

Under dictargs the specification for non positional arguments is a dict. Each key in the
dict will be the name of a local variable that the parameter will populate. The values
in the dict will be a key/value set of configuration option for the named parameter
system.

Developers can specify if a named parameter has a default if not given. They can also
specify if the parameter is required. A non-mandatory parameter will not be mapped to
a local variable, but it is understood that developers may want to advertise their
presence for documentation purposes.

		proc foo {{{args {dictargs {
		   bar {default: baz}
		}}}}} {
		   puts $bar
		}
		% foo
		> baz
		% foo bar bing
		> bing

The primary use case for this tip are new functions that take only named parameters. This
TIP also calls for the creation of a new command, provisionally named *dictargs::proc*. _dictargs::proc_
will accept the same number of arguments as _proc_, but the format for the argument
spec will be a dict instead of a list. For TclOO methods, we will also create an
dictargs::method as a shortcut to:

		oo::define myclass method namemethod {{{args {
		   bar {default: baz}
		}}}} { ... }

The workings of dictargs::proc and dictargs::method are essentially:

		proc dictargs::proc {name argspec body} { proc $name [list [list args $argspec]] $body }

Named parameters are designed to be deliverable in any order. Named parameters
can also be omitted. As such it is necessary to provide more introspection than the
current incarnation of **proc** provides. If the procedure requires
more than the default behavior or populating local variables with the argumemnt given,
it can introspect with **$args** with **\[dict exists\]** to detect if an argument was
given at all. The argument spec that was given by the developer will also be available
as the local variable **argspec**.

		% dictargs::proc p {a {default: {}}} { return $args }
		% p
		a {}
		% p foo bar
		a {} foo bar

Given these rules, we can get into non-intuitive failures. For instance, what would
happen if a default value was not provided for an argument, and the command call
does not provide one.

		% dictargs::proc q {a {default: {}} b {}} {
		  # Proc assumes b will be given
		  set result [list $a $b]
		  foreach {f v} $args {
		    if {$item ni [dict keys ${argspec}]} {
		       lappend result $item [dict get $args $item]
		    }
		  }
		}
		% q
		error: No such variable b ; # Stack trace points to first use if $b
		% q b foo bar baz
		a {} b foo bar baz

It would be helpful to have that field (b) treated as a mandatory argument,
and have the error thrown in the procedure's argument parser, not in the procedure
itself. After some play testing with the rules I have worked out what properties will
be tracked for each named parameter, and how those properties are calculated if not
specified directly in the options.

## Configuration Options for Dictargs Named Parameters

The following general rules apply to all values given in the configuration dict for
a named parameter.

1. If a value for any option is given as an empty string, it is inferred to be null.
2. A variable will be created with the same name as the named parameter.
3. A field that is given, but not otherwise reserved by the spec, is ignored by the parser but made available for introspection via the **argspec** dictionary.
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
Type: value
Default: null

When non-null, if a named parameter is missing from the call assume the value specified.

Example:

		% procx u {
		  a { default: {A useful value} }
		} {
		  puts $a
		}
		% u
		> A useful value
		% u a {Less usefull}







|







138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
Type: value
Default: null

When non-null, if a named parameter is missing from the call assume the value specified.

Example:

		% dictargs::proc u {
		  a { default: {A useful value} }
		} {
		  puts $a
		}
		% u
		> A useful value
		% u a {Less usefull}
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
the following rules apply to aliases:

1. If a parameter with the canonical name is given, alternatives will be ignored.
2. If multiple non-canonical parameters are given, the last value given will be the value for the canonical field.

Example:

		% procx u {
		  a {
		    default: {A useful value}
		    aliases: {alpha A}
		  }
		} {
		  puts [list $a $args]
		}







|







160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
the following rules apply to aliases:

1. If a parameter with the canonical name is given, alternatives will be ignored.
2. If multiple non-canonical parameters are given, the last value given will be the value for the canonical field.

Example:

		% dictargs::proc u {
		  a {
		    default: {A useful value}
		    aliases: {alpha A}
		  }
		} {
		  puts [list $a $args]
		}
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

This TIP will be rolled out in 3 stages.

## Stage 1 - Pure Tcl (finished)

Stage 1 is a pure-tcl implementation to allow the community to try out the rules and
see if this new concept is a good fit for Tcl. The implementation will use a macro-like
template to place the implementation rules as a pre-amble to the body given to procx or
methodx. This implementation will affect line numbers in traces, but should provide
the new features without introducing a major impact on performance.

This implementation has been posted to the http://wiki.tcl.tk/49071 and will be kept up
to date.

## State 2 - C Extension (finished)








|
|







193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

This TIP will be rolled out in 3 stages.

## Stage 1 - Pure Tcl (finished)

Stage 1 is a pure-tcl implementation to allow the community to try out the rules and
see if this new concept is a good fit for Tcl. The implementation will use a macro-like
template to place the implementation rules as a pre-amble to the body given to dictargs::proc or
dictargs::method. This implementation will affect line numbers in traces, but should provide
the new features without introducing a major impact on performance.

This implementation has been posted to the http://wiki.tcl.tk/49071 and will be kept up
to date.

## State 2 - C Extension (finished)

205
206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221
222
223
224
225
226
      cd tip479
      fossil clone http://fossil.etoyoc.com/fossil/tip479 tip479.fossil
      fossil open tip479.fossil
      tclsh make.tcl all

## State 3 - Core patch

Stage 3 will introduce named parameters as a feature of the core. Modifications
have been checked into the Tcl core as the tip479 branch.


At present if the last positional argument is named *args* a default value
for that field, while recorded, is ignored. We will utilize that space to
overload what arguments the procedure will accept beyond the positional arguments.

Within tclProc.c are modifications to read that argument spec (if given).

### TclCreateProc

In TclCreateProc, if an named argument spec was registered for the function, additional
*localPtr* entries are registered beyond *args* to hold the expected values added by
the spec.








|
|
>

<
|
<

<







217
218
219
220
221
222
223
224
225
226
227

228

229

230
231
232
233
234
235
236
      cd tip479
      fossil clone http://fossil.etoyoc.com/fossil/tip479 tip479.fossil
      fossil open tip479.fossil
      tclsh make.tcl all

## State 3 - Core patch

The original specification required modifications to the core. These modifications,
while they did not seem to impact normal operations, also did not seem to actually
improve performance over the pure-tcl or C compiled extension.


The modifications




### TclCreateProc

In TclCreateProc, if an named argument spec was registered for the function, additional
*localPtr* entries are registered beyond *args* to hold the expected values added by
the spec.

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
named argument spec registered. If a spec was present, after it performs its
normal population of local variable matching the parameters for the procedure,
the function calls *ProcEatArgs* which pairs key/value pairs beyond the last positional
parameter with registered name parameters (and their associated local variable.

### init.tcl

Two macros for procx and methodx are placed in the init.tcl file to
ensure those facilities are present at runtime. The community is presently
in a debate as to whether seperate commands are the best approach, or modifying
proc and oo::define::method to accept flags is better.

### info argspec

In the info namespace, add an *argspec* command to return the named argument spec for







|







250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
named argument spec registered. If a spec was present, after it performs its
normal population of local variable matching the parameters for the procedure,
the function calls *ProcEatArgs* which pairs key/value pairs beyond the last positional
parameter with registered name parameters (and their associated local variable.

### init.tcl

Two macros for dictargs::proc and dictargs::method are placed in the init.tcl file to
ensure those facilities are present at runtime. The community is presently
in a debate as to whether seperate commands are the best approach, or modifying
proc and oo::define::method to accept flags is better.

### info argspec

In the info namespace, add an *argspec* command to return the named argument spec for
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
    }

## Extra Metadata for argument handling

The standard only defines the behavior for several properties for parameters, but
does not prohibit storing additional properties. Consider:

    procx prettyproc {
      color {
        comment: {Color of the button}
        type: color
        default: green
      }
      flavor {
        comment: {What flavor candy emerges when pressed.}







|







311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
    }

## Extra Metadata for argument handling

The standard only defines the behavior for several properties for parameters, but
does not prohibit storing additional properties. Consider:

    dictargs::proc prettyproc {
      color {
        comment: {Color of the button}
        type: color
        default: green
      }
      flavor {
        comment: {What flavor candy emerges when pressed.}
324
325
326
327
328
329
330











331
332
333
334
335
336
337
338
339
         if {$flavor ni $flavors} {
            error "Invalid flavor $flavor. Valid: random OR $flavors"
         }
      }
    }

# Spec Revisions












2017-11-03 - Renamed @proc to procx, @args to argsx and @method to methodx. Removed
the internal variable @spec from bodies. Added

2017-10-28 - Removed the *variable* option. The core implementation really needs
a canonical name for the variable in the index. If a user really wants a different
name in the arguments, we have the aliases facility. Also clarified that this
tip will be modifying how a default for the args parameter is interpreter and
that procxs and methodx are just convenience wrappers.







>
>
>
>
>
>
>
>
>
>
>








|
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
         if {$flavor ni $flavors} {
            error "Invalid flavor $flavor. Valid: random OR $flavors"
         }
      }
    }

# Spec Revisions
2018-10-19 - Replaced the idea that this tip would be providing the only means of
named argument parsing with the concept that this tip would provide the shims for
developers to provide such a mechanism, as well as provide a reference implementation
called "dictargs".

Renamed procx to dictargs::proc and methodx to dictargs::method, and clarified that
argument parsing will be performed by dictargs::parse

Removed the core patches, and instead casting this TIP as an information one for
developers who wish to either exploit dictargs or utilize their own non-positional
argument processing scheme.

2017-11-03 - Renamed @proc to procx, @args to argsx and @method to methodx. Removed
the internal variable @spec from bodies. Added

2017-10-28 - Removed the *variable* option. The core implementation really needs
a canonical name for the variable in the index. If a user really wants a different
name in the arguments, we have the aliases facility. Also clarified that this
tip will be modifying how a default for the args parameter is interpreter and
that procx and methodx are just convenience wrappers.