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?
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?
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 a compose
combinator 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 features such as currying and arbitrarily definable infix operators, so even though semantically, the above snippet of code is equivalent its Haskell counterpart, it is quite unreadable even for Haskellers.
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:
list()
, you don't get a "normal" list back;(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;map
, reduce
, and so on;(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]
if
guard by adjusting the range: range(2, 21, 2)
. –
Lodged [i**2 | i <- [2,4..20]]
, pretty similar to python). In general I can recommend using generator expressions rather than function composition if working with iterators, but function composition and partial application is severely lacking in Python compared to Haskell. –
Brassie 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 a compose
combinator 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 features such as currying and arbitrarily definable infix operators, so even though semantically, the above snippet of code is equivalent its Haskell counterpart, it is quite unreadable even for Haskellers.
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:
list()
, you don't get a "normal" list back;(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;map
, reduce
, and so on;Ironically (since list comprehensions are something that Python borrowed from languages like Haskell), I'd probably write the code similarly in both languages:
# Python
for xsquared in [x**2 for x in range(1, 21) if x % 2 == 0]:
print(xsquared)
# legal, but not idiomatic; you don't construct a list just
# to throw it away.
# map(print, [x**2 for x in range(1, 21) if x % 2 == 0])
and
-- Haskell
main = (mapM_ print) [ x^2 | x <- [1..20], x `mod` 2 == 0 ]
or more briefly in each:
# Python
for xsquared in [x**2 for x in range(2, 21, 2)]:
print(xsquared)
-- Haskell
main = (mapM_ print) [x^2 | x <- [2,4..20]]
Functions in Python are more difficult to compose than in Haskell. A Haskell function takes one argument and returns one value. It's easy for the compiler to check that f . g
makes sense given the defined type signatures for f
and g
. Python, however, has no such type signatures (even in 3.5, the type hinting is optional and only used during static analysis, not during runtime).
Further, Python functions can take an arbitrary number of arguments (no currying), and tuples are variable length, not fixed length. Suppose g
returns a tuple. Should f ∘ g
(my personal choice for a composition operator should such a thing ever be adopted, and Unicode operators be permitted) be equivalent to f(g(...))
or f(*g(...))
? Both make sense, and point to the "need" for two different types of composition. What if g
's return value has too many or too few values for f
? What about keyword arguments to f
? Should they be taken from a dictionary returned by g
? What seems like a simple operation becomes quite complex to define in Python.
One other thing I may be completely wrong about. I get the impression that whereas each function in Python is compiled as a distinct piece of code, Haskell can compile optimized code for each composition, so that f . g
isn't just naively converted to \x -> f (g x)
. At least in Python, for
def f(x):
return x + 5
def g(x):
return 3 * x
this is what the compiler could generate for f∘g
def fg(x):
return f(g(x))
which would be far less efficient than the equivalent of what I understand the Haskell compiler could generate:
def fg(x):
return 3*x + 5
for_ [2,4..20] (print . (^2))
. Or, pointy style, for_ [2,4..20] $ \x -> print (x^2)
. This emphasizes that the list is being used as a Traversable
and not really as a Monad
. –
Eating For this case you should better use a list comprehension like @CoryKramer said.
To apply partial application in Python you should use functools.partial
, would be something like this
from functools import partial
def compose(func1, *func2):
return func1 if not func2 else lambda x: func1(compose(*func2)(x))
myMap = partial(map, lambda x: x**2)
myFilter = partial(filter, lambda x: x%2 == 0)
myFunction = compose(myMap, myFilter)
myFunction(range(20))
Since map function returns list which is iterable and filter also you can nest them -
map(function1, (filter(function2,list)))
For more information I would recommend you read the map function documentation also filter function documentation
© 2022 - 2025 — McMap. All rights reserved.
.
allows function partial application,map
andfilter
recieve (like in Python) a function and a list somap(function).filter(function2, list)
means thatmap
appliesfunction
to each element that returnsfilter
after applyingfunction2
to elements oflist
. That's cause haskell is lazy by default – Resent.
is function composition, roughlyf . g
standing roughly for Pythonlambda x: f(g(x))
. Instead$
is applicationf $ x
is simplyf(x)
, but allows one to avoid parentheses e.g.f $ x+y+z
meaningf(x+y+z)
. – Copyeditdo
over there is redundant. – Rheumatic