23 ms·
Why is learning functional programming so damned hard? (2019)
- choeger 5y agoBecause functional programming is so damned hard if you don't grasp recursion. Otherwise, it is simple. Sorry for the cheap pun, but I think the main difficulty is getting along with recursion. And corecursion, of course ;).
- onlyrealcuzzo 5y agoI also think that most people who learn a functional language try to take what they know in an imperative language and translate it on the fly to a functional paradigm. This seems like a lot more work than just learning how to think functionally.
- dagw 5y agoThe intro to programming course for the math department at my University was taught in Haskell. And it was really interesting to see how people who had never programmed before on the whole did better than those who had a bit of programming experience. Those that had programmed before kept trying to make Haskell behave how they thought a programming language 'should' behave, while those who had never programmed just looked at Haskell and went "I guess this is what programming is" and just rolled with it. The only problem was that once they moved onto the intermediate course, taught in Java, they where completely lost again.
- scns 5y agoIntroduction to Programming at CMU is done in SML. They make the same observations as you. Those that have prior exposure to programming struggle. Those students without experience find it easy. They are a bit baffled in the next course, which is done in an imperative language.
- rgrmrts 5y ago15-150 (during my time), but it wasn’t the intro to programming - the course is titled “introduction to functional programming”. The intro to programming is Python (unless it changed). The trajectory for students is Python to C to SML (15-110 to 15-122 to 15-150). Some people take the C and SML courses in the same semester. I was really happy to have been introduced to many different paradigms over the course of a few years. But to both your points, once we moved on to Java I had a really bad time.
- TchoBeer 5y agoAlternatively, learning a new thing is easier if you have a grounding somewhere. Maybe the ideal would be a guide like "this is how functional programming differe from imperative programming"
- bsaul 5y agorecursion is always synonymous to me of unbounded memory usage. Understanding the memory consumption of a for loop updating a local variable looks easy. But a recursive function ? forget it...
- emodendroket 5y agoWell that's why you want a function to be tail recursive, right?
- bsaul 5y agoyeah but how do i know for sure that the compiler is going to optimize the memory or not ? Some compilers are smarter than other to rewrite recursion in a tail recursive way, and others aren't, and it's probably not possible all the time anyway.. It seems like a very convoluted way to do a simple thing ( sometimes).
- emodendroket 5y agoIn Scala, as I recall, you could annotate a method as tail recursive so it wouldn't build if it weren't. A serious functional language pretty much has to have an answer for this since you're supposed to use recursion instead of iteration.
- yawaramin 5y agoScala has the '@tailrec' annotation for this, and OCaml has the '[@@tailcall]' attribute for the same reason.
- carnitine 5y agoA tail recursive function can always be optimised into code which does not consume memory on recursion, and it’s a pretty basic fact to learn about a given language/implementation.
- 5y ago
- sltkr 5y agoI think recursion is just one of the hurdles. Another one is the use of higher-level functions and function composition. Another one is the use of monads (at least in pure functional languages like Haskell).
- nightski 5y agoMonads aren't really related to purity. Haskell uses one instance of a monad, the IO monad to encapsulate IO. But there are other ways of doing it. Most monads have nothing to do with side effects. You see things like functors/monads all the time even in regular imperative languages. For example in .NET we have Linq, Nullable types, etc... They aren't as well defined as in Haskell but still an incredibly useful pattern. You also have higher ordered functions and whatnot in imperative languages now. In fact, in JavaScript and .NET they are used all over the place.
- DaiPlusPlus 5y agoI don’t believe .NET’s `System.Nullable<T>` is a monad. For example, a type that can encapsulate any other type fulfils the definition of a monadic-value, but `Nullable<T>` cannot be used with reference-types and delegates, only a strict subset of value-types (as `Nullable<T>` itself is a value-type, but you cannot use `Nullable<T2>` for T1 in `Nullable<T1>`). While .NET allows for composition with delegates, it’s not very smart, and results in unnecessary type information erasure; for example in Linq if you take a projection of a fixed-size list, then that length information is lost if you then add another step after the projection. (Linq internally has some ugly, but inconsistent implementation hacks; for example, IList<T> length information may be preserved but IReadOnlyCollection<T> length information will not.
- nightski 5y agoC# has nullable reference types now. But I agree it's not perfectly a monad in implementation due to engineering decisions. But the pattern generally follows that of the Option monad in Haskell, even if it falls short. If you know the Option monad you'll find it really easy to pick up .NET's nullable types. That's my point, you find these patterns all over in common programming languages and even though they are hard to learn it's worth it.
- Banana699 5y ago> I think the main difficulty is getting along with recursion meh, recursion is easy. Recursion is mainstream. It _began_ as a way to fake a loop without mutation, but I struggle now to remember a mainstream language without it. The hardest part is all the new patterns you have to grok, it's like learning programming again from a blank slate, because it kinda is. It doesn't get any easier when Haskell decides it needs 7 new syntaxes and someone rushes to compose them all in one line.
- nwallin 5y agoI disagree entirely. I understand recursion just fine. I can look at a problem and think "this problem space is correctly represented by a tree/graph" and can meaningfully translate that into a program that shoves the data into a tree/graph-like data structure, and my natural inclination when computing on a tree/graph-like data structure is to use recursion. On the other hand, I understand that not all problems lend themselves to recursive solutions. I can look at a problem and think "this problem space is correctly represented by a matrix" and the reality is that linear algebra problems are often iterative, not recursive, in nature. It is certainly possible to do the simplex algorithm recursively with immutable data, but it's just so much straightforward to do it iteratively on a mutable matrix. The other thing is that most of my day job is just hooking one API up to another API to achieve my business's desired outcome. I don't need "interesting" algorithmic solutions for 95% of what I do. I just... extract data from one API and plug the data into another. You don't need recursion for that, and the "list of steps to do in order and (possibly) loop over that" paradigm with whichever seasonings you wish to add to it is just fine. (OOP is the flavor of the ~~day~~century at my company) edit: I forgot the bit that ties it all together. I am absolutely no good at functional programming. I've tried to sit down and learn Haskell or Lisp a dozen times and always failed. I've bought books. I'm still no good. It just takes me ten times as much code that's completely unreadable to do something that would be simple and straightforward in C++.
- silon42 5y agojust do a "pure object procedural" style of functional... all "objects" are immutable (or by value), as are the functions arguments and return values. But within a function you can still use variables and for loops.
- thysultan 5y agoIt's not functional programming, it's functional languages that go bat shit crazy with the amount of symbols they use that'd make looking at heliographs a refreshing pass time.
- Jtsummers 5y agoOut of curiosity, what are the crazy amounts of symbols present in, say, Erlang, SML, Scheme, or Common Lisp?
- throwaway81523 5y agoWe're talking about typed FP so only SML in your list really counts. So let's see: functors, polymorphism, higher-kinded types (does SML have those?), Hindley-Milner type inference, etc. Then for Haskell (the main topic of the linked article), bring in a bunch of unfamiliar algebra such as the notorious monoid on the category of endofunctors. It is actually worth understanding that. I liked this article (prerequisite: some exposure to Haskell): https://www.haskellforall.com/2012/08/the-category-design-pattern.html https://www.haskellforall.com/2012/08/the-category-design-pa... This is also good: https://en.wikibooks.org/wiki/Haskell/Category_theory https://en.wikibooks.org/wiki/Haskell/Category_theory
- Jtsummers 5y ago> We're talking about typed FP so only SML in your list really counts. I like your sense of humor. Appeal to Wikipedia Fallacy: https://en.wikipedia.org/wiki/Functional_programming https://en.wikipedia.org/wiki/Functional_programming LISP certainly shows up in the discussion. It's even called the first functional programming language!
- throwaway81523 5y agoThere's not really an official definition of FP. There are some proposed ones that involve types and some that don't involve them. Mainly though, this is a thread about the linked article, which is about the tribulations that the author had learning Haskell. Most of those tribulations were with the type system and I think that matches most people's experience. You can't transplant it to Lisp.
- PragmaticPulp 5y agoThis article is a good example of why I don’t use uncommon programming languages for actual projects. I watched an Elm-based project steadily slip behind schedule while the team insisted that Elm and FP were actually going to save us a lot of time… eventually some day. These uncommon FP frameworks and languages can be good tools in the right, experienced hands. They can also be fun for side projects and as learning exercises. But every time I’ve watched programmers try to use uncommon functional languages for real projects they end up like this article: > More accurately, learning Functional Programming concepts used in Haskell in 3 months after having thrown out 30,000 lines of code on a project that was now monumentally behind schedule was the hardest thing I had to do in my career. When you’ve reached the point of being severely behind schedule, throwing out mountains of code along the way, and struggling mightily just to get basic things accomplished: It’s time to stop. Don’t double down on a new language that you also have to learn from scratch. Pick something tried and true and get the work done. Revisit the functional language at a later time for an unimportant project or a side project, not something with a deadline. If the programming language or ideology has become more important than shipping the project, we’ve lost the point.
- guerrilla 5y agoI think it's more that you shouldn't use a tool that you're incompetent at. It has nothing to do with how common or not it is. Just know what you're doing or don't do it.
- PragmaticPulp 5y ago> I think it's more that you shouldn't use a tool that you're incompetent at. It has nothing to do with how common or not it is. These two points are closely related, though. Common tools will always have more available programmers, more documentation, more tutorials, more help, more libraries, more maturity. We had a server written in a functional language at a company I worked at. It was fine, but when the two people who wrote it left the company it became a huge pain point to even hire someone to work on it. Consultants knew this and demanded exorbitant fees for basic work on the project. Eventually we just rewrote it from scratch in a common language and saved a huge amount of time and money compared to trying to build teams and schedules around this obscure language.
- pjc50 5y agoIt's "monad is just a monoid in the category of endofunctors": if you don't understand something, here is an explanation in terms of other things you understand even less.
- AzzieElbab 5y agoMonad is a thingie that lets you sequence computations in the same context. Context being something like Future or Stream or List
- kayodelycaon 5y agoI’m not sure if this is supposed to be sarcastic or not but it doesn’t make any more sense to me.
- chii 5y agoit doesn't make sense to an imperative programmer because "sequence computations" is like water to a fish. the idea that computation isn't always sequenced doesn't occur to someone who hasn't encountered functional programming.
- kayodelycaon 5y agoNot sure what you mean by that. Threaded and event-driven systems don’t necessarily have a predictable sequence. Same with data flow through any non-trivial web application using background processing. I’ve worked on systems that run through a chain of background workers. Each job had a complete list of operations (one per worker) to preform. When each worker finished, it posted the job back to the general queue with the new state and one less operation to preform. All programs are eventually sequenced. You can’t work on data that doesn’t exist yet. I’m pretty sure I don’t lack the ability to understand what your talking about. I am sure I don’t know what the words you are using mean.
- ModernMech 5y ago
- dustinmoris 5y agoObservation from the real world: Functional programming seems to be extremely easy if you have never learned imperative programming first. I have seen beginners grasp FP much faster than OOP and write production ready code only after a few weeks/months of learning whereas beginners need on average multiple years to learn "production ready" design pattern style OOP. On the other hand I have observed some of the best OOP developers really struggle with FP. It's not that they find FP hard to learn, they find it really hard to unlearn OOP and the thinking that the way things are done in OOP is the holy grail of good software design. For example, only recently there was this blog post trending on HN (Am I stuck in a local maximum - https://blog.ploeh.dk/2021/08/09/am-i-stuck-in-a-local-maximum https://blog.ploeh.dk/2021/08/09/am-i-stuck-in-a-local-maxim...), which was triggered by a "blue tick" OOP programmer (tastapod on Twitter - inventor of BDD) making false statements about FP because he seemingly struggled learning it and wasn't able to work out how to program without mutations. He came to the conclusion that all functional programmers actually use mutations by default and immutable data structures are not common in FP at all. It was a completely unfounded assertion and clearly one made from frustration by someone who was so hard wired into OOP programming that they couldn't adapt to the FP way of thinking. It was a prime example of an "old dog" (citing the original article) finding FP harder than the new guys.
- jacquesm 5y agoYes, this is very true. It took me a lot of time 'unlearn' bad habits about state and side effects before functional programming really clicked. The interesting bit is that it made my other programs better as well, because I still find myself avoiding mutable state and impure functions.
- dnautics 5y agoI'd personally say it's less about unlearning statefulness and learning what the alternative tools are and not be bullheaded and kick and scream about not having the tools one is used to, e.g. map and reduce vs for and while. Once you learn how to use them, their utility and benefit (they explicitly limit the scope of the changes in the loop and reduce mental overhead) become clear. But a lot of people never get past "why can't I have a for loop" and don't get there.
- Igelau 5y agoLisp has always seemed like the most teachable FP language to me. I imagine it would be hard to start elsewhere.
- cerved 5y agoHaskell seems like a terrible language to start with
- wyager 5y agoDisagree. I think it’s a lot easier to learn than something like C++ or Java, which a lot of people start with. People just tend to forget how much they struggled with these very complicated languages when they were getting started.
- abeppu 5y agoYeah, it seems weird that the author latched onto some things as part of their definition of "functional programming" which aren't really required. I still find SICP to be an impressive self-contained foundation for functional programming, and "Functor" and "Monad" aren't mentioned as named concepts. Is there a better name for the domain the author is talking about? "Type-driven functional programming"?
- valenterry 5y ago> Yeah, it seems weird that the author latched onto some things as part of their definition of "functional programming" which aren't really required. They are required when taking the original meaning of functional programming though (nowadays often called "pure functional programming" to differentiate it).
- legobmw99 5y agoMy two cents is that anything is hard to learn without an application you can test your knowledge against. It’s always hard to learn language X until you go ahead and build something in it. Functional programming is hard because in a lot of cases, especially ones beginners encounter, the imperative solution is simpler. Purity and types are things I think you only truly appreciate when you’re writing large or complicated programs. I wasn’t able to really grasp FP (beyond using things like map() in Python) until I took a compilers course which used OCaml, and the ability to pass around and destruct these very complicated immutable trees was a very natural problem to tackle in the domain
- throwaway81523 5y ago> My two cents is that anything is hard to learn without an application you can test your knowledge against. It’s always hard to learn language X until you go ahead and build something in it. If it's something really new and different, like Haskell is to an old school imperative programmer, I think the opposite approach is best. Treat it like a topic in math, start from zero, work out small problems to exercise the basic concepts, then start putting them together. Immutable data structures are another new and shocking thing, but less complicated than fancy typed FP is. Start with seeing how you can "update" the first element of a linked list by making a new first element and linking it to the existing tail. Then see how AVL or red-black trees let you do something similar with tree nodes in log(N) time, so you can use those instead of hash tables without a monstrous slowdown. That's probably all you need, but the next thing after is probably Chris Okasaki's book Purely Functional Data Structures. It is pretty readable once you've seen some basics.
- mesarvagya 5y ago> I wasn’t able to really grasp FP (beyond using things like map() in Python) until I took a compilers course which used OCaml, Which compilers course is it?
- legobmw99 5y agoIt was two courses actually, the first was taught out of https://www.cs.cornell.edu/courses/cs3110/2019sp/textbook/ https://www.cs.cornell.edu/courses/cs3110/2019sp/textbook/ and the professors personal notes on the history of programming language design, and the second was taught primarily out of Andrew Appel’s tiger book with some content from the dragon book
- deleted 5y ago[deleted]
- macintux 5y agoI would dip my toes into FP occasionally (but very, very briefly) for years. I bought a book on Scheme in 1995, to give you a sense of how long I wandered in the wilderness. It wasn't until I discovered Erlang in 2012 (thanks, Seven Languages in Seven Weeks) that I finally found the motivation, aided in no small part by the fact that it's a very simple language and it's designed for server programming, where I've always been happiest. I still haven't graduated into category theory or type theory. I still don't know the difference between a monad and a monoid. But functional programming really speaks to me, because I have an old, tired brain and I need pure functions wherever I can employ them to keep things straight.
- stadium 5y agoPure functions concept was a breakthrough for me too. As a design pattern they are wonderful, there is so much less mental context to keep track of. Now whenever I see mutated variables and class attributes, or random side effects besides reading/writing to a database, it kinda makes me cringe and think it's a "oh here we go" into a rabbit hole just to understand what the code is doing. 9/10 the code does what it's supposed to, but the mutations and side effects makes understanding and extending it so much harder.
- exdsq 5y agoA monad is just a monoid in the category of endofunctors
- theCodeStig 5y agoFP can be distilled to purity/referential transparency. Category theory is a nice complement to FP, but it's absolutely not required.
- m_arnold 5y agoIf you want to know: A monoid is just a collection of things that can be associatively "added". Think addition with integers, or append with lists. Members of the dreaded monad can be sequenced, or composed, while taking into account their context. For instance: if I want to get a value from stdin, then use that value safely; or make a network request and then use the result safely; or run a function that can fail, and use it or short-circuit as needed.
- jqgatsby 5y agoHis daughter’s question “Why do we use functions?” is something I found myself asking during my math undergrad, and no one could give me a straight answer. To answer it to my own satisfaction, I eventually arrived at a form of predicate logic so I could directly experience how cumbersome it was to try to express everything that way. I liken it to trying to speak in a language that lacks a definite article: doable, but way more verbose.
- polishdude20 5y agoI always thought of functions as just boxes. We like to put similar things in boxes because that lets us remove the complexity of having to manage many similar things at once. When you can just say "this group of balls here needs to be put in the closet" you don't need to think or know that one is a tennis ball, one is a basketball and one is a softball. A function to me is just a way to wrap up a complicated idea or task into a box. It's what we already naturally do with everything in our lives.
- dtagames 5y agoIndeed, but functions are not the same thing as functional programming. (We reuse so many words in this field!) What you're describing are regular functions. If those functions hold a state, they're not functional; they're procedural. For example, a function that holds onto a counter and increments it by some value that is passed in cannot be functional because the counter exists in a hidden state, unknown to anyone else and unpredictable until runtime. This is what Backus was trying to fix. A functional version of that same function would need two parameters, one for the amount to increment by and a second one for the counter's current value. Often, a function can be rewritten in the functional style and thereby eliminate state (at least from that function). Whew!
- polishdude20 5y agoSo at some point up in the heirarchy, you're keeping track of that counter. So there will be a parent of the incrementing function which is itself not a function because it keeps state. You could say your top level program is not a function because it keeps state.
- jacquesm 5y agoHot tip: use the stack that you know best if you want to ship. Feel free to experiment with other stuff for hobby/entertainment/educational purposes.
- tel 5y agoLearning FP takes real investment both in time spent learning and practicing the concepts and in time spent slowly misusing them in real projects until you develop your taste for where they're appropriate. This investment is regularly underestimated. Using FP introduces real advantage in terms of taste and simplicity, meaning that "advanced" concepts are not nearly as prevalent as someone who just learned them might hope. The rule of 3 is helpful and under-applied. Programmers new to FP get eager to use cool tech as opposed to leverage improved taste. FP can be utilized in many languages but in ones that don't guide your hand toward it—your Haskell, your OCaml, your Elm—it's easy to have it "mix" with other styles. It is not the case that combining FP and non-FP styles immediately make sense or work. It is the case that the strengths can be combined if done thoughtfully. All of these points generalize, though. As with any programming work, taste is important. It takes a while to develop and often needs to be developed within the context of a team. Tasteless FP is an awful, awful waste of time, energy, money. Someone who likes to throw all the jargon at your is a hobbyist, a proselytizer, or a fan. Not terrible, but not necessarily someone who can yet manage all of the necessary tradeoffs and balances. We take that sense of taste somewhat for granted in "mainstream" programming styles.
- makerofthings 5y agoI have a good 30 years of C/C++ under my belt and have been learning Haskell for the last 4 years. You can write a buggy version of a program in C after spending an afternoon learning it. You can write a bug-free version in haskell but you'll be spending a few weeks worrying about monads. I think C just looks easier because you can learn enough to be dangerous without much effort, getting to a level where you can write reasonably safe C will take a lot longer than getting to that same level in Haskell.
- the_only_law 5y agoAfter trying to join in on the monad jokes for forever, I opened up the the Wikipedia page on Monads (in the functional programming context, not the page in raw category theory) and it actually kinda made sense.
- exdsq 5y agoThe problem that made monads make sense for me was when I had to chain (err, val) tuples for six or seven functions that only took val and handling the (err, _) bit was awkward. Someone showed how to rewrite it using monads to handle that without the boilerplate and voila.
- javert 5y agoIf someone would make a tutorial that replicates the process you just described, that might be a good way to teach monads.
- ghayes 5y agoI strongly suggest starting with a language like Elm to get into FP, since you start to use `map` and `andThen` quite often, but you also get sick of writing `a |> Result.andThen fn1 |> Result.andThen fn2`. This can help a programmer realize why it might be better to have a concise syntax for this, like: myFun : String -> Result String Int myFun a = do b <- fn1 a c <- fn2 b return c That said, I think the other problem with Haskell is the definition of the bind operator: it is not obvious to a beginner which concrete function is actually being called for each monadic operation. Idris2, for instance, lets you specify the bind operator in its do notation[0]. [0] https://idris2.readthedocs.io/en/latest/tutorial/interfaces.html https://idris2.readthedocs.io/en/latest/tutorial/interfaces....
- marcodiego 5y agoBecause you're already used to a very different approach to express the solution to a problem. It is like somebody who already knows an western language trying to suddenly learn Mandarin.
- Jtsummers 5y agoHah, I was pondering how to write a longer comment expressing this same thing so I'll piggyback on yours. Paradigms are modes of thinking. You can't just pick up a new paradigm on the fly when you've spent your entire life in another one. Some individuals are exceptions, but most of us aren't so lucky as to have such a natural aptitude for changing our minds on the fly. In order to be introduced to a new paradigm you have a couple options: 1. Sink or swim. In Haskell, this is the monad tutorial. Why the hell do people start their instruction here? Did they start here? If they did, did they succeed from that point or did they have to find another path and just forgot that this was a really stupid way to start? 2. Baby steps. "See Spot. See Spot run. Run Spot, run!" Learning Italian (previously Spanish), this is literally the level I'm at (actually, a bit better, but still highly constrained by my limited vocabulary). In Haskell, this is: double :: Int -> Int double x = 2 * x quadruple :: Int -> Int quadruple x = double (double x) Simple functions tested in the REPL. Then you teach them about function composition (drawing on their knowledge from mathematics, where it's the same idea and not merely an analogous idea) and make a point-free version of quadruple. Then you show how functions can be passed around so that you can do: square_function f = f . f quadruple = square_function double Maybe give that first function a better name, my coffee hasn't kicked in yet. My point, though, is that functional programming in Haskell does not rely on monads when teaching the topic. There are a million things to teach before you even reach that point, and only once the student has a foundation in Haskell's syntax, base semantics, and type system do they need to be introduced to monads. At which point it'll make a lot more sense because they'll be able to grok what monads add to the language. By analogy (hah!), we don't start C language learners with implementing a generic swap or sort function. That would be way beyond their initial capability, relying no too many ideas that they have no foundation for (that said, it's a shorter path to that in C than monads in Haskell). So why do people think that learning a totally novel (to them) paradigm like functional programming, especially in the uber-FP language Haskell, can be done by starting at the deep end without studying its fundamentals?
- jghn 5y agoMost people are taught from day 1 to code in some combo of imperative and OOP styles. The issue is not that FP is hard to learn. The issue is that most people start off from a much lower baseline knowledge-wise than they will when learning a new imperative and/or OOP language, framework, etc.
- moralestapia 5y agoI was going to comment on a similar line. I come from the OOP school and have done 10s-100s(?) of C/C++ projects, which I like, btw. But when I discovered FP it was an eye opener, the breadth of things that became possible/tractable is great, even my OOP programs became much better because of it. I think that the OOP curriculum is actually a regression, and would recommend anyone who is just starting out to try out FP first.
- eightysixfour 5y agoAs someone who only dabbles in code, I’m very interested in starting FP first, but I find that the beginner tutorials mostly assume previous programming knowledge/skills, and the communities aren’t really geared towards folks who don’t have backgrounds from other areas. It is certainly possible I’m not looking in the right place for the right thing, but it should probably be an area of improvement for those communities in the future IMO.
- blagovest 5y agoHey! I am writing an ebook about high-level programming concepts that starts off with a FP view point and builds on top of it. If that sounds interesting, I'd love to share (I am eventually going to sell it but I am fine giving away drafts)
- eightysixfour 5y agoAbsolutely, would love to take a look.
- CalChris 5y agoSome people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems. That isn't even a criticism of regular expressions nor is this a criticism of functional programming. It's more about that fashionista need to use something rather than solve something.
- dtagames 5y agoI had a chance to speak with John Backus when I worked at IBM where he invented functional programming with the goal of removing some of the spaghetti and incomprehensibility of imperative and procedural code. It certainly was not an immediate hit! Despite being an IBM Fellow (the company's highest rank with complete freedom to work on whatever you want), John was having trouble getting any traction for his ideas. I certainly didn't grok it at the time. I couldn't see the utility over the procedural PL/AS and imperative assembler we were already using to create the mainframe's higher-level language compilers. I've since become a big believer in the functional style, sadly after John's passing. It's certainly not the solution for everything. Even the lambda calculus requires that you feed it a starting series of "magical" integers to work. But functional is a useful way of thinking about programming, especially for library functions. I would say the key difference between the functional style and imperative/procedural is not the presence of recursion but the lack of state. A functional function cannot have any internal state store, nor rely on anyone else having one. In other words, all of its arguments and values must be fully defined by parameters. This is a super-critical concept in debugging because it helps bulletproof your function. Having said that, no real working program can be fully bulletproofed with the functional style because we need to hold onto state in real programs. (Is the user logged in?, etc.) We cannot pass these values in every single time and have a practical program. I think merging these concepts of functional when you can and state when you must is the easiest approach. Certainly there are many functions in every program which are functional in style in that they do not contain or rely on any state, and those are good jumping-off points for starting to understand the functional style.
- emodendroket 5y agoOf course a program without side effects is useless, but the idea is deferring those as much as possible, isn't it?
- dtagames 5y agoYes! JS and TS allow this kind of functional style which I try to adhere to. There must be state, of course, but as few functions as possible should rely on it and certainly no function should "reach" into anywhere else to get a value. Those things have to be passed-in. The change from AngularJS to Lit (or React), is an example of this kind of functional refactoring. AngularJS had two-way databinding (state!) and it attempted to pass that state upward when things changed. This made horrible spaghetti and impractical large apps. Lit and React are only top-down. Yes, each component has state but only at the top. It gets passed-in as a parameter to other things, but they can't change it in return. This is much more modular and debuggable.
- emodendroket 5y agoWell, as alluded to, mostly because it requires you to start over and relearn basic things. But the compiler devs for your language specifically setting out to break your program is an unusual hurdle. Based on that alone I would never touch Elm.
- splintercell 5y agoIt’s that attitude which makes elm, an excellent programming language to learn function programming on.
- emodendroket 5y agoMaybe it's great for learning, but many of us wish to deliver a useful project and not just do something for purely didactic purposes
- macintux 5y agoWhich would be fine if the creators didn’t also push for Elm to be used for production use, which is clearly a dubious proposition.
- mjaniczek 5y agoThis has been discussed to death, I feel. People building their apps on a discouraged "leaked" implementation detail (JS native/kernel modules) got cut off from using it. The reason for disallowing that implementation detail wasn't "hey, somebody is using it, let's teach them a lesson" but, as far as I've heard, improvements to dead code elimination. You can (and people, me included, do) use Elm in production peacefully. Huge apps, nontrivial JavaScript interop needed. You can do all that without depending on JS native modules. I feel like whether you use a (discouraged) implementation detail of the language is a good indicator of whether you'll have a bad time later on when that implementation detail changes ¯\_(ツ)_/¯
- myWindoonn 5y agoGood post from somebody with an overflowing teacup. Haskell's not magic or special; it's Just Another Programming Language, really. Reading their monad tutorial (https://gist.github.com/cscalfani/b63552922a8deb2656ecd5ec8a1a77a8 https://gist.github.com/cscalfani/b63552922a8deb2656ecd5ec8a...) it doesn't sound like they actually understand monads; they don't know about algebraic laws or the join operation.
- Fiahil 5y agoFunctional Programing is hard to learn because it literally changes the neural pathways in your brain. Once you learn it, there is no going back : your grey matter will be changed forever. I used it a lot in the past, and less nowadays (Rust), but I occasionally have to teach my coworkers on basic FP principles so they can use and write good Python code. Simple descriptions make the learning more palatable and doesn’t scare people (“you could use a Monoid here” -> “Try using a class like this, with an `empty` and `combine` function, here”). It usually takes about 6 months of daily practice to learn basic FP skills and 1-2 years to go from beginner to “intermediate” level. Occasionally, you might encounter a FP grandmaster who will melt your brain in less than 2 minutes of conversation, some things never change.
- dtagames 5y agoSo true! It's a tough disease to catch but totally incurable. Once you have the bug, you start to pick-up code smell in anything that isn't functional. You see code and ask, "Why are we touching that?, Why are we holding onto this?" Being able to think in a functional style encourages you to throw away as much code as possible and hang-on to as few crufty elements of state as are minimally required. It also makes you very anti-OOPS and hesitant to define "classes," since these violate the first principles of functional.
- pjmlp 5y agoIt is really? Personally I think it is a matter of what teacher one happens to bump into. I was quite lucky to have such set of teachers for logic programming (Tarsis World and Prolog), and Functional Programming (Lisp, Miranda, Caml Light). It felt no different form other programming classes. In fact, it had higher success grades than thermodynamics, electromagnetism physics or the most feared of all assignments, data structures and algorithms. So dammed hard depends very much on the learning path.
- stackedinserter 5y agoProbably because every single FP tutorial is very far away from real tasks that average software developer deals on everyday basis. They describe pure functions and categories of endofunctors, while I have tasks like "invoke this stateful external API if that stateful external API returns specific values".
- darksaints 5y agoIt's just the Haskell family of functional programming languages that is hard to pick up. There are plenty of functional language families that are quite easy to understand and much more pragmatic: * ML - includes SML, OCaml, F#, Scala, and Rust * Lisp - includes Common Lisp, Racket, Scheme, Clojure * Actor Model - includes Erlang, Elixir, and Pony, as well as other languages that have actor model systems at the library level You'll find endless sources of opinions on why the Haskell family is so hard to use well. My personal opinion is that most languages create abstractions with concrete types of problems in mind. Haskell created abstractions with other types of abstractions in mind. If you ask the question "what is a monad used for?", the average Haskell user isn't going to respond in any form about making side effects safer, because that's just one thing that they do...they're going to respond with other abstractions. And after 45 minutes of explanations of what they are, they still haven't yet gotten to the explanation of what you can do with it. And then when you finally understand what you can actually do with it, you have to confront the fact that they made an incredibly easy thing hard, just in case you might want to use it a different way. I cant recommend this rant enough - https://existentialtype.wordpress.com/2011/05/01/of-course-ml-has-monads/ https://existentialtype.wordpress.com/2011/05/01/of-course-m....
- JaggerJo 5y ago+1 We've been using F# with great success for a few projects now. Our stack is super simple. On the backend we use F# (on .NET with ASP.NET Core) + Postgres. On the frontend we use F# (via Fable with React and the Feliz Bindings). https://zaid-ajaj.github.io/Feliz/ https://zaid-ajaj.github.io/Feliz/ And we have a huge shared library. Works like a charm.
- mrdoops 5y agoAgreed. I would bet on a language like Elixir or F# being simpler to learn and grow for a complex system than a class-oriented-imperative-oop (Java, Ruby, Python, etc) language any day.
- ddek 5y agoF# is such a sleeper language IMO. It's compositional tools are quite beautiful. I don't think it'll ever get wide adoption though, which is quite sad. It's just such a sensible ML language. The abstractions chosen (computational expressions, for example) were the best choice for F#, rather than copies of other FP languages. They can't implement any form of HKT because of limitations in the CLR, so they had to use alternatives. Elixir is also really great IMO. The pipeline composition is a really nice model. If adoption grows more the tooling should step up, because it's lacking a little. The language server can't do things like rename a function, there isn't a complete TreeSitter parser either. I also have this fear with actor model that I'm inadvertantly leaving some process dangle somewhere, which in my experience is not unjustified.
- _query 5y agoIn my experience when learning functional programming with Haskell there are two kinds of complexity: 1. Complexity caused by the change of paradigm: This specifically hits programmers with a lot of experience in OOP languages. For getting into the FP-mindset your brain needs a bit of time to get rewired. You need to switch thinking about mutating objects to thinking in mapping streams of data. Given that many OOP languages are adopting functional ideas like map and fold/reduce, nowadays a lot of developers already have a bit of experience in thinking in the FP-way, and this will get better with time. 2. Complexity caused by the tools: There's a reason the author in the above post started out with Elm instead of Haskell :-) Writing a few recursive functions in haskell is still pretty simple. Where it get's very complex is when you want to build real world applications. To build a simple web app you need to 10s of decision on what tools and libraries to use. Here's a couple questions you'll need to find an answer to when building a haskell app: - What GHC and what language extensions do I need? How do I install it? - What package manager do I use? Cabal, nix, stack? - What web server library? - What database library do I need? Do I need an ORM, are there even ORMs in haskell even though there are no objects? - What HTML template library to use? - How do I compose it all together? What is a monad stack? When you have only very few experience in haskell it's really hard to not get stuck here. The quality of documentation of most haskell tools also doesn't very much help here. I believe that the value haskell can bring is signifcant, and by fixing the tooling situation we can a lot more people to adopt haskell in the future. With IHP we're trying to fix the tooling situation and build a haskell-based web framework that is as easy to use a rails or laravel :) To combine the benefits of purely-functional programming with the RAD approach of rails, laravel and django. It's now already the most active haskell web framework and we have many people starting their haskell journey with IHP. We have live reloading in dev mode, a JSX-inspired template language and many code generators to quickly get started with shipping real world apps. Check out this video of building a simple blog app in IHP: https://www.youtube.com/watch?v=UbDtS_mUMpI https://www.youtube.com/watch?v=UbDtS_mUMpI To get started (supports macOS, linux, windows) check out the IHP Website: https://ihp.digitallyinduced.com/ https://ihp.digitallyinduced.com/
- throwaway81523 5y agoThe saying about Haskell is that it has the steepest unlearning curve. It probably helps to have seen some abstract algebra since many of its ideas come from there. The online book learnyouahaskell.com is pretty readable though. As that guy was a big executive whose time was interchangeable with money, he might have had better luck treating FP as a topic in math that he was having trouble with, and hiring someone for one-on-one tutoring, either in person or online. It might have gotten him through the various stumbling blocks quicker.
- agumonkey 5y agoculture and the paradox of simplicity fp and math go abstract fast, people can't see any tangible machine or pragmatic process fp-ists enjoyed that and pushed it to 11, making it even weirder
- lucie_cupcakes 5y agoHere is an archive of the text-only view from the article: https://archive.is/aoB1o https://archive.is/aoB1o
- zelienople 5y agoWhy is learning anything so damned hard? Mainly because we are monkeys to whom a reasonably accurate reductive analysis can be applied if we label the axes "asshole" and "idiot". It was a good blog post by a reasonably intelligent monkey who seemed to score low on the "idiot" axis. But some other monkey pegged the "asshole" axis by preventing us from highlighting the text so we could right-click and search to answer the primary question, "What is it?" Namely, what is functional programming? Even after besting the right-click hurdle, no one easy and succinct answer is available. The meta-analysis of this blog post therefore circles back around to perform a second-phase adjustment of the author's "idiot" score once we realize that he has done exactly what he is complaining about; he is a bad teacher because he did not answer the "What is it?" question. He seems like a reasonably nice person, so I think we have to leave his "asshole" score alone in the second phase. The collective effort, however, scores high on both the "idiot" and "asshole" axes, and this is the core of the "bad teacher" problem. I spent most of my long university career angry about this problem. Why is is that monkeys can't teach? Partly, it is because they are arrogant assholes who don't want to weaken their position by making it easy for others to access their expertise. And partly it is because they are monkeys who think more highly of themselves than is justified, and therefore they cover their ignorance with jargon, hand-waving, and obfuscation. Other than extinction, I'm not sure how to solve the problem, but I'm sure that the first step is by starting every lesson by answering the "What is it?" question succinctly and concretely, and, more importantly, realizing that if you can't do that, you should shut the fuck up and go away.
- btkramer9 5y agoTo your point about others not teaching well to preserve their status, I'm sure it exists but not that prevalent. I think people who have known something for a long time just forget how to empathize with not understanding what they've known for so long. For this reason alone I think this is why fellow students tend to be much better at tutoring/helping each other instead of their professors.
- scns 5y agoThe experts blind spot comes to mind: https://thevaluable.dev/expert-blind-spot-software-development/ https://thevaluable.dev/expert-blind-spot-software-developme... https://nscblog.com/continual-learning/understanding-understanding-the-expert-blind-spot/ https://nscblog.com/continual-learning/understanding-underst... Dropping the judgements, assuming positive intent and trying to see the situation might help.
- dingosity 5y agoShouldn't this article have been titled "Why Is Learning Haskell So Damn'd Hard?" Don't get me wrong. I like Haskell. But yeah, I learned Haskell because I used to sit at a desk across from Brian O'Sullivan (who later wrote "Real World Haskell".) And even having Brian around to answer questions, it still required stretching of the brainpan. But at least I had a guide.
- motohagiography 5y agoI fail to learn Haskell about once every 5 years and sometimes I think of functional languages as not languages for expressing concepts, but more like a musical notation that denotes a set of abstractions mapped to an underlying ontology made from things you just train yourself on with practice - and then use the functional language as a lookup reference to until you can in effect "sight read," or compose those underlying elements to the page. The algebra and category theory are the true instrument, and the language is the composition notation tool. Whereas with OO and imperative programming, the imperative or OO language itself is the instrument or tool that you can just pick up and bang away on and music will come out. To extend the metaphor, OO is an instrument on which you can play a canon like "row your boat" without knowing how to write one, some, or all of them. It's lovely that we have these tools for specifying and composing functions, but perhaps the gap is in effectively expressing and teaching the underlying ontology. Maybe as a writer, my next attempt should be to write that explanation of it as I go. The most trouble I had with Haskell was pronouncing statements after reading them, which prevented me from composing the ideas in my head because the syntax doesn't give you a lot of hints. It's like trying to parse calculus without names for the letters in the greek alphabet. Most people are proficient in their own written languages, and they are capable of mapping ideas to abstractions, but if there is not a way to express those ideas lexically in their language, most are going to stumble. The incomplete and error prone explanations by novices that the author refers to also create an artificial conceptual barrier, but mostly because there is always the hint and implication (or perhaps just personal anxiety) that you are not writing real functional programs and that your naivety is accumulating a hidden sunk cost that you will eventually be exposed for and made ashamed of because you aren't a category theorist, which is a consequence of this mostly aesthetic idea that functional code is about competing to advance knowledge in a science and not just making stuff you enjoy or people like. The clostest thing I found to a kind of function-punk where you could just bang away on it because it was fun was the excellent Learnyouahaskellforgreatgood, but it focused on the notation less than the underlying ontology - e.g. the instrument - and what I think I might need is more of a category-punk that provides the equivalent to composition with a few concepts, and then add the notation to those. Not sure there's another attempt in me, but anyway, provocative article about something so important, and that I think ultimately FP will really advance the way people think about the world, if only in another generation or two.
- del_operator 5y agoTeaching someone to abstract out a name for something they want to do takes lots of practice, but then when they are pushed to have to abstract can lead to decision paralysis around when to do it. Having folks practice this together and feel out the nuances helps a lot. Control flow is also a tricky early concept for any new programming setting, but also, learning to compose is too. For some students tackling control flow and composition early by explicitly composing abstracted funcs feels great. However, I’ve oft seen many of my students want to get granular with control flow for a long while before they abstract or utilize many abstractions, probably because their mental model of what they want to do is stepping through each detail of an example they have in mind. Another component for andragogical learning is to just sell the WHYs of functional patterns: why care, why remember, why it’s helping, why they should just give it 5, why it’s ok to forget, et cetera. Learning and jumping straight into synthesizing, evaluating and analyzing code for a potential production setting has been difficult with a loose understanding. You need good scaffolding in your backlog, lots of time, patience, context sharing, and resourceful experts to turn to check your understanding and promote good tooling/solutions in the ecosystem. I saw this when my team switched to elixir. I saw this when my team stitched to K8s. Lots of delays I’m happy we worked through. Pairing a lot helped through it
- cutler 5y agoI think learning functional programming is harder with a statically-typed language as there's so much more to learn which revolves solely around the type system. I would recommend anyone new to FP to try Clojure first. No mon[a|oi]ds necessary. I also think the transition from procedural languages is easier than from OO languages. I was lucky not to be exposed to Java or C++ in the early days of my programming career, opting for Perl instead. When I transitioned to Ruby I also encountered Clojure at the same time and could appreciate the functional/lisp elements in the design of Ruby.
- stadium 5y agoI had only one FP experience with scala. I actually liked that it was statically typed. I have nothing to compare it to though. But, higher order functions and type parameters are still a bit mind bending for me. I didn't have to write much code for those, but when I did it was really hard. I'm sure I'd need at least couple days of reading and playing around to somewhat grasp how to implement them again.
- valzam 5y agoI actually think that FP programming and FP type systems are almost completely seperable and most of the FP jargon comes from FP type systems. Elixir is another example that makes FP programming very straightforward. That being said I think once you get the basics of FP programming learning about FP type systems is worth it. Especially FP effect systems (i.e. IO monad).
- deltasixeight 5y agoI mean it's not like this type system is exclusive to functional programming. You can tack it on to an imperative language (see rust). It's mostly the type theory that is hard here but people talk about functional programming because of association thanks in large part to haskell.
- mjaniczek 5y ago> No mon[a|oi]ds necessary. Mon[a|oi]ds are everywhere, but in some languages you just don't know about it. Which of course lets you not have to learn those concepts knowingly, but they're still there and you still could be a better developer for at least knowing they're there and they're behind some of the repeating patterns you see (string concatenation / promises / etc.) I believe "not worrying about them at first" is actually a good path to learn these patterns. Using them first, some time later having a thought "oh hey, the API of how JSON decoders are written is kinda similar to how I'm generating random values, that's cool" and only later on learning that the it is in fact monads/monoids/functors/whatever and that they have laws and you can exploit that (eg. [repeated squaring for anything that's a semigroup](https://blog.jle.im/entry/shuffling-things-up.html https://blog.jle.im/entry/shuffling-things-up.html)) and whatnot.
- bobcostas55 5y agoOne aspect I don't see mentioned very often is that the FP model of computation is completely different from the model of the underlying hardware, which makes it very difficult to reason about things (and often involves putting blind hope in the compiler).
- jhgb 5y ago> is completely different from the model of the underlying hardware That's kind of the point of programming languages, otherwise you'd be writing 100% assembly.
- bobcostas55 5y agoI don't really agree, the point is to provide high-level abstractions that hide a bunch of complexity. But even in a high-level non-functional language, there's not a lot of friction in terms of the model of the language vs the hardware. An array of ints in java is basically 1:1 to the underlying continuous, mutable block of memory.
- jhgb 5y ago> the point is to provide high-level abstractions that hide a bunch of complexity And these abstractions are not hiding the model of the machine? For example the machine has a small set of fixed-size registers, but your language probably has an unlimited amount of arbitrarily sized, lexically-scoped local variables.
- soheil 5y agoHonestly a much less gimmicky and concise guide can be found here [0]. The focus is on scala but it does away with longwinded storytelling style explanations and petulant comics. It gets to the point, gives a couple of examples and tells you exactly why each tenant of functional programming is useful in one sentence. [0] https://nrinaudo.github.io/scala-best-practices/definitions/referential_transparency.html https://nrinaudo.github.io/scala-best-practices/definitions/...
- jokethrowaway 5y agoAs someone who is huge on Functional Programming, I'm not sure you're reaping the benefits you're talking about. Your dedication is impressive, but it's nowhere close to being required to build any kind of product. Sure, I agree that learning FP will make you a better development, but spending all these learning resources in order to build a product is not very convenient. I've been shipping code with mediocre languages all my life. Even now, I routinely pick node.js over Haskell or Rust just because the complexity of the solution I need to build is not high enough to justify writing amazing bug-free code. Sure, you may get more bugs and less help from the compiler, but there is more material online and I can easily find a cheap developer to throw at the project. I've been developing for 15 years. After 5 years of C++, PHP, JS, I decided to jump into Haskell for my side projects. I can't say the learning curve was as hard as you made it out to be: there are plenty of great resources for learning Haskell and you don't need to go through Elm or Purescript (which weren't even a thing when I learnt Haskell). Actually the differences between the languages may make things more confusing. In the last couple of years I stopped using Haskell completely, simply because Rust (a language designed to build things, unlike Haskell which is more of a language research project) is functional enough, pleasant enough to use and is developing a nice community. The most useful FP concepts trickled down in other languages. FP already won and nobody even noticed it.
- bob1029 5y agoI have found that my best use of functional programming occurs within hybrid systems. When you can use either technology on a per-function or class basis, it really helps with adoption. Languages like C#8+ almost feel like cheating when it comes to mixing ideologies. Some developers think this sucks, but I view these complaints similar to those who would complain that someone might try to use a hammer to install a screw. The general theme in my mind is to use the imperative code to construct the walls of the functional matrix wherein I author the actual business logic as pure functions. FP for me is usually in the form of SQL, LINQ or switch expressions. I personally have never wanted to go all the way with FP. I see how you can do it in theory, but I question the practical engineering value of actually pursuing this. The most important & scary parts of our product are within this "hosted" functional scope.
- crvdgc 5y agoSpecific to the "jargons" the author talked about. I find recent introduction level Haskell books actually do a surprisingly good job covering all the concepts the author mentioned. And they don't suffer from the online learning material problems. The two books I read and can recommend are Programming Haskell by Graham Hutton & Get programming with Haskell by Will Kurt. Another source that helped me a lot is the video series Category Theory by Bartosz Milewski [0]. It requires basically no background on category theory and defines all the concepts along the way. Bartosz also uses Haskell code as examples. You only need to know the syntax up to type class to understand. As for the awful experience people get from learning Haskell, I think it depends a lot on people's expectations. Most people only learn one language from school or early career, and then migrate their experience from one language to another. You don't learn the concept of "stack" twice, you only "learn" how to express them with a different syntax. So people can "learn" new languages with significantly lower effort than they learned the first language. And when they find Haskell or other ML-family functional programming languages, they can't find the corresponding concepts in their known language. This time people actually need to learn new concepts as they learned their first language. The experience is very similar to learning algorithms or OOP design patterns. And I don't think it's harder to learn Haskell up to the level mentioned by the author than e.g. reading the gang-of-four book. [0]: https://youtube.com/playlist?list=PLbgaMIhjbmEnaH_LTkxLI7FMa2HsnawM_ https://youtube.com/playlist?list=PLbgaMIhjbmEnaH_LTkxLI7FMa...
- deleted 5y ago[deleted]
- exdsq 5y agoMandatory Hitler reacts to Functional Programming comment https://youtu.be/ADqLBc1vFwI https://youtu.be/ADqLBc1vFwI
- reverseblade2 5y agothis 3d bin packer is fully coded in F# and I wouldn’t be able to do so without it. https://bindrake.com/ https://bindrake.com/
- Const-me 5y agoI don’t consider myself an FP programmer but when I use modern C# for code that’s not terribly performance-critical, I use these FP concepts (purity, composition, currying, immutability, etc) when they’re a good choice. For suitable problems, FP can be awesome. However, I often write performance-critical CPU-bound code, often impossible to do with FP. To write fast code for modern processors, programmers have to think about RAM layout and L1D cache misses. With these immutable data structures, FP code ain’t exceptionally fast because for optimal performance it’s essential to reuse the L1D-cached lines of virtual address space. Also, better to avoid any heap allocations on hot paths. Much easier to achieve these two things with imperative code, with their mutable variables, native stack and for loops.
- bennysomething 5y agoChrist that's a long winded article! Also I'm getting tired of "I learned to program on a stone tablet with a chisel" type stories. Wow, you wrote assembly, swoon!
- jasperry 5y agoI love functional programming and I think every CS major should learn the key concepts of it: higher-order functions, immutability, purity, and how they can lead to better designs and more reliable software. However, I believe that even after the learning curve has been scaled, the task of writing code in functional languages is objectively more difficult in at least one way: Coding a function in the expression-oriented syntax of functional languages has a higher cognitive load than coding that same computation as a sequence of statements. You simply have to hold more things in your head.
- matheusmoreira 5y ago> higher-order functions, immutability, purity These concepts aren't hard to understand and are actually incredibly useful. At some point though things get so abstract it's really difficult to understand what's going on. I've seen Haskell libraries that do things I can't explain for reasons I don't understand and the extremely detailed README with diagrams and arrows and everything somehow made it even more complex.
- Twisol 5y ago> At some point though things get so abstract it's really difficult to understand what's going on. A lot of that comes down to extremely advanced use of Haskell's type system. Advanced type systems are valuable, and they're easier to add to FP languages, but I wouldn't consider them to be a fundamentally FP concept. (We see very expressive type systems being included in imperative languages like Rust and Swift these days.) I think it's useful to keep in mind that Haskell is used a lot for research. You don't see Java used as a research sandbox in the same way, so you're not likely to run into run into these kinds of libraries in Java. (Though, as an aside, I consider anything that heavily uses reflection in Java to be nearly as inscrutable.) It's actually impressive just how many of these research-level libraries are practically useful in some way, even if the bar to understanding them is higher. I don't think it's a universal truth that libraries making advanced use of type systems must be difficult to understand. Taking the `lens` library as an example (and I think you were alluding to it, anyway), there's a fairly long-running thread of research around what a "lens" even is, and how they fit into a broader domain of "optics". The `lens` library was created based on one particular representation of lenses, and the community tried to find ways to unify the various kinds of optics in a way that made sense to them. I think the more recent understanding of "profunctor optics" might give us a more accessible route to understanding optics in general, separate from the whole hierarchy of distinct optics you can derive from it.
- default-kramer 5y agoThe author's struggles with Haskell resonate. I suspect it's just a very hard language to learn. I've given up attempting to learn it at least twice now. I took Martin Odersky's Scala MOOC sometime around 2012-2014 and it was easy. It just made sense and the IDE experience was nice. I would recommend it to anyone who uses C# or Java and wants to learn FP. Although after 8 years I don't know if it's still as good, or if something better has come along. Either way, the course is still available: https://www.coursera.org/learn/scala-functional-programming https://www.coursera.org/learn/scala-functional-programming Next I worked through SICP and that was the best CS book I've read, although significantly more difficult than the Scala course.
- odersky 5y agoNote that the course got revamped this year. It is now based on Scala 3 and new content was added. Some of the new topics are: enums, extension methods, and givens.
- GnarfGnarf 5y agoI lost interest in FP when my FP solution for the Fibonacci series was exactly how not to do it.
- toast0 5y agoYou can write the closed form in FP just as well as any other language. Most FP languages will let you write a recursive form that won't blow the stack. In C, you'd need to used closed form or a loop if you value your stack.
- AnimalMuppet 5y agoHow do you write the FP version that won't blow your stack? You can't just use TCO, can you? (Because of the two calls.) And if you're using memoization, you could do that in C. And, even a naive implementation wouldn't blow your stack, it would just melt your CPU into a puddle of slag. Or am I missing something here?
- toast0 5y agoTCO works if you write it properly: fib(0) -> 0; fib(1) -> 1; fib(N) when is_integer(N), N >= 2 -> fib(N, 2, fib(0) + fib(1), fib(1)). fib(N, N, Current, _Last) -> Current; fib(N, I, Current, Last) -> fib(N, I + 1, Current + Last, Current). But if you write the same thing in C, it'll still blow your stack (of course, you can transform it into a loop), and you'll need to use bigints somehow, these numbers get big fast. It does use a lot of cpu though; takes not quite 10 seconds to run fib(1000000) on my Pentium G2020. Closed form is certainly faster, although I didn't benchmark it :D
- toast0 5y agoToo late to edit, but if you do fib(N) when ... -> fib(N - 2, fib(0) + fib(1), fib(1)). Then you can check for N = 0 to return Current, and do N - 1 in the iteration. One less value to pass in each recursion should make it faster, and it's less to think about.
- nephanth 5y agoImho, ocaml is a way better language than Haskell when you are learning FP coming from other paradigms: it doesn't require you to learn mathematical constructs such as monads or monoids when you are beginning, it allows you to use imperative or OO constructs (though they are discouraged by the language)... When you are used to FP, though, more abstraction can be nicer
- Mikeb85 5y agoFunctional programming is hard because some proponents of it spend more time on theory and proving why you should use FP than simply doing things with it. That's why most things shipped with FP is created with languages like Ocaml, Scala, JavaScript, Clojure, etc... You know, functional but more pragmatic and multi-paradigm.
- yodsanklai 5y agoIt's not hard at all. It's routinely taught to beginners. However, it's not a silver bullet. I found that basic software engineering principles are way more important than the language. I've seen extremely messy OCaml code and super clean C code. What is important is how the code is organized at high level. Whether you use a for loop or a fold, an error monad or an exception mechanism matters less. I'm also wary of functional programming gurus that tend to over-abstract things and use all the language latest features, making code very hard to read. Also, when developing in a niche language, you tend to miss important tools and need to rely on unstable third-party libraries. I used to be quite enthusiastic about FP, but I think I'd stick with more mainstream languages unless there's a good reason not to.
- swiley 5y agoProgramming is about clearly communicating theory. Mariners use "port" and "starboard" for vehicle relative directions not because it's "cool" but because it's clear and unambiguous. Problem decomposition works the same way: some problems decompose better with FP (I would argue databases do) some with OOP, some with EF etc. The more you know the clearer code you can write and the faster you can extract the theory from other people's code.
- IIAOPSW 5y agoWhat makes you say "port" and "starboard" are more clear than "front" and "back"?
- swiley 5y agoThe alternatives are left and right respectively. They're more clear because left and right are usually relative to the speaker or listener who may be facing each other and are often moving around.
- noir_lord 5y agoAlso the problem of right having another meaning. “You want me to turn right, right?”
- TheRealNGenius 5y agoIt's really not
- chadcmulligan 5y agoMaybe one of you functional gurus can answer this - One of the problems I have with functional programming is how do you handle multiple data structures on a single thing. For example - say you have a drawing program, with say just one shape - a square. Now to make it fast when the user clicks somewhere you'd have a data structure like a k-d tree that can find your shape quickly. Now you also have another structure a list of the shapes, because you don't want to put them in the k-d tree, you update both structures when shapes change. You'd also want other structures for say undo/redo. I can't see anyway to do this in functional programming. Same applies to database style programming.
- hvocode 5y agoThat’s not that hard in an FP language. I routinely write code with multiple structures referring to the same thing. My usual solution is to have an identifier for the thing that indexes into an array or map, and then the other structures contain that ID instead of the object itself. It’s basically a pointer like I’d use in any other language. The details and choices for how to represent the IDs and structures is usually application specific, but that’s true in any language: how you do something should be the choice that best fits your problem. There isn’t anything about functional programming that makes it impossible or particularly difficult.
- chadcmulligan 5y agook, cool, would you know some sample code somewhere you could point me to? Just to see an example, always thought you couldn't do this.
- yawaramin 5y agoHow do you do it in whatever paradigm you use? It's probably very similar in FP, just the technical implementation is different.
- revskill 5y agoWhy using a StateMonad when i could just use OOP ? Simple, elegance and easier to read, write and test.
- caconym_ 5y agoImperative programming is intuitively literal in a way that formal functional programming in something like Haskell isn't, because you have to understand some nontrivial abstract mathematical formalism to build nontrivial programs. I think a lot of programmers, including me, have a gift for the former kind of thinking but not the latter. So we tend to struggle with abstractions like monads, because we're looking for some literal ground truth about them that doesn't really exist. Learning Haskell to the point where I could use it for practical work was one of the best things I ever did for my programming brain. I'd recommend it to anybody.
- elihu 5y agoI think Haskell is kind of hard to learn for these main reasons: what you know from imperative programming mostly doesn't transfer, you kind of need to know a pretty big set of library functions in order to be productive, and for practical programs you often need to know a few tricks that aren't obvious that allow you to mix pure code and mutable state (how to exploit laziness, how and when to use the ST monad, what should be in the IO monad, etc..). Additional roadblocks are that the syntax is strange if you're not familiar with ML-derived languages, the type system is fairly complex and you need to understand quite a bit of it to make progress, laziness can cause performance problems (lost parallelism, excessive memory use) if you're not careful, and the tooling isn't always as user friendly as it could be. That said, I'm glad I learned Haskell, and though I don't know everything about the language, these days I'd feel pretty comfortable using it for anything I'd use any other decently-performing garbage collected high-level language for.
- ridiculous_fish 5y agoI would add, Haskell is bad at naming stuff, especially function arguments and generic type parameters. It has a tradition of point-free style which deliberately avoids names, and when names are required, they're typically meaningless single-letters. This makes it hard to build intuition and to follow code. For example, the main Haskell graph library (fgl) has two type parameters, named 'a' and 'b'. Instead of meaningless letters, why not 'NodeLabel' and 'EdgeLabel'? Now it's obvious what they mean!
- elihu 5y agoYeah, that example seems like not-very-good naming. The standard library uses a lot of terse names, but that's often because in a lot of cases the types could be almost anything. Might as well just call them a, b, and c. There's another general principle that the length of the names of variables should scale with the size of the context in which they're meaningful. So, a 10,000 line program might use a four-or-five syllable name, whereas a one-line function that takes two arbitrary arguments can just call them "x" and "y". I do agree that points-free style and terse code can be problematic. Haskell lets you use very high levels of abstraction, but if the next person to look at your code doesn't understand those abstractions it'll tend to look like gibberish. I think my own preference tends to be not to go out of my way to make the program extra terse unless there's some compelling reason, like avoiding a lot of repetition. I think plain old low-level C code is (sometimes) easy to read just because it has a lot of visual cues like "for" loops that your brain can pattern match on to tell you what the general control flow is. In higher level code, you often end up with fewer visual cues. Which is good in the sense that it eliminates redundancy, but bad in the sense that it can take longer to understand what unfamiliar code actually does. Something that seems to happen in Haskell more than other languages I've used is that sometimes you can kind of forget how the lower layers of abstraction actually work, and it sort of just does what you want as if by magic. For example, the Lens library let's you update data structures using a syntax similar to imperative languages, but behind the scenes it's actually constructing a new data structure from the root on down to the "edited" leaf, and (unlike in imperative languages generally) if something goes wrong you can just throw up your hands and error out, and the old data structure is still there. It's like the convenience of imperative languages, but with transactions practically for free. But the type signatures that the Lens library uses are fantastically complicated. Though I sort of understand what the Lens library does, I really have no idea how it works internally. And I've decided that that's actually fine.
- danieltanfh95 5y agoI think it's important to take note that these issues arise from typed functional programming languages. Untyped functional programming languages aka dynamic FP languages avoid most of these problems entirely by having robust and useful metaprogramming features, whereas in languages like Haskell, most experiences Haskellers would tell you to avoid Template Haskell like the plague. Just to give you a sense of the issue in typed FP, and I will take Haskell as the prime example, the average Haskeller will not be able what a Monad is beyond the standard definition in category theory. I tried it once, got the whole Slack channel explode as everyone tries to tell me "I will just get it". This is the result of my study. https://medium.com/glassblade/pragmatic-monad-understanding-c6f6447d1bcb https://medium.com/glassblade/pragmatic-monad-understanding-... IMO the main problem is that typed FP langs don't try to connect their abstractions to the mainstream languages when these abstractions clearly have some counterpart or an intuitive close cousin in typed OOP. Worse, these abstractions usually are gaping holes in the language design but the typed FP community thinks its a feature.
- Jtsummers 5y agoWhat are these mysterious "untyped functional programming languages"?
- mbrodersen 5y agoMonads allow you to compose functions with incompatible in/out types. That’s all it is. And (as a nice bonus) add extra code in-between the two functions you are joining. It opens up a huge number of cool things you can do.
- robertwt7 5y ago> The Technological Debt, i.e. the cost and burden of a large codebase that’s complex and difficult to maintain, is also higher in Javascript. One of the reasons for this is that the language is fragile. I found this rather bias. Is it true for those who has tried FP / elixir? I have never used it, I don't think my company will ever consider changing the codebase at all. However java / python as backend and typescript (react) as front end seems to scale soo easy for us here.
- jo32 5y agoFP is not a new idea. If FP is easy and efficient, most projects will be written in it.
- sriram_malhar 5y agoI think FP is hard because (a) lack of libraries/bindings (b) impedance mismatch with underlying host ecosystem. In every personal project where I have used an FP language (Elm, Elixir, Haskell) I have run into these problems. Then I have to put my project aside to get into the weeds to get some bindings working. Often the bindings will be abandonware, or work just well enough for you to make progress until you run into severe enough problems. This has happened for me using OpenCV, ZBar, async I/O, threading, lower-level kernel APIs, databases, just to name a few. Often I think, I could have solved the problem so much faster if I had just stuck to Python or Javascript, or in the language the library was written in. In the end, you end up swimming upstream. This includes the effort of handing it over to someone else. Elm tries hard to provide an impedance-mismatch free environment for UI development, until you reach a point where you want to do something just slightly different; then it becomes painful. Sure you can integrate Elm with JS using ports or even React with Elm, but creating a polished UX experience is a different level of effort.
- namelosw 5y agoFunctional programming is not hard to learn. Try learn Elixir if you don't believe my word. My observations on why people think functional programming is hard: 1. They find it hard because they're not learning functional programming, instead, they're learning functional programming, advanced statically typed systems, type classes, and monadic programming altogether. Languages like Haskell fit into this category. It seems the Author took this path (Elm, PureScript, Haskell). 2. The ecosystem is simply not there. Say I just learned Python and there are many meaningful projects I can build to continuously improve my skill. Say I just learned SML, there are not many exciting things to be done. 3. The ecosystem favors power users. Clojure the language is not hard to learn. The ecosystem is not bad at all, both quality and quantity. But: 3.1 I agree that the idea of "libraries over frameworks" seen in Clojure community is better, but when there's less constraint, the responsibility falls on users' shoulders. Working with libraries feels like assemble my own PC. This is also why many people find Vue is easier to learn than React, and Intellij IDEA is easier than Emacs. 3.2 The ideas are novel. It may not be harder to learn, but it takes time. Rails programmer knows what to expect in a Web framework, be it Django or Spring Boot, so when they learn them they just learn like the last 20% of them. But for Clojure I find myself keep learning things like Fulcro and Pathom, the ideas are exciting but it's not ready for the mass. Just imagine how hard it would be to educating "the Algebraic Effects" to React users in 2013. In fact, the React ecosystem has been influenced by ClojureScript/Om, but the React community did pushed hard for the education part. At that time I feel like I came across those buzzword "functional" "reactive" "immutable" "undirectional" in the community every single day - it became such a cliche so that people start absorbing them. Otherwise, functional programming without these problems is easy to learn. Rails programmers can be productive with Phoenix in a matter of days - many of them didn't even bother to learn Elixir or functional programming, they just picked them up along the way.
- eitland 5y ago> I believe that Functional Programming is far far better than Imperative Programming. I know this because I was willing to suffer for 3 straight months learning something I had failed at multiple times in the past just so I didn’t have to write in JavaScript. Or, one could just swallow ones pride, learn TypeScript, and have the benefit of working in a sane language that transpiles effortlessly into Javascript. (Not a bad word about Javascript wizards or its inventor. Given the constraints the results are nothing but amazing. I just wish we didn't have to suffer an untyped language for so long before someone came up with the obvious solution that is TypeScript.)
- mikewarot 5y agoHere's a data point... I haven't been converted yet, but I have found a series on youtube which I feel will likely get me there https://www.youtube.com/watch?v=Vgu82wiiZ90 https://www.youtube.com/watch?v=Vgu82wiiZ90 Observations: 1> The syntax is strange, I'm not sure it needs to be, a list of parameters then the return type, with none of the usual commas and parens, it's like someone heard a critique of lisp, and went too far the other way. It's almost as bad as Forth 2> lazy evaluation as a feature? ok, I guess. Is there somewhere else that over-eager evaluation happens? (like in Metamine, where you can do a super fancy equals, and if any of the terms change, the whole thing is re-evaluated, like cells in a spreadsheet) That would balance it out 3> Everything is an expression - this is the worst part of C ever, and it's being called a feature 4> Invariants as good - this is the worst part of python, and it's being called a feature 5> procedural code is banned completely - yeah, this likely breaks a lot of brains, but I'm willing to let it slide if it works in the end 6> definitions of functions being conditional, that one is also a brain breaker... in the normal world, you define a function once, and forever... the 3 different styles of spreading out/pattern matching the inputs is a bit of a hill to climb 7> having to worry about tail recursion instead of regular recursion, it's the metaphorical equivalent of manual memory management, it ads mental complexity that the programmer really shouldn't have to deal with. That's as far as I've gone along this phase change... I'm pretty good at keeping opposing views both straight in my head, so maybe I'll be one of the few this doesn't break? Who knows. Aside/Tangent - Perhaps the difficulties being encountered are why John McCarthy wanted to keep S expressions hidden under the hood in his unrealized programming language.
- themulticaster 5y agoI don't want to diminish your first impressions, however I think you might have misunderstood one or two points in that video. Perhaps I can clarify: Regarding 3/"everything is an expression": C does explicitly not have this property, it has statements and expressions. The typical example to illustrate this point is the lack of a true "if expression" in C. [1] Given the following C code: int x; if (a > 5) { x = 1; } else { x = 2; } The value x is only assigned once, so it would be nice to express this using const. We can achieve this using the ternary operator, but that only works if the if statement is simple enough. In Rust on the other hand, ifs are true expressions and "return" values, allowing you to write the statement above like this: (Syntax might be slightly incorrect as I'm writing this without a compiler at hand) let x = if a < 5 { return 1 } else { return 2 }; From what I'm observing the (functional programming) idea of "everything is an expression" is well-received and also included in recent imperative languages such as Rust. Regarding 4: Do you mean immutability instead of invariants? I'm not quite sure what you are referring to in the context of Python. Regarding 7: This was mentioned somewhere else as well, but in a functional programming language you don't have to worry about the compiler recognizing and optimizing tail recursion. The only exception are pathological (extremely and obviously inefficient) function definitions, for example functions that try sum an infinite list of integers and don't terminate or functions that reverse a long list. [1] Terminology: "if" in C is a statement, and the ternary operator (a?b:c) is an expression similar to "if", but there is no true "if" expression.