Curried Functions in Standard ML

I banged my head against the wall trying to learn about curry functions. This is what I understand so far; suppose I have a function:

fun curry (a b c) = a * b * c;

      

or

fun curry a b c = a * b * c;

      

In ML I can only have one argument, so the first function uses a 3-tuple to get around this / access a

, b

and c

.

In the second example, I actually have:

fun ((curry a) b) c

      

where curry a

returns a function, and (curry a) b

returns a function, and ((curry a) b) c

returns another function. A few questions:

1) Why is it preferable to use a tuple? I can just use intermediate functions curry a

and (curry a) b

. My book mentions a partial implementation, but not fully understood.

2) How do you determine which function curry a

, (curry a) b

really? ((curry a) b)

c is simple a * b * c

, right?

Thanks for any help clarifying this,

bclayman

+3


source to share


1 answer


There is an element of flavor using curried vs. non-curried. Of course, I am not using currency functions. For example, if I were to write a gcd function, I would refer to it as a function designed to work with a tuple, simply because I rarely use a particular partially generated gcd function.

Where cardinal functions are really useful, in defining higher order functions. Let's consider map

. It's easy enough to write a non-curry version:

fun mymap (f,[])= []
|   mymap (f,x::xs) = f(x)::mymap(f,xs)

      

It is of a type fn : ('a -> 'b) * 'a list -> 'b list

that takes a tuple consisting of a function between the two types and a list of elements of the input type that returns a list of elements of the output type. There is nothing wrong with this function, but it is not the same as SML map

. The built-in card is of the type

fn : ('a -> 'b) -> 'a list -> 'b list

which is in the curry. What does the cardinal function do for us? First, it can be seen as a functional transformer. You supply the map with a function f

to work with elements of a particular type, and it returns as a function map f

that is designed to work with all lists of elements. For example, if

fun square(x) = x*x;

      

Whether the function intended for a square ints

is then val list_square = map square

determines list_square

as a function that takes a list of elements and returns a list of their squares.



When you use map

in a type call map square [1,2,3]

, you must remember that the function app is left associative, so it parses as

'(map square) [1,2,3] . The function

map square *is* the same as the function

list_square I defined above. The invocation

map square [1,2,3] takes that function and applies it to

[1,2,3] yielding

[1,4,9] `.

The curried version is really nice if you want to define a function metamap

that can be used to apply functions to each element of the matrix that is considered a list of lists. Using the curry version, it's simple:

fun metamap f = map (map f)

      

used like (in REPL):

- metamap square [[1,2],[3,4]];
val it = [[1,4],[9,16]] : int list list

      

The logic is that it map

overrides the function from being applied to elements to being applied to lists. If you want the function to be applied to lists of lists (like matrices), just apply map

twice is all metamap

. Surely you could write a version without the black version metamap

with our unrefined function mymap

(it wouldn't be that hard), but you couldn't come close to the elegance of the 1-line definition above.

+2


source







All Articles