I've had smart people complain that they couldn't figure out how to write simple programs in Haskell after days of honest effort. One problem, I believe, is that functional languages encourage more of a bottom-up approach to design, whereas many C/C++ coders naturally decompose problems in a top-down manner. So math types, who are used to building up grand structures from smaller propositions, find pure FP pretty congenial because they can write smaller independent functions and attack the parts of the problem that they know how to solve.
Is this true? What are the major obstacles preventing you from dividing your problem top-down in a FP? Just because you're able to better divide programs bottom-up in FP doesn't mean the other way around is harder, right?
Anecdatally, I'm a very top-down programmer, and also a discrete math & logic nerd, and I tend to prefer FP. I'm not sure why you think "math types" tend to work bottom up. Perhaps we mean different things by top-down/bottom-up? Can you give an example of what you think of as "top down" design versus "bottom up" design, and why it seems "top down" is easier in an imperative language and "bottom up" easier in a functional one?
Bottom-up design is the creation of little stand-alone components that can be understood in isolation and, when written abstractly, perhaps useful in multiple contexts. I find that pure FP languages ease bottom-up design because of the lack of shared mutable state and their strong support for composition.
> One problem, I believe, is that functional languages encourage more of a bottom-up approach to design
Anecdotally, I tend to write Haskell "top-down", e.g. writing QuickCheck tests for top-level functions, writing dummy definitions with `undefined`, and fleshing things out as and when the tests require it.
>whereas many C/C++ coders naturally decompose problems in a top-down manner.
That's kind of weird, considering this quote from Ken Thompson, when asked how he thinks of such good abstractions:
"It is the way I think. I am a very bottom-up thinker. If you give me the right kind of Tinker Toys, I can imagine the building. I can sit there and see primitives and recognize their power to build structures a half mile high, if only I had just one more to make it functionally complete."
Interestingly, the Haskell fans that I know happen to be mathematicians by inclination, and programmers because it seems useful. I think you are right that mathematical thinking maps quite naturally onto Haskell. I also think that most programmers are only middling-good mathematicians and aren't the type that enjoys doing air-tight proofs in their spare time as a hobby.
It's an example of that style of thinking that made Dijkstra write his famous quote,
"It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration."
The article could use a better example. "C++ templates are complicated and hard to write compared to straight-line imperative code" is not going to convince any functional programmers that imperative programming is nice, even if you can argue that C++ templates are a "functional" language.
If all the programs you write are like recipes, then imperative probably makes a lot of sense. Imperative is never a problem for small programs. It's when they grow over time that they become more and more difficult to reason about. In the best case you manage the complexity with good architecture, refactoring and automated testing practices; in the worst case you end up with a program that is not worth changing because every bug fix introduces two new bugs. Functional programming allows breaking through that ceiling at the expense of a good deal more rigor.
And yet the software industry survives and thrives with only niche use of functional programming at best...
EDIT: To clarify. I am agreeing with the original author. If Functional Programming is as great as it is supposed to be it would have been eating everyone's lunch by now. It has been around forever, yet it remains quite niche. This indicates strongly that there must be underlying reasons it is not more popular.
There are low frequency systemic laws at play. Software reuse is still a myth. Large project don't see any benefit to invest into FP abstractions. It's not even business smart so far since you make a shit load of money from accidental complexity. Tools to manage tools to manage tools that makes you look pro. I know from experience that even in high skilled domains, IT shops and their client are probably only at 30% efficiency (6 monthes turnover means ~2 months of actual work, low quality one, before leaving). There are no forces at play that respond to the problem that FP solves.
I completely agree with your comment regarding functional programming becoming more useful as complexity increases - even the author's example is rife with functional programming. Think about the steps where the flour, baking powder and salt are added to the small bowl. The act of measuring these ingredients can easily be thought of as a pure function that takes a large amount of a raw ingredient, a measure and returns the correct amount. I'm sure there are many examples of pure functions implied but
I have to say that I rather like working backwards - yes, a finished cake really is cooled after baking after batter that has been mixed. If you're putting together an engine in your garage you work the same way - nobody rebuilds their carburetor while it's mounted on the intake manifold (okay, not many people even have carburetors anymore). You build "assemblies" and create the larger whole from sub-assemblies. The same applies to virtually all manufacturing ... I spent a lot of time designing electronic systems that all started by populating circuit boards.
Nail on the head there. The humble state machine is a more important part of programming than people like to admit. And a very large amount of business logic is most naturally expressed in terms of data state manipulations, real world state manipulations, I/O. To do I/O in FP you have to deal with monads, which are hard to understand and work with.
(You can tell that monads are hard to work with because there are so many articles telling you that they're easy)
The common ground is functional-ish and mostly-stateless programming ("const" etc). C# is actually becoming very good at this, and you can always cross into F# if you want.
> The common ground is functional-ish and mostly-stateless programming ("const" etc). C# is actually becoming very good at this, and you can always cross into F# if you want.
Newish C# is just functional programming dressed up as Java, and goes far beyond const. C.f. functional-ish C# programs to C programs that use const. Striking difference.
F# is just .NET's version of a straight up a very traditional and canonical functional language. If F# isn't functional then nothing except maybe untyped lambda calculus is functional.
The article's title is correct only in the tautological sense that unpopular things are weird, even in the paradoxical case where those unpopular things are actually fully embraced while remaining unpopular.
> (You can tell that monads are hard to work with because
> there are so many articles telling you that they're easy)
lol yeap, you made me curious so I went to search for those articles, and sure enough, there are plenty. My favorite quote was: "Monads are easy, you just need to put in the work."
My second-favorite quote was: "if someone is really convinced that monads are easy, they are going to be in for a demoralizing shock"
I think you're conflating functional programming with Haskell.
You can use something like Clojure without touching monads, just use functional data structures to represent values instead of using objects, and the using mutable state is simple just not the default and you rarely need to go there.
In fact if you're using something like React and something like Redux you're going in that direction from the other side - in Clojure it's basically the natural way to solve the problem (and frankly all of their concepts map much better to CLJS than JS)
I feel like people pushing Haskell and all of the weirdness that comes from lazy evaluation did more harm to FP perception in the general programmer community than good.
> * feel like people pushing Haskell and all of the weirdness that comes from lazy evaluation did more harm to FP perception in the general programmer community than good*
You've hit the nail on the head. Haskell has the combination of being very extreme and popular enough that it is damaging the perception of FP as a whole. I really wish Haskell didn't exist.
For anyone skeptical of FP who might be reading this: start with a dynamic FP language or a typed FP language that is strict rather than lazy. Examples of the former are Erlang/Elixir or Scheme. Examples of the latter are OCaml, Elm, F# or good old Standard ML.
I enjoy "functional languages" because they force good practices, especially those that may not pay off immediately. Merely offering some nice features (C#, Java 8, etc) doesn't force you to write your new code better, let alone refactor all that old code, and thus I'd argue the benefits don't follow.
Nothing is going to force you to write new code better or refactor old code if you don't have the motivation yourself. I can guarantee you that I could write horrible spaghetti code in any functional programming language.
Monads aren't "hard to work with". The .NET core libraries contain a number of them which are some of the most commonly-used types in C# (and F#). To name a few:
* IEnumerable<T>
* IObservable<T>
* IQueryable<T>
* Lazy<T>
* Task<T>
You'll rarely hear the term monad in the MSDN documentation though. Maybe it's opaque terminology that scares people away from monads rather than monads themselves.
here's a state machine representing a lock in elm:
type State = Locked | Unlocked
type alias Model = State
lock : Model -> Model
lock model = Locked
unlock : Model -> Model
unlock model = Unlocked
open : Model -> Bool
open model =
if model = Locked then True else False
i think working with state machines in imperative languages is messy, mistake prone and hard to reason about. functional languages are just better for managing state
> The humble state machine is a more important part of programming
> than people like to admit
Here is where I feel a lot of OOP applications are “doing statefullness wrong.” Everyone gloms onto the popular GoF patterns, but explicit state machines are not nearly popular enough.
A colleague once remarked that in business programming, every model should be a state machine until you can demonstrate you can do without. For example, many Ruby on Rails models have a concept of validation. Most such models can be greatly improved by treating “invalid” as an explicit state with its own methods and behaviours distinct from the methods and behaviours of a valid model (which may encompass other states as well).
I find that if you’re going to have state, go all the way, reify it, make it something you can name and manipulate. Implicit state is the GOTO-ridden spaghetti-code of imperative programming style.
The author's attempt to rewrite the cake recipe in a functional style misunderstands the paradigm completely.
The correct image here is all the stuff -- oven, pans, butter, flour, salt, etc -- entering a funnel on the left and producing a baked cake on the right. The funnel itself, if you were to peer inside it, would be composed of multiple sub-funnels: pans with batter + oven as the last step, empty pans and bowls of batter as the step before that, and so on.
It's natural, not weird. The metaphor is used all the time on cooking shows, where they show you bowls of all the ingredients, and the final product next to them.
It’s easy to imagine the funnel you describe, if we imagine we are building a factory for cakes. Naturally, the ingredients arrive on various conveyor belts or in pipes, are mixed, and cakes emerge from the other end.
I love to make fun of FP because people love it. But actually it is boring simple and as elegant as limited.
Any language used for programming aims at being understood by humans.
So if you make a cooking bot in a MIT lab, you do it in LISP. And FP is ok.
But, if you do it in an industrial context programming robots you might use Graph(C|S)et presentation (coding by geometry). And this is OK to have no code and just a diagram.
If you do it in a lab, you may use LabView.
And in a kitchen, probably natural language.
And it works pretty darn well.
Programming is not about being right it is about being understood and there are no "one best way" except your code is maintainable by industry standards.
So the author just got if wrong by the premisce that functional or whatever matters.
The author does have a point. Another issue is that the problems FP solves are problems that I think many developers genuinely don't have. Or when they do have them, they occur in very specific ways that can easily be handled with an 80% solution bolted onto an existing imperative-first language. Node, LINQ and Numpy all come to mind. These have the advantage of confining the weirdness to one spot, which makes it easier for users to accept them because they don't feel trapped the way they might in a language like Haskell or Racket.
Meanwhile, FP does introduce some of its own problems, and I think many of my fellow FP fans are too quick to downplay them. Performance is one. It might be solvable by compilers, but for those of us who are not likely to spend much time hacking on the compiler for our language of choice that's a small consolation.
The end result being, it's still the case that my adventures in functional programming (which are generally done using a multiparadigm language like F# or Scala) will go something like this more often than I care to admit:
1. Implement imperative solution.
2. Feel guilty, go back and replace it with functional one.
3. Find performance problem. Dig out optimizer.
4. Optimizer points to code from step 2.
5. Revert back to version from step 1.
I dunno. In Rust we've found that the functional idioms are frequently faster than the imperative ones. That's because it's easier for the compiler to remove bounds checks with functional programming styles. Also, we eliminated the overheads usually assocated with FP (closure construction, indirect branches).
I suspect that this is an area where Rust shines in a way I haven't seen described for other functional languages, so I don't know if it's as relevant to people working in those other languages, but I haven't really written anything performance sensitive and functionally-styled except in Rust.
Anecdotally, I have found a couple of times that I needed to manually implement a beautifully simple map/filter/fold call sequence in Rust because it was in a hot loop and just wasn't performing as well as a dynamic programming version that I wrote in the course of benchmarking. I've also found that plenty of times the functional style beat my hand-rolled code, which is an absolutely fantastic result, but I don't think that the compiler is yet sufficiently intelligent to replace all of my hand-rolled loops :).
> FP solves are problems that I think many developers genuinely don't have
I've felt this way [1] about Python, well, more than once. I'm feeling the same way by learning Haskell.
Except that with Python I learn the good parts fast, and then it's back to normal life. With Haskell things keep getting better and better... It's in part because Haskell is harder, but the improvements are also much larger.
The history of the software industry has basically been 40 years of customers choosing code that exists, solves their problem right now, and doesn't make them think too hard about how to use it. They choose this over correct code (hence, Microsoft) and over fast code (hence, Python/Ruby/Javascript/PHP).
FP actually helps you write both fast and correct code. The problem is that it requires a good deal more planning up-front and more effort to change things when requirements change. This has usually been a non-starter with the majority of customers: they want software that does what they need it to do, right now, and will continue to do what they need it to if they change their mind. If it screws up occasionally or makes people wait, that's okay, it's still faster & more accurate than a human is.
I think you're also trying to imply that it's inherently more difficult to write correct code in imperative style. TBH, I'm not convinced this is the case for most developers.
You can't just look at what fans of functional programming say. That's a self-selected group, so you're dealing with an observation that may be biased for any number of reasons.
The closest I've come to seeing an objective comparison was when I was TAing intro CS classes in undergrad, so I had the opportunity to see the same people have to use both. In that case, it seemed to me like folks had difficulty writing correct code regardless of paradigm. The difference was, with the imperative language you'd see a lot of mostly-working code with a few bugs. With the functional one you'd see a lot of failing to even get off the ground.
If we assume, for the sake of argument, that that's a reasonable characterization of what the typical person experiences when they try FP, is it really any surprise that it's not more popular? Nobody wants to work in a language that makes them feel frustrated and/or stupid. And you're not sweetening the deal at all when you insult them by implying that their code is inherently buggy because of the language they use.
I tend to expect performance to be a problem with FP, but I don't tend find it to be the case in practice. Immutable data structures are slower than mutable data structures, but typically only by a fixed order of magnitude. FP also tends to make use of more functions and closures, but those are usually quite performant.
I'm a little curious as to why you find FP performance problematic. I guess you must be trying to solve some pretty CPU-intensive problems?
The title is much better than the article. It is very different, "weird" relative to norm, and the industry is doing well enough that there just is no pressure to do something different.
Honestly that makes sense to me, and I don't blame programmers who are happy with their jobs/lives/workflow/whatever from switching. But when the mainstream idolizes "disruption" and "innovation" above all else, y'all start looking mighty hypocritical. Please, for the sake of us functional programmers, choose one of your revolutionary aesthetic or your shitty languages, but not both.
There are many styles of functional programming; the thing they have in common is that they all use first-class, high-level functions to implement features which other languages might implement via special-purpose syntax, bytecode, compiler passes, etc.
The "lambda the ultimate" papers are the classic example of using functions to implement a variety of language features, including jumps. Hence the author's claim that functional programming is an "inside-out" approach is more of a question of style than of paradigm.
For example, even simple function composition can approach the clarity of imperative code for following a step-by-step process. The result tends to look like UNIX pipes, although traditionally they're read "right to left" (but you can trivially define your own composition function which goes the other way).
Here's an example of the author's task, written in a compositional style rather than an "inside out" style:
a "kitchen monad" may seem to be a overkill for doing what you (or your mom) may need to bake a small number of cakes for you to consume
but
bakery monads are what is needed if you want to bake hundreds of thousands of cakes of hundreds of different variations (types) in a manageable manner.
FP starts to make (more and obvious) sense there not in the small household kitchen. But even then that is not to say that FP doesn't make sense in a small household kitchen. Only, it is very difficult to make sense of that sense at the smaller scales.
I was just attempting to inject a little humor into the situation while bringing attention to the fact functional and imperative are not necessarily mutually exclusive. In fact, I'm of the opinion that Haskell is the best imperative programming language. It's just really hard to explain why that is the case when the popular sentiment is that monads are scary and impossible to understand. In any case, these threads always devolve into ideological bickering which I try to avoid.
Why the heck would you write the functional recipe the way he wrote it?
1. Take an oven and apply 175 C setting on it, producing a preheated oven. Set this aside.
2. Take two 8 inch round pans and some grease and flour, produce two 8 inch round pans with grease and flour applied on them.
3. Take a small bowl, some flour, baking soda, salt, salt and a whisk (a first-class function), return a small bowl with flour, baking soda and salt whisked in it.
and so on.
When you think of functional programs as series of operations chained together, it becomes really natural. In most recipes you don't need to loop back to an existing state.a
The real cake recipe can specify how to achieve the results instead of just saying that they should be achieved. It does not have to deal with issues like lazy evaluation or defining a batter monad. It does not have to trail off into "and so on" or else go on for far too many lines because of how tortured an exercise it is to make "pure functional" cake recipes.
If you want to argue with this, just deny that cake recipes are a good metaphor for programming, since they are essentially imperative and that is begging the question. But you aren't going to convince people that weirdly abstracted cake recipes are easier to write or follow.
In one of my classes freshman year, one of my favourite professors (Kathi Fisler <3) gave us an assignment where we made a silly game with an airplane that flies around and drops things. And I remember wondering how to do that without mutable state for the location of the plane, etc. Then I just remember seeing in the instructions for the assignment something like:
> write a function that takes a world and returns a new, updated world with the plane at its next location
and I just remember sitting there with my mind completely blown. Looking back at it, it doesn't seem as profound as I thought it was at the time, but maybe that's just because the novelty has worn off for me and a few years in industry have made me cynical, who knows :-)
Anyway, I think just learning to look at a problem differently I think gives you valuable insight into it and into your mind. Doesn't matter if you end up using it or not.
> I spend too much time trying to figure out how to say things as opposed to figuring out what to say.
Ha! Again, I think this is just a different approach. When I tried out haskell, it was intriguing that the modus operandi was: "think a lot about a problem, then write down a little code". I like that this is the opposite of my instinct to jump in with a solution, then kinda heuristically change it to get to code that works, and ultimately I think lead me to think a bit more and end up with clearer problem definitions and solutions.
Does Kathi Fisler have any online coursework? Because your experience sounds great and I'd love to be able to experience it or recreate it for my own enlightenment.
It was kinda based on How To Design Programs (http://www.htdp.org/) though to be honest I never touched the book and relied mostly on the in class instruction (which was what made this class awesome IMHO). I found the website for a newer version of the class:
This class also had some great epiphanies for me, but again, I think the instruction being top notch helped keep my attention going. This is more advanced stuff conceptually.
> Everyone should learn at least one functional programming language and try to apply what they learned in other languages. But if functional programming languages want to become popular, they have to be less about puzzle solving.
Because FP relies on an insane amount of theory.
Often FP aficionados are incapable of explaining functional programming concepts to others without using FP idioms or jargon. I still don't have the foggiest idea of what a monad or a functor is, or why the hell would I want to use currying or partial application, when my imperative code just works (that's not entirely true, and javascript has done a great job at popularizing some concepts, like closures, partial applications or first class functions).
Now compare with OOP : polymorphism , inheritance and encapsulation are easy to explain : Everybody can understand that a machine can be turned on by pressing a button, no matter what machine it is(phone,computer,screen) : polymorphism. It's easy to explain encapsulation : you don't need to how the inner working of a microwave in order to use it. Same with inheritance.
I agree that one should learn at least an FP language so one is forced to deal with a specific problem a certain way, that's good exercice. But I shouldn't have to understand category theory in order to learn Haskell or Clojure.
On one hand you say that FP aficionados are unable to explain FP concepts to others, and on the other you succinctly do it later on in the same paragraph (first class functions, closures, partial application).
I think you're conflating Haskell with FP, which is definitely not the case. No one needs to understand category theory to write Clojure or Lisp.
Nah, OOP concepts like polymorphism, inheritance, and encapsulation were crazy difficult to understand when they first came out. There were a lot of Car & BankAccount examples that didn't really reflect how people actually use OOP in the real world.
The thing is, all the lucrative software engineering positions created in the late 90s required knowledge of OOP. Which meant that all the wannabe software engineers put in the time and effort to learn it, because that was how they could get a high-paying job. And so now you have a critical mass of people for whom OOP is second-nature, who can explain things to junior developers when they ask questions.
Follow the money. FP isn't popular because outside of certain niche domains (eg. high-assurance software), it doesn't provide a competitive advantage large enough to build billion-dollar industries off of. If knowledge of FP suddenly started becoming necessary to get a six-figure software engineering job, you'd see many more people putting in the effort to learn about FP concepts. (This may already be happening with React in web development, with Hadoop/Spark/MapReduce in big-data, and with Swift in iOS development, but it's a new trend that'll take years to mature.)
Graphical interfaces pushed OOP into the mainstream. As plain structured programming was not powerful enough to create GUIs, people were forced to learn it.
The fact is, for an industry that talks so much about lifelong learning and innovation, software developers are very conservative.
I'll agree that the terminology is not helpful to most people. But I would contend that if you understand object frameworks with callbacks, you already understand a useful and practical way of seeing functors and monads without being aware of the fact. And if you understand the basic idea of git, you already understand the idea of building new data structures that share large immutable parts of old structures.
The author says that when they imagine what they want to tell the computer, they imagine an imperative sequence of commands and side-effects. Lo and behold, an imperative language is a closer match to what they are thinking than a functional language.
No. Shit. Sherlock.
I’d say the same thing about human languages. Can you imagine me criticizing Japanese because when I try to translate the English in my head to Japanese on a word-for-word basis, it’s a lot of hard work and the result is terrible?
The point of learning a different language is not to learn how to translate what is in your head into whatever the language demands, it is to change what is in your head. Alan Perlis put it best:
> A language that doesn't affect the way you think about programming, is not
> worth knowing.
What I would say, is that if you know language X and want to compare it to language Y, you need to keep working at Y until it affects the way you think about programming. If it doesn’t, you can then say that it failed to improve you as a programmer. If it does, you can then give a thorough review of how your new perspective has improved you, and whether it is worth the effort.
I’m not saying FP is better or worse, but I am saying that “It’s terrible for writing the imperative programs in my head” is not a good argument.
Then he goes on to describe his real problem: struggling with C++ templates to write a generic parser. Which is sort of ironic since functional languages are great for writing parsers.
In fairness he explicitly says he is trying to explain why it's not popular. Not why it's not a gold thing to learn. He days it's a good thing to learn at the end of the article.
The reason he says it's not popular is because the vast majority of people think imperatively when dealing with a problem that involves state and time. They might think functionally when trying to express an immutable law or when categorising the qualities of a horse or whatever. But when doing something that involves changing the state of the world in time and space (which is a significant part of many programs) they think imperatively. And even C will support enough of the functional stuff to do the rest (see Numerical Recipes, for example)
This is true, however in most programs, the entities that map directly to stateful entities in the “real world” are in a minority, and the bulk of the program consists of entities that serve the program’s implementation needs.
Those can be written in any style we like. For example, in languages like JavaScript and Ruby, if we wish to compose classes out of smaller components, we can imperatively modify classes, or we can write functions that take existing classes and return new classes consisting of the originals composed with whatever additions we desire.
What I am trying to say is that while imperative may feel more natural for problems involving state and time, we often perceive that problems require state and time even when they don’t.
---
I can fully accept the argument that people don’t like FP because they perceive that their problems need imperative solutions. But that is not the exact same argument as “people don’t like FP because their problems involve space and time."
OK I think that's a good and useful point and a motivator for learning FP. Nevertheless because most people continue to perceive things imperatively and because as the article says FP is puzzle like then the author still is probably right when they say that FP will remain unpopular.
I like "solving puzzles" but I don't like solving them under pressure. I also don't like deciphering someone else's puzzle when I am in a hurry and when I'm unlikely to never need this piece of code again. So even for people who like puzzles it's often the case that they just need to get something working fast that's reasonably correct.
The fact that life is short and that the world changes so rapidly is what makes worse better.
There are a lot of problems with this piece, like there are always problems with these OOP/imperative vs. FP posts.
> Both humans and machines are really good at implicit state attached to time.
That may be true for machines; they always know where all their state is. But we humans are terrible at managing state, especially implicit state, especially when we have to reason about how it changes as time passes. We can't keep all that state in our head, and as soon as a system gets reasonably complex even simple values mutating get hard to juggle in our puny minds. I would venture to guess, unscientific as it may be, that a majority of bugs in software have to do with humans not being able to reason effectively about state.
Pure functions taking immutable data in and spitting out new values eliminate many of the challenges with managing state. It's vastly simpler for us humans to understand what's going on with our state when we deal with chaining together a bunch of functions manipulating data immutably, and push managing mutable state out to the very specific points at the edge of the system. It's not a silver bullet--but it helps. And it's not weird at all, once you're used to it; quite the opposite.
> Writing functional code is often backwards and can feel more like solving puzzles than like explaining a process to the computer.
> I'm writing this blog post because I ran into a related problem recently. C++ templates are accidentally a functional language.
Functional programming advocates can't even agree on a definition of what functional programming is, and I'm even less sure what the author intends. What they have chosen to highlight is not what I recognize as functional programming; while I think it's good the author wants to talk about immutability as one facet of FP, it seems pretty bizarre that they claim that functional program often dictates you write your code backwards, or that they choose an already implemented solution and their attempt to refactor it functionally using C++ templates as a place to critique functional programming in the real world.
More generally, the big problem with these articles is that they fundamentally focus on the wrong things. Whether someone has issued a blistering indictment of OOP and all its failures as a methodology or we get a piece like this, we end up arguing over nonsense because we don't have a standard definition of OOP or FP that everyone can agree on, and it's the wrong level of abstraction to be arguing over anyways.
What we should be talking about are specific language features and what they bring to the table, including how they interact with other language features, how they are implemented in various environments, etc. What does immutable data do for us? How can pure functions help us reason about our code better? What kinds of type systems are out there, and what positives or negatives do they have in different contexts? Does encapsulation bring anything to the table or is it a net negative? Etc.
I'd encourage anyone considering writing yet another glib takedown of OOP or FP to take a step back before they put text to screen and consider instead discussing specific features that have widely agreed upon definitions and how those programming language features lead to certain outcomes, without making a dramatic overarching statement about the weirdness or badness of OOP or FP. Pieces like this only further reinforce the superficial pop culture that currently dominates our industry.
I also learned programming with references to cooking recipes. But you know, biggest problem is that's bullshit. First of all because people can't translate a cooking recipe into something good without vast prior experience. But also because cooking recipes are single threaded and fairly simple and our current systems are anything but.
In fact I think the cooking recipe analogy does way more harm than good. And let's be honest, there's nothing weirder than X = X + 1, being the number one reason for why many people can't understand programming.
78 comments
[ 3.2 ms ] story [ 142 ms ] threadAnecdotally, I tend to write Haskell "top-down", e.g. writing QuickCheck tests for top-level functions, writing dummy definitions with `undefined`, and fleshing things out as and when the tests require it.
You can't pick up C in a couple days and write programs if you don't understand pointers, const's, etc.
"It is the way I think. I am a very bottom-up thinker. If you give me the right kind of Tinker Toys, I can imagine the building. I can sit there and see primitives and recognize their power to build structures a half mile high, if only I had just one more to make it functionally complete."
It's a bad analogy though.
http://pages.cs.wisc.edu/~remzi/Naur.pdf
"It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration."
EDIT: To clarify. I am agreeing with the original author. If Functional Programming is as great as it is supposed to be it would have been eating everyone's lunch by now. It has been around forever, yet it remains quite niche. This indicates strongly that there must be underlying reasons it is not more popular.
I have to say that I rather like working backwards - yes, a finished cake really is cooled after baking after batter that has been mixed. If you're putting together an engine in your garage you work the same way - nobody rebuilds their carburetor while it's mounted on the intake manifold (okay, not many people even have carburetors anymore). You build "assemblies" and create the larger whole from sub-assemblies. The same applies to virtually all manufacturing ... I spent a lot of time designing electronic systems that all started by populating circuit boards.
(You can tell that monads are hard to work with because there are so many articles telling you that they're easy)
The common ground is functional-ish and mostly-stateless programming ("const" etc). C# is actually becoming very good at this, and you can always cross into F# if you want.
No.
> The common ground is functional-ish and mostly-stateless programming ("const" etc). C# is actually becoming very good at this, and you can always cross into F# if you want.
Newish C# is just functional programming dressed up as Java, and goes far beyond const. C.f. functional-ish C# programs to C programs that use const. Striking difference.
F# is just .NET's version of a straight up a very traditional and canonical functional language. If F# isn't functional then nothing except maybe untyped lambda calculus is functional.
The article's title is correct only in the tautological sense that unpopular things are weird, even in the paradoxical case where those unpopular things are actually fully embraced while remaining unpopular.
My second-favorite quote was: "if someone is really convinced that monads are easy, they are going to be in for a demoralizing shock"
"A ‘newbie’, in Haskell, is someone who hasn’t yet implemented a compiler. They’ve only written a monad tutorial"
You can use something like Clojure without touching monads, just use functional data structures to represent values instead of using objects, and the using mutable state is simple just not the default and you rarely need to go there.
In fact if you're using something like React and something like Redux you're going in that direction from the other side - in Clojure it's basically the natural way to solve the problem (and frankly all of their concepts map much better to CLJS than JS)
I feel like people pushing Haskell and all of the weirdness that comes from lazy evaluation did more harm to FP perception in the general programmer community than good.
You've hit the nail on the head. Haskell has the combination of being very extreme and popular enough that it is damaging the perception of FP as a whole. I really wish Haskell didn't exist.
For anyone skeptical of FP who might be reading this: start with a dynamic FP language or a typed FP language that is strict rather than lazy. Examples of the former are Erlang/Elixir or Scheme. Examples of the latter are OCaml, Elm, F# or good old Standard ML.
* IEnumerable<T>
* IObservable<T>
* IQueryable<T>
* Lazy<T>
* Task<T>
You'll rarely hear the term monad in the MSDN documentation though. Maybe it's opaque terminology that scares people away from monads rather than monads themselves.
A colleague once remarked that in business programming, every model should be a state machine until you can demonstrate you can do without. For example, many Ruby on Rails models have a concept of validation. Most such models can be greatly improved by treating “invalid” as an explicit state with its own methods and behaviours distinct from the methods and behaviours of a valid model (which may encompass other states as well).
I find that if you’re going to have state, go all the way, reify it, make it something you can name and manipulate. Implicit state is the GOTO-ridden spaghetti-code of imperative programming style.
The correct image here is all the stuff -- oven, pans, butter, flour, salt, etc -- entering a funnel on the left and producing a baked cake on the right. The funnel itself, if you were to peer inside it, would be composed of multiple sub-funnels: pans with batter + oven as the last step, empty pans and bowls of batter as the step before that, and so on.
It's natural, not weird. The metaphor is used all the time on cooking shows, where they show you bowls of all the ingredients, and the final product next to them.
Any language used for programming aims at being understood by humans.
So if you make a cooking bot in a MIT lab, you do it in LISP. And FP is ok.
But, if you do it in an industrial context programming robots you might use Graph(C|S)et presentation (coding by geometry). And this is OK to have no code and just a diagram.
If you do it in a lab, you may use LabView.
And in a kitchen, probably natural language.
And it works pretty darn well.
Programming is not about being right it is about being understood and there are no "one best way" except your code is maintainable by industry standards.
So the author just got if wrong by the premisce that functional or whatever matters.
It does not.
Meanwhile, FP does introduce some of its own problems, and I think many of my fellow FP fans are too quick to downplay them. Performance is one. It might be solvable by compilers, but for those of us who are not likely to spend much time hacking on the compiler for our language of choice that's a small consolation.
The end result being, it's still the case that my adventures in functional programming (which are generally done using a multiparadigm language like F# or Scala) will go something like this more often than I care to admit:
Anecdotally, I have found a couple of times that I needed to manually implement a beautifully simple map/filter/fold call sequence in Rust because it was in a hot loop and just wasn't performing as well as a dynamic programming version that I wrote in the course of benchmarking. I've also found that plenty of times the functional style beat my hand-rolled code, which is an absolutely fantastic result, but I don't think that the compiler is yet sufficiently intelligent to replace all of my hand-rolled loops :).
I've felt this way [1] about Python, well, more than once. I'm feeling the same way by learning Haskell.
Except that with Python I learn the good parts fast, and then it's back to normal life. With Haskell things keep getting better and better... It's in part because Haskell is harder, but the improvements are also much larger.
[1] https://xkcd.com/353/
Here's the one billion dollar question. Which is harder?
1) Correct code
2) Fast code
3) Available code
4) Familiar code
The history of the software industry has basically been 40 years of customers choosing code that exists, solves their problem right now, and doesn't make them think too hard about how to use it. They choose this over correct code (hence, Microsoft) and over fast code (hence, Python/Ruby/Javascript/PHP).
FP actually helps you write both fast and correct code. The problem is that it requires a good deal more planning up-front and more effort to change things when requirements change. This has usually been a non-starter with the majority of customers: they want software that does what they need it to do, right now, and will continue to do what they need it to if they change their mind. If it screws up occasionally or makes people wait, that's okay, it's still faster & more accurate than a human is.
You can't just look at what fans of functional programming say. That's a self-selected group, so you're dealing with an observation that may be biased for any number of reasons.
The closest I've come to seeing an objective comparison was when I was TAing intro CS classes in undergrad, so I had the opportunity to see the same people have to use both. In that case, it seemed to me like folks had difficulty writing correct code regardless of paradigm. The difference was, with the imperative language you'd see a lot of mostly-working code with a few bugs. With the functional one you'd see a lot of failing to even get off the ground.
If we assume, for the sake of argument, that that's a reasonable characterization of what the typical person experiences when they try FP, is it really any surprise that it's not more popular? Nobody wants to work in a language that makes them feel frustrated and/or stupid. And you're not sweetening the deal at all when you insult them by implying that their code is inherently buggy because of the language they use.
I'm a little curious as to why you find FP performance problematic. I guess you must be trying to solve some pretty CPU-intensive problems?
Honestly that makes sense to me, and I don't blame programmers who are happy with their jobs/lives/workflow/whatever from switching. But when the mainstream idolizes "disruption" and "innovation" above all else, y'all start looking mighty hypocritical. Please, for the sake of us functional programmers, choose one of your revolutionary aesthetic or your shitty languages, but not both.
The "lambda the ultimate" papers are the classic example of using functions to implement a variety of language features, including jumps. Hence the author's claim that functional programming is an "inside-out" approach is more of a question of style than of paradigm.
For example, even simple function composition can approach the clarity of imperative code for following a step-by-step process. The result tends to look like UNIX pipes, although traditionally they're read "right to left" (but you can trivially define your own composition function which goes the other way).
Here's an example of the author's task, written in a compositional style rather than an "inside out" style:
but
bakery monads are what is needed if you want to bake hundreds of thousands of cakes of hundreds of different variations (types) in a manageable manner.
FP starts to make (more and obvious) sense there not in the small household kitchen. But even then that is not to say that FP doesn't make sense in a small household kitchen. Only, it is very difficult to make sense of that sense at the smaller scales.
1. Take an oven and apply 175 C setting on it, producing a preheated oven. Set this aside.
2. Take two 8 inch round pans and some grease and flour, produce two 8 inch round pans with grease and flour applied on them.
3. Take a small bowl, some flour, baking soda, salt, salt and a whisk (a first-class function), return a small bowl with flour, baking soda and salt whisked in it.
and so on.
When you think of functional programs as series of operations chained together, it becomes really natural. In most recipes you don't need to loop back to an existing state.a
If you want to argue with this, just deny that cake recipes are a good metaphor for programming, since they are essentially imperative and that is begging the question. But you aren't going to convince people that weirdly abstracted cake recipes are easier to write or follow.
In one of my classes freshman year, one of my favourite professors (Kathi Fisler <3) gave us an assignment where we made a silly game with an airplane that flies around and drops things. And I remember wondering how to do that without mutable state for the location of the plane, etc. Then I just remember seeing in the instructions for the assignment something like:
> write a function that takes a world and returns a new, updated world with the plane at its next location
and I just remember sitting there with my mind completely blown. Looking back at it, it doesn't seem as profound as I thought it was at the time, but maybe that's just because the novelty has worn off for me and a few years in industry have made me cynical, who knows :-)
Anyway, I think just learning to look at a problem differently I think gives you valuable insight into it and into your mind. Doesn't matter if you end up using it or not.
> I spend too much time trying to figure out how to say things as opposed to figuring out what to say.
Ha! Again, I think this is just a different approach. When I tried out haskell, it was intriguing that the modus operandi was: "think a lot about a problem, then write down a little code". I like that this is the opposite of my instinct to jump in with a solution, then kinda heuristically change it to get to code that works, and ultimately I think lead me to think a bit more and end up with clearer problem definitions and solutions.
http://web.cs.wpi.edu/~cs1102/a08/
and OMG! I found the assignment!
http://web.cs.wpi.edu/~cs1102/a08/Assignments/Hwk2/index.htm...
> A function update-world that consumes a world and produces a world reflecting what happens when no key is pressed
OK, not phrased quite how I remember it but I guess that's where the epiphany happened :)
I took another class with her that was programming languages, and we used Programming Languages: Application and Interpretation (https://cs.brown.edu/courses/cs173/2012/book/) and the course website (http://web.cs.wpi.edu/~cs4536/a14/)
This class also had some great epiphanies for me, but again, I think the instruction being top notch helped keep my attention going. This is more advanced stuff conceptually.
Because FP relies on an insane amount of theory.
Often FP aficionados are incapable of explaining functional programming concepts to others without using FP idioms or jargon. I still don't have the foggiest idea of what a monad or a functor is, or why the hell would I want to use currying or partial application, when my imperative code just works (that's not entirely true, and javascript has done a great job at popularizing some concepts, like closures, partial applications or first class functions).
Now compare with OOP : polymorphism , inheritance and encapsulation are easy to explain : Everybody can understand that a machine can be turned on by pressing a button, no matter what machine it is(phone,computer,screen) : polymorphism. It's easy to explain encapsulation : you don't need to how the inner working of a microwave in order to use it. Same with inheritance.
I agree that one should learn at least an FP language so one is forced to deal with a specific problem a certain way, that's good exercice. But I shouldn't have to understand category theory in order to learn Haskell or Clojure.
I think you're conflating Haskell with FP, which is definitely not the case. No one needs to understand category theory to write Clojure or Lisp.
The thing is, all the lucrative software engineering positions created in the late 90s required knowledge of OOP. Which meant that all the wannabe software engineers put in the time and effort to learn it, because that was how they could get a high-paying job. And so now you have a critical mass of people for whom OOP is second-nature, who can explain things to junior developers when they ask questions.
Follow the money. FP isn't popular because outside of certain niche domains (eg. high-assurance software), it doesn't provide a competitive advantage large enough to build billion-dollar industries off of. If knowledge of FP suddenly started becoming necessary to get a six-figure software engineering job, you'd see many more people putting in the effort to learn about FP concepts. (This may already be happening with React in web development, with Hadoop/Spark/MapReduce in big-data, and with Swift in iOS development, but it's a new trend that'll take years to mature.)
The fact is, for an industry that talks so much about lifelong learning and innovation, software developers are very conservative.
You can learn what is a monad or an functor by reading/writing code with them. Pure Math is incredibly hard to learn without context.
https://github.com/melling/ComputerLanguages/blob/master/oca...
I've been collecting my notes for a while on Haskell and OCaml. After reading this article, it seemed like OCaml was a good FP choice to start with:
http://roscidus.com/blog/blog/2014/06/06/python-to-ocaml-ret...
No. Shit. Sherlock.
I’d say the same thing about human languages. Can you imagine me criticizing Japanese because when I try to translate the English in my head to Japanese on a word-for-word basis, it’s a lot of hard work and the result is terrible?
The point of learning a different language is not to learn how to translate what is in your head into whatever the language demands, it is to change what is in your head. Alan Perlis put it best:
What I would say, is that if you know language X and want to compare it to language Y, you need to keep working at Y until it affects the way you think about programming. If it doesn’t, you can then say that it failed to improve you as a programmer. If it does, you can then give a thorough review of how your new perspective has improved you, and whether it is worth the effort.I’m not saying FP is better or worse, but I am saying that “It’s terrible for writing the imperative programs in my head” is not a good argument.
The reason he says it's not popular is because the vast majority of people think imperatively when dealing with a problem that involves state and time. They might think functionally when trying to express an immutable law or when categorising the qualities of a horse or whatever. But when doing something that involves changing the state of the world in time and space (which is a significant part of many programs) they think imperatively. And even C will support enough of the functional stuff to do the rest (see Numerical Recipes, for example)
Those can be written in any style we like. For example, in languages like JavaScript and Ruby, if we wish to compose classes out of smaller components, we can imperatively modify classes, or we can write functions that take existing classes and return new classes consisting of the originals composed with whatever additions we desire.
What I am trying to say is that while imperative may feel more natural for problems involving state and time, we often perceive that problems require state and time even when they don’t.
---
I can fully accept the argument that people don’t like FP because they perceive that their problems need imperative solutions. But that is not the exact same argument as “people don’t like FP because their problems involve space and time."
I like "solving puzzles" but I don't like solving them under pressure. I also don't like deciphering someone else's puzzle when I am in a hurry and when I'm unlikely to never need this piece of code again. So even for people who like puzzles it's often the case that they just need to get something working fast that's reasonably correct.
The fact that life is short and that the world changes so rapidly is what makes worse better.
> Both humans and machines are really good at implicit state attached to time.
That may be true for machines; they always know where all their state is. But we humans are terrible at managing state, especially implicit state, especially when we have to reason about how it changes as time passes. We can't keep all that state in our head, and as soon as a system gets reasonably complex even simple values mutating get hard to juggle in our puny minds. I would venture to guess, unscientific as it may be, that a majority of bugs in software have to do with humans not being able to reason effectively about state.
Pure functions taking immutable data in and spitting out new values eliminate many of the challenges with managing state. It's vastly simpler for us humans to understand what's going on with our state when we deal with chaining together a bunch of functions manipulating data immutably, and push managing mutable state out to the very specific points at the edge of the system. It's not a silver bullet--but it helps. And it's not weird at all, once you're used to it; quite the opposite.
> Writing functional code is often backwards and can feel more like solving puzzles than like explaining a process to the computer.
> I'm writing this blog post because I ran into a related problem recently. C++ templates are accidentally a functional language.
Functional programming advocates can't even agree on a definition of what functional programming is, and I'm even less sure what the author intends. What they have chosen to highlight is not what I recognize as functional programming; while I think it's good the author wants to talk about immutability as one facet of FP, it seems pretty bizarre that they claim that functional program often dictates you write your code backwards, or that they choose an already implemented solution and their attempt to refactor it functionally using C++ templates as a place to critique functional programming in the real world.
More generally, the big problem with these articles is that they fundamentally focus on the wrong things. Whether someone has issued a blistering indictment of OOP and all its failures as a methodology or we get a piece like this, we end up arguing over nonsense because we don't have a standard definition of OOP or FP that everyone can agree on, and it's the wrong level of abstraction to be arguing over anyways.
What we should be talking about are specific language features and what they bring to the table, including how they interact with other language features, how they are implemented in various environments, etc. What does immutable data do for us? How can pure functions help us reason about our code better? What kinds of type systems are out there, and what positives or negatives do they have in different contexts? Does encapsulation bring anything to the table or is it a net negative? Etc.
I'd encourage anyone considering writing yet another glib takedown of OOP or FP to take a step back before they put text to screen and consider instead discussing specific features that have widely agreed upon definitions and how those programming language features lead to certain outcomes, without making a dramatic overarching statement about the weirdness or badness of OOP or FP. Pieces like this only further reinforce the superficial pop culture that currently dominates our industry.
In fact I think the cooking recipe analogy does way more harm than good. And let's be honest, there's nothing weirder than X = X + 1, being the number one reason for why many people can't understand programming.