bury_5 February 2016

From Haskell to Python: how to do currying?

I recently started coding in Python and I was wandering if it's possible to return a function that specializes another function.

For example, in Haskell you can create a function that adds 5 to any given number like this:

sumFive = (+5)

Is it somehow possible in Python?

Answers


Garrett R February 2016

Yup. Python supports lambda expressions:

sumFive = lambda x: x + 5

for i in range(5):
   print sumFive(i),

#OUTPUT 5,6,7,8,9


rakesh a February 2016

For the most generic Haskell style currying, look at partial from the functools module.


John Coleman February 2016

Python functions can return functions, allowing you to create higher-order functions. For example, here is a higher-order function which can specialize a function of two variables:

def specialize(f,a,i):
    def g(x):
        if i == 0:
            return f(a,x)
        else:
            return f(x,a)
    return g

Used like this:

>>> def subtract(x,y): return x - y

>>> f = specialize(subtract,5,0)
>>> g = specialize(subtract,5,1)
>>> f(7)
-2
>>> g(7)
2

But -- there is really no need to reinvent the wheel, the module functools has a number of useful higher-order functions that any Haskell programmer would find useful, including partial for partial function application, which is what you are asking about.


Michal Frystacky February 2016

As it was pointed out, python does have lambda functions, so the following does solve the problem:

# Haskell: sumFive = (+5)
sumFive = lambda x : x + 5

I think this is more useful with the fact that python has first class functions (1,2)

def summation(n, term):
    total, k = 0, 1
    while k <= n:
        total, k = total + term(k), k + 1
    return total

def identity(x):
    return x

def sum_naturals(n):
    return summation(n, identity)

sum_naturals(10) # Returns 55
# Now for something a bit more complex
def pi_term(x):
    return 8 / ((4*x-3) * (4*x-1))

def pi_sum(n):
    return summation(n, pi_term)

pi_sum(1e6) # returns: 3.141592153589902

You can find more on functional programming and python here


obadz February 2016

I think the other answers are misunderstanding the question. I believe the OP is asking about partial application of a function, in his example the function is (+).

If the goal isn't partial application, the solution is as simple as:

def sumFive(x): return x + 5

For partial application in Python, we can use this function: https://docs.python.org/2/library/functools.html#functools.partial

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

Then, we must turn the + operator into a function (I don't believe there's a lightweight syntax to do so like in Haskell):

def plus(x, y): return x + y

Finally:

sumFive = partial(plus, 5)

Not nearly as nice as in Haskell, but it works:

>>> sumFive(7)
12


ajcr February 2016

Python's design does not naturally support the evaluation of a multi-variable function into a sequence of single-variable functions (currying). As other answers point out, the related (but distinct) concept of partial application is more straightforward to do using partial from the functools module.

However, the PyMonad library supplies you with the tools to make currying possible in Python, providing a "collection of classes for programming with functors, applicative functors and monads."

Use the curry decorator to decorate a function that accepts any number of arguments:

from pymonad import curry

@curry
def add(x, y):
    return x + y

It is then very easy to curry add. The syntax is not too dissimilar to Haskell's:

>>> add5 = add(5)
>>> add5(12)
17    

Note that here the add and add5 functions are instances of PyMonad's Reader monad class, not a normal Python function object:

>>> add
<pymonad.Reader.Reader at 0x7f7024ccf908>

This allows, for example, the possibility of using simpler syntax to compose functions (easy to do in Haskell, normally much less so in Python).

Finally, it's worth noting that the infix operator + is not a Python function: + calls into the left-hand operand's __add__ method, or the right-hand operand's __radd__ method and returns the result. You'll need to decorate these class methods for the objects you're working with if you want to curry using + (discl

Post Status

Asked in February 2016
Viewed 1,450 times
Voted 14
Answered 6 times

Search




Leave an answer