[Richard Suchenwirth] 2002-01-21 - ''Currying'' (named after the founder of combinatory logic, Haskell Curry [http://www.cs.oberlin.edu/classes/dragn/labs/combinators/combinators11.html] - [Playing Haskell]) is in my understanding the act of deriving a function from another by adding one or more constant arguments. For instance, we can specialize the following general two-operand multiplier ====== proc mult {a b} {expr $a*$b} ====== into a "doubler" that multiplies its single argument by 2: ====== proc double {a} {mult $a 2} ====== If all added arguments come before the "real" variables (in the last example, the order of the arguments can be swapped), we can easily use Tcl's ''[interp] alias'' command that does the same effect more efficiently, and saves one level in the call stack: ====== interp alias {} double {} mult 2 ====== (the {} indicating twice that the current interpreter is meant). Conversely, even without being aware, we have used currying whenever * a proc body consists of a single command with other args than of the proc * an interp alias has more than four arguments We can use currying to factor out frequent calls or parts thereof, which might include assignment to a variable: ====== interp alias {} -> {} set res -> 1 ;# equivalent to: set res 1 ====== and of course we may curry the process of interp currying:} ====== proc curry {new args} {eval [list interp alias {} $new {}] $args} ====== Such custom currying may seem like obfuscation, but it makes for tighter code, as the following example, '''German plurals''', shows. Repeated action like the adding of umlaut dots (a -> ä) is factored out into a custom curry, as is the concatenation with a suffix (which otherwise would require frequent bracing of the variable name, as seen in [English plurals]: ====== proc sjoin args {join $args ""} ;# spaceless concat proc de:pl word { curry + sjoin $word curry a:+ sjoin [string map {a ä} $word] curry o:+ sjoin [string map {o ö} $word] curry u:+ sjoin [string map {u ü} $word] curry -1+ sjoin [string range $word 0 end-1] curry -2+ sjoin [string range $word 0 end-2] # The language inside the switch body looks strange, but clear: switch -- $word { Drucker - Fenster - Lehrer - Löffel - Messer - Schüler - Hamster - Gitter - Finger - Schweinchen {+ ;# leave unchanged} Hund - Berg - Pferd - Krokodil - Tier - Problem - Bildschirm - Meer - Tisch - Begriff - Schaf - Haar - Kamel - Dromedar - Schwein {+ e} Bahn - Elefant - Frau - Tür - Zahl - Tastatur - Staat - Ohr - Burg {+ en} Kind - Rind - Bild - Schild - Geist - Brett {+ er} Katze - Frage - Pflanze - Straße - Gabel - Gasse - Diskette - Platte - Seite - Ratte - Kammer - Henne - See - Auge - Hase {+ n} Garten {a:+} Bach - Baum - Traum - Maus - Laus - Platz - Stadt - Kraft - Hand - Zaun {a:+ e} Haus - Mann - Tal {a:+ er} Kloster - Tochter - Vogel {o:+} Sohn - Hof - Wolf {o:+ e} Dorf - Horn {o:+ er} Mutter {u:+} Fluss - Fuß - Kuh - Stuhl - Fuchs - Zug {u:+ e} Buch - Tuch - Huhn - Mund {u:+ er} Bus - Zirkus {+ se} Thema {-1+ en} Globus - Zyklus - Faktum - Medium - Museum {-2+ en} Kaktus {-2+ een} Genus {-2+ era} Modus {-2+ i} Nomen - Pronomen {-2+ ina} Atlas {-1+ nten} default {+ s} } } ====== '''Discussion:''' The "curry" names are defined directly before use, which increases readability. They are however never undefined and thus may clutter the current interpreter. A cleaner solution would be to arrange for their destruction on leaving of this proc (see [Local procedures] for an analog example). After marveling at the power of currying, and thinking further, it occurred to me that this is of course also possible in C by using macros: #define RES res = #define DOUBLE(x) mult(x,2) So, no big deal... or is it? You cannot pass these C macros as arguments (would raise syntax errors), while the Tcl curry examples above are callable by name, and thus can be further processed (e.g. with [functional composition]...) ---- [escargo](?): Some references on currying: * [http://www.cs.nott.ac.uk/~gmh/faq.html#currying] * In Scheme [http://www.cs.oberlin.edu/classes/dragn/labs/combinators/combinators11.html], [http://www.engr.uconn.edu/~jeffm/Papers/curry.html]. * In Haskell [http://www.haskell.org/pipermail/haskell-cafe/2001-August/002087.html]. * In Perl6 [http://archive.develooper.com/perl6-language@perl.org/msg09950.html]. ---- One difference to "real curry" as in [Feather CurryObj] is that you have to pass in a name. It seems more functional to make up a name and return that, which would be identical to the call like used in [Lambda in Tcl]: ====== proc curry2 args { set name [info level 0] eval [list interp alias {} $name {}] $args set name } ;# RS % set double2 [curry2 mult 2] ==> curry2 mult 2 % $double2 5 ==> 10 ====== ---- [RS]: It appears as if currying is a special application of ''lambda'' (see [Lambda in Tcl]) and can also be implemented like this: ====== set double3 [lambda x {mult 2 $x}] ==> lambda x {mult 2 $x} $double3 5 ==> 10 ====== at the cost of one more level in call stack, so the ''[interp] alias'' solution appears to be gentler. ---- [SS] demonstrated in the [Tcl chatroom] on 2005-03-29 how to do curry with [Jim closures]: Curry in Jim: ====== proc curry {cmd args} { lambda args [list cmd [list pref $args]] { uplevel 1 [list $cmd {expand}$pref {expand}$args] } } ====== ---- [aspect] discovered the following on [Block-local variables] (months too late): ====== proc know what {proc unknown args $what\n[info body unknown]} know { if {[llength [lindex $args 0]]>1} { return [uplevel 1 [lindex $args 0] [lrange $args 1 end]] } } ====== If this isn't curry I don't know what is: ====== % namespace path ::tcl::mathop % set double {* 2} % $double 5 10 ====== .. the above finally brought [Let unknown know] irrevocably into my standard prelude. If I was slightly less conservative I'd argue for it to be default behaviour in all circumstances -- who deliberately puts a space in a command name anyway? I think the above is really beautiful, very Tclish, and reflective of of how powerful Currying is in the language named after him ([Haskell]). [NEM] Yes, this auto-expansion of leading word is really very powerful. Several of us would like to see it eventually become part of Tcl's default evaluation rules (in Tcl 9 or so). It was also one of the original motivations for my proposing [namespace unknown], so that you can do these sort of tricks on a per-namespace basis: ====== proc expand {next cmd args} { if {[llength $cmd] > 1} { uplevel 1 $cmd $args } else { uplevel 1 $next [linsert $args 0 $cmd] } } namespace unknown [list expand [namespace unknown]] ====== [Lars H]: But what need is there for having [unknown] do expansion, now that we've got [{*}]? ====== % namespace path ::tcl::mathop % set double {* 2} % {*}$double 5 10 ====== Yes, this means you need to decide whether you're passing around command names or command prefixes. So what? Tcl has always been a [Write What You Mean] language. [NEM] It's ugly. Command prefixes should be the default, not command names, so you'd end up using this everywhere. <> Concept | Arts and crafts of Tcl-Tk programming | Functional Programming