5
votes

The chapter on Partial Functions from the book Learn You a Haskell For Great Good contains the following code:

multThree :: (Num a) => a -> a -> a -> a
multThree x y z = x * y * z

ghci> let multTwoWithNine = multThree 9
ghci> multTwoWithNine 2 3
54
ghci> let multWithEighteen = multTwoWithNine 2
ghci> multWithEighteen 10
180

I am currently playing with the functools library in Python, and managed to replicate the behavior of those functions using it.

from functools import partial

def multThree(x,y,z):
  return x * y * z

>>> multTwoWithNine = partial(multThree,9)
>>> multTwoWithNine(2,3)
>>> multWithEighteen = partial(multTwoWithNine,2)
>>> multWithEighteen(10)
180

One thing I would now like to do is see if I can replicate some of the more interesting higher-order functions from the same book chapter, such as:

zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys

However, I'm not sure how to do this, or if partial() is even useful here.

4
You should edit and fix your functions, making sure they are all syntactically correct. Your first, for example, is missing its arguments.Thomas M. DuBuisson
Technically you'd have to use partial in any conversion, since all Haskell functions are automatically curried, and partial emulates the ability curried functions have to be partially applied. Or you can write the python versions as curried functions, but then you have to call them like foo(a)(b)(c).Wes
Minor note: you want "partially applied functions" not "partial functions".Philip JF

4 Answers

5
votes

Python's built-in map function behaves like Haskell's zipWith:

>>> def add(x,y): return x + y
... 
>>> map(add,[1,2,3],[10,20,30])
[11, 22, 33]
2
votes
def add(a, b):
    return a + b

x = [1, 2, 3, 4]
y = [5, 6, 7, 8]

>> map(add, x, y)
[6, 8, 10, 12]

Also, do check out the Python builtin itertools module: http://docs.python.org/2/library/itertools.html

0
votes

This Python code acts similar to the zipWith' function you gave:

def zip_with(f, l1, l2):
    if len(l1) == 0 or len(l2) == 0:
        return []
    else:
        return [f(l1[0], l2[0])] + zip_with(f, l1[1:], l2[1:])

This function has a couple of shortcomings, though, compared with the Haskell function. The first is that it doesn't look as nice, because Python doesn't have pattern matching syntax; we have to use len, [0], and [1:] instead. The second is that the Python function does not use lazy evaluation in any way, so zip_with will always go through the entire list, even when it could get away with stopping early. The third is that this function calls itself once for every element of the resulting list, and Python has a recursion limit of about (or exactly?) 1,000, so this function will raise an exception if the output list is more than about 1,000 elements long.

The second and third problems could be solved using generators instead.

0
votes

This is a good candidate to use built-in zip function and list comprehension:

>>> zip_with = lambda fn, la, lb: [fn(a, b) for (a, b) in zip(la, lb)]

>>> add2 = lambda x,y: x+y
>>> zip_with(add2, range(10), range(1,11))
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]