
A Small, Nice Thing - mycodebreaks
http://codahale.com/a-small-nice-thing/
======
johnloeber
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.

~~~
bpicolo
map(sum, zip(a, b)) scales as well as Clojure here, though not identically
concise

~~~
Scarbutt
I'm not a regular python user but the article did mention that map is somehow
not pythonic.

~~~
masklinn
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`.

~~~
bpicolo
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]

------
zurn
More traditional Python:

    
    
      >>> import operator
      >>> a, b = [1, 2, 3], [4, 5, 6]
      >>> map(operator.add, a, b)
      [5, 7, 9]
    

List comprehensions are a relatively late addition to the language and don't
really warrant being called "more Pythonic" in this case IMO.

(Yet more traditional Python would be the plain for loop, of course)

~~~
ratboy666
Which brings you back to APL

    
    
      1 2 3 + 4 5 6
      5 7 9

~~~
Avshalom
or if you need more than two arrays:

    
    
          +/ (1 2 3) (4 5 6) (7 8 9)
       12 15 18
    

as well as octave and julia's:

    
    
      [1 2 3] + [4 5 6]

------
baconmania
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.

~~~
krat0sprakhar
> 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.

~~~
pathsjs
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

~~~
masklinn
> 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).

Variadic map is by no means limited to lisps.

------
SwellJoe
Perl 6:

@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...](https://perl6advent.wordpress.com/2009/12/06/day-6-going-into-
hyperspace/)

------
Animats
Python:

    
    
        >>> import numpy
        >>> numpy.array([1,2,3]) + numpy.array([4,5,6])
        array([5, 7, 9])
    

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.).

------
krat0sprakhar
The OCaml version (for 2 lists) is almost as good as clojure

    
    
        List.map2 ( + ) a b;;
    

However, for 3 or more lists, Clojure definitely wins (since there's no
variadic equivalent of map in OCaml) -

    
    
        let x = List.map2 ( + ) a b in List.map2 ( + ) x c;;
    

Which got me wondering, is there a particular reason why only a `map2` exists
in OCaml?

~~~
mbel
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.

~~~
eru
You can also write:

    
    
        liftA2 (+)
    

or

    
    
        (+) <$> a <*> b
    

if you have the right type-class instance.

~~~
droque
You could also go all the way and declare a Num a => Num [a] typeclass, with
(+) = zipWith (+).

------
gregmac
The C# is pretty readable as well

    
    
        a.Zip(b, (x,y) => x+y)
    

Only requires explaining method invocation, Zip(), and lambdas.

------
hathym
Elixir: List.zip([a, b]) |> Enum.map(fn {x, y} -> x + y end)

------
mapcars
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.

~~~
vorador
Everybody has to start somewhere.

