Python is not a functional programming language.
But I do want it to be one!
Why Functional?
Why functional? Because functional codes can be more intuitive to write and easier to read, and it might enable better performance in some cases (like you don't need to append
to a list and pay for the possible copy).
Of course, sometimes imperative codes are better. But in Python you can always be imperative anyway.
Python can do functional stuff. However, as it is Object Oriented down to the core, and the typical Pythonic code doesn't involve much functional programming, common functional patterns require tedious boilerplates to implement.
Problems of Pythonic
There are plenty of functional packages out there, and even some functional tools inside the standard library. However, they just don't quite fit my preference.
Standard functools
and toolz
provides some higher-level functional tools, but it is too Pythonic. Why it is not good to be Pythonic? Python encourages data to flow from right to left, from inside to outside! For Haskell, you have .
and $
to compose functions, but you don't have that in your pocket writing Python.
a = list(map(func, filter(pred, iterable)))
# ^^^^^^^^ data enters here
# <----- through filter
# <-- through map
# <--- collect into a list
# First you type your data
iterable
# And then you realize you need to wrap it inside a filter
filter(pred, iterable # So you move left to write the filter
) # And you move right to write the right paren!
# And then you realize you need to wrap it inside a map
# Oh my. Another round.
# ...
You might say we have list comprehensions. Looks better, but now we jump back and forth!
a = [func(x) for x in iterable if pred(x)]
# ^^^^^^^^ data enters here
# through filter --------->
# <---------- then jump back here
# <--- through map
# <----- collect into a list
Well, in Haskell the data also comes after the function. But its unique syntax and operator .
and $
will at least save you from wrapping everything in a hell of parentheses. If you want it from left to right, no problem, Haskell has &
for you.
-- Haskell
a = map func . filter pred $ iterable
-- ^^^^^^^^ data enters here
-- <----- through filter
-- <-- through map
a = iterable & filter pred & map func
-- ^^^^^^^^ data enters here
-- -----> through filter
-- --> through map
In Rust, you can chain methods on iterators. But Python doesn't give you anything on its iterators.
// Rust
let a = iterable
.filter(pred)
.map(func)
.collect::<Vec<_>>();
That's also possible in JavaScript Array
s, and in the future will be also available in the new Iterator
s.
// JavaScript
const a = iterable
.filter(pred)
.map(func);
Problems of Static Type Checking
PyFunctional
provides idiomatic chainable functional operations, but it is too dynamic. It is not typed. Unknown
will pollute the whole logic chain, and functional programming without static type checking is more error-prone, especially when composing functions.
Recent advancement on typing
library, like ParamSpec
, really makes a typed functional library possible.