I will freely admit to thinking I knew something about functional programming after learning about lambdas, map, filter and reduce in Python (and lamenting the language's lack of tail call optimization). Three days with Haskell set me straight.
Haskell, Scheme (a LISP), Erlang, Clojure will all set you straight. What's great is, once you grok one; the others are much easier to get a handle on.
I don't get the jump between talking about how "functional" doesn't evoke purity, yet "expression-oriented", does. To be fair, most (probably all) functional languages aren't fully pure. Haskell is probably more pure than most of them, but eventually you have to get things done and dive into the sin bin.
The term "functional" really evokes "first class" invokable function symbols that can be passed around in a higher-order kind of way. Everything else is secondary.
Expression-Oriented evokes purity in that expressions have external facing properties which can be understood and even kept in the type system. If you use global mutation and you don't keep track of it in the type system you can't say much about those properties anymore. You can however use mutation locally and maintain your referential transparency which is a nice way to get around the limits of some not-so-functional-friendly algorithms.
I'm planning on writing about that next time around because my tracking shows barely anyone gets to the end of an article beyond a certain length.
Making things like if/else/switch statements true expressions is a great feature, but I'm not sure it deserves a new foo-oriented programming buzzword.
The reason I suggested it is because functional has been so badly perverted. We need a term that evokes the actual spirit/philosophy of the language, not just the heritage.
I would also accept "Compositional Programming". Just as the secret to deeply understanding Erlang is to understand that it is primarily about reliability and not about "functional", the secret to understanding where the bulk of the Haskell community is going (except the ones strictly studying theory for theory's sake) is that they are primarily working on composition. Purity and functional and lazy are just some of the buzzwords being turned in that direction. Just as aping Erlang's message passing in $YOUR_FAVORITE_LANGUAGE while missing everything it does for reliability is really missing the point, dropping a map and a fold into your language (and eyeing Monads, then skittering away, though I suspect this is often not because they're too complicated but because the languages are too weak to make them syntactically palatable) is also really missing the point.
(Also, mocking Haskell and/or its community for being too theoretical is really missing the point. It's one of the few places where progress is being made on the topic of practical compositional programming. No, the work's not done yet, but, then, that's sort of my point, work is in fact being done. Almost everybody else is just shuffling the same handful of 25-year-old primitives around.)
I think the real reason Monads haven't been widely adopted is that there isn't a compelling need for them. Laziness, purity and referential transparency are nice-to-haves in most languages, whereas in Haskell, they're need-to-haves.
Computers are imperative by nature. They have state. That Haskell has the capability to shield us from that is really, really cool, but that doesn't mean it's necessary to write good software.
Do you have an example of something that can be more comfortably expressed with a monad, even in a language with imperative constructs? To me monads have always seemed like a crutch for a deficient language.
(A lisp advocate once told me tail recursion was great because it let you write an event loop without getting a stack overflow. The example code they gave would have been much clearer as a "while" loop, but I guess lisp doesn't have those. It seems like the same thing to me)
That explains why I want tail recursion in lisp, a language that doesn't have "while". But it doesn't explain why I would ever want or need tail recursion in python, which already has while.
In the same way, I can see why I want monads in Haskell. But why would I ever want or need monads in a language that has first-class imperative constructs?
Agreed about tail recursion in Python, promoting recursion as an alternative to loops is contrary to the sprit of Python and "one obvious way to do it".
Monads vs imperative languages is a bit different though, since Haskell-style monads are more than just a way of allowing an imperative style. For example many other features like list comprehensions and parsing is also based on monads in Haskell. However the real value of Haskell-style monads is that it is clear when you don't use them. So it is explicit in the type system which functions have side effects and which don't.
>Monads vs imperative languages is a bit different though, since Haskell-style monads are more than just a way of allowing an imperative style. For example many other features like list comprehensions and parsing is also based on monads in Haskell.
So maybe monads are useful for implementing language internals. But if we assume my language has list comprehensions and a parser (however they're implemented internally), what would I as a user of the language want monads for?
I keep hearing that monads are great, but every code example I've seen using them seems like something that I could do more easily in python, without a monad. So I'd really like to see any examples of code that you would want to write using monads, even in a language that has imperative constructs.
"Do you have an example of something that can be more comfortably expressed with a monad, even in a language with imperative constructs?"
STM.
And it may not be immediately obvious that it does need to be a monad, if you don't deeply understand how it is being used. There's more to it than meets the eye at first.
I'm going to run on the theory you don't know Haskell. In this case, it's like someone who doesn't know OO at all asking why on Earth they'd want the Factory pattern. It isn't that I can't explain it per se, but no satisfying answer can be fit into an HN post. So I didn't try.
To forestall the "copout" accusation: Basically, being a monadic value means you get the full range of monadic programming constructs available to you while still being isolated by the type system, so you get isolation while still being able to do real work, and the STM system takes extensive advantage of the fact that the do syntax actually desugars into a long series of closures, which means it can do things like retry and choice and stuff in a natural, safe way.
But I am well aware that doesn't sound very impressive in isolation, because there's still a great deal of important nuance lost in that description, in much the same way that a similarly-detailed description of the factory pattern sounds generally unimpressive. ("What, it lets me either construct one thing or the other? Big whoop, here's how I do it in my procedural language, basically by just doing it. What's the big deal? Why does it make such a big fuss over something so simple?" Which is a good question, if asked honestly, but a terrible argument, which is how it would usually be meant.)
Well, he said "computers" not "computation", but I can see how stating the former necessarily begets the latter. I want to back him up in some ways, but to also narrow the scope of his statement to not include all computers:
The presence of a separate, non-computational storage in computers based on the von Neumann architecture implies the representation of state (at some level or another) is possible by those same machines. Consequently, we are tempted to utilize that capacity for state as storage for intermediate data within program execution itself, rather than just as a place to store the executed program itself.
I could be totally missing the mark on it having to do with the von Neumann model of storing, fetching and executing programs. But from my limited reading about computers such as ENIAC, which required the circuit to be altered to route signals prior to execution, and then run through the program all at once. Someone please correct me if I'm wrong, because this stuff fascinates me and I'd love to be told straight.
TL;DR: Storage implies state, so we wrote tools (languages) to leverage that.
Yes absolutely the von Neumann architecture gave rise to the dominant means of computation - formulas and algorithms translated into imperative programs yoked to instruction pointers and addressable memory.
But there is that step of translation. Perhaps we became so fluent that we stopped noticing we were doing it, and perhaps even to start thinking imperatively. But it's not "natural".
Look at computing polynomials by differences (giving rise to the idea of the difference engine): better described as an iterated procedure than an imperative program.
Or "constuctive": Construct an ellipse naturally by the pins and string method. Bisect an angle naturally using straight-edge and compass.
Isn't it though? Good is certainly a relative term.
Programming is a young endeavour, and advancements will be made in languages for a long time to come. We should continually reevaluate our tools so that we can make the best software we can.
I think this is somewhat misleading. If I understand correctly, only the SelectMany method in Linq is monadic. This is only a minor part of Linq, and not at all comparable to how monads are used in Haskell.
Monads are to state as fruits are to apples: one of their applications (and probably the most common one) is for state, but it's by no means the only application.
No offense, but I think you may be pandering to people who don't know what they're talking about.
Guido adds first class functions and functools to the standard library and people suddenly start calling Python "functional." The same is probably going to start happening to Java once λ expressions get added.
I think functional programming has been most clearly defined as those languages whose base model of computation is the λ-calculus (or F_ω, etc.). Python is not functional because, despite having functional features, the idiomatic way of writing Python mirrors Turing machine computation.
If you restricted yourself to only the functional features of Python, well, that would be Python in name, but is that really how people write Python?
The problem is that the vast majority of programmers don't know what they're talking about. Scott Hanselman calls them then silent 99%. They don't blog or comment and they do real work everywhere.
The point is terminology matters, especially in how those who can't be bothered to dig deeply come to understand things.
As someone who has regularly given tech talks on functional programming to audiences completely foreign with the ideas involved for almost five years I'm completely sick of explaining it. It takes up far too much of my time when I could be teaching other cool stuff.
Calling it Expression Oriented programming is something I've taken to recently and it get people on the right track off the bat. They're focusing on the expressions and not just trying to understand how everything relates to functions.
I highly recommend Backus' "Can Programming Be Liberated from the von Neumann Style?" as a classic critique of the expression-statement dichotomy (which Backus calls "the von Neumann bottleneck" and then immediately blames himself for rather than von Neumann - Backus of course having led the creation of FORTRAN).
Most languages nowadays are value-level. The difference between the two is that function-level programs combine "sub-programs" into one giant new program. Value-level takes primitive types and transforms them into more complex types (I believe this is the same sort of complexity that Rich Hickey disparages).
No. Clojure is fine. I actually think that everyone's first functional language should be scheme, but Clojure is not too different.
Erlang, Haskell, and Scala all want to sell you on some broader paradigm of functional programming, but they're all still functional at their core and understanding the fundamentals is far more important.
Node.js isn't a language, it's just some libraries added on to Javascript.
Being a functional language is different than supporting functional programming. A functional program is one that fits the mold he describes: high-order functions as the primary abstraction, pure wherever possible, heavy emphasis on transformation of values rather than updating state. A language that supports functional programming is one where such a program is possible (without too much trouble). A functional language is one where programs that don't fit that mold are difficult or discouraged.
To the author's point, supporting functional programming is not hard and most languages do it. Basically all languages, except Java and maybe like Pascal or something. I'm ok with that, though. I'm fine that I can write my own JavaScript functionally, even though other people may not. (Though it's annoying when I interface with less-than-functional third-party libraries.) After all, part of the reason to learn Lisp or Haskell is to make you better at Blub, right? Merely supporting functional programming means that I can still put those lessons to decent use.
Really, the volume of discussion ad nauseum around what normally amounts to syntactic sugar is over the top. I'm not disparaging the functional programming paradigm, but conversation seems to eventually gravitate toward measurements of how functional something is, which seems pointless.
I have to say, I'm not very fond of hyperbole-laden posts such as this one. People who devote the time and enthusiasm learn the details and hopefully enlighten themselves. People who lack that kind of enthusiasm will likely be confused throughout their lives across many of fronts. What is so surprising, interesting or new about this?
Things are often called what they are called for continuity and for legacy reasons. What does it matter if we call it "Functional" vs. "Expression-Oriented"? Will Haskell, ML, Clojure, etc. or their impression on people change overnight because we have a new cool name? I guess a bunch of new/experimental library now claim to be Functional, but so what if they do?
Maybe it's a bit too late here, but I don't see the point in this kind of sentiment.
I think Ruby does a pretty good job with this, as far as an object oriented main stream language goes. For example, the switch case example from the article can be implemented in ruby as
case n:
when 1: "one"
when 2: "two"
etc, and the entire construct can be assigned to a variable because it is an expression.
49 comments
[ 4.1 ms ] story [ 99.3 ms ] threadThe term "functional" really evokes "first class" invokable function symbols that can be passed around in a higher-order kind of way. Everything else is secondary.
I'm planning on writing about that next time around because my tracking shows barely anyone gets to the end of an article beyond a certain length.
(Also, mocking Haskell and/or its community for being too theoretical is really missing the point. It's one of the few places where progress is being made on the topic of practical compositional programming. No, the work's not done yet, but, then, that's sort of my point, work is in fact being done. Almost everybody else is just shuffling the same handful of 25-year-old primitives around.)
Computers are imperative by nature. They have state. That Haskell has the capability to shield us from that is really, really cool, but that doesn't mean it's necessary to write good software.
(A lisp advocate once told me tail recursion was great because it let you write an event loop without getting a stack overflow. The example code they gave would have been much clearer as a "while" loop, but I guess lisp doesn't have those. It seems like the same thing to me)
In the same way, I can see why I want monads in Haskell. But why would I ever want or need monads in a language that has first-class imperative constructs?
Monads vs imperative languages is a bit different though, since Haskell-style monads are more than just a way of allowing an imperative style. For example many other features like list comprehensions and parsing is also based on monads in Haskell. However the real value of Haskell-style monads is that it is clear when you don't use them. So it is explicit in the type system which functions have side effects and which don't.
So maybe monads are useful for implementing language internals. But if we assume my language has list comprehensions and a parser (however they're implemented internally), what would I as a user of the language want monads for?
I keep hearing that monads are great, but every code example I've seen using them seems like something that I could do more easily in python, without a monad. So I'd really like to see any examples of code that you would want to write using monads, even in a language that has imperative constructs.
STM.
And it may not be immediately obvious that it does need to be a monad, if you don't deeply understand how it is being used. There's more to it than meets the eye at first.
And you're right, it's not immediately obvious that it needs to be a monad. Could you elaborate?
To forestall the "copout" accusation: Basically, being a monadic value means you get the full range of monadic programming constructs available to you while still being isolated by the type system, so you get isolation while still being able to do real work, and the STM system takes extensive advantage of the fact that the do syntax actually desugars into a long series of closures, which means it can do things like retry and choice and stuff in a natural, safe way.
But I am well aware that doesn't sound very impressive in isolation, because there's still a great deal of important nuance lost in that description, in much the same way that a similarly-detailed description of the factory pattern sounds generally unimpressive. ("What, it lets me either construct one thing or the other? Big whoop, here's how I do it in my procedural language, basically by just doing it. What's the big deal? Why does it make such a big fuss over something so simple?" Which is a good question, if asked honestly, but a terrible argument, which is how it would usually be meant.)
The presence of a separate, non-computational storage in computers based on the von Neumann architecture implies the representation of state (at some level or another) is possible by those same machines. Consequently, we are tempted to utilize that capacity for state as storage for intermediate data within program execution itself, rather than just as a place to store the executed program itself.
I could be totally missing the mark on it having to do with the von Neumann model of storing, fetching and executing programs. But from my limited reading about computers such as ENIAC, which required the circuit to be altered to route signals prior to execution, and then run through the program all at once. Someone please correct me if I'm wrong, because this stuff fascinates me and I'd love to be told straight.
TL;DR: Storage implies state, so we wrote tools (languages) to leverage that.
But there is that step of translation. Perhaps we became so fluent that we stopped noticing we were doing it, and perhaps even to start thinking imperatively. But it's not "natural".
Look at computing polynomials by differences (giving rise to the idea of the difference engine): better described as an iterated procedure than an imperative program.
Or "constuctive": Construct an ellipse naturally by the pins and string method. Bisect an angle naturally using straight-edge and compass.
Programming is a young endeavour, and advancements will be made in languages for a long time to come. We should continually reevaluate our tools so that we can make the best software we can.
Guido adds first class functions and functools to the standard library and people suddenly start calling Python "functional." The same is probably going to start happening to Java once λ expressions get added.
I think functional programming has been most clearly defined as those languages whose base model of computation is the λ-calculus (or F_ω, etc.). Python is not functional because, despite having functional features, the idiomatic way of writing Python mirrors Turing machine computation.
If you restricted yourself to only the functional features of Python, well, that would be Python in name, but is that really how people write Python?
The point is terminology matters, especially in how those who can't be bothered to dig deeply come to understand things.
Calling it Expression Oriented programming is something I've taken to recently and it get people on the right track off the bat. They're focusing on the expressions and not just trying to understand how everything relates to functions.
www.cs.cmu.edu/~crary/819-f09/Backus78.pdf
http://en.wikipedia.org/wiki/Function-level_programming
Most languages nowadays are value-level. The difference between the two is that function-level programs combine "sub-programs" into one giant new program. Value-level takes primitive types and transforms them into more complex types (I believe this is the same sort of complexity that Rich Hickey disparages).
Erlang, Haskell, and Scala all want to sell you on some broader paradigm of functional programming, but they're all still functional at their core and understanding the fundamentals is far more important.
Node.js isn't a language, it's just some libraries added on to Javascript.
To the author's point, supporting functional programming is not hard and most languages do it. Basically all languages, except Java and maybe like Pascal or something. I'm ok with that, though. I'm fine that I can write my own JavaScript functionally, even though other people may not. (Though it's annoying when I interface with less-than-functional third-party libraries.) After all, part of the reason to learn Lisp or Haskell is to make you better at Blub, right? Merely supporting functional programming means that I can still put those lessons to decent use.
What's forcing you to stay in Blub to begin with? There's great same-platform alternatives to everything except for Javascript.
Things are often called what they are called for continuity and for legacy reasons. What does it matter if we call it "Functional" vs. "Expression-Oriented"? Will Haskell, ML, Clojure, etc. or their impression on people change overnight because we have a new cool name? I guess a bunch of new/experimental library now claim to be Functional, but so what if they do?
Maybe it's a bit too late here, but I don't see the point in this kind of sentiment.
etc, and the entire construct can be assigned to a variable because it is an expression.
As an aside, ruby 1.9 also supports generic predicates for case statements through lambdas, which is very concise and powerful. Example: https://gist.github.com/838163, source: http://flazz.me/predicates-in-ruby-case-statements