Where do the “functional” roots of Python come from?
- Transfer
I never thought that Python would be influenced by functional languages, regardless of what people say or think. I am familiar with imperative languages such as C and Algol68 and although I made functions “first class” objects, I did not see Python as a functional programming language. However, it was clear that users wanted more from lists and features.
List operations were applied to each list item and created a new list. For instance:
In functional languages such as Lisp and Scheme, operations such as this were designed as built-in functions of the language. Thus, users familiar with such languages implemented similar functionality in Python. For instance:
The subtlety of the above code is that many people did not like the fact that a function is applied to list items as a set of separate functions. Languages such as Lisp allowed functions to be defined "on the fly" when mapping. For example, in Scheme, you can create anonymous functions and perform mapping in a single expression using a lambda, as below:
Although functions in Python were "first class" objects, it did not have any such mechanism for creating anonymous functions.
In late 1993, users darted around various ideas to create anonymous functions. For example, Mark Lutz wrote code for a function that creates functions using exec:
Tim Peters wrote a solution to the solution that simplified the syntax, allowing users the following:
It became clear that there really was a need for such functionality. At the same time, it seemed tempting to define anonymous functions as lines of code that the programmer must manually process through exec. Thus, in January 1994, map (), filter (), and reduce () functions were added to the standard library. In addition, the lambda operator was introduced in order to create anonymous functions (like expressions) in a more direct syntax. For instance:
These additions were significant at an early stage of development. Unfortunately, I do not remember the author, and the SVN logs did not record it. If this is your merit, leave a comment!
I never liked using the lambda terminology, but due to the lack of a better and obvious alternative, it was adopted in Python. In the end, it was the choice of the now anonymous author, and at that time the big changes required a lot less discussion than at present.
Lambda was really intended only to be syntactic sugar for defining anonymous functions. However, this choice of terminology had many unintended consequences. For example, users familiar with functional languages expected lambda semantics to match those of other languages. As a result, they found that the Python implementation was very lacking in advanced functionality. For example, the problem with the lambda is that the provided expression could not access the variables in the surrounding context. For example, if you had this code, map () would abort because the lambda function would work with an undefined reference to the variable 'a'.
There were workarounds for this problem, but they consisted of using “default arguments” and passing hidden parameters to the lambda expression. For instance:
The “correct” solution to this problem for internal functions was to implicitly transfer references to all local variables to the function context. This approach is known as closure and is an important aspect of functional languages. However, this feature was not introduced in Python until the release of version 2.2.
Curiously, the map, filter, and reduce functions that originally motivated the introduction of lambda and other functionality were heavily replaced by “list comprehensions” and generators. In fact, the reduce function has been removed from the list of built-in functions in Python 3.0.
Even though I did not think of Python as a functional language, introducing closures was useful in developing many other features of the language. For example, certain aspects of new-style classes, decorators, and other modern features use closures.
Finally, even though many functional programming features have been introduced over the years, Python still lacks certain functionality from the “real” functional languages. For example, Python does not perform certain kinds of optimizations (e.g. tail recursion). The dynamic nature of Python made it impossible to introduce compile-time optimizations known in functional languages as Haskell or ML.
List operations were applied to each list item and created a new list. For instance:
def square(x):
return x*x
vals = [1, 2, 3, 4]
newvals = []
for v in vals:
newvals.append(square(v))
In functional languages such as Lisp and Scheme, operations such as this were designed as built-in functions of the language. Thus, users familiar with such languages implemented similar functionality in Python. For instance:
def map(f, s):
result = []
for x in s:
result.append(f(x))
return result
def square(x):
return x*x
vals = [1, 2, 3, 4]
newvals = map(square,vals)
The subtlety of the above code is that many people did not like the fact that a function is applied to list items as a set of separate functions. Languages such as Lisp allowed functions to be defined "on the fly" when mapping. For example, in Scheme, you can create anonymous functions and perform mapping in a single expression using a lambda, as below:
(map (lambda (x) (* x x)) '(1 2 3 4))
Although functions in Python were "first class" objects, it did not have any such mechanism for creating anonymous functions.
In late 1993, users darted around various ideas to create anonymous functions. For example, Mark Lutz wrote code for a function that creates functions using exec:
def genfunc(args, expr):
exec('def f(' + args + '): return ' + expr)
return eval('f')
# Sample usage
vals = [1, 2, 3, 4]
newvals = map(genfunc('x', 'x*x'), vals)
Tim Peters wrote a solution to the solution that simplified the syntax, allowing users the following:
vals = [1, 2, 3, 4]
newvals = map(func('x: x*x'), vals)
It became clear that there really was a need for such functionality. At the same time, it seemed tempting to define anonymous functions as lines of code that the programmer must manually process through exec. Thus, in January 1994, map (), filter (), and reduce () functions were added to the standard library. In addition, the lambda operator was introduced in order to create anonymous functions (like expressions) in a more direct syntax. For instance:
vals = [1, 2, 3, 4]
newvals = map(lambda x:x*x, vals)
These additions were significant at an early stage of development. Unfortunately, I do not remember the author, and the SVN logs did not record it. If this is your merit, leave a comment!
I never liked using the lambda terminology, but due to the lack of a better and obvious alternative, it was adopted in Python. In the end, it was the choice of the now anonymous author, and at that time the big changes required a lot less discussion than at present.
Lambda was really intended only to be syntactic sugar for defining anonymous functions. However, this choice of terminology had many unintended consequences. For example, users familiar with functional languages expected lambda semantics to match those of other languages. As a result, they found that the Python implementation was very lacking in advanced functionality. For example, the problem with the lambda is that the provided expression could not access the variables in the surrounding context. For example, if you had this code, map () would abort because the lambda function would work with an undefined reference to the variable 'a'.
def spam(s):
a = 4
r = map(lambda x: a*x, s)
There were workarounds for this problem, but they consisted of using “default arguments” and passing hidden parameters to the lambda expression. For instance:
def spam(s):
a = 4
r = map(lambda x, a=a: a*x, s)
The “correct” solution to this problem for internal functions was to implicitly transfer references to all local variables to the function context. This approach is known as closure and is an important aspect of functional languages. However, this feature was not introduced in Python until the release of version 2.2.
Curiously, the map, filter, and reduce functions that originally motivated the introduction of lambda and other functionality were heavily replaced by “list comprehensions” and generators. In fact, the reduce function has been removed from the list of built-in functions in Python 3.0.
Even though I did not think of Python as a functional language, introducing closures was useful in developing many other features of the language. For example, certain aspects of new-style classes, decorators, and other modern features use closures.
Finally, even though many functional programming features have been introduced over the years, Python still lacks certain functionality from the “real” functional languages. For example, Python does not perform certain kinds of optimizations (e.g. tail recursion). The dynamic nature of Python made it impossible to introduce compile-time optimizations known in functional languages as Haskell or ML.