8 ms·
Functional Programming with Python
- gpsarakis 13y agoGreat presentation! Just a question/remark: Partial function application seems handy but using functools.partial to create a new function seems obsolete since you can use non-keyworded variable length argument lists with *args in your function definitions?
- deleted 13y ago[deleted]
- hcarvalhoalves 13y agoLove the video mentioned in the slides: https://www.youtube.com/watch?v=o9pEzgHorH0 https://www.youtube.com/watch?v=o9pEzgHorH0
- d0m 13y agoLook, I love functional programming. I use it all the time and I procrastinate in the shower about the best to add currying to all my favorite languages. That being said, even after all those years, I still find this: expr, res = "28+32+++32++39", 0 for t in expr.split("+"): if t != "": res += int(t) print res SO MUCH more readable than this: from operator import add expr = "28+32+++32++39" print reduce(add, map(int, filter(bool, expr.split("+")))) A couple years ago I created a library called Moka in python to make functional and nesting easier. That would be written this way: (Moka(expr) .do(string.split, '+') .keep(lambda x: x != "") .map(int) .reduce(moka.add)) Still, the imperative version, althought it has state and feels hacky for a functional hacker, is still pretty damn clear and easy to read.
- ak217 13y agoexpr = '28+32+++32++39' print sum(int(i) for i in expr.split('+') if i is not '')
- llimllib 13y agoIt's really weird to me that the author prefers map(add, ...) to sum(generator expression).
- rbanffy 13y agoBecause with map you can use any function and sum maps __add__. It also illustrates passing functions as arguments. More subtly, a generator (or list comprehension) implies the order in which the results are generated and an implicit but observable changing state. If I'm trying to write (or, at least, express, since we are talking about Python) parallelism, map would be a better choice.
- Locke1689 13y agoMore subtly, a generator (or list comprehension) implies the order in which the results are generated and an implicit but observable changing state. If I'm trying to write (or, at least, express, since we are talking about Python) parallelism, map would be a better choice. This is definitely not true -- generator expressions can be expressed using lazy list semantics. By your logic every Haskell list comprehension would be stateful. I would encourage you to write the denotational or operational semantics of your expressions in these matters rather than simply going by intuition.
- rbanffy 13y ago> generator expressions can be expressed using lazy list semantics That does not change the fact the results are given in a certain order. Lazy list semantics means the data will be calculated as it's needed, therefore, the calculation cannot be parallelized.
- Locke1689 13y agoNot true. See http://hackage.haskell.org/packages/archive/parallel/3.1.0.1/doc/html/Control-Parallel-Strategies.html#v:parList http://hackage.haskell.org/packages/archive/parallel/3.1.0.1...
- justinpombrio 13y agoThere is something to be said for functional code which clearly shows the operations being performed in (reverse) order. In Haskell: (putStrLn . show . sum . map read . filter (not . null) . split '+') "28+32+++32++39" or, if you like list comprehensions, (putStrLn . show . sum) [read x | x <- (split '+' "28+32+++32++39"), not (null x)] But don't underestimate the power of syntax! It's much easier to see the structure of your (d0m's) first example than of the second at a glance. I won't comment on the readability of my Haskell code as I'm already far to conditioned. (Note that I had to define 'split' by hand; it is a gaping hole in the otherwise very comprehensive Data.List library.)
- hcarvalhoalves 13y agoI personally find Haskell's syntax a bad example in language syntax. It's super powerful, but with too many inconsistencies, idiosyncrasies and noise. That, on top of having to wrap your head around new or difficult concepts, makes it a chore. Most code I (try to) read is intractable.
- tome 13y ago> I personally find Haskell's syntax a bad example in language syntax. It's super powerful, but with too many inconsistencies, idiosyncrasies and noise. Interesting. Could you give an example? I find Haskell to have pretty much the most consistent, non-idiosyncratic and clean syntax of any language I know.
- bjourne 13y agoThe order of operations is incredibly haphazard. Here is GP:s example again decorated with in which order the expression is evaluated: {6}({9}putStrLn . {8}show . {7}sum) {5}[{2}read x | x <- ({1}splitOn "+" "3++4"), {4}not ({3} x)] Here is the same code expressed in Factor: "3++4++9" "+" split harvest [ string>number ] map sum present print Annotated with evaluation order: "3++4++9"{1} "+"{2} split{3} harvest{4} [ string>number{5} ]{6} map{7} sum{8} present{9} print{10}
- hcarvalhoalves 13y agoFunny. I've been mostly exposed to imperative programming (learned on BASIC and Pascal), but nowadays I find functional "style" much more intuitive. The problem with loops and control structures is that they don't tell, at a glance, what data structure you're operating on and what transformations you're doing. It gets exponentially worse with else clauses and nesting. It just feels bureaucratic to program like that. In a functional style, you have more expressive primitives to work with (map, reduce, filter, fold, ...). That turns the code more concise by focusing on the data structures rather than variable mangling. That said, I find the syntax of most languages (even functional-orientated ones) a chore. This is an area that could improve a lot.
- nimish 13y agoIt's often faster too. List/generator comps are the way to go in python. As much as I like the combinatory approach, python does not make it very readable or maintainable.
- tome 13y agoI love your Moka example. To me it's so much clearer than either of the others and it's basically what I would write in Haskell. The understandability-complexity of the Moka style scales linearly with the number of components, because it is written "one-dimensionally". The problem with the imperative style is that its understandability-complexity is superlinear in the number of components because any variable in a nested loop can interact with any other variable. I also like your use of "keep" as opposed to "filter". I can never remember if "filter" means "filter out" or "filter in"!
- d0m 13y agoThanks. The library was highly inspired from Arc where most of the keywords are short and sweet. (keep, fold, [] shortcut, etc.)
- 0101 13y agoI wrote a library to help with this as well. f = X.split('+') | where(X) | foreach(int) | sum print f(expr) http://0101.github.io/pipetools/ http://0101.github.io/pipetools/
- agumonkey 13y agoFew things : - functional is accumulative in thinking. I can evaluate each step gradually without doing anything more than function composition. In imperative programming its coupled. - functional is side-effect free, can't mis-assign res. - functional is abstract friendly, I can pass the predicate I want, same for accumulation/combination. I kinda agree that the imperative code is somehow easier to read (3 little chunks of lines instead of 3 nested composition) but to write, test or adapt the functional way as some advantages.
- fexl 13y agoI agree that functional programming is wonderful, but imperative programs can often look especially clear. Quite a conundrum. In Fexl (http://fexl.com http://fexl.com), you can reduce the nesting by using the ';' ("pivot") syntax. So instead of this: print (fold add 0 (map int (filter valid_int (split "+" exp))) You can eliminate the nesting as follows: print; fold add 0; map int; filter valid_int; split "+" exp You could simplify that form by redefining "int" to return 0 on any invalid input. So we redefine int as follows: \int=(\str valid_int str (int str) 0) Presumably the original int dies on invalid input. Actually I would prefer that the inner int function returns either no or (yes val), i.e. a "maybe", and then we could wrap the default 0 around that. But in any case we now have a robust, fault-tolerant int function, and we can now say: print; fold add 0; map int; split "+" exp
- _ZeD_ 13y agoeverytime I see reduce(operator.add, ...) and variations like that I think GvR was right to relegate reduce to the functools module... it's just that sum(...) covers the most common (90%? only?) usage...
- ak217 13y agoNot just that. More generally, comprehensions cover the most common use cases and are tremendously more readable.
- sirclueless 13y agoNot just the common ones, pretty much every one. Python 3 even removed the reduce builtin because it was so unreadable. Generator expressions and list comprehensions do so much more in a much more elegant way.
- jfarmer 13y agoHow can they do "so much more" when both map and filter can be implemented in terms of reduce, but the opposite is not true? I can agree with you in terms of readability, even as a mathematician. For example, the list comprehension syntax mirrors the mathematician's "set-builder notation," so it feels very natural. However, consider something like a "max", "min", or "argmax" function. Like map and reduce, you can implement this in terms of reduce, but I don't see how you can implement it in terms of a list comprehension. Or consider something like an RPN calculator, which could be implemented as a single reduce call whose initial value is an empty stack and whose input list is the list of RPN tokens. It seems that list comprehensions can do less in a very objective sense and couldn't be used to replace reduce without other supporting characters. Python has those supporting characters, of course, so there's a good 80/20 argument for favoring list comprehensions over a more opaque call to reduce.
- jfarmer 13y ago"Like map and filter," I mean, not "Like map and reduce."
- zipfle 13y agoI'm a python-native programmer who just recently picked up SICP. I've been trying to use some more functional tools; I told a friend at work one day that I was going to try to do everything with just map and filter. But I also just had an experience I haven't had much since I was extremely new: the experience of going back to something I wrote last week and not being able to decipher it. I'm going to keep it up, partly because I like it and also because I have some hope (unjustified?) that map will give me better performance than looping. I wish there was a way to enable tail recursion though--seriously, I promise I'll ask for clear stack traces when I need them.
- bulatb 13y agoNot pretty, but it does the trick. Kinda. Or at least it won't destroy the stack if you recurse too deep. from collections import namedtuple TailCall = namedtuple('TailCall', ['function', 'args']) def tail_recurse(fn, args): next_call = fn(*args) while isinstance(next_call, TailCall): next_call = next_call.function(*next_call.args) return next_call def count_to_4(start): """Dummy tail-recursive function""" if start == 4: return 4 else: return TailCall(count_to_4, [start + 1]) print tail_recurse(count_to_4, [-6])
- deleted 13y ago[deleted]
- dorolow 13y agohttp://wiki.python.org/moin/PythonSpeed/PerformanceTips http://wiki.python.org/moin/PythonSpeed/PerformanceTips List comprehensions beat map in CPython. As a fellow pythonista, I can guarantee you that using the builtin higher-order functions excessively will cause you nothing but pain. The feeling of being lost when you revisit code will be common. I had a coworker who undertook a plan similar to yours, except he gave himself less rigid constraints. He is a very smart guy but he fell into the tar pit of code masturbation and couldn't finish the project he took on. He simply couldn't manage the complexity of his application in his quest to be As Functional As Possible. The project was never completed and after wasting many man hours, the company was forced to purchase $25,000+ worth of software to compensate. It didn't reflect well on him. Python has functional programming support, but it's not intended to be the dominant paradigm. Python 3 even removed reduce as a builtin. The high order functions are still useful, but definitely not all the time. The principles of functional programming, such as limiting side effects and statefulness, are always useful. But Python has awful lambdas and no lexical scoping (fixed in 3). Python's functional support will always be hobbled as Guido doesn't support it. Since you seem to have a healthy interest in functional programming, I'd suggest looking at Clojure. If you want to challenge yourself, it's a good choice as you'd have to learn lisp (macros, code is data, s-exprs, etc) and the JVM ecosystem, while being almost totally constrained to writing functional code. Haskell is even more strict and is a pure functional language, forbidding state entirely and relying on monads for things like I/O. I almost like it as much as Clojure. Erlang and Ocaml are also worth checking out.
- GeneralMayhem 13y agoSo, basically, with lots of work, you can make something that's technically functional but with none of the beauty of a language that's meant for it. I understand the appeal of functional programming, really I do, but why can't we accept that sometimes just because you can doesn't mean you should?
- ivanbrussik 13y agowow, i love your presentation design, type and function :)
- mahmud 13y agoIt gave me motion sickness, was not expecting transparent, 3D rotation.
- ttflee 13y agoThe part I enjoy most is the Lato font. It looks really sweet.
- westurner 13y agois this a reveal.js rendering of an IPython notebook ? ipython nbconvert --to slides <notebook.ipynb> http://ipython.org/ipython-doc/dev/interactive/nbconvert.html#converting-notebooks-to-other-formats http://ipython.org/ipython-doc/dev/interactive/nbconvert.htm...
- dickler 13y agoThere's little reason to use python instead of haskell these days. - compiled code, fast - cleaner, nicer, conciser syntax - type safety built in - pattern matching - numpy equivalents are now available - purity (see John Carmack talking about how it helps when codebases get large) To switch from python to haskell easily within minutes, realize that a python function is simply do notation in haskell. So replace assignment (=) with arrow (<-) and write with "func v1 v2 = do". Then pure functions are those without do and no assignment written lisp/scheme style. "$" means you can get rid of the parentheses. After that, declaring types is equivalent to expressing how syntax tree can be written. You can use these to plan out how you attack your problem and then let compiler help you when writing out your functions. You could even just write out types and outsource the function writing to someone else (similar to your oo architects can parcel out code writing). So, all python, golang, and ruby programmers - you really should check out haskell and realize it's superiority. They've taken your niche. (If anyone wants to pay me to code in haskell, send me an email at haskellpostgresprogrammer@gmail.com, looking for telecommute work)
- dev360 13y agoIt is very nice and I would agree with you. I had a lot of problems interacting with the IO monads in the beginning though, and the type system in a production environment is a lot more intricate than what most of the literature purports ;).. Other than that it was pretty easy to start writing code without knowing much.
- deleted 13y ago[deleted]
- pekk 13y agoIt's a completely different language with different goals. It's like you said "there's little reason to eat eggs instead of drinking wine these days." Python was expressly NOT intended to be a compiled language with heavy emphasis on type safety.
- deleted 13y ago[deleted]
- Locke1689 13y agoThe missing slide: profiling. I love functional programming. Nonetheless, I strongly believe in using the natural idioms of your language: they are more widely used, more widely optimized, and more often thought about. I think you would find that the functional style is not idiomatic in Python most of the time. This can hurt readability, performance, and interoperability. Overall, I would encourage people to do what is natural -- it is often also what's correct. For example, Python generators expressions are lazy sequence semantic constructions and are both functional in semantics and better than the equivalent map/reduce semantics.
- matchu 13y agoThe itertools library offers imap, ifilter, etc. for big cases, but I tend to agree that generators read better and are shorter surprisingly often.
- b0rsuk 13y agoGood points. Python lists are surprisingly slightly faster than tuples (which are immutable). That's because lists are extremely common, and more effort went into optimizing them.
- dev360 13y agoSo I felt really torn with this. I applaud the author for introducing Python devs to FP; I love functional programming. Python was one of the things that opened my eyes to FP with its cute little m/r syntax and for that I'm forever grateful. Over the years, I have found that python code that people write to solve problems will fall in roughly four categories: a) completely ignorant and inefficient code; b) concise, idiomatic python code with a focus on readability; c) fancy functional code where the author is showing off their FP savvy (peppered with recursion and m/r); and d) raw, boring C-influenced code that looks primitive and boring to read, but is incredibly efficient and algorithmically sound. As much as I used to love going for b), I then prided myself for writing c). But last few years I have realized how under performing c) is for most serious work, so I tend to avoid it and try to strike a balance between d) and b), but preferably the latter. To write code in c) and tell yourself, well the data size will never be so great that the stack will blow, or memory will suffer, is in many ways just like avoiding null checks all together and saying to yourself your data will always have perfect integrity... e.g. any sane person will tell you that you cannot rely on this mindset in the long run. So, for me at least it comes down to this: to try to pretend that you can write FP code effectively in Python is an exercise in futility. FP is more than writing a map and reduce. If thats your basis of reasoning, yeah I agree take that and run with it - I find m/r to be a neat syntax to use here and there in your program but its really challenging to write a whole program in python that is composed of several chained map / reduce functions to a large extent because things are not oriented around generators by default.. I'm not saying its impossible, just that it goes against the grain. I also argue that recursion and guard expressions (and lazy evaluation) are at the heart of the FP paradigm and if you want to do it right, then its just retarded to sit and do this in Python when there is no tail recursion.. how are you going to appreciate this concept when its so limiting?? If you want to muse yourself and marvel at pretty code, write it like this and have fun with it, but please pick another language if you want to actually ship production code. And ... so sorry to piss on the parade.
- tome 13y agoYour comment is very close to how I feel. I spend significant effort trying to shoehorn FP concepts into Python, because I wanted to use FP but was forced to use Python. In the end I decided to quit my job so I could use Haskell instead. Programming FP in an actual FP language is much more satisfactory!
- tome 13y agoIf you're forced to write Python and you like FP I think it makes sense to try to twist the Python you write towards FP as much as possible. The advice "Stop writing classes" is particularly helpful in my experience. However the "What did we miss?" slide tells it like it is. FP in Python is never going to be like FP in, e.g., Haskell. Moreover, the libraries you use are going to have objects and mutable state everywhere which ends up being very frustrating. If you're not forced not to use FP, I recommend just going with Haskell (or OCaml or Scala or whatever according to your taste).
- ams6110 13y agoI recommend just going with... If you're writing some kind of server, I'd nominate Erlang for serious consideration. The OTP framework is fantastic and rock solid.
- goatslacker 13y agoI used to dream about FP and Python; then I read about Guido's plans for removing `lambda` and `reduce` (http://www.artima.com/weblogs/viewpost.jsp?thread=98196 http://www.artima.com/weblogs/viewpost.jsp?thread=98196) and that is when I stopped writing Python.
- hat_matrix 13y agoThat disappointed me also, but the FP voice from the community is strong enough. Reduce lives on in functools and the functional package also adds additional functions, like compose. It's one of the packages I install first in a new Python installation. But true, Python will never grow into Haskell.
- hmsimha 13y agoAlso, lambda doesn't appear to be going anywhere. It's one of my favorite features.
- hat_matrix 13y agoYes, not going away but not growing beyond the crippled, single-statement version it is now...
- bcl 13y agoThe official Python docs also have a section on FP. http://docs.python.org/dev/howto/functional.html http://docs.python.org/dev/howto/functional.html
- ghostdiver 13y agothis code is so smart, maybe because it is Python after all from operator import add expr = "28+32+++32++39" print reduce(add, map(int, filter(bool, expr.split("+")))) but in JavaScript it looks much more readable: "28+32+++32++39".split(/([\+]+)/g).reduce((r,v) => r+v, 0);
- westurner 13y agoGreat deck about functional programming in Python. Also: * `operator.attrgetter`, `getattr()`, `setattr()`, `object.__getattribute__` [1][2] * `operator.itemgetter`, `object.__getitem__` [3][4] * `collections.abc` [5][6] [1] http://docs.python.org/2/library/operator.html#operator.attrgetter http://docs.python.org/2/library/operator.html#operator.attr... [2] http://docs.python.org/2/reference/datamodel.html#object.__getattribute__ http://docs.python.org/2/reference/datamodel.html#object.__g... [3] http://docs.python.org/2/library/operator.html#operator.itemgetter http://docs.python.org/2/library/operator.html#operator.item... [4] http://docs.python.org/2/reference/datamodel.html?#object.__getitem__ http://docs.python.org/2/reference/datamodel.html?#object.__... [5] http://docs.python.org/2/library/collections.html#collections-abstract-base-classes http://docs.python.org/2/library/collections.html#collection... [6] http://docs.python.org/3/library/collections.abc.html http://docs.python.org/3/library/collections.abc.html