Amateur Topologist

Everything but topology.

Why I Love Currying

So I’ve been playing around with Haskell a lot lately and using it for various random stuff; I haven’t progressed to the point where it’s my go-to language for random programs (I still use Python for that), but I at least have an idea of how to use it. And there’s one feature of Haskell that I miss sorely when I write code in Python, or pretty much any other vaguely functional language: currying.

In Haskell, every function takes a single argument. A function of multiple arguments, such as map, which applies a function to every element in a list, actually only has one argument; for example, map can be interpreted either as taking a function and a list and returning a list, or as taking a function and returning a function that takes a list and returns a list. More formally, in Haskell, these two type declarations are equivalent:

map :: (a -> b) -> [a] -> [b]
map :: (a -> b) -> ([a] -> [b])

This process, of taking a multi-argument function and converting it into a series of single-argument functions is known as currying, after the mathematician Haskell Curry (who, obviously, is also the source of the name Haskell); the process of partially applying arguments to a function in this way is known as ‘partial application’, but is also called currying. One of the most obvious examples of currying is in sections: the function (0 ==) is syntactic sugar for (==) 0, and returns whether its argument is equal to zero. Furthermore, we can also partially apply the predicate to filter, to make a function that filters its argument on a fixed predicate. So, these three examples are completely equivalent:

removeZeros :: [Integer] -> [Integer]
removeZeros xs = filter (\x -> x /= 0) xs
removeZeros xs = filter (/= 0) xs
removeZeros = filter (/= 0)

(where /= is Haskell’s not-equal operator). The first is the most explicitly-written version, using no currying at all. The second curries the predicate; (/= 0) x is the same as x /= 0. Finally, since removeZeroes applied to an argument is the same as applying filter (/= 0) to it, we might as well define the former as the latter. Or, to take another example, look at the sortBy: it has type (a -> a -> Ordering) -> [a] -> [a], where Ordering is a datatype that can either be EQ, LT or GT for equal, less than, or greater than. So if you have some custom function you want to sort a list on, you can just say mySort = sortBy f and it will be the same as writing mySort xs = sortBy f xs, only cleaner and neater. Or in my Data.Nimber module (specifically lines 38, 39, and 43), many operations on Nimbers that’re required in order for me to call then ‘numbers’ are just the identity operation. So instead of saying abs x = x, I can just say abs = id.

Furthermore, without currying, you couldn’t have variadic functions; in order to work inside Haskell’s type system, the two types a -> b -> c and a -> (b -> c) have to be the same type. The full explanation involves typeclasses, and is (in my opinion) worth a read, because it’s a good explanation of a pretty horriblexcellent (it’s both at once, you see) type system hack.

As an aside, this also means that id :: a -> a, the identity function, is in a sense the same thing as ($) :: (a -> b) -> a -> b, which is function application. You can see this by substituting (b -> c) for a in the type of id, then removing parentheses:

id :: a -> a
id :: (b -> c) -> (b -> c)
id :: (b -> c) -> b -> c

So, in particular, f `id` x is the same as f $ x, which is just f x. Another way to think of this is that f `id` x = id f x = (id f) x = f x.

9 ResponsesLeave one →

  1. Yes! I tried Python a bit and coming from Haskell I really missed the currying.

    You say that (== 0) is syntactic sugar for (==) 0, but that is not true:
    (== 0) rewrites to \x -> x == 0
    (==) 0 rewrites to \x -> (==) 0 x rewrites to \x -> 0 == x

    So in this case it is not such a problem, because (==) is commutative, but this is definitely not true in general for operators. So instead, say that (0 ==) is syntactic sugar for (==) 0. :-)

    Reply
  2. Are you sure that \x -> (==) 0 x rewrites to \x -> 0 == x? I don’t see how that makes sense.

    Reply
  3. Erik

     /  February 13, 2010

    Why does that not make sense? The term

    f 0 x

    is the same as

    0 `f` x.

    If ‘f’ is an operator,

    0 == x

    is the same as

    (==) 0 x

    Reply
  4. augustss

     /  February 13, 2010

    But you didn’t really say why you love currying. :)

    Reply
  5. Yitz

     /  February 13, 2010

    OK, here you go:

    from functools import partial

    def curry(n, func):
    ”’Curry a function of n arguments”’
    if n < 2:
    return func
    def func_(x):
    return curry(n – 1, partial(func, x))
    return func_

    def currify(n):
    '''Decorator to curry a function of n arguments'''
    return partial(curry, n)

    # Example

    @currify(2)
    def map_str(f, xs):
    return ''.join(f(x) for x in xs)

    rot13 = map_str(rot13_char)

    Reply
  6. ITYM schoenfinkelization.

    Reply
  7. Why does that not make sense? The term

    f 0 x

    is the same as

    0 `f` x.

    If ‘f’ is an operator,

    0 == x

    is the same as

    (==) 0 x

    Reply

Leave a Reply