13 ms·
Why functional programming matters (1990) [pdf]
- brudgers 7y agoSome previous discussions https://news.ycombinator.com/item?id=14138196 https://news.ycombinator.com/item?id=14138196 https://news.ycombinator.com/item?id=13129540 https://news.ycombinator.com/item?id=13129540 https://news.ycombinator.com/item?id=9502049 https://news.ycombinator.com/item?id=9502049
- AlexanderDhoore 7y agoFor me (and I'm going to sound like a massive fan boy here) Rust has surpassed functional programming. It gives me mutable data when needed and immutable when shared. So ONLY having immutable data structures now seems stupid. We can have best of both worlds. Read this if you don't follow: "In Rust, ordinary vectors are values" http://smallcultfollowing.com/babysteps/blog/2018/02/01/in-rust-ordinary-vectors-are-values/ http://smallcultfollowing.com/babysteps/blog/2018/02/01/in-r...
- deleted 7y ago[deleted]
- marcosdumay 7y agoFrom the abstract, the main point of the article is: > ...higher-order functions and lazy evaluation, can contribute significantly to modularity. You can not have lazy evaluation without referential transparency, and you can not get referential transparency with mutable variables. Rust does good use of high-order functions, but gets nothing near the modularity of Haskell code.
- rtfeldman 7y ago> Rust does good use of high-order functions, but gets nothing near the modularity of Haskell code. This has not been my experience. After years of doing pure functional programming (in Elm) professionally, I was surprised how much Rust felt like writing in an ML family language. I still prefer referential transparency (though I don't think it would have been the right design for Rust), but "nothing near" does not fit my experience. I'd say the ergonomics of borrow checking versus a GC (which you don't have to think about) is the bigger gap. John Hughes is a big believer in the value of laziness. There are many of us who believe laziness turned out to be a dead end; it makes some code nicer/more elegant, makes performance optimization much harder, and brings space leaks into your life. Are those costs worth the benefits? Not even close to worth it, if you ask me. I'll gladly take the referential transparency and pass on the laziness. Almost all the researchers who started working on Haskell specifically because they wanted to explore the power of laziness...ended up pivoting to research type theory instead. I don't think that's a coincidence.
- zenhack 7y agoDon't get me wrong, I like Elm a lot, but the drop in expressiveness/ability to build abstractions from Haskell is substantial. Especially when you're looking at modularity, there are a bunch of things that Elm can't abstract out that both Rust and Haskell can manage just fine. I haven't used Rust heavily enough to comment on how it compares in great detail, but comparing to Elm as a proxy for Haskell doesn't really work. Frankly, paradigms are a really lousy way to think about languages. I wrote a series of blog posts about this[1], but this opening lecture from one of Brown's PL courses I think does a better job of making the point: https://www.youtube.com/watch?v=3N__tvmZrzc https://www.youtube.com/watch?v=3N__tvmZrzc It's useful to talk about what say, GC, laziness, lifetimes, ownership, typeclasses/traits, higher-kinded types, higher rank types, variants, elm-style records, etc. do to a language, and how they compose, but I think you can't go very far talking about how "paradigms" compare. [1]: https://zenhack.net/2018/07/14/three-funerals-in-the-name-of-clarity-3-systems.html https://zenhack.net/2018/07/14/three-funerals-in-the-name-of...
- rtfeldman 7y ago> It's useful to talk about what say, GC, laziness, lifetimes, ownership, typeclasses/traits, higher-kinded types, higher rank types, variants, elm-style records, etc. do to a language, and how they compose, but I think you can't go very far talking about how "paradigms" compare. Sure. My experience has been: * GC, lifetimes, and ownership are all high-benefit and high-cost. The cost with GC is at runtime (where the cost is so high that in many domains GC is not tolerated at all; in many others, of course, we take it for granted as fine), and the high costs of lifetimes and ownership are at development time. * Variants and records are high-benefit, low-cost. * Higher-rank types are low-cost, low-benefit. * Laziness and higher-kinded types are both features with costs that significantly outweigh their benefits. It sounds like you disagree with the last bullet point. If so, then either we've had different experiences or we walked away with different conclusions from them.
- zenhack 7y ago> * Laziness and higher-kinded types are both features with costs that significantly outweigh their benefits. > It sounds like you disagree with the last bullet point. If so, then either we've had different experiences or we walked away with different conclusions from them. I more or less agree on laziness (at least lazy-by-default; having a lazy type as found in OCaml available is a big win for little downside). Re: Higher-kinded types: I'm curious as to what you think the high costs are? My impression is that they've mostly been left out of Elm due to pedagogical concerns. Is it just that or are there other things?
- zenhack 7y ago> you can not get referential transparency with mutable variables Sure you can: https://homepages.inf.ed.ac.uk/wadler/topics/linear-logic.html#linear-types https://homepages.inf.ed.ac.uk/wadler/topics/linear-logic.ht... Rust's ownership types and lifetimes actually allow for this just fine. The latter is the essence of how Haskell's ST Monad works; you can use lifetimes to get locally-mutable state without violating global invariants, since once they go out of scope they can't be reused. It's interesting to observe that, without "magic" standard library functions and `unsafe`, Rust's type system actually completely constrains mutability, and if a function doesn't have `mut` somewhere in its type signature, it doesn't break referential transparency. That said, in practice, the language does have magic functions that violate this property, and they do so in a way that means you can't use the above reasoning principle at all. Also, mutability being constrained by the types is not the same thing as typical code not using it everywhere, which is the situation with rust-as-found.
- igouy 7y ago> You can not have lazy evaluation without referential transparency, and you can not get referential transparency with mutable variables. "… allowed to update the unique object destructively without any consequences for referential transparency." Uniqueness Typing https://clean.cs.ru.nl/download/html_report/CleanRep.2.2_11.htm#_4.5.1_Basic_Ideas https://clean.cs.ru.nl/download/html_report/CleanRep.2.2_11....
- ojnabieoot 7y agoBeing an OCaml/F# fan, I find this comment amusing. Rust is in fact heavily influenced by functional languages, particularly OCaml - OCaml-style algebraic datatypes and pattern matching are huge advantages Rust brings over C++ in terms of reasoning about complex domain logic. And OCaml/F# both have variables which are immutable by default but can be labelled as mutable in (e.g.) performance-sensitive regions. So it's odd to say Rust "surpassed" what other functional languages have been doing for literally decades now. I almost think of Rust as a lower-level OCaml (faster and better support for memory management, but with somewhat weaker support for typeclasses and an inconvenient ADT syntax). An ML language you can conceivably write an OS in, but I would personally be nervous to write a complex finance application vs. OCaml. And of course Rust also has typeclasses, with a design strongly influenced by (but somewhat weaker than) Haskell. Monads and Readers are all over the shop in modern Rust precisely because they are so useful in modern Haskell. Basically, the reason why Rust is such a good low-level programming language is the reason that functional programming matters.
- steveklabnik 7y agoFun fact: rust’s original implementation was in OCaml.
- jolux 7y agoI sometimes lament it's not more OCamlish, to be frank ;P Learning Rust inspired me to go learn OCaml. Not being a systems programmer and wanting pervasive garbage collection I don't want to go back now!
- foldr 7y agoOCaml's type system isn't capable of preventing shared mutable references. So you don't get the same compile-time safety guarantees in OCaml when you use mutation.
- brmgb 7y ago> you don't get the same compile-time safety guarantees in OCaml when you use mutation. OCaml is a garbage collected and mono-thread language. You obviously have the same guarantees in that you can't do what would be problematic. As a side note, a lot of the people currently writing Rust would ihmo be better served by Ocaml. You rarely need the protections Rust provides and they have a real complexity cost.
- nestorD 7y agoI love Rust but comes from an Ocaml/F# background (and, as others have said, the inspiration is palpable) and you seem to have a common misunderstanding around immutable datastructures. Immutable datastructures in functional languages are not datastructures that cannot be mutated, they are datastructure that efficiently produces a new, updated, version on each operation (such as the cons list). While they are slower than traditional datastructure for a typical use case, they tend to be a lot easier to think about, make parallel code much easier and let you easily get back in time to previous versions of your datastructures (when discovering rust, I was actually disapointed that they were not available in the std). The im[0] crate offers what seem to be good Rust implementations of that kind of datastructures (with abetter explanaition of why you would want them). [0]: https://docs.rs/im/13.0.0/im/ https://docs.rs/im/13.0.0/im/
- foldr 7y ago>Immutable datastructures in functional languages are not datastructures that cannot be mutated, This claim seems dubious. But in any case, I think the OP was just talking about immutable data, i.e., in Rust terms, data to which there is no mutable reference. The data in question could be a simple integer.
- nestorD 7y agoIt seems dubious because the FP community tends to use the expression 'immutable data structures' were 'persistent data structures' would be the proper term. As a matter of fact I have seen data structures, in FP language, called immutable while having operations to mutate them in place. I believe the OP spoke of both Rust immutable data and the fact that most FP languages have only immutable datastructures (different mecanism to deal with similar problems).
- foldr 7y agoI think they were referring to an actual difference between Rust and OCaml, Haskell, etc. Rust allows you to mutate data via mutable references that are guaranteed to be unique. Mainstream functional programming languages do not allow you to do this (though linear typing may land in GHC soon).
- h91wka 7y agoPurely functional algorithms and data structures are typically slower. Imperative code is also more concise than equivalent functional code. The only real benefit of functional programming is that it makes code much easier to reason about. But this difference is so great, that in most cases I am ready to pay the price. P.S. Just to clarify my position to the commenters who want to convert me: I write code in functional languages most of the time for the living, so I know well enough how ST monad works, about "O(log n) == O(1)" meme, and so on. But I also have some background in HPC, where I used imperative languages. So I have plenty of experience in both paradigms, and I know exactly what are their weak and strong parts.
- willtim 7y ago> Purely functional algorithms and data structures are typically slower. Functional persistent data structures bring many advantages, even to imperative programmers. For example, if your text editor used a persistent data structure, it probably wouldn't need to lock you out during a save and would support better undo. Mutation is efficient, but comes with many drawbacks, especially when state is shared or provenance is required. This is why functional data structures and ideas are appearing outside of functional programming, for example ZFS, git, Blockchain, Spark, Kafka etc etc > Imperative code is also more concise than equivalent functional code. This couldn't be further from the truth. Just as Fortran liberated arithmetic expressions from the verbosity of imperative programming, FP allows us to move beyond non-compositional word-at-time sequences of mutation commands and build much higher-level abstractions. Modern imperative languages have borrowed many ideas from FP, but are still no way near as expressive as e.g. Haskell. Of course, imperative programming is still very useful and that's why Haskell has good support for it.
- bitL 7y agoIf it mattered we wouldn't have needed articles like this. It's a useful niche for certain tasks, that's about it. Certain types of people with certain thinking patterns strongly prefer it, other people don't. :shrug:
- willtim 7y agoPeople should learn a functional language, even if they do not intend to ever use it professionally. Languages are tools for thought and if you only know imperative programming, it will limit your thinking (for example, one well-respected OOP programmer on stackoverflow seriously suggested modelling a bank account with a mutable number). Functional programming ideas are gaining traction and have made big contributions to distributed computation, filesystems and databases recently. Don't get left behind by dismissing it all as niche. You'll also get a preview of the upcoming Java and C# features.
- bitL 7y agoI agree; I recommend everybody to master imperative, OOP, functional, logical and constraint-based declarative languages. They are all useful, certain things are much easier in one or the other type.
- mpweiher 7y agoThis is an odd article, brilliant in parts, somewhat less so elsewhere. The analysis of why we need modularity and that this means we need new kinds (plural) of "glue" is spot on. Note the plural. But then he goes on to present exactly two kinds of glue, so the smallest N for which the plural is justifiable. I contend that he was richt that we need a lots of different kinds of glue, which means that the glue must be user-definable. And what is "glue"? Well, architectural connectors, that's what. Why Architecture Oriented Programming Matters https://blog.metaobject.com/2019/02/why-architecture-oriented-programming.html https://blog.metaobject.com/2019/02/why-architecture-oriente...
- gambler 7y agoRant ahead. Functional programming discussions on HN are pretty depressing. Many of the statements about FP that I see here right now are the same old shit I've heard about Java in mid 00s. You just need to mentally translate some buzzwords, but the essence is the same. Seems like the software industry is just running in circles. Something get hyped, people jump on it, fail, then search for the next bandwagon. Some examples: 1. Endless yammering about low-level correctness. As if it's biggest problem in software engineering right now. In reality, most domains don't need perfection. They just need a reasonably low defect rate, which is not that hard to achieve if you know what you're doing. 2. Spewing of buzzwords, incomprehensible tirades about design patterns. FP people don't use the term "design pattern" often, but that's what most monadic stuff really is. Much of it is rather trivial stuff once you cut through the terminology. (Contrast this with talks by someone like Rich Hickey, who manages to communicate complex and broad concepts with no jargon.) 3. People who talk about "maintainability" of things while clearly never having to maintain a large body of someone else's code. Etc. --- The #1 problem in software right now is not correctness or modularity or some other programming buzzword. It's the insane, ever-growing level of complexity and the resulting lack of human agency affecting both IT professionals and users.
- robcohen 7y ago1. Google knows what they are doing. How many zero days does Chrome have this month? 2. Yeah, monads and applicatives aren't too hard, but really understanding monad transformers well is challenging. 3. I mean, yes, there are a lot of junior-ish devs who see the potential about FP and then spout how much better it is, but isn't that just like complaining about how annoying people are on twitter? I'm unsure its meaningful to make this criticism, or at least I'd like an example.
- cryptica 7y agoAmen. When I read that paper, it was clear that the author's definition of modularity was very different from my own. When I think about an algorithm like merge sort, mini-max decision trees or other low-level algorithms, the concept of modularity doesn't even enter my head. It doesn't make any sense to modularize an algorithm because it is an implementation detail; not an abstraction and not a business concern. Modularity should be based on high level business concerns and abstractions. The idea that one should modularize low-level algorithms shows a deep misunderstanding of what it means to write modular software in a real-life context outside of academia. It seems that FP induces confusion in the minds of its followers by blurring the boundary between implementation details and abstractions. OOP, on the other hand, makes the difference absolutely clear. In fact, the entire premise of OOP is to separate abstraction from implementation. Referential transparency is not abstraction, in fact, it goes against abstraction. If your black box is transparent in terms of how it manages its state, then it's not really a black box.
- BoiledCabbage 7y agoIf anyone is looking for a really eye-opening video take a look at that this. "Domain Modeling Made Functional" It's applying Functional Programming to plain old Enterprise line of business apps in F#. The author argues why it's so much simpler, and simply walks through an example with cases. It's hard not to be persuaded. It leaves behind all of the lofty crazy maths you see in a lot of presentations of FP, and just shows how it's a simpler technique to solve real world problems. Domain Modeling Made Functional - https://www.youtube.com/watch?v=Up7LcbGZFuo&feature=youtu.be&t=508 https://www.youtube.com/watch?v=Up7LcbGZFuo&feature=youtu.be... (Watch on 1.25x speed) It's really impressive, and also surprising why more people promoting FP don't show it's tangible benefits and simplicity vs. the theoretical/abstract way it's usually presented. It's also a great introduction to 'Domain Driven Design' and data modeling regardless of the language you use.
- qsymmachus 7y agoAgreed 100%, sometimes the FP community is its own worst enemy. Too many FP enthusiasts seem to think that "real" FP requires mastering category theory, which is cool and all but really just a particular subset of the paradigm, and a particularly Haskell-centric subset at that. I encourage anyone who wants to learn functional programming to pick up the classic "Little Schemer" by Friedman and Felleisen. It's a charming book, designed for undergraduates, that teaches you all the important and appealing aspects of FP. And you won't be made to feel like an idiot because you don't know what a monoid is.
- mettamage 7y agoI'll dust it of my nightstand. I really should get to it, I guess now is a good day. Thanks!
- mettamage 7y agoAfter watching it for 20 minutes, I want to know more. Where can I learn more about this magic called functional programming? Is there a good course? I want to do it for BLOBAs (as he calls them) apps as I'm not too math-heavy. Also, to what extent does JS support FP? I use anonymous functions/callbacks (I mean red functions, pun intended) and closures (even as poor man's objects, lol) but I don't really know why that is FP and what makes it FP. This is a bit of too big of an ask now that I think about it. I sound like a person who never programmed before and to be like: so what programming language should I use? Is there anything good around for JS to create BLOBAs?
- martin1975 7y agoOn one end we have FP, which is great for expressiveness and its declarative nature of getting things done, side-effect free, with nearly perl like terseness :)... but on the opposite end you have von Neumann CPU architectures which know nothing about FP. The utopian "silver bullet" is always something in the middle of course, a language that can give you the power and expressiveness of FP without sacrificing all the things that FP style compels you to bear - like mutability, performance optimizations/fine tuning, etc. Personally, I find languages like Scala to be right there in the middle, where neither performance nor FP expressiveness is sacrificed severely. That makes Scala flippin complex like C++ was, but it is probably the best attempt to bridge this gap if we all just want to learn "one language to rule them all" and incorporates both FP and imnperative/mutable style techniques that milk the hardware for what it's worth as well as give you the power/expressiveness of FP. Of course one can write an operating system in Haskell... just as one can imitate functional style programming in C... or we can come up w/some "middle ground" language that takes the cake from both worlds and call that "the best". My point is, this quest or journey for the best language/paradigm never ends and always shifts across the years as hardware develops and as we discover better ways of doing more with less (e.g. the crux of FP). IMHO, ultimately you have to pick an appropriate language (tool) for the job at hand. If we forget that languages/programming paradigms are just tools... then, you'll forgive me for saying, we become tools ourselves. Or... fools, rather. To me, whomever has realized this trade-off, is a true zen master of programming/engineering...not the one who espouses one paradigm/language over another. That's just me tho... YMMV.
- lalaithion 7y agoIs Scala really that much more performant than Haskell? I haven't done much high performance computing in either, but the Scala compiler is significantly slower than the Haskell compiler (both self hosted).
- randomidiot123 7y agoSlower compilation doesn't mean slower execution. Scala has "Native" and JVM implementations with different performance characteristics. Scala is a hybrid that allows imperative code with a lower level of abstraction than Haskell, closer to hardware. Thus it is potentially more performant.
- iflywithbook 7y agoOne of my favorite papers. Incidentally, although I'd spent my entire career chasing some elusive notion of "good design", this paper was the first I have seen to explicitly define "good design" as "modular". It is now generally accepted that modular design is the key to successful programming... However, there is a very important point that is often missed. When writing a modular program to solve a problem, one first divides the problem into subproblems, then solves the subproblems, and finally combines the solutions. The ways in which one can divide up the original problem depend directly on the ways in which one can glue solutions together. Therefore, to increase one’s ability to modularize a problem conceptually, one must provide new kinds of glue in the programming language. Complicated scope rules and provision for separate compilation help only with clerical details — they can never make a great contribution to modularization I now tend to see all the other design principles as ways to improve modularity. Everything else is mostly just fluff.
- fwguru 7y agoI think functional programming is an useful (and probably fun for some people) exercise to see how far you can go when solving problems with a limited or "pure" set of tools. We got and are steel getting a lot of insight from it. Like forcing a boxer to only box with one hand while the other one is tied behind his back. I'm sure one would have learn quite a few new tricks not to get battered in the ring that way. Things do get weird when people start forgetting the the world is a bigger place and there are a lot of ways do the same thing and the reality doesn't care about purity. That makes some people bitter and they dig down even harder into their limited world and start evangelizing it even harder to signal their smartness. In their bitterness and anger they do not see that functional programming has made it big time. Almost every important language today supports multiple paradigms, including functional.