103 comments

[ 0.23 ms ] story [ 151 ms ] thread
How new is this? Seems like there is more functional tools in Python than I remember. Didn't see the new 'match' statement in this doc.
Is the match-case statement a functional concept? I see it in functional languages but I'm not sure it is inherently functional...
functional I can't say, but match was highly related to structural typing which was very much central in FP/LP culture
Think this is answer. It isn't technically needed to be called 'functional', but it is so integral to type systems that it seems to be used in all functional languages. Maybe we can call 'match' statements an emergent phenomena of functional languages.
and i'm sure the idea floated around in non FP languages too, but

1) mutable programming shifts the idioms very very far away from passive destructuring, they like to deref pointers and reuse memory cells

2) it's also linked to parametric type systems I believe, which was missing until cpp/java5 in the mainstream (while FP had this since milner which was in the 70s)

Thanks for posting this. Many years ago (2006?) I was a just-out-of-university programmer and a friend of a friend who wrote Python for Canonical sat me down and tried to teach me functional programming using Python. I was a poor learner: I didn't 'get it' (functional programming in general) and, to my regret, learned nothing.

Much time has passed and I hope I'm a more aware and open-to-new-things person today :) I use Python these days though in a traditional procedural / OO way. This post reminded me that maybe I should look into writing it functional-style. Thankyou!

I find that learning functional style in a language that does not enforce it, is extra difficult. If the language does not provide guard rails, then new learners will fall back on non-functional methods, and not even realize it.
I very much support this view. If you want to teach someone functional programming, you go straight to Haskell or a Lisp. They don't need to master the language or anything, but the cold shower of "There are fundamentally different paradigms in programming than those I am used to" combined with a language that will allow nothing but functional code tends to work a lot better than "Hey guys! Let's torture Java to support map/filter/reduce!"
IMHO broaden your boundaries a bit and learn a lisp or clojure-based functional style, like grab a getting started in clojure book and work through it. You will learn a lot more than this guide goes into, it's really just looking at functions in a functional style but doesn't spend nearly enough on data structures and state which are really the core of functional programming (and what python doesn't do well out of the box for functional programming).

Learning a bit of clojure will really open your eyes to what functional programming means, and you can take some of those learnings back to python.

I’ll second this. Learning Clojure radically changed how I think about programming, regardless of what language I program in now. Both because of the restrictions of FP itself, but also (and, IMO, at least as importantly) because pervasive FP solutions at the language/stdlib level are an excellent source of inspiration and guidance for “thinking in” FP.

The great thing is that FP as an approach does translate very well to most imperative/OO contexts. It just isn’t necessarily very obvious how it will, until/unless you’ve been fully immersed and embraced it.

can you make anonymous functions with more than 1 expression now?
Just curious, but from a pragmatic view, what is the advantage of using anonymous functions instead of named functions for any mildly complex code?
It's pretty common to define "higher-order" functions in functional programming. These will sometimes be applied to code blocks.

For example, Python's with statement could have been entirely programmed as a library function if lambas supported code blocks:

    with(open("file.txt"), lambda f:
        content = f.read()
        return sum(int(c) for c in content.split(" "))
    )

Functional languages usually have a pretty limited "core" and implement most of their features through their standard library, usually using higher-order functions, overloaded operators ...
But I can do all that with python already? Your example would work this way too:

    def _(f):
        content = f.read()
        return sum(int(c) for c in content.split(" "))
    with(open("file.txt"), _)
I don't think they were saying you can't do it in Python, just offering what is arguably a nicer syntax, but of course that is subjective. The anonymous function version lets you write things in the order they happen and also "binds" the operations together, ie, someone can't accidentally add a completely unrelated line between the `def` and the `with`. It ultimately doesn't matter, people just get used to the conventions of their preferred languages and we all trudge on.
Readability and scoping are two big advantages. Anonymous functions are often short and sweet, but also highly specific to a single section of the code. It's not meant to be reused. Having to name that one-off use case and hoist it into a function elsewhere makes it harder to maintain and understand. With proper anonymous functions, you can just read through it in one go.
But you don't need to name it big or maintain it somewhere else? Just use a throwaway name and write it where you use it. I mean python even allows overriding functions with the same name in the same scope. You could literally go with using just the same name everywhere.
True, but you're missing the point.
Then what is the point?
Readability. If you're using the same name everywhere, then your code is really hard to understand for other devs:

    val = my_list.reduce(foobar)

What is even going on here?
one option is to define your function inline, directly above the line where you would have liked to write the complex anonymous function. I guess if you are worried about namespace pollution you can 'del' the symbol afterward.
Sadly, Python is a pretty poor functional language.

The core of functional programming is about avoiding mutable states, not much about anonymous functions or passing functions as data.

To do proper functional programming in Python, there should be IMO:

- a way to enforce non-mutable variables/objects;

- non-mutable collections;

- proper support for recursion and tail-recursion optimization;

- a better syntax for anonymous functions than single statement lambdas.

Generators are nice, but do not have much to do with functional programming.

I agree but it's not so dire, the tuple type (and variants like namedtuple) is pretty powerful, non-mutable and often my first choice for data structure in python. Everything else can be built on top of it if you're really motivated.
I would add frozenset as the other immutable collection. The other points are well taken though
For tail recursion, you can use this snippet of code:

  class Recurse(Exception):
      def __init__(self, *args, **kwargs):
          self.args = args
          self.kwargs = kwargs
  
  class Terminate(Exception):
      def __init__(self, retval):
          self.retval = retval

  def tailrec(func):
      def wrapper(*args, **kwargs):
          while True:
              try:
                  func(*args, **kwargs)

              except Recurse as r:
                  args = r.args
                  kwargs = r.kwargs

              except Terminate as t:
                  return t.retval

      return wrapper

  @tailrec
  def fact(n, acc=1):
      if n == 0:
          raise Terminate(acc)

      else:
          raise Recurse(n - 1, acc * n)
Of course, it will be slow because it relies on exceptions :P
You can do a similar thing (that's around 2-3x faster) with a trampoline:

  def factorial(n):
      res = fac(n)
      while callable(res):
          res = res()

      return res

  def fac(n, acc=1):
      if n == 1:
          return acc
      else:
          return lambda: fac(n-1, n*acc)
(comment deleted)
Agreed, Python is designed for something other than functional programming, so a functional programming enthusiast would get better mileage out of something else. Even if you were to write all of your Python programs against the grain in a functional style, you'd still need to operate in a community of modules that don't provide referential transparency.
Even worse when some modules have global mutable state like matplotlib and co.
Why do you consider anonymous functions so important? Regular functions can be used the same way?
OP said anonymous functions are NOT as important as immutable data.
its in the list they provided of improvements python needs...
It's not a list of improvements python needs. It's a list of things python already does / has.
do I have bots responding to me? whats hard about comprehending this?

> To do proper functional programming in Python, there should be IMO:

> ...

> - a better syntax for anonymous functions than single statement lambdas.

(comment deleted)
Oh... either that was added in an edit or I had a brain blip that caused me to skip the bulleted list. Apologies.
You want tail call optimization, not just tail-recursion, i.e. mutually recursive functions.
Syntactically maybe, but I find it has a quite workable functional subset.

Integers, floats, tuples, named tuples, and frozensets are all immutable, functions are values, etc. E.g.: https://joypy.osdn.io/notebooks/Derivatives_of_Regular_Expre... -or- https://github.com/calroc/xerblin/blob/master/xerblin/btree....

It's not fantastic, but it's not that bad.

I love both python and functional programming and I do write functional style python routinely. One barrier I hit is there is no immutable dict type. There is MappingProxyType but it's insufficient. I prefer using custom dataclass like objects built with pydantic and type checked with mypy. It's better than dict and can be made immutable but requires tons of boilerplate code which is a bit unpythonic.
> there is no immutable dict type

Does the namedtuple not suffice? Apologies if I'm being dense.

- - - -

It's not an immutable dict type (for one thing, this has linear lookup, the BTree would be better) but it's fun:

https://stackoverflow.com/questions/13708701/how-to-implemen...

In Python:

    from functools import partial

    def empty_dict(key):
        raise KeyError

    def _dict_add(dictionary, key, value, lookup):
        return value if key == lookup else dictionary(lookup)

    def dict_add(d, key, value):
        return partial(_dict_add, d, key, value)


    d = empty_dict
    d = dict_add(d, 'key0', 23)
    d = dict_add(d, 'key1', 18)

And then...

    >>> d('key0')
    23
    >>> d('key1')
    18
    >>> d('keyn')
    Traceback (most recent call last):
      File "<pyshell#8>", line 1, in <module>
        d('keyn')
      File "/usr/home/sforman/tmp_fn_dict.py", line 7, in _dict_add
        return value if key == lookup else dictionary(lookup)
      File "/usr/home/sforman/tmp_fn_dict.py", line 7, in _dict_add
        return value if key == lookup else dictionary(lookup)
      File "/usr/home/sforman/tmp_fn_dict.py", line 4, in empty_dict
        raise KeyError
    KeyError


- - - -

> objects built with pydantic and type checked with mypy

FWIW, after ~15 years of professional Python development, I'm not into types [in Python]. My attitude is that if you really need them you should switch to e.g. OCaml or something that does them right. I love Python but I wouldn't use it anymore for anything other than scripts and maybe prototyping.

> My attitude is that if you really need them you should switch to e.g. OCaml or something that does them right.

I strongly disagree. I personally think there is no programming problem where types aren't useful. When I write code, I think in terms of types, and rarely in terms of anything else. Python is useful in tons of problems OCaml isn't such as data science, statistics etc. Besides python has a very large ecosystem which matters in order not to reinvent the wheel.

> I personally think there is no programming problem where types aren't useful.

I'd have to disagree with that just on first principles: no absolutes. (Note that that's an absolute.) :)

Anyway, I'm not trying to argue that types are not useful, they are. What I'm saying is that Python's loose approach to typing is useful "in the small" so to speak, but as projects grow larger you want "harder" types, checked by machine. Same thing with DB schema: No-SQL only makes sense in the small domains where the schema can be ignored or treated as an afterthought. Above a certain size and you grow a schema again and might as well do it right.

> I'm not into types [in Python]

In Python type hints are just that: hints.

mypy is a very poor static analyzer, and a lot of Python code (especially in frameworks like Django) is inherently dynamic and cannot be type-checked. And that's ok.

Though, documenting your API with type-hints really helps, especially with VSCode (via pyright) popovers when you hover a symbol/function.

Not for Lisp or some ML derived languages, FP isn't whatever Haskell does.
Not having immutability isn't the worst thing IMO. I guess if you can't trust your coworkers to not modify their inputs, it's a different story.
> The core of functional programming is about avoiding mutable states,

I can't keep up, does this mean scheme is not a functional language anymore by modern definition?

> The core of functional programming is about avoiding mutable states, not much about anonymous functions or passing functions as data.

I disagree since any paradigm can be done either with or without mutable state. So it is not something that defines functional programming.

In any case you can also use immutable data types with python anyway.

The main annoyance with python is in my opinion your last point: The lambda syntax is too limited. So it is necessary to define lots of nested small functions instead.

Also python syntax is not as nice in chaining function calls over multiple lines.

> Also python syntax is not as nice in chaining function calls over multiple lines.

Not sure if this is exactly what you meant, but I implemented something like this in reply to a similar comment a few months ago which may be of interest.

https://news.ycombinator.com/item?id=35105298

I do not think functional programming is about mutable state at all.

It's about functions being first class values, nothing more.

Lisp and Scheme are examples of functional languages that do not restrict mutability.

The term for what you are talking about is pure functional programming!

I don't think this is correct. Programming languages with support for functional programming are not always pure functional languages. However, functional programming per se is always pure by definition; if you're mutating anything, it's not functional.

(You can have "quasi functional" if you mutate only local variables, such that the black box view of your functions appears pure. Nobody knows whether the map function in (map f list) internally contains a loop or tail recursion.)

Languages which support functional programming and other paradigms are not called functional; it is not correct to call Common Lisp and Scheme functional.

Not only do these have mutation, they have sequencing via strict evaluation: doing one thing that has a side effect, followed by another. Side effects like I/O and mutation are easily obtained and ordered, just like in Fortran or Java.

Pure functional programming is always about purity, functional programming is about first class functions. Pure functional programming is a subset of functional programming, not the other way around.

I'm not really sure where you are getting your information from but everything that I have read disagrees with this idea that functional programming is about purity.

https://en.wikipedia.org/wiki/Functional_programming

I think in recent times the term functional programming has been overloaded to mean what you describe, and many people use it that way, but I don't think it should be used that way.

Maybe we should just say "non pure functional" or "pure functional" so it's always clear what we are talking about!

Many mainstream programming languages implement procedures and functions with the same syntax, and call procedures functions. Procedures can take arguments and return a value, like functions, but also have side effects. Nobody asked for this terminology mixup; it just happened in the reference manuals of programming languages. Lisp calls procedures functions; so does C.

Programming with procedures isn't functional programming; it is procedural programming. This is true even when the procedures are first-class procedures, and are used in declarative patterns like mapping, reducing and so on. There is no word which refers to programming with first-class procedures in a declarative style. It is different from "Fortran-like procedural", and is rooted in OOP. First class procedures are objects; and in fact in many languages you can take a solution based on first class procedures and replace those procedures with objects. Mutated lexical variables become member variables (or slots) and so on.

The rhetoric in this area conflates two meanings of "pure" and "purely", causing equivocation.

- "pure X" as in "consisting of nothing but X" and "purely X" meaning "solely X"

- "pure" as in having no side effects: a pure expression can be replaced by its value without a change in meaning.

A "not purely functional language" uses "purely" in the former sense. The language emphasizes functional features, but has procedural features also. It doesn't refer to a language which emphasizes declarative programming patterns around first-class procedures, though such a language can fit the description.

A "not purely functional program" likewise: it emphasizes functional programming but has procedural bits in there. It doesn't refer to the program being composed entirely of the declarative use of higher order procedures, like mapping over collections while doing I/O or mutation and such.

If we take the value of a variable and then filter it through functional stages, only to then assign the transformed value into the same variable by assignment, that is an example of "not purely functional". The data transformation is functional; the assignment isn't.

@dataclass is the new final
Does the type system let you express that a class shouldn't be subclassed? I remember this possibility was mentioned in a PEP and got deferred. It would be really useful with the new match/case pattern matching feature, because then you could have proper sum types, and mypy could enforce exhaustiveness. AFAIK you have to do a workaround with a "assert False" or similar at the end.
I thought I knew functional programming from knowing Python, but looking back I didn't really "grok" it until I moved to Elixir. Now I like it and prefer it. I don't run into any of the types of bugs I used to create in Python, mostly from being lazy and fiddling with data inside of a loop. I miss do miss early returns though.
I moved from Erlang to Python and was miserable. I still write most of my code in a functional style, but I dearly miss using a language that enforced those constraints.
shameless plug: I maintain a small library to do functional pipes.

You can write:

    (
        range(10)
        | Map(lambda x: x * 10)
        | Filter(lambda x: x % 2 == 0)
        | Reduce(lambda a, b: a + b)
    )
instead of:

    x = range(10)
    x = map(lambda x: x * 10, x)
    x = filter(lambda x: x % 2 == 0, x)
    x = reduce(lambda a, b: a + b, x)
and more. https://tandav.github.io/pipe21/

    import pandas as pd
    import functools
    
    (
        pd.Series(range(10))
        .apply(lambda x: x * 10)
        .where(lambda x: x % 2 == 0)
        .pipe(lambda s: functools.reduce(lambda x, y : x + y, s))
    )
Readability isn't the best. Also what you present here is method chaining and not functional pipes.
Is the difference mostly syntactic?
with dot chaining you are restricted to only the functions that the API you're working with provides. With functional pipes you can use whatever functions you want.
In this case, the API provides .pipe, with which you can then use whatever functions you want.
What difference does it make? They're conceptually the same thing. You're mapping immutable data to map/reduce/filter like pure functions to get new data.
Improved code readability. The pipe operator was a game changer for me even after 20 years of programming (I know I was probably touching wrong things).
This looks cool! I always wanted something similar for pandas, because I found it very elegant in dplyr in R. AFAIK they do have a `.pipe` method now but it could definitely be better in the future.
Hmm, I've never seen code like the latter so it's unclear what problem this solves. I'd just write the pandas code snippet given below. Possibly with polars to make it lazy.
It is a fairly common pattern in low level stuff like hashing algorithms or cryptography.
My library solves 2 problems.

1. It does not require to wrap your iterable into some wrapper to use functional methods. It takes an iterable/object and returns another iterable/object. You don't have to unwrap it after transformations.

2. it uses oneliners (library is 80LOC single file) for most of the methods. You can just copy-paste it to use instead of install and import. E.g map, filter, reduce is just:

    class B:
        def __init__(self, f): self.f = f
    class Pipe  (B): __ror__ = lambda self, x: self.f(x)
    class Map   (B): __ror__ = lambda self, x: map   (self.f, x)
    class Filter(B): __ror__ = lambda self, x: filter(self.f, x)
    class Reduce(B): __ror__ = lambda self, it: functools.reduce(self.f, it, *self.args)
What blows my mind is that Python was designed for maths and data science by a maths and data science guy. It should be natural to work with immutable values and pipelines in Python, which are natural functional constructs. Yet everything in Python is mutable and pipeline-style programming is not really supported. ???

(A long time ago I tried to teach programming fundamentals to a maths person. She was completely puzzled by x=x+1. X equals to X+1? That was nonsense in their eyes.)

> Python was designed for maths and data science

I don't think so.

> During Van Rossum's stay at CNRI, he launched the Computer Programming for Everybody (CP4E) initiative, intending to make programming more accessible to more people, with a basic "literacy" in programming languages, similar to the basic English literacy and mathematics skills required by most employers. Python served a central role in this

Mathematics is an extremely broad field, the mathematics of a numerical analyst and someone doing algebraic geometry are very different and a programming language for each would look quite different.

In python the C and even C++ heritage is quite clear, but it is in no way designed like Miranda and Haskell, which actually target to some extent expressing particular mathematical constructs in code, something which neither C, C++ not python ever aspired to.

> What blows my mind is that Python was designed for maths and data science by a maths and data science guy.

Neither of these is true. I actually still find it a little weird that Python became so widely adopted by data science people, and that definitely doesn’t seem to be the main user GvR originally had in mind when creating the language.

That isn't weird. It's what they've been taught. CS departments started teaching Python to other departments because of its ease, and it stuck.
This is false. It was mostly designed as a tool for Unix hackers, with motivations that seem not too dissimilar from perl. It was the ease of use of the language that brought people in from the math/science community who usually weren't programmers. This has often historically led to terrible and non-idiomatic (but working) code, because if you have an idea in your head you can quickly hit the ground running.
In their definition of programming languages types, I'm not sure I see how SQL which they cateogrize as declarative really differs from FP, especially with lazy evaluation. Both are a succession of functions applied onto a previously declared variable : just think of a long SQL query with many ctes modifying the previous ones; each of them can be thought of as a variable which takes its value from the application of a function on other variable (a bit like the let function in ocaml).

I always thought of LaTeX as being the prime example of declarative programming.

SQL does not support higher-order functions or lambdas (except possibly in some special dialect I'm not aware of)
Right, good point. I just find the structure and the mindset to be similar.
What's so useful about iterators and generators. The article says how to use them but not why. If you already know how to make list comprehensions and use "for elt in coll", do they let you do anything new?
Fundamentally? No, it's all just machine language in the end.

Practically? Yes, generators are often more memory-efficient, and thereby often more compute-efficient.

Is it only about efficiency, and not expressivity? If so, can the compiler figure out where to transform my equivalent structures into iterators and generators?
The expressivity gain depends on what you're left with without the feature. It was hard for me to imagine where the previous comment draws the line around iterators and generators.

An optimizing compiler would need to know what actions are pure in order to rewrite code to be lazy. Python allows so much dynamism, I can only see that happening with a very sophisticated tracing JIT.

You could probably find more uses, but here are some:

Generators allow one to easily transform code from non-buffered to buffered. Consider this example:

    for y in x:
        do(z)
Now, x may be a collection that was eagerly evaluated in previous steps, let's say a list, but then you've discovered that this list is too big to fit in memory, and you want to generate it in manageable fixed-size chunks, so you replace the code that created x to make x a generator. You don't have to touch the code above -- it will work the same way because generators have the same interface as collections.

Another use: generators are used to implement async / await. I personally find this idea ridiculously stupid, but a lot of people (and especially those who don't understand what it does) like it a lot. Yielding mechanism, which is a feature of generators, is the one that's used to communicate / switch between co-routines (tasks) of asyncio.

Another aspect, besides bufferization is that you might want to delegate control over how much looping you want to do to a separate chunk of code. I.e. you may want to separate the generation of elements (hence, generator) from eg. filtering them, or transforming them in some way, or reducing them etc. If you didn't have this ability, you'd have to generate the entire collection upfront, and if your computation takes multiple steps that you'd like to separate into different code chunks, you'd have to also generate intermediate collections, even though, potentially, you don't need some of the elements in those collections. Consider, for example:

    def powers(start, end, power):
        return (x ** power for x in range(start, end))

    def flt(a, b, c):
        return a + b == c

    def disprove_flt(upto, upto_power):
        for power in range(upto_power):
            for a, b, c in zip(powers(1, upto - 2, power), powers(2, upto - 1, power), powers(3, upto, power)):
                if flt(a, b, c):
                    return a, b, c
        return None, None, None
Which is, of course not a correct way to search for the counterexample to Fermat's last theorem, but I tried to find a popular enough subject so that the example was easier to follow.

An exercise to the reader: rewrite the code above in such a way as to eliminate "upto" and "upto_power", i.e. to search until a counterexample is found (or indefinitely).

It's the difference between evaluating all the elements of a list, consing them, and returning it all at once and evaluating the elements one at a time (i.e. lazy evaluation). Saves memory and time and it gives you a chance to quit early.
Fun to work on doing FP in languages that don't really support it, but in my view a language has to be built for FP for it to be a practical option in any real applications. Several obvious reasons for Python being a poor lang in which to do FP:

  - mutable data structures 
  - no built-in function composition
  - limited support for HOF
  - no tail call optimization (AFAIK)
  - performance in general isn't great and I imagine it's even worse when relying heavily on recursion, etc
Still, a fun project!
It doesn’t have to be all or nothing though. I’ve definitely seen (and written) functional Python in ‘real applications’ sprinkled here and there in an otherwise normal codebase.

In fact, I’d wager that any Python programmer worth their salt would suggest using a list comprehension and a lambda over a loop in most data transformation situations. Also currying comes in handy often once you have it in your toolkit.

It’s a bit silly to call two very mature libraries that are part of the Python standard library a ‘fun project’ just because the language itself supports multiple programming paradigms.

No, it doesn't need to be all or nothing, but the scenario I was directing my point at was where people try to "do FP in lang X", not just sprinkling in functional concepts here are there. At this point, almost every language has some functional programming concepts in it, but I wasn't addressing that. I could've been clearer, but I've seen many cases where people would rather be programming in Haskell or some other functional language and then when "forced" to use some other language, like Python, they try to essentially recreate Haskell in that language and my point is that this really doesn't work or, at the very least, it's more trouble than it's worth and the resulting codebase is a complete mess because you've tried your hardest to do FP in a language that ultimately isn't suited for it. You are absolutely right though that it needs not be all or nothing. To the extent you can rely on some of these constructs and enable equational reasoning by avoiding mutation, etc. you'll be better off, no doubt (I do this too in Python and other languages because wherever I can make it easier to reason about my code, I will do so). I'm just cautioning against trying to do something akin to pure FP in Python.
This I agree with completely! I’m a big believer of “when in Rome”. As in, try to be idiomatic in whatever language you need to use. I once saw a Python codebase where someone had tried very hard to write Java in Python.. That was equally horrible!
Exactly. Java in Python sounds scary :)
Scheme is one of the few FP languages with required TCO on the language standard, and alongside Lisp, Caml Light, Standard ML, OCaml, F#, Scala, supports mutable data structures.
Right. I've used Scala pretty extensively and even Scala, which obviously has much better support for FP than Python, is a pretty unpleasant experience if you try to turn it into "Haskell on the JVM", in my opinion.
Worthless functionality in a worthless language. Only topped by the bizarre and unverified claims in its documentation:

    Most programming languages are procedural:
Did the author count? -- The answer is a clear and resounding "No". The author pulled this factoid out if his rear end. Maybe. Maybe not. It's not even clear by what's meant by "all languages" -- all possible languages? all languages known to author? all languages used in Github? And why should anyone care about this kind of multitude?

    Lisp
Again, people who've never seen any Lisp, or vaguely remember their college days when some course requested from them to write a function to figure out if a string is a palindrome in Scheme think that Lisp is a single language. The author just decided to demonstrate his blistering ignorance by including something that he thought would render him as more experienced than their readers.

Later the author perpetuates all sorts of absurd myths about "functional programming" s.a. increased modularity or ease of debugging. Apparently, author had never used step-debugger nor had he wrote anything in any popular programming language that advertises itself as functional to experience first-hand this "ease" he's talking about. Needless to say that nothing in functional programming prevents programmers from writing long functions... In practice, however, some languages which advertise themselves as "functional" have pathologically bad / hard to read syntax (eg. Haskell), and functions longer than some 10 lines or so become too difficult to understand even to people who believe themselves to be proficient in those languages.

Author is simply lying when he claims that generators are a kind of function, which can be simply verified:

    >>> def generate_ints(n):
    ...   for i in range(n):
    ...     yield i
    ... 
    >>> type(generate_ints(1))
    <class 'generator'>
    >>> type(generate_ints(1)).mro()
    [<class 'generator'>, <class 'object'>]
    >>> isinstance(generate_ints(1), type(generate_ints))
    False
Generators and functions are unrelated. Neither is a kind of other.

On top of that, author frequently violates Python's coding conventions (eg. capital letters in variable names, assigning lambdas to variables).

---

But, bottom line: crappy language deserves no better documentation than this.

Have a beer, bro