Version 5 of curry

Updated 2016-05-23 01:03:23 by pooryorick

How to curry procedures in Tcl

See Also

interp alias
Custom curry
Hot curry

Description

A curried function is one which accepts multiple arguments one-at-a-time by using nested single-argument functions. In this way, the core programming language can consist of only simple single-argument functions (as in lambda calculus).

Curried functions are the norm in Haskell, whereas other functional programming languages (e.g., ML) typically prefer using tuples instead.

For example, consider a simple function that accepts two arguments and returns their sum. In Tcl, we would write this straight-forwardly as a two-argument procedure:

proc sum {a b} { expr {$a + $b} }
sum 1 2 ;# => 3

However, if we imagine that procedures were all limited to a single argument, then there are two approaches we could take to achieving our goal. One would be to use a list (tuple) to pass the arguments:

proc sum ab { expr {[lindex $ab 0] + [lindex $ab 1]} }
sum {1 2} ;# => 3

Another alternative is to use a curried function. To do this, our initial sum procedure accepts the first argument and then returns another procedure which will accept the second argument and compute the result:

proc sum a {
        list apply [list b [format { expr {%d + $b}  } $a]]
}
{*}[sum 1] 2 ;# => 3

(Note: this style of programming is made much simpler by lexical closures).

One useful effect of the curried style is that it is simple to partially apply a function creating a more specialised function. For example, many higher-order functions like map, fold and so on, can be specialised in this way to create a variety of other functions, e.g.:

sum = foldl (+) 0
product = foldl (*) 1
...

However, such higher-order functions must be carefully designed to accept arguments in the correct order for this to work.

In Tcl, curried functions are seldom (if ever) used, as they are clumsy to construct and perform poorly. Instead, it is more usual to "partially apply" commands by creating a command prefix -- i.e., a list consisting of the command name, followed by some optional extra arguments. This can then either be used directly as a callback command or by using the {*} syntax, or can be converted into a normal command using interp alias. For example, given a definition of foldl, we can define sum and product as follows:

proc foldl {f z xs} {
    foreach x $xs { set z [{*}$f $z $x] }
    return $z
}
set sum [list foldl ::tcl::mathop::+ 0]
{*}$sum {1 2 3 4}
# OR:
interp alias {} sum {} foldl ::tcl::mathop::+ 0
sum {1 2 3 4}

PYK 2016-05-22: namespace code can also be used for currying, but it does have the effect of introducing another level. Here is a command that does the same thing, but with a more convenient syntax and without the extra level:

proc curry {cmd args} {
    list apply [list {cmd curried args} {
        tailcall $cmd {*}$curried {*}$args
    } [uplevel {namespace current}]] $cmd $args
}