Functional programming design patterns

There are a number of common design patterns for functional programming. These are typical approaches to functional programming that are used in a variety of contexts. 

Note the important distinction from object-oriented design patterns. Many OO design patterns are designed to make management of state more explicit, or aid in composition of complex, emergent behavior. For functional design patterns, the focus is almost always on creating complex behavior from simpler pieces.

There are many common functional design approaches shown throughout this book. Most have not been presented with a particular name or story. In this section, we'll review a number of these patterns.

  • Currying: This can be called a partial function application and is implemented with the partial() function in the functools module. The idea is to create a new function based on an existing function plus some (but not all) of the function's arguments.
  • Closures: In Python, it's very easy to define a function that returns another function. When the returned function includes variables bound by the outer function, this is a closure. This is often done when a function returns a lambda object or a generator expression. It's also done as part of creating a parameterized decorator. 
  • Pure functions: These are the common stateless functions. In Python, we may also work with impure functions to deal with stateful input and output. Additionally, system services and random number generators are examples of impure functions. A good functional design will tend to emphasize pure functions to the furthest extent possible, avoiding the global statement.
  • Function composition: The itertools library contains a number of tools for functional composition. In previous chapters, we also looked at ways to use decorators for functional composition. In many cases, we'll want to create callable objects so that we can bind functions together at runtime.
  • Higher-order functions: Python has a number of built-in functions that use other functions, these include map(), filter(), min(), max(), and sorted(). Additionally, libraries such as functools and itertools contain other examples.
  • Map-reduce algorithms: They are easily built using the higher-order functions. In Python, these amount to a variation on reduce(f, map(g, data)). We can use a function, f(), to handle reduction, and a function, g(), to perform an item-by-item mapping. Common examples of reductions include sum(), as well as many of the functions in the statistics library.
  • Lazy (“non-strict”) evaluation: This is exemplified by Python generator expressions. An expression such as (f(a) for a in S) is lazy and will only evaluate f(a) as values are consumed by some client operation. In many examples, we've used the list() function to consume values from a lazy generator.
  • Monads:  Imposing ordering is generally needless in Python because ordering of operations is unavoidable. We can use the pymonad library to provide some explicit syntax that can clearly show how ordering should be implemented within more complex expressions. This is helpful for input and output, but also for complex simulations where behavior is stateful.

In addition to these common functional programming design patterns, there are some additional techniques that are part of doing functional programming in Python:

  • Transforming tail recursion into a for statement: Python imposes an upper limit on recursion and there are rare cases where loops will allow us to exceed this limit. More importantly, recursion involves overhead in managing stack frames, which a for statement avoids.
  • Iterable functions: Using the yield from statement makes it very easy to create functions that are iterable collections of results from other functions. Using iterable results facilitates functional composition. 
  • Python decorators and callable objects can behave as  functors. In the ML-like languages, functors are used to take type definitions as parameters. In Python, type definitions are often class-based, and it's sensible to combine these with callable objects or decorators.

All of these functional design patterns can be characterized as typical or common ways to design and implement functional programming. Any kind of design that's frequently repeated forms a pattern we can learn from and use for our own software design.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset