
Joel Spolsky: Can your programming language do this? - krat0sprakhar
http://www.joelonsoftware.com/items/2006/08/01.html
======
onan_barbarian
I think there's some reasonable stuff buried in here, I really do.

But... having actually spent some time in the trenches dealing with a hard
problem on a massively parallel machine - more than once - I find it hard to
believe that something like map/reduce or the like - or any given small-scale
language feature is going to be particularly significant in terms of
parallelizing any goddamn thing that's actually hard to do. I see a lot of
fatuous claims that language feature X is the missing link in parallelism for
the everyday working programmer but I don't see a lot of new solutions for
anything hard as a proof of concept.

We've only had OpenMP and all sorts of other kludgy extensions for Fortran and
C for what, about 15 years? I'm not saying that they're great or elegant or
anything, but so many of the things that are hard about parallel programming
are NOT FUCKING SOLVED BY MAP-REDUCE. Oops, sorry, shouty. But anything that
can be solved by map-reduce wasn't enormously hard to begin with. Map-reduce
was itself initially publicized in terms of 'less useful but easier for mere
mortals than generalized parallel prefix' which made sense to me.

What doesn't make sense for me is all this frenzied enthusiasm for dragging
parallel programming into the never-ending programmlng language abstraction
wars; at least when the topics being discussed only touch on the very
shallowest things needed by parallel programming. You want some respect, solve
something hard.

Yes, you can do the same thing to each element of an array. Whaddya want, a
cookie?

~~~
Peaker
Here's the main Haskell hero, Simon Peyton Jones giving a talk about data
parallel Haskell, which I think is a promising language abstraction to do
_hard_ things with parallelism relatively easily:

<http://www.youtube.com/watch?v=NWSZ4c9yqW8>

As for techniques that can be used now: Haskell's `par` and `pseq` combinators
allow adding parallelization to "hard" code after-the-fact pretty easily.

~~~
SeanLuke
Wasn't the promise of haskell the idea that parallelism would be implicit?
What are operators like those doing there?

~~~
Peaker
No, that was never "the promise" of Haskell.

Making automatic implicit parallelism in Haskell is trivial - but it will
yield _too much_ parallelism and the overhead will trump the benefits.

The `par` and `pseq` combinators allow the programmer to specify which
computations to parallelize explicitly to avoid parallelizing computations
that are too small to be worth it and to allow the programmer to care for
data-locality.

Despite being explicit, they are still far easier than other explicit
parallelism mechanisms such as explicit threads because they are:

* So easy to throw in the program

* Guaranteed not to alter the semantics of your program - so you can just throw them in there and profile the performance changes -- and you know they won't break your program. They can alter the performance for better or worse.

------
kragen
> Correction: The last time I used FORTRAN was 27 years ago. Apparently it got
> functions.

FORTRAN had user-defined functions since FORTRAN II in 1958; see
[http://archive.computerhistory.org/resources/text/Fortran/10...](http://archive.computerhistory.org/resources/text/Fortran/102653989.05.01.acc.pdf)
on page numbers 5, 14, and 15.

Joel unfortunately completely misses the point of why C and Java suck at this
stuff: you can use functions as values in them (anonymous inner classes in
Java) but _they aren't closures_. And his comment about automatically
parallelizing "map" is a little off-base; if you take some random piece of
code and stick it into a transparently parallel "map", you're very likely to
discover that it isn't safe to run multiple copies of it concurrently, which
is why languages like Erlang have a different name for the "parallel map"
function. The "map" in MapReduce is _inspired by_ the function of that name in
Lisp and other functional languages; it isn't a drop-in replacement for it.

As usual, while Joel's overall point is reasonably accurate, most of his
supporting points are actually false to the point of being ignorant nonsense.
I think someone could tell as good a story in as entertaining a way without
stuffing it full of lies, although admittedly my own efforts fall pretty far
short.

~~~
edanm
"Joel unfortunately completely misses the point of why C and Java suck at this
stuff: you can define anonymous functions in them (anonymous inner classes in
Java) but they aren't closures."

You can't define anonymous functions in C.

And I wouldn't "attack" Joel for "stuffing the article full of lies". It's
more like "abstracting away the details". When people talk of the benefits of
some practice or programming paradigm, they don't always mention all the work
going into it - they just explain the concept. That's what a good teacher
does, IMO. He takes complex concepts and explains the important parts.

~~~
kragen
> You can't define anonymous functions in C.

You are of course correct. I have corrected the parent post.

My point is, though, that you almost can't define anonymous functions in
Python, either, but you can do nearly all of these clever patterns. The
difference is that Python functions _are_ closures.

> And I wouldn't "attack" Joel for "stuffing the article full of lies". It's
> more like "abstracting away the details".

I agree that that's what a good teacher does, but I don't see this article as
doing that.

------
grav1tas
I think it might be important to note that while the terms map and reduce do
come from Lisp, they're not one-to-one with what these functions do in Lisp.
The original MapReduce paper mentions the borrowing, but doesn't really go
into specifics. There's a good paper by Ralf Lämmel that describes the
relation that MapReduce has to "map" and "reduce" at
[http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.104....](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.104.5859)
. I liked this paper much better and found it the most informative functional
explanation to MapReduce (note, it's in Haskell).

I think MapReduce is really part of a more general pattern where you have an
(in more Haskell-y terms) unfold (anamorphism) to a foldr (catamorphism). If
your operations on the items in your intermediate set of data in the MapReduce
are associative/commutative, you can work out parallelization more or less for
free. It's pretty cool stuff, and really not that complicated when you sit
down and think about it.

~~~
Dn_Ab
Wow. Thank you. I wish I could upvote you more. I have never looked into
Google's "MapReduce" but if what you say is true then my assumptions on it
were completely wrong. I assumed it was 1-1 with map and reduce. But if it is
generating a set of unfolds into 'essentially' fold (is it? I only skimmed the
first pages due to time) then that is a very important detail and makes the
name a bit of a misnomer. Why don't people make a bigger deal about this -
unfold, fold is not harder but it requires a different mindset and approach
than map fold.

If you are approaching it thinking it is just a map and fold then you are
going in disadvantaged and will have to unlearn that fact to properly leverage
something that is actually even more impressive/useful/powerful than a mare
fold o map.

------
sthatipamala
This article shows that Javascript is truly the common man's functional
programming language. Despite its ugliness, it got lambdas/anonymous functions
right.

~~~
ionfish
As right as you can get them in an imperative programming language, perhaps,
but the semantics of closing over environments containing mutable variables is
still confusing to even the smartest of people (cf. [1]; it's about Python,
but JavaScript has essentially the same nature in this regard).

[1] <http://math.andrej.com/2009/04/09/pythons-lambda-is-broken/>

~~~
sid0
Not ECMAScript Harmony! [1] And no, that isn't the definitive semantics of
closing over mutable variables. It is one out of at least two possible
semantics.

[1] <http://brendaneich.com/2010/11/paren-free/> \-- see the "Implicit Fresh
Bindings" section.

~~~
ionfish
Harmony's new _for in_ construct implicitly introduces a fresh let binding per
iteration, yes, but it doesn't fundamentally change the language semantics
with regard to the kind of example Eich mentions. So while one can, in
Harmony, write code that behaves in the way that people like Andrej Bauer
expect, it doesn't mean that the language's closure semantics have suddenly
changed.

It's also hard for me to see how one could have a 'definitive' semantics
_without_ having at least one alternative—not that I claimed that either JS's
closure semantics were the only ones going. Presumably the alternative you
mean is where the variables present in the local environment are copied on
creation of a closure.

I'd also resist the idea that I claimed that JavaScript's closure semantics
_were_ in some way definitive, although I can see how one might infer that
from what I did write. That being said, given the potential problems with the
alternative, I would claim that they're a reasonable choice.

~~~
sid0
_it doesn't mean that the language's closure semantics have suddenly changed._

Yes, but what are the issues with fresh let-bindings per iteration?

 _Presumably the alternative you mean is where the variables present in the
local environment are copied on creation of a closure._

No, that's bad -- it leaves you unable to show that objects with mutable state
are reducible to closures.

------
JonnieCache
Something that I only realised the other day which made me feel kinda
embarrassed: in ruby, the reduce method is called inject.

For years I've been doing MapReduce functions, without realising it. MapReduce
was in my mental pile of "genius things that cleverer people than me do, must
be looked into when there is time."

For info on inject: <http://blog.jayfields.com/2008/03/ruby-inject.html>

~~~
SandB0x
In Ruby the reduce method is also called reduce:

    
    
        [1,2,3].inject{|s,i| s += i}
        => 6
        [1,2,3].reduce{|s,i| s += i}
        => 6

~~~
Peaker
What would it do on an empty list? It should probably take an argument for the
empty case...

What is the history behind the name "inject" here?

~~~
kragen
The name comes from Smalltalk. I imagine that the idea is that you're
"injecting" an operator between items in the list: 1 2 3 4 becomes 1 + 2 + 3 +
4.

------
rivalis
Even when I'm working in a language that doesn't have first class functions, I
find it easier to lay out my code by writing functional pseudocode and then
"unrolling" maps into loops, closures into structs/objects, compositions into
a sequence of calls, etc. It probably leads to idiomatically awful Java, but I
find it easier to read and write, and nobody else needs to deal with my code.
So...

------
tybris
Yup, any language I've worked with, including Java and C, can do that just
fine. They just spread the verbosity differently. Organizing large projects is
a pain in JavaScript, trivial in Java. Using anonymous functions is a pain in
Java, trivial in JavaScript.

(Not so) fun fact: The public MapReduce services by Google and Amazon do not
(directly) support JavaScript.

------
ajays
He gives reduce as an example of "purely functional programs have no side
effects and are thus trivially parallelizable.", but reduce by definition is
not trivially parallelizable.

~~~
Zaak
Reduce is trivially parallelizable if you can ensure your operation is
associative, but you have to work in languages like Epigram and Agda to
guarantee that.

 _(retract "he's wrong")_

~~~
rntz
No, you just have to make sure the function you pass is associative.

Only in really strongly typed languages like Agda can you make the
associativity constraint /part of the type/ of the function, and thus
statically checked by the compiler; but it's perfectly reasonable to make it
part of the specification of the function's behavior that it expects its
argument to be associative. (Or, slightly more generally, that the order of
reduction is unspecified; in which case only an associative function will
produce a totally deterministic result.)

~~~
wnoise
Even when you can't have a _proof_ of associativity in the type, it's
perfectly reasonable to document the associativity requirement in types --
e.g. the Monoid typeclass in Haskell.

------
chuhnk
Has anyone else just read this and realised they need to go off an learn some
form of functional programming? I ignored it for such a long time because I
felt it wasn't relevant to my current situation but I was wrong. You gain some
incredible fundamental knowledge that you would otherwise be completely
oblivious to.

Is lisp really the way to go though?

~~~
merijnv
I would recommend Haskell over Lisp, because Haskell will _force_ you to do
functional programming, where-as Lisp is less rigid in that regard. In
addition the #haskell IRC channel on FreeNode is filled with people who have
infinite patience with Haskell newbies and it has an excellent introductory
text at <http://learnyouahaskell.com/>

About two years ago I decided to learn Haskell. Had about 4 false starts and
didn't "get" it until my 5th attempt, at which point I figured out what the
type system _meant_ and had a bit of a Matrix "I know kung fu!"-moment. Two
years of hanging around in #haskell later, I know a billion more things about
programming/programming languages and learned type theory (although I still
don't grok all conversations there :p).

Some other comments on the usefulness of Haskell:

<http://news.ycombinator.com/item?id=1145743>

<http://news.ycombinator.com/item?id=1145743>

~~~
silentbicycle
You may also have luck learning FP with Erlang. It's also functional, but
approaches it from a different direction, focusing on reliability and
concurrency rather than types and purity. Between Haskell and Erlang, one of
them will probably fit your mind better.

I started FP with OCaml, but OCaml makes it too easy to continue writing
imperative code when you're actively trying to learn new idioms.

~~~
merijnv
I agree Erlang is also very interesting. I think some of the features looks
absolutely fascinating (hot updatable code!), etc. I just haven't found the
the time to learn it yet.

I wish Erlang's syntax was more like Haskell, though :\

------
gaius
FTA:

 _The very fact that Google invented MapReduce, and Microsoft didn't, says
something about why Microsoft is still playing catch up trying to get basic
search features to work_

I don't believe this is true, and that's easy to prove: There was parallelism
of SELECTs in SQL Server 2000. So there is a part of MS that is perfectly
happy with the idea, even in another bit of MS isn't. They just need to talk
more...

------
justwrote
Yes, it can! Scala:

    
    
      def Cook(i1: String, i2: String, f: String => Unit) {
        println("get the " + i1)
        f(i1)
        f(i2)
      }
      
      Cook("lobster", "water", x => println("pot " + x))
      Cook("chicken", "coconut", x => println("boom " + x))
    
      List(1,2,3).sum
      List(1,2,3).mkString
      List(1,2,3).map(println) // or more verbose List(1,2,3).map(x => println(x))

------
pmr_
Today I tried to explain someone what exactly boost::mpl::fold does and how it
is supposed to be used (For those unfamiliar: boost::mpl is a collection of
compile-time metaprogramming mechanisms for C++).

I took me a while to realize that the person I was explaining it to had only
little problems with the templates and compile-time part but close to no idea
what a fold or a lambda are.

Not knowing some basics of functional programming can keep a person from
understanding so many different things and I have encountered those in
different fields (e.g. explicitly like in formal semantics or implicitly in
different theories of morphology).

I think the real point here is that different paradigms offer you new views
onto the world and enhance your understanding all the programming language
things aside.

------
becomevocal
I think this could also be called 'can your brain think like this?'... Many
programmers stray from thinking in a massive way and tend to problems with
similar, familiar codebases.

~~~
sthatipamala
Programmers are perfectly willing to think this way if you disguise it like
this: <http://api.jquery.com/each/>

People are put off by esoteric and academic-sounding names like "first-class
functions". But people will adopt it if you put it in a practical context.

~~~
pietro
It might even be the other way round. I have a (slightly) harder time teaching
first-year students how to use the for loop in JavaScript than how to use
anonymous functions in jQuery. I think it's a better match for how the brain
works.

------
svrocks
Does anyone else think it's a travesty that the AP Computer Science curriculum
is taught in Java? Java was my first programming language and I've spent the
past 8 years trying to unlearn most of it

~~~
Deestan
Java's not too bad for educational purposes; at least it has a rigid type
system and checked exceptions, both of which makes it harder to ignorantly
slam some code together and nugde it until it works.

~~~
svrocks
"ignorantly slam some code together and nugde it until it works"

haha pretty sure I still do that most of the time...followed by rewriting
everything and calling that "refactoring"

------
hdragomir
I remember my days as a CS student.

The single most mind-opening course I took was functional programming, where I
learned LISP and Prolog.

That knowledge today is crucial as it deeply changed my mindset when tackling
most any problem.

------
bluehavana
It's funny that he mentions Google as an example of a company that gets the
paradigm, but most of Google is C++ and Java. C# has better functional
paradigm support than both of those.

~~~
bad_user
The Google Search engine was at some point written in Python.

~~~
pragmatic
The crawler was in Python, not the search engine itself.

<http://infolab.stanford.edu/~backrub/google.html>

 _Both the URLserver and the crawlers are implemented in Python._

------
cincinnatus
I don't like the way in line functions hurt the readability of code. Is there
anything out there that solves that issue?

Also I haven't had an excuse to use it yet but F# seems to have great
syntactic sugar for parallelizing things in a more natural way than the
typical map reduce.

~~~
dpritchett
Inline anonymous functions don't have to look so funny. Here's an example in
coffeescript that takes no arguments and performs a quick action:
[https://github.com/dpritchett/chatbox/blob/master/public/cha...](https://github.com/dpritchett/chatbox/blob/master/public/chatbox.coffee#L54)

Explanation: After the 500ms textbox fadeIn finishes it fires a an anonymous
callback function which calls a 3000ms fadeOut.

------
ericf
I implemented these examples in Ruby 1.9, would love to know if there are more
efficient ways of doing some of these:

    
    
        def cook(p1, p2, f)
          puts "get the " + p1.to_s
          f.call(p1)
          f.call(p2)
        end
    
        cook( "lobster", "water", lambda {|x| puts "pot " + x })
        cook( "chicken", "coconut", lambda {|x| puts "boom " + x })
    
        @a = [1,2,3]
        @a.map {|x| puts x*2}
        @a.map {|x| puts x}
    
        def sum(a)
          @a.reduce(0) do |a, b|
            a + b
          end
        end
    
        def join(a)
          @a.reduce("") do |a, b|
            a.to_s + b.to_s
          end
        end
    
        puts "sum " + sum(@a).to_s
        puts "join " + join(@a)

~~~
flatwhatson
Here's one more efficient way... use perl!

    
    
      #!/usr/bin/perl
      
      use Modern::Perl;
      use List::Util 'reduce';
      
      sub cook {
        my ($i1, $i2, $f) = @_;
        say "get the $i1";
        $f->($i1);
        $f->($i2);
      }
      
      cook "lobster", "water",   sub { say "pot "  . shift };
      cook "chicken", "coconut", sub { say "boom " . shift };
      
      my @a = (1, 2, 3);
      
      map { say $_ * 2 } @a;
      map { say $_     } @a;
      
      sub my_sum {
        reduce { $a + $b } 0, @_;
      }
      
      sub my_join {
        reduce { $a . $b } "", @_;
      }
      
      say "sum "  . my_sum(@a);
      say "join " . my_join(@a);

------
mncolinlee
The moment I read this, I immediately thought of the work I performed on
Cray's Chapel parallel language. Chapel has an elegant way of expressing
functional parallel code like this that is much more difficult to write in
Unified Parallel C and High Performance Fortran. In fact, one Google search
later and I found a student's presentation on Chapel and MapReduce.

[http://www.slidefinder.net/l/l22_parallel_programming_langua...](http://www.slidefinder.net/l/l22_parallel_programming_language_features/29576692)

------
buddydvd
Can Xcode 4 compile code using Objective-c blocks into iOS 3.x compatible
binaries? This article made me realize how much I miss anonymous
functions/lambda expressions from C# and javascript.

~~~
astrange
<http://code.google.com/p/plblocks/>

------
nickik
WOW, welcome to the year 1959.

~~~
anonymoushn
We don't have coroutines yet. Maybe in another 5 years javascript make it all
the way to 50 years ago.

------
hasenj
I think this article was my first introduction to functional programming.

Yea, don't look at me like that. My university mostly taught us Java/C++; we
only did functional programming in one course.

------
SpookyAction
"Look! We're passing in a function as an argument. Can your language do this?"

Umm, yes it can....

#!/usr/bin/perl

sub cook_time { ($hours, $min) = @_; $result = "$hours hours and $min
minutes\n"; return $result; }

sub animal { $animal = shift; return $animal; }

sub cook_animal { ($get_animal, $get_time) = @_; return "Cook $get_animal for
$get_time"; }

print cook_animal(animal(cow),cook_time(5,23));

------
ScotterC
Last time I used FORTRAN was all of 11 months ago. Thank god I've moved on to
O-O and can actually declare functions.

------
jasonlynes
i'm smarter for reading this. need more.

~~~
troels
Joel is a good writer. Especially his early stuff is golden. Go read some of
it.

------
leon_
Yes, Go lets me do this. Though I don't like passing anonymous functions too
much as the codes becomes hard to read rather soon.

------
mkramlich
ahhh... Joel at his best. great piece of writing. and a gem about programming
languages and abstraction.

------
mariusmg
So are we supposed to be impressed by clojures now ? Or are we supposed to be
impressed that the "great" Joel Spolsky (a ex manager in EXCEL team !!!!)
writes about them.

