Reputation: 1423
I have begun to understand a few examples related to currying but I am still not comfortable with the concept of currying as I would like to be. I know that currying can be used to do partial evaluation but I am not sure how it would work in certain cases.
I know how it works in the example below:
fun funkyPlus x y = x*x+y;
so let's say we only pass an argument for x then it is equivalent to the following:
fun funkyPlus 3 = (fn x => fn y => x*x+y)3
which ends up returning
fn y => 9+y
Now, I am trying to apply this idea to the built in function foldl
.
I know the code for it is:
fun foldl f b [] = b
|foldl f b (h::t) = foldl f f(h,b) t.
My question is, what if we do not pass all the arguments to foldl
(i.e. we only pass it the first argument which is the function ('a*'b->'b)
). In the first example I gave, it was fairly simple to see how the function works when only one of the arguments is passed to it. However, I am having trouble seeing how foldl
would work when there is only one argument passed to it.
Help.
Upvotes: 5
Views: 504
Reputation: 31619
The first step is to turn your top-level set of equations for foldl
into a lambda expression which uses case analysis, like so:
val rec foldl = fn f => fn b => fn lst =>
case lst of [] => b
| (h::t) => foldl f (f(h,b)) t
Now you can use the same logic as before. Taking as an example the function fn (x, y) => x * y
, we can see that
val prod = foldl (fn (x, y) => x * y)
is equivalent to
val prod = (fn f => fn b => fn lst =>
case lst of [] => b
| (h::t) => foldl f (f(h,b)) t) (fn (x, y) => x * y)
which beta-reduces to
val prod = fn b => fn lst =>
case lst of [] => b
| (h::t) => foldl (fn (x, y) => x * y) ((fn (x, y) => x * y)(h,b)) t
which beta-reduces to
val prod = fn b => fn lst =>
case lst of [] => b
| (h::t) => foldl (fn (x, y) => x * y) (h * b) t
Now since we know from our first definition that prod
is equivalent to foldl (fn (x, y) => x * y)
, we can substitute it into its own definition:
val rec prod = fn b => fn lst =>
case lst of [] => b
| (h::t) => prod (h * b) t
We can then mentally convert this back to a function defined by equations if we like:
fun prod b [] = b
| prod b (h::t) = prod (h * b) t
That's about what you'd expect, right?
Upvotes: 1
Reputation: 177875
This does not mean what you think:
fun funkyPlus 3 = (fn x => fn y => x*x*y)3
It defines a function which takes an argument that must be 3, and which evaluates to its RHS if it is 3 and is undefined otherwise. What you mean to say is this: If we only provide an argument for x, we have the following:
funkyPlus 3
→ (fn x => fn y => x*x+y) 3
and so forth.
Secondly, there is an error in your foldl
:
fun foldl f b [] = b|foldl f b (h::t) = foldl f f(h,b) t;
^^^^^
Type clash: expression of type
'a * 'b
cannot have type
'c list
This is because (h,b)
is parsed as the third argument to foldl
and not as the argument to f
. Parenthesize it:
fun foldl f b [] = b|foldl f b (h::t) = foldl f (f(h,b)) t;
> val ('a, 'b) foldl = fn : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b
Now, getting to your question, ML can tell us that an expression like foldl add
would have type int -> int list -> int
.
But in general, it may help to realize that function application is entirely mechanical. If we have these two definitions:
fun foldl f b [] = b
| foldl f b (h::t) = foldl f (f(h,b)) t;
add (x,y) = x + y;
then var example = foldl add
would be equivalent to this:
fun example b [] = b
| example b (h::t) = example (h::t) (add(h,b)) t;
All that’s been done is that add
has been substituted for f
in the body of foldl
, nothing more (although I have taken the liberty of replacing foldl add
with example
in the body).
Upvotes: 2