Functional programming with Python
ua.pycon.org
ua.pycon.org
reduce(operator.add, map(len, ss))
No! The simpler (and more efficient, as it doesn't have to build a list of results!) way to do that is using the built-in sum() with a generator expression: sum(len(s) for s in ss)
The rest of the slides are full of similarly-complex idioms. Another one: def square_sum(a, b):
return sum(map(lambda x: x**2, range(a, b+1)))
Please, no! Generator expressions are there for a reason. Much simpler and much more efficient would be: def square_sum(a, b):
return sum(x**2 for x in range(a, b+1))
In fact, most places you see map() in Python can be rewritten as generator expressions.For stuff like functools.partial, in simple cases, isn't it clearer to just write this?
def debug(message):
log('debug', message)
I like his use of operator.itemgetter/operator.methodcaller, though. They're useful for sort keys and stuff, and faster and often clearer than lambdas.Also: namedtuple is great instead of "using classes as attribute containers".
It's true that Python will use a list comprehension or a generator expression where you would normally do this, but Python 3 still includes map() and filter(). (Despite Guido not being a big fan of these functions - http://www.artima.com/weblogs/viewpost.jsp?thread=98196).
My point is simply that the presentation does a good job at exposing what FP is and how to implement it in Python. Moving from maps to comprehensions comes naturally, once you get the hang of the idea.
PS: I have the same question about functools.partial. Can somebody answer this?
$ python -m timeit 'import string;from operator import itemgetter;[itemgetter('slice') for x in string.letters]'
100000 loops, best of 3: 14.2 usec per loop
$ python -m timeit 'import string;from operator import itemgetter;[lambda x: x.slice for x in string.letters]'
100000 loops, best of 3: 10.4 usec per loop
> Also: namedtuple is great instead of "using classes as attribute containers".Er, OK. Just don't look at the implementation[1]. Cannot be unseen.
[1]: http://hg.python.org/cpython/file/2.7/Lib/collections.py#l23...
python -m timeit 'import string;from operator import itemgetter; getter=itemgetter('slice'); [getter(x) for x in string.letters]' >python -m timeit -s "from operator import itemgetter; elem0 = itemgetter(0)" "elem0('s')"
10000000 loops, best of 3: 0.095 usec per loop
>python -m timeit -s "from operator import itemgetter; elem0 = lambda x: x[0]" "elem0('s')"
10000000 loops, best of 3: 0.138 usec per loop reduce(operator.add, map(len, ss))
You know, it's hard to understand new concept. And it's twice harder to explain new concept with sophisticated code examples. So I used as simple code and situations as possible. The same for square_sum - it's easier "to see" functional pattern in map(f,l) than in (f(x) for x in l).Regarding to debug function and partial function application, - it depends. As for me
debug = partial(log, "debug")
is easier and meaningfully describe what's actually going on. But here we have only one function, what if you have to create 5-6 same functions? Would you prefer "def" syntax instead of partial? I have one more example of using this pattern in more sophisticated situation - code listing for dictionary binding to list of functions, "partial" looks really naturally.python's data structures are mutable, and mutable data structures necessarily expose a different interface than immutable data structures. using mutable datastructures in immutable style necessarily has severe performance penalties; and to do meaningful stuff you need to use a statement, and you can't put a statement in a python lambda. And Guido thinks lambda's are un-pythonic and a misfeature which he seems to regret [1].
python is a great sandbox for imperative programmers interested in FP, and these are great slides. it's awesome to play with these concepts in a familiar language - thats how i started - but its important to note that FP is just not pythonic, and thus it's probably a mistake to put functional-style python into production.
[1] http://news.ycombinator.com/item?id=3962316
if you are interested in functional python, i wrote about it a bit this year in my blog, because despite all this, python is a nice language for evangelism aimed at imperative developers.
[2] implement generators without yield http://www.dustingetz.com/2012/10/03/implement-python-genera...
[3] easy monads http://www.dustingetz.com/2012/10/07/monads-in-python-identi...
[4] hard monads http://www.dustingetz.com/2012/10/02/reader-writer-state-mon...
[5] peter norvig's Lispy refactored using monads https://github.com/dustingetz/monadic-interpreter
http://www.artima.com/weblogs/viewpost.jsp?thread=147358
This is one of my (very few!) gripes with FP in Python.
In Haskell, the only places (that occur to me right now) where you'll have multiple lines are:
* Pattern matching function arguments
* Giving things names with `let` or `where`
* In a monadic `do` block, which is just sugar that reduces to single-expression chain of lambdas
And all of these are language design decisions that require more than just multi-statement lambdas.
Random other tidbit that doesn't deserve its own comment:
In Python, instance.method is the same as doing ClassName.method(instance). This can be helpful when using map/filter, because you get to do map(ClassName.method,lst) instead of map(lambda x: x.method,lst).
----
I don't know if this is a problem if you're going hardcore with your FP. When we're talking about "lines" in Python, one line is exactly one statement, and if you're doing FP, that statement is an expression. Multiple statements are going to be either stateful, or declaring a temporary name for an intermediate value.
I am probably not being sufficiently imaginative right now, but I can't think of any fully-fledged functional pattern which would require multiple statements in a lambda, which would not be better abstracted out and given a name, and I would genuinely like to hear some examples of cases where a multi-statement lambda make for "better" code (or to hear how my presentation of this problem might be flawed, because I haven't done any serious Python programming recently enough to be very close to the issue).
----
In Haskell, a language where the syntax has been exhaustively designed to facilitate FP, the only places where you'll see multiple lines (I'm pretty sure this is exhaustive, without using GHC extensions, eg. Arrow `proc` blocks) are:
* Pattern matching function arguments
* Giving things names with `let` or `where`
* In a monadic `do` block -- which is just sugar that reduces to single-expression chain of lambdas, and are not equivalent to multi-statement lambdas
>>> a = range(12)
>>> [a.pop(i) for i in range(3)]
[0, 2, 4]
>>> a
[1, 3, 5, 6, 7, 8, 9, 10, 11]
As long as list methods have methods that change state of the object, even when processing said object, Python handles FP at a very superficial syntactic level. >>> a = tuple(3*x for x in range(5))
>>> a[2]
6
>>> map(lambda x: str(x), a) #can be iterated
['0', '3', '6', '9', '12']
>>> a[2] = 9
TypeError: 'tuple' object does not support item assignment
>>> a.pop()
AttributeError: 'tuple' object has no attribute 'pop'But in the process of translating a comment I wrote in another forum from Ruby to Python, it became apparent that, as long as it's pythonic to implement `__add__` for any monoidal type, the resulting polymorphic `sum`, together with list comprehensions, will cover pretty much everything[0].
So I'm actually coming around to Guido's side here.
----
[0] Assuming there's a consistent __mul__/product for alternative monoid instances, and with `all` and `any` to handle numerically-cast booleans.
I think you were right the first time; removing reduce from Python is very lame.
I don't have the math to back this up, so please correct me if you know otherwise (and especially if you can demonstrate otherwise, though I know this being wrong is not contingent on that), but: I'm pretty sure `mconcat` with a `map` (or a list comprehension) is exactly as general as `fold`.
> Plus, unless you implement something akin to newtype wrappers for alternative monoid instances (hardly Pythonic), you're going to be restricted to a single foldable function for any given type.
Again, I know this isn't an argument and demonstrates shitty memory on my part, but a single solid example here would be key to me understanding this debate:
What is an example of a type with more than 2 monoids on it, whose fold could not be cleanly expressed with a sum/product and a map/comprehension, which is not already included in the standard library (any/all/gcd/lcm/max/min)?
[I suspect there either won't be an easy answer, or it will be so basic that I'll be brutally embarrassed... this is all kinda talking out of my ass, I have no exposure to category theory outside of reading haskell papers]
Maybe I'm being obtuse, but I don't quite understand what you mean when you say "mconcat with a map". But to see why mconcat isn't as general as a fold, you need only to take a look at the types:
mconcat :: Monoid a => [a] -> a
foldl :: (a -> b -> a) -> a -> [b] -> a
mconcat is constrained to only operating on monoidal types, whereas foldl has no such constraint. Likewise, mconcat must always yield a result of the same type as the list elements, while foldl is capable of accumulating a result of any type.mconcat can be implemented in terms of a fold, e.g.
mconcat = foldl mappend mempty
Such a partially applied foldl is obviously less general than an unapplied foldl, as its first 2 arguments are fixed. There is no way to implement foldl (or even foldl1) in terms of mconcat.There are plenty of uses for folds that don't involve monoids, and therefore can't be implemented with mconcat plus a Monoid instance. As a trivial example, take
Prelude> foldl (/) 400 [4, 4, 5]
5.0
There is no "quotient monoid", because a monoid is defined as a type with an associative binary operation and an identity element with respect to that operation. Division has a right identity (1), but it isn't associative, so it can't be used as a monoidal operation like addition and multiplication can. But foldl still works fine in this case, as the above example shows, where mconcat would not (unless you write a Monoid instance that violates the monoid laws).Associativity notwithstanding, it's also not adequate to say that 2 monoid instances should be enough for any type. Maybe that is true in some or even most cases, but it is certainly not true in the general case. Haskell has newtypes to mitigate this problem, essentially enabling an arbitrary number of Monoid instances to be defined per type, but Python only lets you write one __add__ and one __mul__ for a given class. On top of that, I don't believe Python even has a product function that folds __mul__ over a list the way sum folds __add__, so really the limit is 1, not 2.
Anyway it's all moot because, as someone pointed out, reduce hasn't been removed from Python, just relegated to a library. :) I think it's a fundamental enough operation that it should be built in, but obviously Guido and I have differing opinions on FP.
http://byorgey.wordpress.com/2012/11/05/foldr-is-made-of-mon...
> We already have sum(); I'd happily trade reduce() for product(), so that takes care of the two most common uses.
product() never made it in as a builtin; anyone know why?
[0]: http://www.artima.com/weblogs/viewpost.jsp?thread=98196
Now if you don't want to type the code every time, that's fine: use collections.namedtuple.
A little mistake on page 48: the Clojure code will fail. Use (map #(* % 2) '(1 2 3)) or (map (partial * 2) '(1 2 3)) instead of: (map #(%*2) '(1 2 3))
It's a work in progress, and I often think to myself, "Is this a good idea".
Here's the latest iteration: I'm in the middle of documenting things for an alpha release: https://github.com/ericmoritz/fp/blob/feature/documentation/...
Long answer:
In [1]: from operator import attrgetter
In [2]: class Person:
...: def __init__(self, name):
...: self.name = name
...:
In [3]: me = Person('walrus')
In [4]: getattr(me, 'name')
Out[4]: 'walrus'
In [5]: attrgetter('name')(me)
Out[5]: 'walrus'
This is potentially useful if you're using `map`: In [6]: people = [me, Person('aschwo')]
In [7]: list(map(lambda obj: getattr(obj, 'name'), people))
Out[7]: ['walrus', 'aschwo']
In [8]: list(map(attrgetter('name'), people))
Out[8]: ['walrus', 'aschwo']
Really, though, the functional programming idioms don't meld too well with Python. I think this is a lot nicer: In [9]: [person.name for person in people]
Out[9]: ['walrus', 'aschwo']PS: map returns a list, no need to call list() on it.
In [1]: sq = lambda x: x * x
In [2]: map(sq, [1,2,3,4])
Out[2]: <builtins.map at 0x2135050>
In [3]: list(_)
Out[3]: [1, 4, 9, 16]f = attrgetter("some_property")
f = lambda obj: getattr(obj, "some_property")
I wish I had the speaker to give a little more with each slide though.