Search code examples
functional-programmingsmlmosml

"Curry" from tuple in SML


I am trying to define a function wrapper that curries a tuple in SML.

fun curry f = fn (x, y) z => f x y z;

Gives me the error

Non-identifier applied to a pattern.

I am new to ML and not sure why the pattern matching in fn doesn't work.

How could I make this work?


Solution

  • I am trying to define a function wrapper that curries a tuple in SML.

    fun curry f = fn (x, y) z => f x y z;
    

    How could I make this work?

    Closures in SML don't allow for multiple arguments, but you can nest them instead.

    What curry usually does is take a function f that normally accepts a tuple (x, y) and instead returns a modified function that takes x and y separately. Here is a number of equivalent ways to define curry:

    fun curry f x y = f (x, y)
    fun curry f x = fn y => f (x, y)
    fun curry f = fn x => fn y => f (x, y)
    val curry = fn f => fn x => fn y => f (x, y)
    

    Its opposite, uncurry instead takes a function f that takes x and y separately and returns a modified function that takes (x, y). Here is one way to write uncurry:

    fun uncurry f (x, y) = f x y
    

    It's easy to mix up the two.

    One way to fix the function you've written so that it compiles is insert an extra => fn:

    fun what_is_this f = fn (x, y) => fn z => f x y z
                                (* ^- there *)
    

    Before giving it a name, let's analyse what it does. It has the type signature:

    fn : ('a -> 'b -> 'c -> 'd) -> 'a * 'b -> 'c -> 'd
                      (* now a tuple -^    ^- still curried *)
    

    meaning that it takes a function of three curried arguments (x, y and z) and returns a modified function where the first two arguments are now in a tuple (uncurried) and the third is still curried. This is really a less generic version of uncurry. A more clear way of writing it would be:

    fun what_is_this f (x, y) z = f x y z
    

    If you use uncurry on a function of three arguments, you get the same effect, but you can't use what_is_this on anything with two curried arguments. So I'd say that this is a less useful variant of uncurry.

    There are however other more useful variants of curry/uncurry. For example, you could make an uncurry_twice that converts f x y z into (uncurry_twice f) ((x, y), z), or an uncurry3 that converts f x y z into (uncurry3 f) (x, y, z):

    fun uncurry_twice f = uncurry (uncurry f)
    fun uncurry3 f (x, y, z) = f x y z