Why Isn't Python Very Good for Functional Programming

Why isn't Python very good for functional programming?

The question you reference asks which languages promote both OO and functional programming. Python does not promote functional programming even though it works fairly well.

The best argument against functional programming in Python is that imperative/OO use cases are carefully considered by Guido, while functional programming use cases are not. When I write imperative Python, it's one of the prettiest languages I know. When I write functional Python, it becomes as ugly and unpleasant as your average language that doesn't have a BDFL.

Which is not to say that it's bad, just that you have to work harder than you would if you switched to a language that promotes functional programming or switched to writing OO Python.

Here are the functional things I miss in Python:

  • Pattern matching
  • Tail recursion
  • Large library of list functions
  • Functional dictionary class
  • Automatic currying
  • Concise way to compose functions
  • Lazy lists
  • Simple, powerful expression syntax (Python's simple block syntax prevents Guido from adding it)

  • No pattern matching and no tail recursion mean your basic algorithms have to be written imperatively. Recursion is ugly and slow in Python.
  • A small list library and no functional dictionaries mean that you have to write a lot of stuff yourself.
  • No syntax for currying or composition means that point-free style is about as full of punctuation as explicitly passing arguments.
  • Iterators instead of lazy lists means that you have to know whether you want efficiency or persistence, and to scatter calls to list around if you want persistence. (Iterators are use-once)
  • Python's simple imperative syntax, along with its simple LL1 parser, mean that a better syntax for if-expressions and lambda-expressions is basically impossible. Guido likes it this way, and I think he's right.

What makes Python not a functional programming language?

A great post already on SO: Why isn't Python very good for functional programming?

The statement "Python is not a functional language" stems the language's design supporting other paradigms over a functional style [1]. That does not mean the language doesn't show functional characteristics as you have discerned. There is even official documentation that describes how you can leverage the python language to write code in a functional style.

Python is a multi-paradigm language and thus has a blend of attributes from each of paradigm it supports. Python can easily be considered one paradigm or not, much like deciding which paradigm is best, it is subject to the opinion of the individual.

For some interesting related readings, here is a SO post touching upon the general difficulty in what makes a language purely functional (for practical uses):
Are there any purely functional Schemes or Lisps?

And this article explores the conflict of real world applications and the technicalities that contrast with functional programming paradigm: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.13.9123

[1] as it was initially designed, and as it is continued to be designed

Why program functionally in Python?

Edit: I've been taken to task in the comments (in part, it seems, by fanatics of FP in Python, but not exclusively) for not providing more explanations/examples, so, expanding the answer to supply some.

lambda, even more so map (and filter), and most especially reduce, are hardly ever the right tool for the job in Python, which is a strongly multi-paradigm language.

lambda main advantage (?) compared to the normal def statement is that it makes an anonymous function, while def gives the function a name -- and for that very dubious advantage you pay an enormous price (the function's body is limited to one expression, the resulting function object is not pickleable, the very lack of a name sometimes makes it much harder to understand a stack trace or otherwise debug a problem -- need I go on?!-).

Consider what's probably the single most idiotic idiom you sometimes see used in "Python" (Python with "scare quotes", because it's obviously not idiomatic Python -- it's a bad transliteration from idiomatic Scheme or the like, just like the more frequent overuse of OOP in Python is a bad transliteration from Java or the like):

inc = lambda x: x + 1

by assigning the lambda to a name, this approach immediately throws away the above-mentioned "advantage" -- and doesn't lose any of the DISadvantages! For example, inc doesn't know its name -- inc.__name__ is the useless string '<lambda>' -- good luck understanding a stack trace with a few of these;-). The proper Python way to achieve the desired semantics in this simple case is, of course:

def inc(x): return x + 1

Now inc.__name__ is the string 'inc', as it clearly should be, and the object is pickleable -- the semantics are otherwise identical (in this simple case where the desired functionality fits comfortably in a simple expression -- def also makes it trivially easy to refactor if you need to temporarily or permanently insert statements such as print or raise, of course).

lambda is (part of) an expression while def is (part of) a statement -- that's the one bit of syntax sugar that makes people use lambda sometimes. Many FP enthusiasts (just as many OOP and procedural fans) dislike Python's reasonably strong distinction between expressions and statements (part of a general stance towards Command-Query Separation). Me, I think that when you use a language you're best off using it "with the grain" -- the way it was designed to be used -- rather than fighting against it; so I program Python in a Pythonic way, Scheme in a Schematic (;-) way, Fortran in a Fortesque (?) way, and so on:-).

Moving on to reduce -- one comment claims that reduce is the best way to compute the product of a list. Oh, really? Let's see...:

$ python -mtimeit -s'L=range(12,52)' 'reduce(lambda x,y: x*y, L, 1)'
100000 loops, best of 3: 18.3 usec per loop
$ python -mtimeit -s'L=range(12,52)' 'p=1' 'for x in L: p*=x'
100000 loops, best of 3: 10.5 usec per loop

so the simple, elementary, trivial loop is about twice as fast (as well as more concise) than the "best way" to perform the task?-) I guess the advantages of speed and conciseness must therefore make the trivial loop the "bestest" way, right?-)

By further sacrificing compactness and readability...:

$ python -mtimeit -s'import operator; L=range(12,52)' 'reduce(operator.mul, L, 1)'
100000 loops, best of 3: 10.7 usec per loop

...we can get almost back to the easily obtained performance of the simplest and most obvious, compact, and readable approach (the simple, elementary, trivial loop). This points out another problem with lambda, actually: performance! For sufficiently simple operations, such as multiplication, the overhead of a function call is quite significant compared to the actual operation being performed -- reduce (and map and filter) often forces you to insert such a function call where simple loops, list comprehensions, and generator expressions, allow the readability, compactness, and speed of in-line operations.

Perhaps even worse than the above-berated "assign a lambda to a name" anti-idiom is actually the following anti-idiom, e.g. to sort a list of strings by their lengths:

thelist.sort(key=lambda s: len(s))

instead of the obvious, readable, compact, speedier

thelist.sort(key=len)

Here, the use of lambda is doing nothing but inserting a level of indirection -- with no good effect whatsoever, and plenty of bad ones.

The motivation for using lambda is often to allow the use of map and filter instead of a vastly preferable loop or list comprehension that would let you do plain, normal computations in line; you still pay that "level of indirection", of course. It's not Pythonic to have to wonder "should I use a listcomp or a map here": just always use listcomps, when both appear applicable and you don't know which one to choose, on the basis of "there should be one, and preferably only one, obvious way to do something". You'll often write listcomps that could not be sensibly translated to a map (nested loops, if clauses, etc), while there's no call to map that can't be sensibly rewritten as a listcomp.

Perfectly proper functional approaches in Python often include list comprehensions, generator expressions, itertools, higher-order functions, first-order functions in various guises, closures, generators (and occasionally other kinds of iterators).

itertools, as a commenter pointed out, does include imap and ifilter: the difference is that, like all of itertools, these are stream-based (like map and filter builtins in Python 3, but differently from those builtins in Python 2). itertools offers a set of building blocks that compose well with each other, and splendid performance: especially if you find yourself potentially dealing with very long (or even unbounded!-) sequences, you owe it to yourself to become familiar with itertools -- their whole chapter in the docs makes for good reading, and the recipes in particular are quite instructive.

Writing your own higher-order functions is often useful, especially when they're suitable for use as decorators (both function decorators, as explained in that part of the docs, and class decorators, introduced in Python 2.6). Do remember to use functools.wraps on your function decorators (to keep the metadata of the function getting wrapped)!

So, summarizing...: anything you can code with lambda, map, and filter, you can code (more often than not advantageously) with def (named functions) and listcomps -- and usually moving up one notch to generators, generator expressions, or itertools, is even better. reduce meets the legal definition of "attractive nuisance"...: it's hardly ever the right tool for the job (that's why it's not a built-in any more in Python 3, at long last!-).

Is Python a functional programming language or an object oriented language?

Python, like many others, is a multi-paradigm language. You can use it as a fairly strictly imperative language, you can use it in a more object-oriented way, and you can use it in a more functional way. One important thing to note though is that functional is generally contrasted with imperative, object-oriented tends to exist at a different level and can be "layered over" a more imperative or a more functional core.

However Python is largely an imperative and object oriented language: much of the builtins and standard library are really built around classes and objects, and it doesn't encourage the sort of thinking which functional languages generally drive the user to.

In fact going through the (fairly terrible) list the article you link to provides, Python lands on the OOP side of more or less all of them:

  • it doesn't use immutable data much (it's not really possible to define immutable types in pure python, most of the collections are mutable, and the ones which are not are not designed for functional updates)
  • its execution model is very imperative
  • it has limited support for parallel programming
  • its functions very much do have side-effects
  • flow control is absolutely not done using function calls
  • it's not a language which encourages recursion
  • execution order is very relevant and quite strictly defined

Then again, much of the article is nonsense. If that is typical of that site, I'd recommend using something else.

If so, if you're trying to program something very mathematical and computational, would Python still be a good choice

Well Python is a pretty slow language in and of itself, but at the same time it has a very large and strong ecosystem of scientific libraries. It's probably not the premier language for abstract mathematics (it's rather bad at symbolic manipulation) but it tends to be a relatively good glue or prototyping tool.

As functional languages are more suitable for mathematical stuff

Not necessarily. But not knowing what you actually mean by "mathematical stuff" it's hard to judge. Do you mean symbolic manipulations? Statistics? Hard computations? Something else entirely?

Which is a more functional programming language, Haskell or Python?

You'll find many criticisms elsewhere about how Python programmers should not use functional features like lambda. Alex Martelli is particularly good at this. But if you approach functional programming as a Python programmer, you will not get much of a feel for why people are interested in functional programming in the first place. In particular, you will not have access to a powerful static type system and algebraic data types. These tools are definitely part of the Haskell mindset.

Functional programming is not so much a set of features a a way of thinking. To see that way of thinking in action and to start to learn it, check out the paper Why Functional Programming Matters by John Hughes. What you learn from Hughes you can easily apply in Haskell. In fact, Haskell is much more likely to force you to learn that new way of thinking. With Python you can use a few lambdas here and there and fool yourself that you're learning functional programming. You won't be.

That said, Haskell can be a lot to swallow all at once. If you're finding it difficult you might want to try Standard ML or Racket (formerly called PLT Scheme or DrScheme). Standard ML will introduce you to more new ideas, but those people don't have their act together to welcome newcomers in the way that the Racketeers or the Haskell people do.

pythonic style for functional programming

As someone who adores functional programming, don't write in functional style in Python.

This hard and fast rule is a little ham-handed, and there are certainly ways to do what you're trying to do using typical functional tools like map, filter, and reduce (called functools.reduce in Python), but it's likely that your functional code will look uglier than sin, in which case there's no reason to prefer it over something imperative and pretty.

result = []
for hero in get_heros("Jedi"):
movies = get_movies(hero)
for movie in movies:
if "A New Hope" in movies:
result.append((hero, movies))

This could be done with a list comprehension, but is probably less readable.

result = [(hero, movies) for hero in get_heros("Jedi")
for movies in [get_movies(hero)] if "A New Hope" in movies]

Functional programming has a clear meaning, but does functional language?

Among people who study programming languages for a living, "functional programming language" is a pretty weakly bound term. There is a strong consensus that:

  • Any language that calls itself functional must support first-class, nested functions with lexical scoping rules.

A significant minority also reserve the term "functional language" for languages which are:

  • Pure (or side-effect-free, referentially transparent, see also)

as in languages like Agda, Clean, Coq, and Haskell.

Beyond that, what's considered a functional programming language is often a matter of intent, that is, whether is designers want it to be called "functional".

Perl and Smalltalk are examples of languages that support first-class functions but whose designers don't call them functional. Objective Caml is an example of a language that is called functional even though it has a full object system with inheritance and everything.

Languages that are called "functional" will tend to have features like the following (taken from Defining point of functional programming):

  • Anonymous functions (lambda expressions)
  • Recursion (more prominent as a result of purity)
  • Programming with expressions rather than statements (again, from purity)
  • Closures
  • Currying / partial application
  • Lazy evaluation
  • Algebraic data types and pattern matching
  • Parametric polymorphism (a.k.a. generics)

The more a particular programming language has syntax and constructs tailored to making the various programming features listed above easy/painless to express & implement, the more likely someone will label it a "functional language".



Related Topics



Leave a reply



Submit