Joel Spolsky: Can your programming language do this?

  • 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?

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

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

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

  • 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

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

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

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

  • 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?

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

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

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

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

  • 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

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

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

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

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

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

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

  • undefined

  • WOW, welcome to the year 1959.

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

  • "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));

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

  • i'm smarter for reading this. need more.

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

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

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