11 ms·
Joel Spolsky: Can your programming language do this?
- becomevocal 16y agoI 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 16y agoProgrammers are perfectly willing to think this way if you disguise it like this: http://api.jquery.com/each/ 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.
- becomevocal 16y agoOf course. But I'm thinking about it in terms of big data. A lot of us are and will be increasingly dealing with sets of data that should be thought of in parallel solutions. Not just a single process.
- pietro 16y agoIt 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.
- sthatipamala 16y agoThis article shows that Javascript is truly the common man's functional programming language. Despite its ugliness, it got lambdas/anonymous functions right.
- owls 16y agoMore proof that people who hate on javascript don't know javascript.
- owls 16y agoMore proof that people who hate on javascript don't know javascript.
- deleted 16y ago[deleted]
- DavidMcLaughlin 16y agoWhen I think about how content I was to use PHP and Java before learning JavaScript, I'm really grateful that a language that every modern web developer is forced to use has first-class functions (and closures), especially when you read up on the history of the language and how, given more time, it could have ended up a lot closer to Java[1]. 1 - http://www.jwz.org/blog/2010/10/every-day-i-learn-something-new-and-stupid/ http://www.jwz.org/blog/2010/10/every-day-i-learn-something-...
- ionfish 16y agoAs 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/ http://math.andrej.com/2009/04/09/pythons-lambda-is-broken/
- sid0 16y agoNot 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/ http://brendaneich.com/2010/11/paren-free/ -- see the "Implicit Fresh Bindings" section.
- ionfish 16y ago
- jasonlynes 16y agoi'm smarter for reading this. need more.
- troels 16y agoJoel is a good writer. Especially his early stuff is golden. Go read some of it.
- buddydvd 16y agoCan 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 16y agohttp://code.google.com/p/plblocks/ http://code.google.com/p/plblocks/
- JonnieCache 16y agoSomething 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 http://blog.jayfields.com/2008/03/ruby-inject.html
- revorad 16y agoA nice relevant post from raganwald - http://weblog.raganwald.com/2008/02/1100inject.html http://weblog.raganwald.com/2008/02/1100inject.html
- raganwald 16y agoThanks you for your nice words. I would have suggested this one, although it has been discussed Ad Nauseum on HN in the past: http://weblog.raganwald.com/2007/03/why-why-functional-programming-matters.html http://weblog.raganwald.com/2007/03/why-why-functional-progr...
- SandB0x 16y agoIn 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
- JonnieCache 16y agoReduce is an alias for inject, and depending on which template you use to generate the docs, method aliases are not given the same prominence as full methods. Some templates simply say "this method is also known as..." underneath the original method in a tiny font. If it were the other way around, and inject was an alias for reduce, I think there would be a lot less confused rubyists.
- DanielRibeiro 16y agoI've wrote about these ancient ways of great languages a while ago: http://metaphysicaldeveloper.wordpress.com/2009/05/02/closures-collections-and-some-functional-programming/ http://metaphysicaldeveloper.wordpress.com/2009/05/02/closur...
- chuhnk 16y agoHas 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?
- yesbabyyes 16y agoTry javascript and/or coffeescript. Valuable skills!
- macmac 16y agoYes
- JonnieCache 16y agoI hear lisp is pretty sweet. Haskell as well, there's a lot going on there. You can program in a very functional style in ruby or javascript if you want something with a more active community, its just that the resources you find on those languages won't be so targeted at FP, because its not their defining feature.
- merijnv 16y agoI 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/ 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 http://news.ycombinator.com/item?id=1145743 http://news.ycombinator.com/item?id=1145743
- svrocks 16y agoDoes 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 16y agoJava'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 16y ago"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"
- kragen 16y ago> 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/102653989.05.01.acc.pdf 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.
- dantheman 16y agoThere's a great talk on parallelism, by guy steele: http://www.infoq.com/presentations/Thinking-Parallel-Programming http://www.infoq.com/presentations/Thinking-Parallel-Program... Notice that you need different data structures.
- edanm 16y ago"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.
- 16y ago
- deleted 16y ago[deleted]
- justwrote 16y agoYes, 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))
- ajays 16y agoHe 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.
- merijnv 16y agoIt is when your reduce operation is associative. (Example: sum, or --- less trivially --- merge sort)
- Zaak 16y agoReduce 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 16y agoNo, 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.)
- Zaak 16y agoGood point. On a related note, I would like to see a "perverse" compiler and library which exhibits random behavior when undefined behavior is specified by the language.
- wnoise 16y agoEven 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.
- leon_ 16y agoYes, Go lets me do this. Though I don't like passing anonymous functions too much as the codes becomes hard to read rather soon.
- hdragomir 16y agoI 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 16y agoIt'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 16y agoThe Google Search engine was at some point written in Python.
- icey 16y agoDo you have a source for this? I'd love to read more about it; I knew they had python in use in some places, but this is the first I've heard that it was used in their search engine.
- pragmatic 16y agoThe crawler was in Python, not the search engine itself. http://infolab.stanford.edu/~backrub/google.html http://infolab.stanford.edu/~backrub/google.html Both the URLserver and the crawlers are implemented in Python.
- pragmatic 16y agoYes the additions that were added to support Linq makes c# (imho) the most advanced production language today. By advanced I mean it's fully supported, everything works as intended. It's not academic. I can use advanced features today for real work and get full IDE support and good documentation. I just built a search engine. I was able to make it massively parallel with some trivial parallel linq code. The power of Linq and the parallel extensions is just amazing.
- nickik 16y agoWOW, welcome to the year 1959.
- anonymoushn 16y agoWe don't have coroutines yet. Maybe in another 5 years javascript make it all the way to 50 years ago.
- tybris 16y agoYup, 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.
- gaius 16y agoFTA: 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...
- rivalis 16y agoEven 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...
- onan_barbarian 16y agoI 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?
- jxcole 16y agoI agree that map reduce does not solve parallel computing. However, that is just one example of how closures/lambdas can help you design software. It helps in almost any system in which you have two different functions that do almost the same thing, but a little different. For example, say you are writing a double linked list. The push to head and push to tail functions may be completely the same, except one uses the head and one uses the tail, so you could just write one function and pass in the head or tail, right? But you can't. The problem is that when you add something to one side, the links are the opposite. That is a new head goes to the right of the old head, while a new tail goes to the left of the old tail. In order to really do this, you also have to pass in a function. Now, I've written a few doubly linked lists in Java (don't get mad, there _are_ good to do this), and in the end I just copy the function and make the inline changes. Trying to create the right interface and then pass in anonymous classes is almost impossible. And even if in my supreme cleverness I could figure it out, the code would be so confusing as to be unmaintainable. Anyway, I would say that closures are immensely useful, not _just_ because of map reduce, but because of a whole class of examples that work in this manner.
- cincinnatus 16y agoI 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 16y agoInline 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/chatbox.coffee#L54 https://github.com/dpritchett/chatbox/blob/master/public/cha... Explanation: After the 500ms textbox fadeIn finishes it fires a an anonymous callback function which calls a 3000ms fadeOut.
- pmr_ 16y agoToday 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.
- mncolinlee 16y agoThe 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_language_features/29576692 http://www.slidefinder.net/l/l22_parallel_programming_langua...
- mariusmg 16y agoSo 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.
- grav1tas 16y agoI 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.5859 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.
- Dn_Ab 16y agoWow. 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.
- hasenj 16y agoI 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.
- mkramlich 16y agoahhh... Joel at his best. great piece of writing. and a gem about programming languages and abstraction.
- ScotterC 16y agoLast time I used FORTRAN was all of 11 months ago. Thank god I've moved on to O-O and can actually declare functions.
- ericf 16y agoI 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 16y agoHere'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);
- SpookyAction 16y ago"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));