Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Haskell-like . / $

In Haskell I would write:

main = do mapM_ print . map (\x -> x^2) . filter (\x -> (mod x 2) == 0) $ [1..20]

in Python I would have to use either many brackets or useless variables ... is there anything like . and $ in Python?

like image 824
Daniel Hauck Avatar asked Oct 28 '15 13:10

Daniel Hauck


2 Answers

(I'm not familiar with Haskell, but if I understand your code snippet correctly...)

You can use a list comprehension to perform the filtering and exponentiation.

[i**2 for i in range(1,21) if i%2 == 0]
like image 173
Cory Kramer Avatar answered Sep 23 '22 15:09

Cory Kramer


I would just use whatever idiomatic Python tools are available, such as list comprehensions, as others have pointed out, instead of trying to pretend you're writing Haskell, but if you really must, you could use compose combinator function even in Python:

# this is essentially just foldr (or right `reduce`) specialised on `compose2`
def compose(*args):
    ret = identity
    for f in reversed(args):
        ret = compose2(f, ret)
    return ret

def identity(x):    return x
def compose2(f, g): return lambda x: f(g(x))

which you could use like this:

from functools import partial

# equiv. of:  map (\x -> x^2) . filter (\x -> (mod x 2) == 0) $ [1..20]
compose(partial(map, lambda x: x**2), partial(filter, lambda x: x % 2 == 0))(range(1, 21))

which admittedly does work:

>>> compose(partial(map, lambda x: x**2), partial(filter, lambda x: x % 2 == 0))(range(1, 21))
[4, 16, 36, 64, 100, 144, 196, 256, 324, 400]

...but as you can see, Python lacks certain concepts such as currying and arbitrarily definable infix operators, so even though semantically, the above snippet of code is equivalent (even identical) to the Haskell snippet, it reads quite hellish.


As to the $ operator: it has little relevance in Python — its primary purpose in Haskell is related to operator precedence, which is a non-issue in Python because you can't really use operators most of the time anyway, and all of the built-in operators have predefined precedence.

And whereas $ can additionally be used as a higher order function in Haskell:

zipWith ($) [(3*), (4+), (5-)] [1,2,3]

...replicating this in Python with its (deprecated) apply "combinator" will, again, lead to code that is just ugly:

>>> list(starmap(apply, zip([lambda x: 3 * x, lambda x: 4 + x, lambda x: 5 - x], map(lambda x: [x], [1, 2, 3]))))
[3, 6, 2]

— again, several fundamental limitations of Python are at play here:

  • laziness isn't built-in and thus not handled automatically, so without "forcing" the starmap using list(), you don't get a "normal" list back;
  • apply is not (a -> b) -> a -> b but (a1 -> a2 -> ... -> aN -> b) -> (a1, a2, ..., aN) -> b, so you need to wrap the list elements with [] and use starmap not the normal map; this is also a result of the lack of currying;
  • lambda syntax is verbose because Guido's personal preference is against lambdas, map, reduce, and so on;
like image 42
Erik Kaplun Avatar answered Sep 25 '22 15:09

Erik Kaplun