[erlang-questions] Funs
Richard O'Keefe
ok@REDACTED
Tue Nov 27 00:21:04 CET 2012
On 26/11/2012, at 9:09 PM, Lucky Khoza wrote:
> I would like to ask about: when do we use funs or anonymous functions in Erlang?, I know what it is, but I can't really figure out when to use it and I saw an example of it being used with Mnesia transactions.
You define an interface that allows funs when you want to
define something that has "call-backs" to support user-defined behaviour.
Such interfaces are nothing new. Programming languages have been supporting
'procedures as parameters' since the late 1950s. (The Algol committee got
the idea from Fortran, which of course got it from mathematical integration,
which goes back to Newton, or possibly Leibnitz. Late 17th century anyway.)
Lisp was to some extent influenced by the lambda calculus (1932) which is
today foundational for languages such as Haskell, Clean, ML, and trace of
which can still be discerned in F#. In 1961 Lisp allowed anonymous functions,
though it was some time before people figured out how to implement them
correctly. The Algol-family programming language Euler allowed anonymous
functions in 1966. Smalltalk (which is sort of a hybrid between the Lisp
family and the Algol family, and the second major OO language) uses
anonymous functions (there called "blocks") _heavily_ (in a code-base I
just checked, about one every 3 lines, though admittedly most are inlined).
You write a fun when you want to pass something to such an interface
and there is no existing named function that does the job and either
no need to define or (say because it captures a variable) no possibility
of defining a suitable named function.
The basic idea is FACTORING.
If you know much about object-oriented programming, you may have heard
of the "Template Method" design pattern. This is a method with a bunch
of steps split out into separate small methods so that they can be
overridden by a subclass. For example, we might have
Array
methods:
total
|r|
r := self initialForTotal.
self do: [:each |
r := self update: r toInclude: each].
^r
initialForTotal
^0
update: r toInclude: each
^r + each
Array subclass: #MyArrayOfSets
methods:
initialForTotal
^Set new "empty"
update: r toInclude: each
^r copyWith: each "r U {each}"
That's not what Smalltalk actually does. It does use the idea of
a method with pluggable parts, but those pluggable parts are
*parameters*.
Collection
methods:
inject: initial into: updater
|r|
r := initial.
self do: [:each |
r := updater value: r value: each].
^r
sum
^self inject: 0 into: [:r :each | r + each]
asSet
^self inject: Set new into: [:r :each | r copyWith: each]
Here the basic method supplies the generic "iterate over a collection
'adding' a result into an accumulator" behaviour, and the caller
supplies what 'adding' means as an anonymous function.
For an Erlang example, suppose we wanted to know how many elements
of a matrix (represented as a list of lists) were negative. The
following code is written for clarity. lists:foldl/3 is the
Erlang analogue of Smalltalk's #inject:into:. It supplies the
"iterate over a collection 'adding' a result into an accumulator"
behaviour and the function argument supplies what 'adding' means
in any particular case.
count(F, Xs) ->
sum(fun (X) -> case F(X) of false -> 0 ; true -> 1 end, Xs).
sum(F, Xs) ->
lists:foldl(fun (S, X) -> S + X end, 0, Xs).
matrix_negative_element_count(Xss) ->
sum(fun (Xs) -> count(fun (X) -> X < 0 end, Xs), Xss).
For another example, even C does this one: sorting.
When you want to sort an array in C, you call
qsort(array, sizeof array/sizeof array[0],
sizeof array[0], comparison_function);
If you only have one place in your program that wants to
use this comparison function, why rip it bleeding out of
the place where it is used and put it at top level where
it might be called accidentally? And if the comparison
function needs to refer to something in its context, you
are in deep deep trouble.
In Erlang, suppose you have a list of {Sender,Timestamp,Content}
triples that you want sorted into ascending order by Timestamp.
lists:sort(fun ({_,T1,_}, {_,T2,_}) -> T1 =< T2 end, Triples)
does the job. By now, even C++ has anonymous functions, not to
mention Objective C[++].
See http://en.wikipedia.org/wiki/Anonymous_function
I
lists:dropwhile
More information about the erlang-questions
mailing list