Today, more and more languages supports functions as first-class values. This
means a function is just a value like any other. You can pass functions as
arguments to functions, but you are also able to create functions and return
them from functions.
Whenever this is done we have to think about the life-time of variables. Usually
all variables are lexical scoped. Consider the following example.
1
2
3
letadd10y=letx=10x+y
1
2
3
4
publicstaticintAdd10(inty){varx=10;returnx+y;}
1
2
3
4
subadd10($y) {my$x=10;return$x+$y;}
1
2
3
4
functionadd10(y){constx=10;returnx+y;}
1
2
3
4
(define(add10y)(definex10)(+xy))
In the example the variable x is created only temporary when the function
is being executed. Once the function is finished the variable x is freed
from memory. But this can change when we return a function.
1
2
3
letadd10()=letx=10funy->x+y
Everytime add10 is called a new function is created and returned.
add10 returns a function instead of doing a calculation. Because the
function that is returned still access the variable x it means the
variable is still not freed from memory. In the cases above the new functions
f and g have access to its own x variable. Both functions
have there own copy of x that is not shared between them.
This seems not so useful because x is always 10. But we can change the
example to be more useful.
1
2
letaddxy=x+yletaddx=funy->x+y
Both definitions of add are the same in F# because of currying. Because
x is now passed as an argument it is now possible to create multiple different
add functions where each function has access to its own different x.
Because x is now passed as an argument it is now possible to create multiple
different add functions where each function has access to its own different x.
Because x is now passed as an argument it is now possible to create multiple
different add functions where each function has access to its own different x.
Because x is now passed as an argument it is now possible to create multiple
different add functions where each function has access to its own different x.
Because x is now passed as an argument it is now possible to create multiple
different add functions where each function has access to its own different x.
This style is related to currying. We do currying
whenever we turn a
function with multiple arguments into a series of one-argument functions. In
F# we could turn the following function.
1
2
letaddxyz=x+y+z
into a curried form by writing.
1
2
3
4
letaddx=funy->funz->x+y+z
but in F# it’s not needed because all functions are curried by default.
With both definitions you can write:
1
letadd10=add46
add10 now accepts the missing argument z. Passing fewer arguments
as needed to execute a function is called Partial Application. In
C#, Perl, JavaScript and Racket you must do this kind of transformation
explicitly.
letadd10=add64// int -> int
letnuma=add645// 15
letnumb=(((add6)4)5)// 15
This is the reason why F# doesn’t use parenthesis for the arguments and they
are also not required like in Racket (or another LISP-like language).
While being more usefull I guess you will still wonder why
you ever want to do this kind of transformation. So here comes a more
advanced example.
Consider we want to create a range function accepting a start and
stop value. But instead of returning a List/Array or other kind
of data, we want to return a function instead. Whenever this function
is called it returns the next value starting with start upto stop.
This concept is also called an iterator and we can implement it easily
with just a closure.
1
2
3
4
5
6
7
8
9
10
11
12
13
// range : int -> int -> (unit -> option<int>)
letrangestartstop=// this is the state
letmutablecurrent=start// A function is returned and has has access to its
// own unique mutable current variable.
fun()->ifcurrent<=stopthenlettmp=currentcurrent<-current+1SometmpelseNone
1
2
3
4
5
6
7
8
9
10
11
12
13
publicdelegateint?RangeDelegate();publicstaticRangeDelegateRange(intstart,intstop){// This is the stateint?current=start;// A function is returned and has has access to its// own unique mutable current variable.return()=>{if(current<=stop){returncurrent++;}returnnull;};}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
subrange($start, $stop) {# This is the statemy$current=$start;# A function is returned and has has access to its# own unique mutable current variable.returnsub{if($current<=$stop){return$current++;}else{return;}}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
functionrange(start,stop){// This is the state
letcurrent=start;// A function is returned and has has access to its
// own unique mutable current variable.
returnfunction(){if(current<=stop){returncurrent++;}else{return;}};}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(define(rangestartstop); This is the state(definecurrentstart); A function is returned and has has access to its; own unique mutable current variable.(lambda()(cond[(<=currentstop)(definetmpcurrent)(set!current(add1current))tmp][else#f])))
The range function defines current and uses start as the initial value.
Every function created by range has its own copy of current. So whenever
we call the function that is returned by range, it just iterates from start
to stop.
First we create a helper function to iterate through an iterator. This way we
don’t need to write the logic for iteration every single time. Even if the
code is short for iteration we can easily make mistakes.
It is now easy to create multiple iterators and iterate through them.
1
2
3
4
5
consta=range(1,10);constb=range(20,80);iter(a,x=>console.log(x));// prints 1 to 10
iter(b,x=>console.log(x));// prints 20 to 80
1
2
3
4
5
6
(define(iterif)(definex(i))(cond[x(fx)(iterif)]))
It is now easy to create multiple iterators and iterate through them.
1
2
3
4
5
(definea(range110))(defineb(range2080))(itera(lambda(x)(displaylnx))); prints 1 to 10(iterb(lambda(x)(displaylnx))); prints 20 to 80
Closures are tied to functional programming. But let’s assume you have an
object-oriented language and want to achieve the same (without using its
functional features). How do closures translate to object-oriented programming?
Answer: They are just classes with fields. A closure is the same as an object
that contains data with a single method you can call.
This is how you implement add10.
1
2
3
4
5
typeAdd10()=letmutablex=10memberthis.Cally=x+y
Like in the closure example it is now possible to create f and g and both
objects will have its own x field.
1
2
3
4
5
letf=newAdd10()letg=Add10()// new is optional
leta=f.Call(20)// 30
letb=g.Call(20)// 30
functionAdd10(){this.x=10;}// I am using invoke because JavaScript already has a global call method
Add10.prototype.invoke=function(y){returnthis.x+y;}
Like in the closure example it is now possible to create f and g and both
objects will have its own x field.
Like in the example with the closure it seems useless to have x as a field,
but we can make it configurable by passing it to the constructor. This would
be add.
1
2
3
4
5
typeAdd(x)=letmutablex=xmemberthis.Cally=x+y
It doesn’t really matter how that one-method is named. Use Run, Execute,
Invoke, Call or any other synonyms for saying function.
typeRange(start,stop,?current)=letmutablecurrent=defaultArgcurrentstart// only get Properties
membervalStart=startmembervalStop=stopmembervalCurrent=currentmemberthis.Next()=ifcurrent<=stopthenlettmp=currentcurrent<-current+1SometmpelseNonememberthis.Iterf=letmutablex=this.Next()whileOption.isSomexdof(x.Value)x<-this.Next()
In the object-oriented code I make the iter function a method. The properties
are not really needed but still shown for comparison. Like mentioned in the
C# version it is possible to use an interface and extension methods. I also
could have used a Range module and use functions again, but then the current
field had to be public. I also could have created a record with a module instead
but here I wanted to show a class.
Now it becomes easy to define multiple ranges and iterate through them.
In the object-oriented code i make the iter function a method. But I also
cold have used an interface for the Range class and create Extensions Methods
for additional functions. This is how LINQ is implemented.
Now it becomes easy to define multiple ranges and iterate through them.