I think this is a little bit disingenuous. The Clojure syntax used for the operation was the cleanest possible, whereas the syntax used in the other languages was not. Here's the Python example:
> [sum(t) for t in zip(a, b)]
It's clear that this alternate version is easier to understand:
> [x+y for (x,y) in zip(a,b)]
Beyond that, of course functional languages like Clojure lend themselves to creating "small, nice things." That's why functional languages like Racket/Scheme are often used to teach introductory CS courses: they're not too hard to read, and lend themselves nicely to explaining basic concepts, while not burdening the user with enormously complicated syntax. However, this can change depending on the context. I think it would be fair of the author to bring up scaling difficulties of Clojure: conventions for "small, nice things" occasionally produce really ugly abstractions at scale.
Yup apparently an earlier version of the article used `map` and someone complained that it's not pythonic.
`map` using a lambda may be undesirable compared to a listcomp, but if you already have a function doing exactly what you want it's perfectly fine. Though in this case a better (existing) function is `operator.add`.
operator.add doesn't scale to n lists. Would be weird in this case as well. You'd need:
map(lambda x: operator.add(*x), zip(x, y))
map might not be `pythonic` but I typically pull out map/reduce/filter for the trivial cases. filter(bool, list_)
is just as understandable as [i for i in list_ if i]
Also disingenuous is the suggestion that you need to explain function invocation in Python, but not in Clojure. Maybe that falls under the umbrella of explaining s-expressions, but I don't think there's a notion of "function invocation" in s-exprs (only in language which use s-exprs as their syntax).
Strange, too, that infix notation (familiar to anyone who's taken arithmetic) needs explanation in Scala and Ruby, but prefix notation gets a pass in Clojure.
Wow, combining map and '+ is so great, I can't wait when you'll find out something serious, like restart system, CLOS, reader macros and many other things availible in CL since ~1990.
Not surprisingly, the sum of two lists is just as nice in Haskell:
zipWith (+) a b
... and things get a little bit hairy with three lists:
zipWith3 (\x y z -> x + y + z) a b c
... which is still nice but for four lists we get to the same point as OCaml:
let a' = zipWith (+) a b in zipWith3 (\x y z -> x + y + z) a' c d
I guess in both OCaml and Haskell the user is expected to define zipWith4, zipWith5, etc. (or map in OCaml's case) if he fells the need to use them.
There is probably a simple and elegant way to get variadic-like behavior in Haskell by folding list of list, or playing with Foldable and Sum, but it's a little bit to early in the morning for me to generalize it.
The fact that Clojure "hides the magic" behind less-verbose syntax can be a feature or a bug, depending on your opinion.
What's certain is that it's disingenuous to say that you have to explain those concepts in other languages, but not in Clojure. You might even say that you can guess what `zipped` and `sum` do, but the Clojure example really gives you no information about what's happening behind the scenes.
> Clojure example really gives you no information what's happening behind the scenes.
I respectfully disagree. Map is almost a universal function and IMO, anyone who knows about map will instantly grok what the Clojure version does. The reason this looks so simple in Clojure is due to the fact that the map function is variadic (as our most other functions in Clj) but that is clearly not magic for any experienced programmer.
I respectfully disagree. I know map, of course, yet clojure (or LISPs in general) is the only language where it is variadic. I definitely did not instantly grok what the Clojure version does - in fact, I know all the languages in the example and each of them was more clear than clojure to me
> I know map, of course, yet clojure (or LISPs in general) is the only language where it is variadic.
According to Wikipedia `map` is also variadic in in D, in J, in Mathematica, in Prolog (and logtalk), in Python and in R (to an extent, `lapply` is not variadic but `mapply` can take 2+ sequences).
I would say that map being variadic on the set-of-things-being-mapped, and + being variadic on the set-of-things-to-add, is more of a "semantic axiom of the language" than plain-old DSL-esque "magic." It's like backtracking in Prolog: an extra thing you can assume everything in the language supports, that lets you code differently.
Learning "about" the variadicity of what are, in other languages, binary infix operations, is certainly a thing to learn, but it's a different kind of thing to learn than learning about "zipped" or "map". It's more of a "changing the way you think" kind of thing than a "knowing what tool to use" kind of thing. Like learning about destructuring pattern-matching, or actor-modelled crash-only software.
"I would say that map being variadic on the set-of-things-being-mapped, and + being variadic on the set-of-things-to-add, is more of a "semantic axiom of the language" than plain-old DSL-esque "magic."
When you debate about any LISP based language is usually my clue.
Sorry, but isn't that the point? Why do I need to know what `sum` does behind the scenes? Same with `map`, `reduce`, etc. Those functions exist to avoid a `for` loop or other forms of imperative iteration for common uses.
Either way, I seem to remember that Clojure does have a REPL function to show you the source code for any function. You can use that to know what's going on behind the scenes.
Magic is bad when it makes it hard to understand the true behavior of the code. I've never seen a situation where I couldn't figure out what map was doing.
Python sequences use "+" for concatenation, but numpy arrays use them for addition. (This is why language designers should not use "+" for concatenation. The mixed-mode semantics get very strange.).
34 comments
[ 2.7 ms ] story [ 52.0 ms ] thread> [sum(t) for t in zip(a, b)]
It's clear that this alternate version is easier to understand:
> [x+y for (x,y) in zip(a,b)]
Beyond that, of course functional languages like Clojure lend themselves to creating "small, nice things." That's why functional languages like Racket/Scheme are often used to teach introductory CS courses: they're not too hard to read, and lend themselves nicely to explaining basic concepts, while not burdening the user with enormously complicated syntax. However, this can change depending on the context. I think it would be fair of the author to bring up scaling difficulties of Clojure: conventions for "small, nice things" occasionally produce really ugly abstractions at scale.
`map` using a lambda may be undesirable compared to a listcomp, but if you already have a function doing exactly what you want it's perfectly fine. Though in this case a better (existing) function is `operator.add`.
map(lambda x: operator.add(*x), zip(x, y))
map might not be `pythonic` but I typically pull out map/reduce/filter for the trivial cases. filter(bool, list_) is just as understandable as [i for i in list_ if i]
Strange, too, that infix notation (familiar to anyone who's taken arithmetic) needs explanation in Scala and Ruby, but prefix notation gets a pass in Clojure.
There is probably a simple and elegant way to get variadic-like behavior in Haskell by folding list of list, or playing with Foldable and Sum, but it's a little bit to early in the morning for me to generalize it.
What's certain is that it's disingenuous to say that you have to explain those concepts in other languages, but not in Clojure. You might even say that you can guess what `zipped` and `sum` do, but the Clojure example really gives you no information about what's happening behind the scenes.
I respectfully disagree. Map is almost a universal function and IMO, anyone who knows about map will instantly grok what the Clojure version does. The reason this looks so simple in Clojure is due to the fact that the map function is variadic (as our most other functions in Clj) but that is clearly not magic for any experienced programmer.
According to Wikipedia `map` is also variadic in in D, in J, in Mathematica, in Prolog (and logtalk), in Python and in R (to an extent, `lapply` is not variadic but `mapply` can take 2+ sequences).
Variadic map is by no means limited to lisps.
Learning "about" the variadicity of what are, in other languages, binary infix operations, is certainly a thing to learn, but it's a different kind of thing to learn than learning about "zipped" or "map". It's more of a "changing the way you think" kind of thing than a "knowing what tool to use" kind of thing. Like learning about destructuring pattern-matching, or actor-modelled crash-only software.
"I would say that map being variadic on the set-of-things-being-mapped, and + being variadic on the set-of-things-to-add, is more of a "semantic axiom of the language" than plain-old DSL-esque "magic."
When you debate about any LISP based language is usually my clue.
Either way, I seem to remember that Clojure does have a REPL function to show you the source code for any function. You can use that to know what's going on behind the scenes.
(Yet more traditional Python would be the plain for loop, of course)
@a «+» @b;
And, for the three (or more arrays, you just keep going) case:
@a «+» @b «+» @c;
These are called "hyper-operators": https://perl6advent.wordpress.com/2009/12/06/day-6-going-int...