Discussion page for [http://dev.crypt.co.za/incubator/doc/tcltm/control/functional-tip.wiki], a proposal for Higher Order Functions in Tcl that is intended to result in a TIP. ---- ''[escargo] 15 Apr 2010'' - There appears to be some damage to the page in the reference link. One heading is '''tr;dr'''. Headings for "Introduction, Motivation and Goals," "A review of FP and higher order functions in Tcl," and "The design of this proposal" appear to be missing. One feature of the design of the proposal is described like this: `filter` ''predicate'' ''list'' is a filter over a list, and predicate is a cmdprefix that must return a boolean. This is somewhat imprecise in that Tcl does not have a boolean data type. More firmly specified would be to say that cmdprefix must return 0 or 1. The discussion about optional arguments would be clarified if the distinction between formal arguments and actual arguments were more clearly drawn. While proponents of functional programming might know about ''left fold'' and ''right fold'' an explanatory reference for the rest of us would be welcome. ''[Twylite] 2010/04/16'': No damage; the "missing" headings are external links. I have updated the doc to make this more readily apparent (I hope). The concept of a "boolean" is defined in the man page for [if] - I have updated the doc to refer to this definition. Thanks for the comments on the "optional arguments" proposal. I will improve this proposal in time; it will not form part of the Higher Order Functions TIP and should probably be a TIP in its own right. The idea is rather rough at the moment but I wanted to get it down and hear some opinions. The tl;dr is deliberately brief, take a look at the manual (link is now more obvious, in the ToC) for more detail and a link to the Wikipedia. ---- [Lars H]: While I certainly agree with the proposal's philosophy that a [command prefix] should be considered the normal Tcl equivalent of an abstract function, I'm somewhat at a loss as to what the [TIP] is supposed to actually propose. The whole thing just looks like a collection of observations. ''[Twylite] 2010/04/16'' Fair point. The TIP will do two things: 1. Present a consistent model for handling functions, and propose that future TIPs (that accept or return command prefixes or functions) conform to (or at least interoperate with) this model. 2. Propose the inclusion in the Tcl core of the following commands: lambda, lamdaexpr, curry, compose, foldl, foldr, filter, map, range. The doc has been updated to indicate this (at the top of the tl;dr). The rationale for including these commands in the core is given in the [http://dev.crypt.co.za/incubator/doc/tcltm/control/functional-intro.wiki%|%Introduction, Motivation and Goals%|%] section. ---- ''[Twylite] 2010/04/16'' [Andreas Kupries] made a point that this proposal does not support infinite lists. That is true. I experimented with [[coforeach]] and [[cofoldl]] implementations, and decided that two paradigm shifts (functional programming, and iterators instead of lists) would be going too far in Tcl 8.x. The fold/map/filter constructs for Tcl 8.x should support the familiar data types of Tcl 8.x, i.e. finite lists. There have been some discussions about moving towards an iterator approach in Tcl 9, which would be an appropriate time to make the higher-order functions iterate over infinte lists. Put another way, I see it as the job of another TIP to propose [[coforeach]], [[cofoldl]] and friends that accept iterators (as coroutines the yield values) rather than lists. It would also make sense to have an [[iterator]] helper to iterate over lists, making the following two statements equivalent in function: foreach element $list { ... } coforeach element [iterator $list] { ... } Tcl 9 proposals to remove the command/variable dichotomy could further simplify the syntax. ---- '''[dkf] - 2010-04-16 04:22:07''' I want a '''map''' that supports the full power of [foreach]. Why? Because it will also allow us to do 1. zipping of lists together <
> <
>`set zipped [[map a $list1 b $list2 {list $a $b}]]` <
> <
> 2. consuming of several values at once <
><
>`set sums [[map {a b} $values {+ $a $b}]]` <
> <
> 3. filtering <
> <
>`set goodOnes [[map x $values {expr {[[isGood $x]] ? $x : [[continue]]}}]]` <
> <
> 4. taking a prefix of the list <
> <
>`set prefix [[map x $values {expr {[[isGood $x]] ? $x : [[break]]}}]]` <
> <
> 5. combinations of the above What's more, we don't need any new bytecodes to do this efficiently. It's essentially just reusing the machinery we already have for [foreach], but keeping the value from each time round the loop (on normal result). ''[Twylite] 2010/04/16'' Hmmm, so a capturing [[foreach]] that assigns values to variables and evals a block, rather than something that invokes a function with arguments. Out of scope for this proposal ;p Very cute though; this would provide list comprehension functionality in a construct that is familiar to Tclers. So I will see your [[map]], and raise you multiple output elements per iteration: proc mapfor {args} { set coro ::__mapfor_[incr ::__mapfor_id] set a [coroutine $coro foreach {*}$args] set out [list] while { ($a ne {}) || [llength [info commands $coro]] } { lappend out $a set a [$coro] } return $out } # Return multiple output elements per iteration % mapfor a {1 2 3 4 5} { yield [expr { $a * 2 }] ; yield x } 2 x 4 x 6 x 8 x 10 x # Zipping % set list1 {1 2 3 4 5 6} % set list2 {a b c d e f} % set zipped [mapfor a $list1 b $list2 {yield [list $a $b]}] {1 a} {2 b} {3 c} {4 d} {5 e} {6 f} # Consuming several values at once % set sums [mapfor {a b} $list1 {yield [::tcl::mathop::+ $a $b] }] 3 7 11 # Filtering % proc isEven {x} { expr { $x % 2 == 0 } } % set goodOnes [mapfor x $list1 { yield [expr {[isEven $x] ? $x : [continue]}] }] # Taking a prefix of the list % proc isGood {x} { expr { $x < 4 } } % set prefix [mapfor x $list1 { yield [expr {[isGood $x] ? $x : [break]}] }] 1 2 3 # Performance % set values {}; for {set i 0} {$i < 1000000} {incr i} { lappend values $i } % set out {} ; time { foreach i $values { lappend out [expr { $i * 5 }] } } 921000 microseconds per iteration % time { mapfor i $values { yield [expr { $i * 5 }] } } 1828000 microseconds per iteration <>Functional Programming | Discussion