A Bit of Heresy: Functional Languages are Overrated(benrady.com) |
A Bit of Heresy: Functional Languages are Overrated(benrady.com) |
The author then conflates Actor based concurrency with functional programming in general. Let me lay my bias on the table right now: I'm sick to death of hearing about Actors. Erlang put them on the map and I think Scala made them popular, but Scala missed the point. Erlang's actors are only one piece of its distributed-ness; there's a lot more to Erlang that lets it scale as well as it does: a VM tuned for a large number of green threads, a transparent network stack, the supervisor model etc. Scala has none of these. Not only that, but actors themselves only solve one tiny problem in the world of concurrent (really, for actors, distributed) computing. They do nothing to address shared state. Finally, neither of these languages is really, truly functional.
If the author has titled the post "Actors are Overrated" I might have agreed with it.
It looks like at the beginning of this year he had decided to learn Haskell as a way to explore functional programming: http://www.benrady.com/2010/01/language-of-the-year-2010.htm....
I simply don't think being unable to learn a language qualifies you to bash it. It'd be more fitting for him to post about the first point in whatever Haskell resource he is using where he became confused.
Proof: It all ends up running on assembler anyhow, which isn't functional or OO or logical or any other paradigm. All of those things are categories of programs, not intrinsic attributes of the environment.
The author isn't being anywhere near as contrary as I think he'd like....
And if you're working in a hybrid language, what assurances do you have, really, that your system is really thread safe? Indeed, the risk compentation effect might give you a false sense of security, giving you a system that actually had more concurrency problems that one written in a "dangerous" language.
The phrase "hybrid language" in the original text is a hyperlink to Scala. By author's own admission, the languages he has tried were Erlang and Haskell. He hasn't mentioned either trying or not trying Scala, but the article seems to imply the latter.
The problem with what he said is that he seems to be dismissing "hybrid" languages like Scala as equally overrated or worse than pure functional languages like Haskell. Just like Jerf pointed out, it's the paradigm that matters more than the language. A language like Scala is designed to make FP paradigm easy to do, without being a pure functional language. As far as I know, it's not designed to make your programs "thread safe". I believe that the author wouldn't have made his claim about "risk compensation" if he had actually studied Scala and tried it out.
The author links to the formal specification of Haskell and decides pattern matching is "academic shitheaddery"?
Is the formal specification of Java any better? http://java.sun.com/docs/books/jls/third_edition/html/gramma...
Any formal language spec will look ugly and hard to read. That's just the nature of formal specs.
Simon Peyton-Jones, the father of Haskell himself, dismisses the idea that avoiding mutable state automatically buys you easy concurrency:
But it turned out to be very hard to turn that into actual wall clock speedups on processes, leaving aside all issues of robustness or that kind of stuff, because if you do that style of concurrency you get lots of very tiny fine-grained processes and you get no locality and you get very difficult scheduling problems. The overheads overwhelm the benefits,
There are some useful and novel ideas in the FP languages but they're no silver bullet. Whatever the conventional solution for concurrent programming turns out to be, it will have to be exploited in languages more accessible to the median programmer than Haskell.
Haskell is a research language. It has become usable for practical purposes, but at heart its still a research language. Haskell focuses on isolating side effects over practicality or ease of use. For certain classes of problems, that turns out to be the most practical thing. Want to be sure certain parts of your program don't access /dev/nuclear_missiles? Haskell is your language. Is your app mainly centered around a complex GUI? Maybe you should look elsewhere.
I notice no mentions of Clojure in the article aside from including it in a list of functional languages. Clojure is a practical language designed for getting stuff done. It offers the most comprehensive solution for managing shared state I've seen in any language and does its best to get out of your way.
Actually, if you want to make sure that parts of your program don't access /dev/nuclear_missiles, I think that E is your language. In Haskell, it's as easy as `unsafePerformIO $ hGetContents "/dev/nuclear_missiles"`, which at least warns you that it's unsafe but doesn't really give any assurances otherwise. And if you're in the IO monad anyway, you don't even need the unsafePerformIO.
By being immutable by default, FP makes message passing simpler and in some cases forces you not to do shared state, so FP helps you do concurrency that way. Closures are old news and are in almost everything now anyway (I'd include Java anonymous inner classes, they just have a nasty syntax, although good for grouping methods, e.g. mouse events).
Other than immutability by default and closures, what makes a language functional anyway? Because if you take the languages you suggested together, that's all I see them having in common.
I never found FP hard to learn or use. I know now that it was because when i got introduced to it, i only had about a year of imperative programming experience and no OO experience at all. I was a rather fresh mind. My advice to all OO programmers willing to learn FP is to approach it with a fresh mind, it may save you a lot of headaches. Imagine you know nothing about programming, you may be surprised how close to the truth that is for some of us(including me).
The author himself says that he is too dumb to understand functional languages. He knows so little that he keeps carping on about concurrency, when that is nowhere near any of the central reasons why functional is valuable. Better support for concurrency is an accident, a side-effect. What makes functional important is the ability to create living abstractions, and understanding how functional allows you to do that and why it is important makes you a better programmer in any language.
There is no heresy here, only ignorance. The author is basically saying functional languages are "overrated" because he is unable to understand them. There are definitely valid arguments to be made against the functional paradigm, but he has made none of them.
He's obviously not really looked at Clojure, then. The design philosophy of Clojure can be summarized as "functional programming is great; people also need to get stuff done."
I think that FP is much more than that, it actually helps me solve problems in a very different and elegant way. I don't know how you classify this benefit but for me it was analogous to solving a problem with recursion or loop; while both can solve problems, recursion seems to be much more intuitive and elegant way - Same for FP vs Imperative approaches.
(Where "seriously" and "hell" are links to the Haskell and O'Caml language documentation.)
God oh god I wish every language I used could be specified as precisely as the Haskell example. I didn't bother figuring out what the notation meant, but if I used Haskell, I could afford the time to understand it. Seriously, Perl and C++ have equivalent complexities; you just aren't expected to understand them. Experienced programmers steer clear of unfamiliar constructs, which works well enough, but it would be so much nicer to actually understand stuff.
No silver bullet. Getting computers to run code correctly (for whatever version of 'correct' you subscribe to) is hard, there is simply no way round this.
It's almost like saying 'the future of engineering is when both my mum and my little brother can build cars. Neither of them will ever understand mechanics.'
When you think of it that way, the idea that programming is for everyone seems a little silly.
What makes you think so? What fundamental complexity in the paradigm is out of their reach? Do you really think that the concepts of imperative and OO programming are simpler? Why?
The future of programming is definitely not to turn users into programmers.
I'm not suggesting that everyone will ever be able to implement quicksort or write a parser, but there is no fundamental reason that high-level programming will always be inaccessible to the masses.
Strings:
1> [98, 114, 111, 107, 101, 110] == "broken".
true
Records are not real records, just compile time labels placed on top of tuples. The syntax, seriously, three different line terminators?Python and Haskell both make great go-to languages, and you can solve a wide variety of problems in them. Erlang is the language you suffer with when you truly need massive concurrency and distribution.
Scala, on the other hand, seems to strike more reasonable compromises on these points and has all the java stuff to draw on when you need it.
If Erlang is suffering, why has no-one written a better front end compiler with saner syntax or a static type system?
Now admittedly the programming models in HPC were ugly (MPI), but nevertheless the lack of shared state and the use of message passing certainly didn't make it easy to write high performance parallel apps.
I think the problem is fundamentally hard. And when a problem is fundamentally hard the solution to it often is "that other thing we haven't really tried yet". Until you've really tried it.
For most business processes, you don't care about performance all that much, you just want things to be easy to change. Message-passing works fairly well for that: it's easy to understand, composable, and let's you swap out one component for another as long as the interfaces are compatible.
BTW, message-passing isn't exactly untried. It's the basis for the service-oriented architectures that underlie Google, Amazon, FaceBook, and many other large businesses. It works very well for that problem domain.
Also, is their situation analogous to multicore today? i.e. does many GB of RAM shared by many cores count as "large-scale shared memory" (or does it not, e.g. because of cache effects)?
You probably wanted to say that the future is not to dumb down programming to the level of my "mom". I agree with that.
With that said, you're right. If you don't care about performance, or if you have very large grains of computation then message passing is relatively easy (although so is just about any model with those requirements). The question is what about when you actually do care about performance and your grains aren't so large that doing communication half-way across the planet isn't acceptable? When I'm trying to get 60FPS in my physics engine, I probably don't want to use a web service interface.
His point is that concurrency is something you still have to consider carefully and design in from the ground up and that no language is a silver bullet for this essentially hard problem. I think he's also probably right to worry that people will think that because they're using Scala and actors, for example, that they have automatically created a correct, performant concurrent application.
That doesn't mean though that languages like Scala don't give you better tools to deal with these issues than Java.
Note however, that languages are extremely good at making us use their prefered paradigm.
For her and many other people I know, it's a bit like driving a car. If you want to drive a car, you have to know certain basic rules and that's it. The guys who do maintenance and repairs are the ones who know what happens under the hood and you take your car to them whenever necessary.
I also know lots of people who know what happens under the hood and love to tinker with their cars. I'm not one of them myself. I do that with computers, but not with cars. I don't see why computers should be a special case where everyone has to know how to tinker with the "stuff under the hood".
Second, programming does not always mean tinkering stuff "under the hood". Advanced spreadsheets are front-end and programming at the same time. Even that:
$ cat * | grep "groceries" | sort
is a program (though a rather trivial one). "Real" programs will still be professional and hobbyist stuff. But scripting can be everyone's business.Third, computers are fundamentally different from any other device: they are general purpose. They can process any information you care to feed them, in any way you care to program them to.
Finally, when our moms want to do something the computer can't presently do, but could, they have 3 alternatives: give up, acquire a program that does the job, or program it themselves. For many many jobs, acquire a program is the only viable solution. But for small, specialized tasks, the existence of a dedicated program is less likely. So if our moms want the damn thing to "just work", they have no choice but to program it.
Knowing how to use computers (and the internet) is becoming as important as knowing how to read. Because computers are general purpose machines, knowing how to program them is an important part of knowing how to use them. It's a big investment, but so is reading.
"Parallel" is about optimizing an otherwise linear, or atomic program. Like map-reduce, which can be run on a single CPU or on a distributed cluster. When SPJ is talking about parallelism he is most likely talking about nested data parallelism.
The distinction between the two can be made without ambiguity with the terms "task parallelism" (concurrency), and "data parallelism" (parallelism).
If you read the interview he is talking about the kind of implicit concurrency FP advocates often suggest you get for free with FP.
I suppose Haskell initially wasn't a concurrent language at all. It was a purely functional language and we had the idea from the beginning that a purely functional language was a good substrate for doing concurrency on. But it turned out to be a lot harder to turn that into reality than I think we really expected because we thought "If you got e1+e2 then you can evaluate e1 at the same time as e2 and everything will be wonderful because they can't affect each other because it's pure."
They had to add things like STM and explicit concurrency management because you don't get this for free just because you're doing FP.
I don't understand how offering several choices is synonymous with inconsistent. If you really really want "consistency", establish a policy for your code and stick with it. All three have distinct uses, and each is better for certain things than for others.
Anyhow, there's nothing particularly special about the Maybe or Either monads; you should be able to easily implement either of those yourself, and calling them a feature of the language---beyond the fact that they happen to be in the standard library---is somewhat specious.
http://stackoverflow.com/questions/3077866/large-scale-desig...
I think it is still a little analogous. The main takeaway is that message passing doesn't make things easy. I've spent many of days debugging message passing applications. You often trade-in one type of problem for another. If anyone is interested in more detail, I can go into an example or two.
The part that often ends up being tricky is the fact that I need to send data from processor A to processor B. And I want to send as little data as possible. So one of the first sources of bugs is that when I do my gather-scatter I make a mistake mapping a value to a coordinate. In shared-memory you never have to do this mapping back and forth, so its not an issue.
Next issue is related to the fact that I don't want to ever block waiting for data. There are a variety of models for handling this. I can do a non-blocking receive, and do some work waiting for the data to arrive. This is often another source bugs as people will often do work that depends on the new data, but they chug along without it. Add the new data when they get it, and alas their computation is already hosed.
And the last common error in this case is handing the data off to the wrong object (or processor) or being confused as to which data you're receiving at any given point in time.
Now all of these can be handled by simply being careful, and using some good programming practices. But they are just simple, if not grossly naive, examples of issues you have with traditional message passing that don't exist in shared memory.
Way less scary than actual magic.
In another sense, I think there's "magic" in the sense of "wonder". Even knowing how some piece of technology works, I find myself constantly in amazement and wonder that it does work.
What you're describing sounds to me like the complexity of ferrying data around and scheduling computations is being offloaded to the app programmer. Presumably the intent behind things like OpenMP on clusters is to take care of all that behind the scenes and let the user pretend that it's all shared and program accordingly. Is that correct? If so, how far would you say such distributed infrastructure has gotten to date? Is it usable for real work, or do people end up having to learn so many limitations and workarounds that they're no better off than programming against the lower-level model in the first place?
Another question: even when there is shared memory you still have to coordinate the various processes that are operating concurrently on it so they don't clobber each other, and that, as everyone knows, is complicated too. So there is a tradeoff here. It sounds like your point is that given a choice, the HPC community would rather program against shared memory using traditional concurrency mechanisms (threads, locks, etc.) than deal with the complexities of the alternatives. Am I reading you correctly? If so, that's a pretty major point which suggests that the general-purpose programming community may be gearing up for a wild goose chase.
Maybe and Either are both monads and by convention Left errCode is the fail method of the Either monad. This will work exactly as you think it should. It will also work with, e.g., io actions that might fail.