Steve Dekorte: "This article on enumerate-map-filter-accumulate does a nice job of showing how handy methods like map are but I'm concerned that this and similiar articles use of the term "functional programming" serves to perpetuate the myth that these design patterns are exclusive to functional programming languages even though lots of imperative langauges are just as capable of them, such as Smalltalk, Ruby, Lua, Io and even C."
[The article he mentions can be found here. I took the link out of the quotation to avoid confusion.]
Two remarks:
1. I think that map, filter and friends are commonly associated with functional programming, because they operate on lists, and functional languages (like Lisp) pioneered first-class lists. Back in the day, "imperative" or procedural languages didn't have them.
Of course, these days, many other languages have lists, so it makes sense for them to have map, filter and the like as well. As a matter of fact, I think my first encounter with map
was in Python.
[Update: As Marius Gedminas pointed out, there's a more plausible reason why map/filter/etc are associated with functional languages: because they accept functions as arguments. They are higher-order functions. I still think they need first-class lists to work effectively, but the availability of higher-order functions is probably more important. (Even though Io's map
uses expressions rather than functions or methods... I suppose these can be thought of as a kind of anonymous function.)]
2. It's amazing how expressive Io is. This Scheme code:
(define (salary-of-highest-paid-programmer records) (accumulate max 0 (map salary (filter programmer? records))))
looks like this in Io:
employees select(role == "programmer") max(salary)
Here's an example with map
:
Io> a := list(1, 2, 3, 4) ==> list(1, 2, 3, 4) Io> a map(* 2) ==> list(2, 4, 6, 8) Io> a map(+ 1) ==> list(2, 3, 4, 5)
Or maybe:
Io> names := list("Guido", "Larry", "Matz", "Steve") ==> list("Guido", "Larry", "Matz", "Steve") Io> names map(asUppercase) ==> list("GUIDO", "LARRY", "MATZ", "STEVE")
Although code like this might be mystifying at first, it's really quite simple. Notice that map
is a method of the list object. An expression like names map(asUppercase)
is equivalent to names map(x, x asUppercase)
. In other words, you iterate over the list, calling each object x
, then call the asUppercase
method on x
. map(asUppercase)
is a convenient shorthand.
(However, it's not a hack. Io allows one to defer the evaluation of an expression (like the asUppercase
in the map
call) until later. In this case, it's implicitly called as a method on each of the list's elements. The same is true for map(+ 1)
in the previous example. If this looks odd or "incomplete", remember that it's a shorthand for map(x, x + 1)
.)
Anyway, Python isn't as expressive as Io here, but isn't doing so bad either. The Scheme example could be written as:
max(emp.salary for emp in employees if emp.role == 'programmer')
(Yay for list comprehensions and generator expressions! :-)
(On a side note, there are Scheme extensions that allow Python-like list comprehensions. I should look into them someday...)