Currying and Closures
Just a quick one
Closures might be well known amongst many C# practitioners - apart from me it seems. I read about them ages ago and then haven't knowingly applied them. Of course, I have been using them (or at least a variation on the theme) often in C# by virtue of using 'lambdas in lambdas' when using LINQ for example, and lambdas which refer to variables in the method in which they are created.I mentioned functional programming including Currying in the last post, and this is still my current focus, its satisfying my ADHD for the time being.
Anyway, It turns out that if you want a bit of a better understanding of how Currying works, then understanding closures can help you take a leap of faith.
Currying in Haskell
The following example is taken from Learn you a HaskellThe function multiplies 3 numbers together and returns the result.
The first line defines a function, multThree, which can be read as taking 3 arguments and returning a value - all of the same type - signified by a.
The second line maps the first 3 arguments to x,y and z which are then used in a function body where x * y * z occurs. When the function is called, e.g. multThree 2 5 5, it will yield the result of 50. There is no need for an explicit return , like in C#, the result is just returned.
In Haskell, every function (well, nearly - I think - functions can actually be uncurried) is curried automatically which means that functions with multiple arguments are broken down so that each argument has an accompanying return value. This opens up the door to Partial Application (to be covered in a future post), which means that not all of the arguments need to be supplied immediately.
Taking the above function definition again but showing where Currying occurs with the use of parentheses:
We have a function which takes an a and returns
a function which takes an a and returns a function which
takes an a and returns an a. Confusing, yes. Bloody confusing.
This is how I have read Currying definitions in lots of places which hasn't been a great deal of help. What I couldn't work out, particularly, was where the actual multiplication would take place, if state was being maintained somewhere and/or if the result was being worked out cumulatively (via some kind callback mechanism maybe).
Reading
After reading a few chapters of a CLR via C#, a mixture of blogs and stack overflow posts, a simple rearrangement of the function makes things alot more clear. (as well as knowing closures are at work)When the multThree function definition is encountered by the Haskell compiler something like this occurs:
Three lambda statements are created.
The first takes x and
returns a lambda which takes y and
returns a lambda which takes z and has the function body which returns the result
(Note the "/x -> ..." notation, this signifies a lambda that takes one argument in Haskell, in C# this is very similar " x => ...." would mean the same thing.)
And with values:
With all of the values known, the function body can multiply all of the values together, to get a result. And this is the important bit, only when ALL of the values are supplied can the function body be executed.
The key thing here is that this is all facilitated by closures which was the missing bridge to working out how the values were getting passed around. Basically each nested lambda or child lambda closes over its calling environment and any of the values present in its calling environment are available to it. Essentially the values, once supplied, are in scope all the way back up the calling chain.
Back to C#
In trying to work all of this out I cross referenced an example by Jon Skeet on Currying and Partial application and a chapter in CLR via C# by Jeffrey Richter about lambdas, which showed how lambdas referencing variables in the method, in which they are defined, close over that method. That is, have access to values in the method.There is a curious side effect in C# (at least) in that a value which is closed over, has its scope widened beyond the method to the class in which the method is defined. This is because a helper class is generated for the lambda method - containing the implementation of the method itself and values which that method will need to use which are copied into the class. This class is then added to the original class containing the anonymous method as a field.
I then cross referenced that this occurred in Haskell (well on a few S/O posts - so still anecdotal - but certainly reasonably explained) and this has cemented my understanding.
Code examples have gone awry, will add them AGAIN!
ReplyDelete