[AMG]: As you might imagine, I like Tcl quite a lot. Most of all I appreciate how "stupid" it is. Heh, let me explain what I mean by that. Unlike modern, enlightened languages, it doesn't have preconceived notions about mathematics, I/O, control structures, definition of data structures, object orientation, or graphical user interfaces. It's too stupid. But that's to its credit, because the reality is that ''all'' computer programming languages are too stupid--- too stupid to know what ''I'' need when it comes to OO, GUIs, or structs. And since Tcl recognizes this, it doesn't get in my way. Sure, something like C++ or Java may suit the needs of many, but they have the side effect of shaping and constraining the programmer's thought processes. With Tcl, it's the other way around. :^) The ''language'' is stupid, and the ''programmer'' is smart. The programmer gets to shape the language. What a concept... Now of course Tcl comes with a standard set of commands that implement most of the above, but (1) they're optional, (2) they can be replaced, (3) they're not part of the language proper, and (4) in most cases they can be redefined within the language. Want a Java without classes or exceptions? Tough. But I have a few complaints about using Tcl. They're not really problems with Tcl itself, just things I wish I had. Heh, this is me renege'ing on my stated desire to have the language be as simple as possible. But not really; I'm most interested in ''maximizing programmer freedom.'' [NEM] ''26 Aug 2006'': I have a few comments, which I've mixed into the flow of text below, marked by my name. I agree with quite a lot of what you've written here. A while ago I wrote up some notes on how I'd like Tcl 9 to look [http://www.cs.nott.ac.uk/~nem/fanatcl.html]. My thoughts have moved on since then, but the core ideas remain similar. [AMG]: Have I influenced you? I had hoped some of my ideas were new, or at least new to the Tcl crew, but it's amazing how similar your paper is to my article. I wonder why we were thinking along the same lines. I haven't finished reading it, though. It's midnight, after all, and tomorrow morning I'll be putting rafters on a new carport we're building. Need rest... ---- Let's start by identifying things which I think every practical language needs. * '''State''' Even an interpreter-less language like [execline] supports state, albeit with the aid of the filesystem. [lisp] without '''defun''' or '''set''' would literally only support one-line programs; that is to say, each line may as well be a separate program. * '''Side effects''' With state comes the need to modify state, and that means side effects, or a command doing more than merely returning a value. Of course, side effects should be documented, minimized, and isolated from functional behavior. * '''Named objects''' The most important side effects are creation and modification of objects in the interpreter state. These object must have names in order to be referenced from one line to the next. * '''Namespaces''' Names must be unique, so they occupy slots in a namespace. But these days it would be unreasonable to require that even temporary variables be globally unique, especially when those temporary variables are used within recursive functions. Therefore it's important to have multiple namespaces, for instance one per each frame on the call stack. And if there is to be a global namespace, it's necessary to support accessing objects in namespaces other than local. * '''Functions and procedures''' (To me, a function has no side effects and a procedure does.) Behaviors need to be packaged and named in order to be used more than once. More than that, it's necessary to have a basis on which new behaviors can be implemented. Just try doing ''anything'' with an [interp] with no commands! * '''Extensibility''' It's not possible to define everything using the language itself. A minimal set of functionality needs to be "bootstrapped" using a lower-level language then exposed to the interpreter. Gotta have a basis for recursion. Even machine code can be extended through new circuitry, which is a language expressed in copper and silicon. If it's possible to do without any of the above and still be able to do more than just 2+2, I'd like to hear about it. [NEM]: You don't need state or side-effects. The lambda calculus for instance is entirely stateless, yet is Turing complete. Side-effects ''are'' needed if you want to interact with the world, though. However, [Haskell] even manages to handle this in a clever way using monads: instead of directly performing side-effects you ''construct'' an imperative program. This separates things into two parts: * A ''pure'' (side-effect free) functional program that constructs the monad; * An imperative program that when run will perform the side-effects. This way all the side-effects are confined in the constructed program (monad) and most of your code can remain (provably) side-effect free. It's a very interesting approach, but I'd still probably just put state directly into the language (in a manner similar to [ML]). [AMG]: That reminds me of a cheat I sometimes do with [Makefile]s. The Makefile has a foo.mk target which depends on some kind of configuration file (for example). foo.mk is built by some inline shell script, external program, or combination of the two that reads the configuration file and outputs the data formatted for [make] (i.e. variable and target definitions). Then I say "'''include foo.mk'''" (surrounded by some mumbo jumbo that avoids rebuilding foo.mk only to immediately delete it when doing "make clean"). Before reading any more of the the Makefile, make checks foo.mk's dependencies, possibly (re-)generates it, and parses the resulting file as if hard-coded into the original Makefile. To summarize, this lets me embed un-makelike processing in a Makefile. It works great, but boy is it weird. ---- Now for features I'd like to have in a language... * '''Tcl syntax and semantics''' Words are strings of non-metacharacters. Words are separated by whitespace. Metacharacters can be quoted to disable their special meaning. Command invocations are performed by giving the command name as the first word and any number of argments as subsequent words. Scripts are sequences of commands delimited by semicolons and newlines. Comments extend from an unquoted '''#''' given as the first word to a newline not preceded by an odd number of backslashes. Metacharacters can be used to perform substitutions, including variable and command substitutions. Metacharacters appearing in substitution results have no special meaning. Individual words can be split into multiple words using a special directive. Variable expansion can never result in the variable being modified. Shining ideal: "everything is a string." * '''Lambda type''' In addition to Tcl's standard string, list, dict, and script types, I'd like a lambda type. Maybe call it a procedure type. Either way, it looks like a two-element list. The first element is a list of parameter names, and the second element is a script to be executed in a new call frame with the arguments appearing as locals with names drawn from the parameter list. This is much like the second argument to [[[proc]]], but the details can vary. Perhaps I might want an optional list of [static variables], transforming [lambda]s into [closures]. Or I could go even farther and have a procedure-in-execution type which is returned by [['''yield''']] or made available to an [['''interp''']] resource limit exception handler. It would contain the instruction pointer and all local variables and could be resumed at any time, possibly multiple times, or maybe even after being read in from disk. Exciting! * '''Optional [[apply]] command''' The [['''apply''']] command's first argument is a lambda and any remaining arguments are passed to the lambda in execution. It creates a new stack frame containing all arguments as local variables then starts executing it, collecting a return value to pass up the stack to the caller. To create a procedure, bind its implementation (a lambda) to a name (i.e. create a variable), then expand the variable as the first argument to [['''apply''']], for instance '''apply $foo'''. But how is [['''apply''']] itself executed? Well, the first word on the line (after expansion) is treated as the name of a lambda object, which is executed using [['''apply''']]'s back-end implementation (to avoid infinitely looking up the name "apply"). In the end, the [['''apply''']] command itself is just a formality, and honestly may not even be worth exposing at the script level. I'm considering dropping it from this page, since even though a lambda is supposed to be anonymous, in order for it to do any good it'll have to be bound to a variable at some point, often through argument passing. And once that happens, it can be called through ordinary command name lookup. The only other thing that comes to mind is running a hard-coded script in a new stack frame without storing it in a variable first. There's not much difference between a new stack frame and a new, temporary namespace, so just use [['''namespace''']], but I'm getting ahead of myself. This command is like the Tcl [[[apply]]] command except that it doesn't support the optional [namespace] element in the lambda. [NEM]: Seems very sensible. If I was writing a Tcl 9 now, I'd replace the namespace element with a dictionary. That gives you the ability to create (immutable) closures (all elements in the dictionary become variables in the new scope). You can't do mutable statics/closures if you want to maintain value semantics for lambdas. Not all uses of lambdas are through variables. You want to allow things like: [[[[get-lambda...]] args...]] [AMG]: Mutability is possible when not using [['''apply''']], since the interpreter is given the lambda (procedure-typed) variable ''name'', not just its value. Another reason to drop [['''apply''']]? Also think about my [['''yield''']] suggestion, where the procedure invocation returns its execution status, frozen in time. To make generators, a certain named (or indexed) element of the intermediate status could contain the (optional) argument passed to [['''yield''']]. Or maybe the name of a result variable could be passed to the procedure when first started, but I'd prefer to avoid this if possible. I bring this up because it provides the illusion of mutability even when using [['''apply''']], but the caller would have to cooperate by storing and reusing the execution state object. * '''Unified namespaces''' Variables, functions, procedures, file handles, GUI windows, interpreters, etc. all contend for names within a single namespace. In Tcl, it's legal to have a [proc] and a variable share a name. I don't like this, because it means a different set of methods must be used to manage objects in each different namespace. Hence [[[unset]]], [[[destroy]]], [[[rename]]], [[[close]]], and many more. One precondition for placing multiple things in the same namespace is that they must at some level be the same thing. Use the previous sentence as an example: both variables and procedures are things, or else I wouldn't have been able to write the sentence! Variables and procedures are both named objects. The name goes in the namespace, and the object is the name's referent. [NEM]: Agree 100%. [AMG]: Things get fun when objects don't have string representations that map bijectively to the "real" value, such that a conversion to a string and back won't hurt. Floats have this problem sometimes, but the error introduced is usually negligible. But what about, say, a command implemented in C? What would [['''puts''']]'ing it do? If it's written to a file, put up on a Web site, downloaded a week later, and re-read into a variable by another computer, will that variable still be usable as a command? This comes to mind because it's a much more visible problem now. Tcl doesn't actually live by its "everything is a string" motto, unless "everything" is only the non-array variables. [[[array]]]s can be converted to and from [[[dict]]]s with [[[array get]]] and [[[array set]]]. [[[proc]]]s aren't strings, but they can be converted through multiple [[[info]]] inquiries, and this fails for C commands. [[[image]]]s aren't strings, but [[[image] '''data''']] and [[[image] '''put''']] can be used to bridge the gap. And who knows what's inside a channel? File name, open mode, seek position, configuration, buffer, etc. Moving all these non-string things into the same namespace as variables means they need to be made to play by the same rules as variables. And the first two rules are that everything is a string. * '''Transparent containers''' Lists and dictionaries are cool, but they'd be cooler still if their contents could be accessed, manipulated, and deleted in-place without the need for special commands. To make this work, object names should include optional indexing suffixes. For instance, this would make it possible to read or change the second-to-last element of a list without [[[lindex]]] or [[[lset]]]. Ditto for the "foo" element of a dict, sans [[[dict] '''get''']] or [[[dict] '''set''']]. These suffixes can be mixed and matched to, e.g., access the third element of the "foo" element of the second-to-last element of a list. Also the index values should be subject to substitution rules, making it possible to do math or indirection without resorting to the underlying commands (like the need to use [[[set]]] directly when the variable name is stored in another variable). These things can get arbitrarily fancy. [NEM] Could you expand on this/give an example? [AMG]: I avoided examples because I hadn't covered the syntax yet, but I take requests. :^) '''puts $global(argv){0}''' or '''puts ${global argv}{0}''' should print out the program name, same as '''puts [[list get [[dict get $global argv]] 0]]'''. As in Tcl, '''set alpha(m) mike''' should set the m'th element of the alpha dict (or namespace!) to mike, but '''set [[dict get $alpha m]] mike''' won't cut it. '''dict set alpha m mike''' would be needed, and that's a bit on the clumsy side. Let's not have wildly different procedures for setting versus getting. In Tcl, it's possible to use [[[upvar]]] to bind to an array element as well as an ordinary variable. Why not a dict element or a list element? What if a procedure expects a variable name rather than a value, and the data you're trying to send and/or modify is contained in a dict or list? In Tcl, it would be necessary to copy it into a new, temporary variable then copy it back after the procedure is done. If a name can be qualified with namespaces (and array keys in Tcl), why not dictionary keys and array indices? Why not all three at the same time? I'd like to minimize the number of commands (or subcommands) that can read and modify variables. And if I can't do that, I'll settle for putting in language features that steer everyone toward a single [['''set''']] command. * '''Dictionary-like [namespace]s''' Namespaces shouldn't be a separate type with names in a separate, ahem, namespace, as they are in Tcl. At heart, they're not much more than dicts. How about having a special variable named '''$local''' that expands to the local namespace? It would be formatted as a [dict], a list with alternating key/value elements, where the key is the variable name and the value is the object. Going further, how about '''$global'''? Still more: '''$caller''' can be the namespace of the calling function, or equal to '''$global''' at the second level of the call stack. And another: '''$parent''' can be the parent namespace, like '''::foo''' is the parent [namespace] of '''::foo::bar''' in Tcl. (But I don't think '''$parent''' namespaces will necessarily have '''$caller''' elements because they may have been created at the top level.) Namespaces wouldn't be equivalent to dictionaries, though. '''$local''', '''$parent''', '''$caller''', and '''$global''' would all have to be in the local namespace (well, in every namespace), but they can't appear in the string representation to avoid infinite recursion. I'll say a bit more on this later on. Oh, and since namespaces are not 100% the same type as dicts, they can have a different "transparent container" access syntax, like how Tcl separates namespace components with ::'s. Yeah, this is a blatant [Python] rip-off. :^) [http://www.ibiblio.org/obp/pyBiblio/poetry.php] [NEM]: My current thought is that I'd make all naming be done by dicts: namespaces, stack frames, etc. Thus all names would be immutable. I'd also have a single global reference heap (accessed via a ref/var command) for mutable references which support assignment and traces. Ideally they'd be GC'ed, but to do that efficiently implies a move away from [everything is a string] which would be a different language. * '''Non-local name resolution''' If a name isn't found in the local namespace, search for it in each parent (not caller) namespace, then search in global. This facilitates cheap-o inheritance and possibly obviates [[[variable]]]. Instead of [[[upvar]]], one can explicitly look for the variables in the caller namespace. Looking in global subsumes [[[global]]]. [[[uplevel]]]'s behavior can implemented by a command that executes a block of code in a different namespace, then specifying caller or global as the other namespace. Gotcha: when a variable is being written to, if it doesn't already exist, it will be created. This suppresses non-local name resolution. This might be unacceptable behavior. I don't know; I'll have to think about it some more. Maybe this is where [[[variable]]] is needed, except under the guise of [['''bind''']] (see below). Some applications may want more control over name resolution. Perhaps each namespace can have an [['''unknown''']] entry to catch this. But would it work only for command invocation or would it also do ordinary substitution and assignment? Also I don't like reserving the name "unknown". This also will take some thinking. [NEM]: By the first sentence, do you mean static/lexical scoping? I'd probably remove upvar and notion of the stack from Tcl, to facilitate things like tail-call optimisation. Instead, I'd adopt something like 3-Lisp's reflective procedures (which I'd put in a package called "meta"): meta proc bind {env next name value} { next [dict replace $env $name $value] } bind foo 12 Here the proc gets passed two extra parameters: $env is the environment dictionary of the ''caller'', and $next is the [continuation] which receives an environment as its first parameter. This is an incredibly powerful construct which allows you to implement almost any possible language construct. (Search for a paper called something like "Reflection and Semantics in Lisp" by Brian Cantwell Smith for details). I'd put it in a separate "meta" package so that the package require documents that crazy meta-programming is coming up. I'd also put a [[meta class]] construct in this package for the built-in object system... :) [AMG]: Static scoping. Remember, the caller namespaces aren't checked, only the local, parent, and global namespaces. A species of dynamic scoping can be done on a case-by-case basis by explicitly using the caller namespace. I'll have to do some more thinking about tail call optimizations. Maybe I can lie a little about the stack by collapsing/reusing certain frames when they won't be needed again yet maintaining the chain of caller namespaces. Making a new stack frame should be cheap, though. I hope. :^) Continuations are cool, too, and I can see why tail-calling into them would be important. Don't want an iterative process to eat up stack! ---- Possibly bad ideas: * '''Ensemble commands''' Ensemble commands could be implemented by nested namespaces (or dictionaries, if you prefer) containing lambdas at the leaf nodes. Command name lookup could be extended to first look inside namespaces, borrowing arguments as subcommand (i.e. child namespace) names. But there's a fundamental problem here. A lambda looks like a single-element dictionary with the parameter list as the key. Well, I guess the lookup could back off one level when it discovers that what it thought was the value is actually a script. Except that the script might also look like a valid dictionary therefore namespace. Hmm. Hmmmmm. * '''Variable binding''' Tcl's [[[upvar]]] command can be used to create an alias for a variable, and [[[interp alias]]] is good for making alternate ways of calling a command. I'd like similar functionality, but since I unified namespaces I'll have to do it all with one command: [['''bind''']] (or maybe [['''var bind''']]). It takes an existing name and a new name to create. Both names will refer to the same object, such that after using one name to change the object, the change will be reflected when accessing the object with the other name. This is distinct from setting one variable equal to another ('''set a $b'''), wherein both names refer to the same object only until one of them is used to change the object, after they point to different objects. Deleting a bound name simply decrements the object's reference count. Each namespace's "local" element is linked to that namespace again. Similar goes for parent, caller, and global. But these elements can't appear in the namespace's string representation because of the infinite loop. Binding in general makes this problem possible. How can a namespace's string representation show binding? I'd prefer that it be possible to keep the links even through converting to and from a string, as may happen if writing the interpreter state to disk then reading it back later. I don't know. * '''Futures''' I'd like for expression evaluation to be put off as long as possible. Doing '''set x $(2+2)''' shouldn't trigger an immediate evaluation of 2+2; instead, '''$x''' should be set to an expression object whose string representation, ''when calculated'', will be 4. But how does this interact with variable binding? Futures work fine (I think) with copy-on-write, since all they need to do is increment reference counts until evaluation is performed; that way they'll hold on to the original object. I guess that's the answer. Resolve the names immediately, and track the objects. Names come and go, but objects remain pinned until garbage-collected. Also I don't know if this would offer a performance increase. It gives some opportunity to detect and cache common subexpressions. Maybe that's worth something. [NEM]: Futures are cool for concurrency as well as lazy evaluation. See e.g., Alice ML's various types of future: [http://www.ps.uni-sb.de/alice/manual/futures.html]. I'd love to have that in Tcl, but I'm not sure what the best form would be. [Colin McCormack] did an implementation of lazy [future]s, which seems about right. ---- I want so much syntax sugar that I'll list it as a separate category. Hyperglycemia!! * '''Basic $variable substitution''' Even in Tcl, this is syntax sugar for [[[set]]], so I here list it under this category. Since I'm considering calling the "set" command [['''var get''']] and [['''var set''']], it's good to have an abbreviation that's usable in the vast majority of cases, but I'll just say [['''set''']] for the rest of this article. Example: '''$hello''' * '''Math $(2+2) substitution''' [[[expr]]] is needed very often, but it's a bit much to type out. Compare '''$(2+2)''' versus '''[[expr {2+2}]]'''. Three characters of "overhead" versus nine! That's quite a lot. Many are tempted to drop the braces, and some don't even know that they're important. To avoid the potential security hole, I suggest providing and recommending a shorthand that does the right thing in the majority of cases. Beginners will stick to '''$(2+2)''' because it's easy to type, shows up early in the tutorial, and is very common in example code published online. More experienced programmers will use [[[expr]]] only for the case of variables containing math expressions rather than simple numeric values. I think it's good language design to make the best practice coincide with the path of least resistance. * '''`Backtick list expansion''' I think [{expand}] is acceptable syntax for Tcl, but a new Tcl-like language is free to use something shorter. For now I'll go with backtick since that seems to be a relatively popular option in [DISCUSSION: Argument Expansion Syntax]. Since it's easy to type, maybe it'll be used more freely, and no one will ask for implicit expansion (e.g., [[[lindex]]]). It is a bit hard to see, but it's used so very infrequently that stealing it as a metacharacter won't hurt anyone. I wonder if there's anything to gain from allowing multiple backticks. Perhaps each extra backtick can flatten the list one level. But what about completely flattening the list? Is that a useful feature to have? Selective flattening makes more sense, because at the leaf levels there may be strings containing embedded whitespace, but the language won't know they're not supposed to be lists. And something complex like this is best spelled out, not left to shorthand. This functionality isn't really sugar, but the shortened name is. * '''(List construction)''' Tcl's [[[list]]] command is underutilized by new programmers, who instead tend to construct lists by hand using quoted strings. This can be disastrous when substitution is thrown in the mix. But it does have the advantages of not needing a leading word ("list") and not requiring that newlines and semicolons be backslashed, so it's seductive even to experienced programmers, especially when automatic expansion is desired and [{expand}] is so cumbersome to type. To solve all these problems, I suggest list construction using parentheses. '''(foo bar $quux)''' would be equivalent to Tcl's '''[[[list] foo bar $quux]]''', with the added bonus that such a list can span multiple lines without backslashes. Also semicolons would not need backslashes, so it can easily be used to produce a script with multiple commands. (Wait, is this business with semicolons a good idea?) To get expansion, just use '''`'''. This means list [concat]enation is accomplished through '''(`$a `$b)'''. Selective expansion is sometimes needed, as in the case of '''$a''' being a program name and '''$b''' being a list of arguments, yet programmers often just say [[[exec]] '''$a''' '''$b''']] and hope for the best. Now this can be had through '''($a `$b)'''. Lastly, this frees up the word "list" so that it can be used as a command ensemble, so [[[llength]]] can become [['''list length''']], etc. An empty list is '''()''' or '''""''' or '''{}''', with the exception that the first is a [pure list]. * '''${Namespace-qualified variable} substitution''' A variable in another namespace can be expanded by placing a list of qualifiers in braces following the dollar sign. Example: '''${global hello}''' The list contained in the braces is subject to substitution and expansion. To get Tcl-style '''${foo bar}''' behavior, where the variable name actually contains a space, use '''${{foo bar}}''. A practical use would be a procedure that needs to get a variable whose name is itself in a variable passed as an argument. That variable is named with respect to the caller's namespace, so "caller" needs to be prepended to the variable name. But the variable name might itself be a qualified list, so it should be expanded: '''${caller `$name}''' Note: this is not strictly sugar. A namespace-qualified variable name can be passed to [['''set''']], as can an unadorned variable name or a name with any of the following indexing suffixes attached. Namespace-qualified variable names, therefore all variable names, are lists. If a variable name contains special characters that shouldn't be interpreted as part of a list, they need to be somehow quoted. * '''$List{indexed} substitution''' An individual element of a list can be named by appending a zero-based index to the variable name. This works for '''$''' substitution and things like [['''set''']]. The index may be also be of the form "end" or "end-n", where '''n''' is a nonnegative integer. Examples: '''${global argv}{0}''' '''$argv{0}''' '''$matrix{1}{2}''' Several more types of list substitution are available. If a two- or three-element list appears between the braces, the substitution expands to a new list whose elements are selectively copied from the list contained in the named variable. The output elements come from input elements with indices equal to the output of the Unix '''seq''' program [http://www.die.net/doc/linux/man/man1/seq.1.html] if given those same two or three elements. Examples: '''$argv{1 end}''' '''$dict{1 2 end}''' '''$dict{2 2 end}''' '''$list{end 0}''' All these forms can be used to name individual list elements. For example, [['''set var{1} hello''']] replaces the second element of the variable named var with the string "hello". [['''set var{1 end} hello''']] truncates var's value to a two element list, the second element of which is '''hello'''. [['''set var{0 2 end} hello''']] replaces the first element with "hello" and deletes every even-numbered element after that. I admit these list range and strided range rules may be a bit complex. Maybe I'll drop them. But they're here for now. * '''$Dictionary(indexed) substitution''' Similar to list substitution, individual dictionary elements can be referenced, modified, and extracted by placing the key name in parentheses after the variable name. Remember that multiple indices (both list and dictionary) can be applied. Examples: '''$global(argv){0}''' '''$images(smiley)(-width)''' '''$users($id)(avatar)''' By the way, I'm not committed to the syntax. At this point, '''$[[2+2]]''' is just as possible as '''$(2+2)'''. [NEM]: I quite like most of these, although I'm not sure I'd want that much sugar. [AMG]: The qualification and indexing aren't really sugar since they're done as part of the variable names rather than as extra "arguments" to the '''$''' "operator". The ranges and strides can be dropped, I guess, but they could be useful substitutes for [[[lreplace]]]. That leaves us with math and list construction. Not so sugary after all, I think, but still more than Tcl. ---- '''Examples''' First, let's make a procedure. set greet {{} {stdout write hello}} Next, invoke it in a variety of ways. stdout write hello `[list get {{} {stdout write hello}} 1] `[list get $greet 1] `$greet{1} apply {{} {stdout write hello}} apply $greet greet If using [['''set''']] is cumbersome, make a procedure factory. set proc {{name args body} {set (caller `$name) ($args $body)} You'll notice that the name "args" isn't special. It never is, the way I'd like for things to work. Instead, prefix a parameter (any one parameter!) with an *asterisk, and it'll receive any extra arguments as a list after all those before it and after it have been assigned. The asterisk isn't part of the parameter name. caller needs to be prepended to the name variable in order for it to be created in the caller's namespace. To make this [['''proc''']] even more Tcl'ish, use "global" instead of "caller". Now let's use it. proc greet {} {stdout write hello} Heh, just like Tcl. Oh wait, that [['''stdout write''']] is unfamiliar. Let's fix: proc puts {msg} {stdout write $msg} proc greet {} {puts hello} That looks like currying. Let's make a curry factory: proc curry {new_name old_name *old_args} {proc (caller `$new_name) {*args} ($old_name `$old_args {`$args})} Heh, just to be cruel, let's do that without the aid of [['''proc''']]: set curry {{new_name old_name *old_args} {set (caller `$new_name) ({*args} ($old_name `$old_args {`$args}))}} This can be broken onto multiple lines: set curry {{new_name old_name *old_args} { set (caller `$new_name) ({*args} ( $old_name `$old_args {`$args} )) }} Not so tough after all. Notice: (1) parentheses make this a lot easier to read than [[[list]]] would; (2) newlines don't need to be backslashed when using parentheses to construct lists; (3) the "args" (extra arguments) parameter doesn't actually need to be called "args"; (4) "caller" needs to be prepended to the new command name or else the command would be created in curry's local namespace; (5) expansion occurs in three places; and (6) args's substitution and expansion need to be quoted so that they won't occur until the new procedure is executed. Now let's do some currying! curry puts stdout write proc greet {} {puts hello} greet Hehehe, that's awesome. :^) [['''greet''']] itself can be seen as the result of a curry, except that it can't accept any additional arguments. curry puts stdout write curry greet puts hello greet How about making a control structure? (As if [['''proc''']] didn't count, heh.) proc do {script noise cond} { if {$noise ni {while until}} { error "invalid syntax: should be \"do {script} while|until {cond}\"" } if {$noise eq "until"} { set cond !($cond) } namespace caller ( `$script ; while $cond $script ) } And use it: set x 1 do { puts $x set x $($x * 2) } while {$x < 16} There's probably something subtly wrong with that use of the semicolon, but I'm not sure. It just makes me a tad uneasy. Any ideas? Maybe it's fine... ---- I put a lot of ideas on this page. Surely many of them are bad. :^) But I may have a few winners, too. Discuss. ---- [[ [Category Language] ]]