Functional programming is not popular because it is weird (2016)
probablydance.com
probablydance.com
Therefore observing that a language or paradigm is popular or unpopular does not say anything about whether it is good or bad. JavaScript is the most popular language in the world. If Netscape had decided to use a Scheme-like language or a BASIC-like language it would still be the most popular language in the world. So paradigm has nothing to do with it.
Functional programming is less popular because no major successful platform has adopted a functional language as the preferred language.
I don't buy that functional languages are unpopular because they are unintuitive. Losts of stuff in JavaScript is highly unintuitive. It didn't prevent it from becoming the most popular language in the world. People will learn what they need to learn to get the job done.
That doesn't seem right. There are plenty of languages that are born from platforms, but I'm skeptical that its anywhere near the majority. Some platformless examples from the top of my head
- Java
- Rust
- Python
- C++
- Go
- Lua
In my opinion: C is not popular because of Unix. Unix is popular because it's written in C. The same is arguably true of Kubernetes. Neither docker nor k8s drove mainstream adoption of Go. Go did that with it's own properties, the same is true of Rust and C etc.
> Functional programming is less popular because no major successful platform has adopted a functional language as the preferred language.
The question here though is why?
The author argues because FP is unwieldy for the general software case (which I assume is enterprise CRUD apps). And I have to agree: State management is a huge part of CRUD apps.
------
EDIT: I am not presuming K8s is popular because of Go. The argument here is the success of the platform and choice of language are related, not directly consequential. K8s could have been written in rust and would probably be as popular because of its feature set
C++ was the easiest way to write kinda-OO code and use C libraries. Then it became de-facto standard for gamedev and desktop app programming.
In Linux world it's still the case that if you want to write a desktop app you should write it in C/C++. Use any other language and you will struggle against the dependency management and package managers forever.
Python is the only clear example of succeeding without a platform out of these languages, and the fact that after decades still Python is less popular than PHP shows that historic accidents and having a good platform is more important than any quality of the language.
On the other hand Perl is dead so maybe it's not as bad :)
This may be because Python lends itself to bring used in integrated environments such as Jupyter.
That's not how it got its popularity. Applets died very soon (the novelty lasted like 1-2 years), and feature-phone apps were never a big thing. Unlike e.g. mobile apps that quickly overtook the desktop, feature-phone J2ME apps at the time were a peanuts business (and there were other ways, like native APIs from Palm, Blackerry, and Windows mobile edition - of yore, not their post-iPhone smartphone OS).
Java, even back in 2000, was big in the enterprise space and remained so (which is why Sun quickly emphasized that and let the applet sdk languish).
I do agree in a very general sense, but there are definitely a few good options or there for desktop software.
For example, Lazarus/FreePascal is one of the best solutions for writing GUI apps even nowadays. It's a shame that it's dead as far as market share is concerned, even if it has a community around it, is open source and still receives regular updates with pretty good platform support.
Though i guess you could say the same about any technology stack that produces executables that are for the most part statically linked and don't complicate dependency management.
Of course, Java with Swing also hasn't disappeared anywhere and is still perfectly capable of producing most desktop software as long as there is any sort of a JDK or JRE on the device. There's also JavaFX/OpenJFX which is supposed to be more modern, but I've experienced more issues with it in comparison to Swing.
I would have avoided python if I could... But I can't avoid numpy/scipy etc etc
[+] to clarify, I was a somewhat experienced programmer, this was one of the final year projects, for distributed systems or parallel computing I believe. A simple system without doubt, but still, a system not a "hello world". Something that I wouldn't have expected to work on first try, had it been written in C.
But of course although it's the default in Java, and not provided out of the box in Python, I also wrote some web framework stuff that adds these runtime errors to Java (by using Reflection to make decisions at runtime instead) and I've seen Python code with more type safety that would tell you earlier that there's a problem. So it's ultimately not only a matter of programming language although that does definitely set the tone.
C++ was the preferred application development language on Windows.
Python is popular because of the numeric and ML ecosystem.
I'm still confused by the cycle of
- You can run Java applets in a web browser!
- Ew. Time to stop doing that.
- I've had a brilliant idea! We need a bytecode-based VM in the browser so a web page can run arbitrary code.
- I'll call it "WebAssembly".
I think that's backwards. I was there. Aside from a few e.g. banking and government applets one was forced to use and a couple of exceptions, applets never got anywhere and died fast.
Enterprise Java became what Java is all about very very soon. Java landed in 1996. Servlets/Tomcat landed in 1998.
By 2000 there was plenty of enterprise Java development - in fact that was the year MS created its own Java copy, in the form of C#/.NET after having tried to extend their version of Java for the Windows platform.
Hopefully it will start replacing C going forward but in that space Rust is competing with Zig and Nim.
Two languages almost nobody uses. It doesn't seem likely it's actually competing with them in that case.
I see this point come up often, but it doesn’t really line up with my own experiences. I find the explicit state control in functional languages makes it much easier to write enterprise CRUD.
Had UNIX been written in PL/I, Mesa or BLISS, those languages would be "C" today.
https://en.wikipedia.org/wiki/BLISS
https://www2.cs.arizona.edu/classes/cs520/spring06/bliss.pdf
Go got popular because it was a Google thing and everyone fanboyed hard over that.
Python was taught in many universities for its flat learning curve, then started to be used in academic research.
Even without an unrelated platform, like JS and the browser, they had something to catapult their adoption.
Python was used in academia long before it was used for teaching. “Courting” scientists was done from extremely early on in the language history (matrix-sig was founded in 1995) and the “extended” indexing added at their request (auto-tupling and slicing predate PEPs as those were introduced for and by Python 2).
Brendan Eich advanced a Scheme-like scripting language for Netscape, which got a facelift from Java’s popularity.
“Scheme was the bait I went for in joining Netscape. Previously, at SGI, Nick Thompson had turned me on to SICP.”
“The diktat from upper engineering management was that the language must “look like Java”.“
“I’m happy that I chose Scheme-ish first-class functions”
JS actually made a few functional concepts mainstream, such as first-class and anonymous functions, and callback patterns.
Source: https://brendaneich.com/page/5/
Python - which took perls lunch over a decade or so and now in more recent history became the obvious language when data scientists and ai/ml practitioners needed to adopt a platform. Its popularity has even led to it also expanding into education in a huge way after Python became the most popular scripting language.
Java - the jvm is an utterly amazing piece of engineering and yet still to this day most people still write java instead of the other better languages (kotlin, scala, clojure) hosted on that platform, and they deploy only to Linux environments. The platform with its write once run anywhere and industry leading garbage collector that gets better than 50% memory utilisation, isn’t the thing that attracts people - its the language.
Functional languages used to be the cool thing before object oriented design came along and unlocked the ability to build bigger systems. The ultimate programming language (lisp) has always encourages functional since what, the 60s?
Scala actually became MUCH more popular with Spark. I think the original point stands - the platform pulls the language.
Java is popular due to the jvm - it was the first jvm language after all. It got popular before others like scala & clojure managed to get off the ground, at all.
Python was hugely popular long before these were a thing. It was already seen as one of if not the most beginner friendly language at the beginning of the 2000s. It became the language of choice for ML because it was already popular with beginners not the other way round.
That's not how history played out at all. Java spent much of its life being pitched as a way to make C++ developers more productive.
The jvm was actively hated by many for years - slow startup times, excessive memory usage, it didn't used to enjoy the rich tooling ecosystem it has now and it was seen as opaque and hard to tweak for performance.
To this day it's not that hard to find Java devs who want off the hotspot jvm - whether that's to go onto other JVMs with different tradeoffs (Azul or whatever) or whether to go native compilation (GraalVM) instead.
However all the Java shops I hear about are also looking into Kotlin at some level of adoption. From all the new JVM languages Kotlin integrates the best with the existing Java environment and for displacing a language in an existing niche an easy upgrade path is the most important. So I think Kotlin will become an important language in the JVM ecosystem, it will just take a long time because these types of businesses are conservative in their tech choices.
I fail to think of any other platform that could run these monstrosity CRUD enterprise apps as fast as the JVM can. Sure, C++ can be written to utilize hardware better, but with all the classes and interfaces around with everything being virtual, a good JIT compiler can skip method lookups over AOT compiled languages.
While both of these things are true, they not connected in the way you imply, since Java is a pretty low level language by today's standards.
In the Java school of business app engineering, writing the code is rarely a big part of the effort, so it doesn't matter if the langugae is not very good or expressive. Java wins at having a big commodity-like labour pool of programmers, and there's a lot of inertia and stability in the platform.
There are of course a lot of people who use more expressive and creative tools in making business apps, like eg the many companies using Clojure, Scala, Ruby, Python etc for them, so it's not the only way to skin the cat.
On the JVM it is like trying to replace C on UNIX.
https://docs.microsoft.com/en-us/powerquery-m/
In fact, Excel itself can be considered a functional, reactive programming environment, and if we grant that, then it is the most popular programming language on the planet.
If whatever Netscape added as their scripting language had been too weird I think there is a good chance that a competing browser would’ve implemented a less weird language and that such a less weird language would have won over the hypothetical too weird language.
For me, Objective-C is too weird so I never bothered with it but I love Swift and only after Swift came out did I start making apps for iOS. And that’s even though I had been wanting to make mobile apps for iOS for a long time.
Nobody would use a language which wasn't supported by the browser with the largest market share, regardless of the merits of the language.
I would also say it is mainly due to Gmail and their G Maps extensive use of XMLHttpRequest, and Douglas Crockford insight on that JS has closure and function as first class citizen, it is just Scheme is C clothing. Check out his Little Javascripter. That and of course the intro of JSON.
«Insight» is like «RTFM»? :)
That is not an "insight". Anyone who had written an event handler in JavaScript would already know that.
One language where FP basically arrived in the mainstream is by the way Scala. And there are other newer language which allow for FP'ish programming like Kotlin.
But otherwise it's true, languages like Schema or Haskell will probably not arrive in the mainstream soon.
That's true for system and low-level application languages (Javascript is an exception, as for the web platform, there's no alternative so it's used for everything).
Perl, Python, Java didn't become succesful because of platform ties (yes, Java was made by a platform vendor, but most of its programmers used Windows and deployed on Linux or AIX, not Solaris).
WhatsApp backend is written in Erlang. A large part of the world's telecom infra is written in Erlang (2G, 3G, 4G, 5G). It powers the highest availability systems in the world. Still nobody cares about it....
That is a very popular perspective. In the past I have seen this sentiment used heavily by people who hate JavaScript as a means to rationalize how JavaScript could have become popular at all.
Unfortunately this sentiment is disqualified by data.
The browser became a popular platform in the late 90s and early 2000s as business and social interest on the web grew. JavaScript has been around cross-platform in a mostly consistent way since 1998 due to the publication of early current standards.
This did not make JavaScript popular though. JavaScript would not become popular for more than a decade later.
In 2008 a couple of things happened. Chrome launched offering the first JavaScript JIT. Before this JavaScript was hundreds of times slower than it is now. Also, jQuery started gaining popularity at this time which allowed employers to hire any dumbass off the street to write in it. Douglas Crockford also heavily evangelized the language for its expensive capabilities: functions as first class citizens, optional OOP (easily ignored/bypassed), native lexical scope.
A little after that around 2009/2010 Node.js launched and GitHub became common knowledge. It’s about this time that JavaScript exploded in popularity. The web was already popular for more than a decade before this.
> Functional programming is less popular because no major successful platform has adopted a functional language as the preferred language.
I would argue functional programming is incredibly popular but less common in the workplace because OOP conventions are what’s taught at school.
Your comment seems to support, rather than disprove, the claim.
JS was around for years without much success before the browser became a popular platform for applications. People used to only write applications for the server or for platforms like Java and Flash that were just embedded as plugins in the browser. They only started using JS after improvements to browsers made it a better platform than the alternatives.
Really, the reason Chrome invested heavily in optimizing Javascript was because it had started to be widely used in more than trivial scripts.
It would help if someone defined what constitutes "using functional programming".
I mean, this is functional programming:
function add(a, b) { return a + b }
From my experience, every developer does functional programming to a certain extent, it's just the extent that varies.Let’s try rewriting your example this way
add = function(a,b) { + a b }
Why is this better? Because + is a function, and add is an object, so you can then refactor the code to say this
add = +
Try doing that in C.
As I see it the code for some programs has a high degree of symmetry (ie repeated patterns), and the best language is a notation the let’s you capture that the best.
If you find yourself cutting, pasting and modifying things slightly each time, then your language is holding you back.
Test-script code is the best example of this…particularly hardware tests. You have the same code copied and pasted dozens of hundreds of times.
Thoreau once said that government is best that governs least. That language is best that makes you type the least.
Your example is strict FP, where a and b are fully evaluated before add() executes. There's also non-strict FP, where everything uses lazy evaluation by default. That has some big advantages but also a few pitfalls, and generally makes things significantly weirder when compared to imperative languages.
Pure FP is where you only have functions and expressions, no variables at all. I think that's where most mainstream programmers draw the line -- sometimes you just want to store something somewhere, without being forced to jump through what seem like weird hoops.
The original elevator pitch for Haskell was that it's a "non-strict, purely functional" language (aimed a unifying a bunch of different research languages, plus the proprietary language Miranda).
I think it rather supports the OPs claim.
I love Python as a language, but realistically it could just as well have been Ruby or some other language.
You're not wrong now, but that's just the chicken, the egg included web applications and its use as a scripting language.
Summer School computing classes even used to teach unit testing in Python.
And the Atlas HLT/DAQ build scripts were based on Python.
REM That is, "goto11"'s sibling comment https://news.ycombinator.com/item?id=28522359
It seems more likely there is a feedback between the popularity of a platform and it's anointed language.
Relations between Objects change, customers don't know what they want, change is always pain with OOP.
Now people say, composition over inheritance. Right, but isn't that the point of functional programming?
Functional programming maps to how I think. The most important questions is always: What data structures do I need? Get your data in order and the rest will flow naturally.
I don't use functional programming because I am a math nerd or something, I am not even good at math. I use is because it is composible and easy to understand. I can refactor without any fear of side effects.
Now is pure functional programming practical? For some tasks, sure but yeah not always. Imperative programming gets stuff done. Work with the strengths of both.
The thing you are not used to always feels weird, that is a problem with you not the thing you try to learn.
For me functional programming was just like a “missing link”. Wait, I can just code like I think, not in this weird statefull way? It’s just so practical for me. I can deliver 10x the value for 0.1 the effort. It’s just not fair, I know for a fact that there are more people with the same model of thinking, I just want them to feel as liberated
Same as you and GP.
I'd add that, for me, "functional programming at the edges" is sufficient: I don't need to go full straight jacket / Haskell-style to get a huge lot of the benefits of FP. I can still use an imperative outer shell and still have lots of functional parts in my code which are easy to reason about.
Can you? Like say you changed you sum() function to only sum every other element, because for the code you're working on right now that made sense. Well now you've screwed up the other places which relied on sum() summing all the elements.
Dumb example because you'd know better, but surely you can still shoot your foot off like that with FP?
And yeah I know that's not the side effects you were talking about, but when I'm changing my OOP stuff, that's the kind of side effect I'm most worried about.
I've never really used a proper functional language though, so it's certainly possible I'm unenlightened.
I would not change sum but just filter out every other element and feed that new list to the sum function. (If you need it often, write a new helper.)
Your sum function should not decide which elements should or should not be added, that is the callers job. It doesn't even have the context to decide on that.
So yes, I would have the guarantee that nothing would break because I did not change sum in the first place.
In practice you probably wouldn't even start with that sum function but simply implement a function that takes two numbers and adds them together. Then the caller can use higher order function like fold and filter to do whatever it needs.
(Of course we assume it is not literally adding two numbers but some complicated logic, otherwise don't even write a helper for it, just sum your stuff when you need it.)
And yes, you can still have logic bugs in functional programming languages and it can't really protect yourself from that when refactoring but I wanted to note how keeping your design simple and having easy to understand composable functions can help avoid bugs.
Of course, and I would do the same in my imperative program. A dumb example like I said.
> Then the caller can use higher order function like fold and filter to do whatever it needs.
Right but then at some point you have a chain of ten of these calls, and you have it _all over_, and so you figure hmm lets make a separate function out of that, code duplication is bad after all.
And then you find your new function has a bug and you need to change it... Will all the users of this new function be OK with that bugfix?
If you don't wrap up these chains into new functions, how do find all the places you need to change once you need to make that bugfix?
> having easy to understand composable functions can help avoid bugs
Sure this I get, which is why my imperative code also contains lots of "do one thing" methods that is used as Lego bricks. And I use a fair bit of functional-ish code, ala LINQ, where it makes sense.
I wish I had discovered functional programming at an earlier stage, where I had more time to experiment. I think it would be very informative to make two non-trivial feature-equal programs in either style so I could compare.
That's not a "side effect"[1] at all. It's a logical bug in the implementation that only affects the function's return value. You absolutely still have to worry about those sorts of bugs in FP, which is why you still need tests or a fancy enough type system to prove the bug cannot happen (for your example that is unlikely to be practical).
"Without any fear of side effects" doesn't mean you don't have to fear anything at all, but it's one less thing to worry about.
[1]: https://en.wikipedia.org/wiki/Side_effect_(computer_science)
Yes, like I said. Point is, in my imperative OOP code, I'm relatively seldom worried about actual side effects, and more worried about introducing bugs due to me not fully grasping all the interactions of the code I'm changing.
Did someone somewhere make an assumption about how this code works (sums all numbers), and am I breaking that assumption when I'm making this change (summing every other number)?
In the example you just gave (summing over a list of numbers), in almost all programming languages you'd resort to runtime verification instead, i.e. tests, just because it's so much more practical. But if you have a PFP language, you still have the confidence that the behaviour of the inputs and outputs is the only thing you have to test, since the function cannot do anything else.* Even in a non-pure FP language, you know that it cannot mutate other variables, although it might have side effects.
* Ok, it might loop forever or crash, but that's comparatively rare.
There's a certain kind of personality that really gets into that (the 'architecture astronaut'), specifically because it is counterintuitive and they like convoluted things. I think the idea that really gets them going is "look, it knows to XYZ itself!"
I do scientific programming, and most of the time, functional style makes way more sense. Doesn't stop someone with the OOP bug from turning all the logic inside out and making a brittle mess, though.
What OOP gives is a way to encapsulate some given part of a program, and make it a working little building block you can later reuse. It can contain mutable state, but is promised to be correct as only that class can touch it, it can be used as a slightly different version of a common interface, etc.
The two are not mutually exclusive at all.
‘Take the pan out of the over once it’s been in for 30min, where the pan contains a folded together eggs and previously mixed flour and sugar’ isn’t more FP than ‘Mix the flour with the sugar, then combine with eggs, then fold together, then put it into the oven, then take out after 30min.’ But people new to it so often think it’s an FP thing for no good reason. It’s a new user issue that ought to be totally fixable as a community.
FP has enough great ideas that I’d recommend everyone learn pure functional solutions just to put into their tool belt, but it’s absolutely true that getting up to speed is harder than it needs to be. My hot take: it won’t be really mainstream until someone figures out how to make dependent types really ergonomic, which seems a long way away.
1. I've never seen the "pipe" notation you talk about in any lang except bash, which is not an FP right? In which languages does "|" denote function application?
2. Aren't dependent types orthogonal to whether a language is functional? Just to make sure we're on the same page on what dependent types mean, I wanna give a example (a contrived one, sorry) of dependent types in Python.
from typing import overload, Literal
@overload
def str_if_zero(num: Literal[0]) -> str: ...
@overload
def str_if_zero(num: int) -> int: ...
@overload
def str_if_zero(num: int) -> int | str:
if num == 0:
return "That's a zero"
return numYou're correct and I think that's kinda the parent's point. I think what the OP is saying is that the much of the syntax ergonomics are orthogonal to FP, which means they don't have to be as weird/frustrating/unusual/insert preference/unergonomic as they are.
A good example is how piping vs nesting are different syntaxes for function composition: x|A|B|C vs C(B(A(x)))
The former might feel more comfortable, familiar, and left-to-right readable for someone new to FP. That said I think a few language do use piping. F# has |> and I think Clojure used >>> maybe? -- signed, humble java programmer.
https://stackoverflow.com/questions/2177110/in-f-what-does-p...
See e.g. https://shuangrimu.com/posts/language-agnostic-intro-to-depe...
h(x).f().g()
Is also very common and I would say it counts, especially if the language support something like extension methods.2. Yes that's what I mean by dependent types, where types depend on values, like f(0): string and f(1): int. And yes, they totally are orthogonal. However, pure functional programming tends to lean on its type system more than other styles, in my experience. But then without dependent types, something as simple as loading a CSV gets hairy relative to something like pandas's pd.read_csv. Maybe others can go into why pure FP and static typing go so much together, but I gotta get back to work.
The example you gave of Python doesn't have tagged union. I really don't know much at all about Python so maybe the type checker can tell you that you're handling all the cases, but with tagged union types you can have to handle every possible output of `str_if_zero` when calling it. That doesn't have to be an FP thing but I only find it when doing Elm and Haskell in my experience.
[0] https://elm-lang.org/docs/syntax#operators
(-> x f g h ...)
[0] https://www.cs.cornell.edu/courses/cs3110/2019sp/textbook/ho...
[1] https://tour.dlang.org/tour/en/gems/uniform-function-call-sy...
[0] https://flix.dev/principles/
[1] https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax?w...
Every C-like language should copy this idea, imo.
Would have absolutely loved it in Go, where over the course of time I had to build up dozens if not hundreds of validators on strings, maps, etc. Ended up resorting to storing them in `companyInitials-dataType` packages, EG `abcStrings.VerifyNameIsCJK()`, but the unified call syntax would be super tidy.
https://hackage.haskell.org/package/flow-1.0.22/docs/Flow.ht...
Oddly enough, the language that put the most thought into this problem is Perl, which provided the pronoun variables to represent "whatever I just computed".
Natural languages always offer multiple strategies for ordering the various parts of a sentence, because the order in which things are mentioned is critical for several purposes, from making it easier for the listener to follow a chain of thoughts to building or defusing tension or correctly positioning the punchline of a joke.
It's not at all surprising that perl put thought into things like this, the entire language concept optimises for elegance of expression.
I think haskell doesn't always look super readable, but I can appreciate it at least because it reminds me of math papers where the final theorem is introduced first, and then it's broken into lemmas, and each of those lemmas is proved etc
Completely agree! I got my first taste of FP writing unreadable nests of python list comprehensions and it's shaped my Java tremendously. I've never spent much time in FP dedicated languages, but it's as good a frame to understand as OOP. I find they even mix and match well.
myhashmap.entrySet()
.stream()
.map(Map.Entry::getValue)
.sorted()
.distinct()
.toList()1. can't be extended.
myhashmap.entrySet()
.stream()
.flattenBars()
.toFoo()
2. (As a result of 1) - applies only to streamsWhy Isn't Functional Programming the Norm? (https://youtu.be/QyJZzq0v7Z4)
Here's the HN thread- https://news.ycombinator.com/item?id=21280429
I learned in that talk, among other things that Oracle spent $500 million to promote and market Java.
- https://www.theregister.com/2003/06/09/sun_preps_500m_java_b...
- https://www.wsj.com/articles/SB105510454649518400
- https://www.techspot.com/community/topics/sun-preps-500m-jav...
Functional programming isn't the norm because — while it's extremely good at describing "what things are and how to describe relationships of actions on them" — it sucks at "describing what things do and describing their relationships to each other". Imperative programming has exactly the opposite balance.
I find the former to just be more valuable and applicable in 80% of real world business cases, as well as being easier to reason about.
Entity Relationship Diagrams for example are an extremely unnatural match to FP in my eyes, and they're my prime tool to model requirements engineering. Code in FP isn't structured around entities, it's structured in terms of flow. That's both a bug as well as a feature, depending on what you're working on.
Most of the external, real world out there is impure. External services, internal services, time. Same thing for anything that naturally has side effects.
If I ask an imperative programmer to turn me on three LEDs after each other, they're like: Sure, boss!
for led in range(3): led.turn_on(); time.sleep 1; led.turn_off()
If I ask an FP guy to turn me on three LEDs after each other, first they question whether that's a good idea in the first place and then they're like... "oh, because time is external to our pure little world, first we need a monad." Whoa, get me outta here!
Obviously with a healthy dose of sarcasm.
Don't get me wrong, for the cases where it makes sense, I use a purely functional language every day: it's called SQL and it's awesome despite looking like FORTRAN 77. I also really like my occasional functional constructs in manipulating sequences and streams.
But for the heavy lifting? Sure give me something that's as impure and practical as all of the rest of the world out there. I'll be done before the FP connaisseur has managed to adapt her elegant one-liner to that dirty, dirty world out there.
This is the equivalent Haskell.
turnOnThreeLEDs = for_ [1..3] (\i ->
do
LEDTurnOn
threadDelay (10^6)
LEDTurnOff
)
or all in one line for_ [1..3] (\i -> do { LEDTurnOff; threadDelay (10^6); LEDTurnOff })
It looks basically the same.EDIT: I would also strongly dispute the idea that FP is structured around flow instead of data structures. In fact I'd say that FP tries to reduce everything to data structures (this is most prominently found in the rhetoric in the Clojure community but it exists to varying degrees among all FP languages). Nor is SQL an FP language (the differences between logic programming a la Prolog and ultimately therefore SQL and FP is very very different).
FP's biggest drawback is that to really buy into it, you pretty much need a GC. That also puts an attendant performance cap on how fast your FP code can be. So if you really need blazing fast performance, you at least need some imperative core somewhere (although if you prefer to code in a mainly FP style, you can mainly get around this by structuring your app around a single mutable data structure and wrapping everything else in an FP layer around it).
If those are bugs, I'd forgive that. If those AREN'T bugs, then keep me far, far away from FP!
Also, what is the point of i? Clearly, each LED should have its own index, but then i is never used again. (I understand this could be pseudocode or there's a lot of other code not included.)
And the ranges are inclusive in Haskell? I feel like a lot of friction between Matlab and Python involves how each language's indexing/slicing/ranges are represented, so it's interesting to see each language's approach (indenting like Python, lower camel case, delays in us, etc.) --- but with every language difference, I'm personally less inclined to want to learn something new without a great reason.
a proper FP engineer would model the problem of turning on LEDs one ofter another as a set of states. A simple way would be a bit set of the LEDs, in an array where each element is the LED's on/off state, like ['000', '100', '110', '111'].
Then, the problem decomposes into two, simpler problems: 1) how to create the above representation, and 2) how to turn the above representation into a set of instructions to pipe into hardware (e.g., send signals down a serial cable).
The latter problem is imperative by nature, but the former - that of the representation of states, is very pure by design! So the FP model provides a solution that solves a bigger, more general problem of turning LEDs into patterns, and this solution is just one instance of a pattern.
So if your boss asks you in the future to switch the bit patterns to be odd/even (like flashing christmas lights), you can do it in 1 second, where as the imperative version will struggle to encode that in a for-loop.
A bad C programmer will write horrible spaghetti code, but it will probably be enough to do the job. A bad Haskell programmer will get absolutely nowhere.
If you think pure FP is great for this stuff, I think you need to explain why imperative languages are regularly used to win the ICFP programming contest (https://en.wikipedia.org/wiki/ICFP_Programming_Contest#Prize...).
I guess you are talking about embedded so I'll concentrate in the LED example. In embedded code size and performance matter, so you try to be as straightforward as you can be. And I think applying "your boss might ask you in the future" to every piece of code is what drives some development far beyond the complex point.
Should I spend a week creating a super-complex infrastructure for turning on/off some LEDs just in case my boss asks my to change the pattern? Should I spend a week thinking the right code-pattern, or trying to "solve a bigger, more general problem"? It's just 3 LEDs blinking... just write the damn for-loop!
At the end of the day, my microcontroller only "digests" sequential instructions. So the simplest thing (for embedded) is to think and feed the microcontroller with sequential instructions. All the rest is just ergonomics for the sake of programmer's comfort or taste.
I'll do the sequence. If my boss asks my to change the sequence, I'll change the sequence. It's not a big deal.
I don't know if in this case one would "struggle" modifying this particular for-loop. And I can think at least 3 five-minutes solutions in C that doesn't require FP to structure a program to quickly change the pattern if required.
The beauty of modern programming is that we don't have to stick to a pure example of either paradigm. We can use FP techniques where it makes sense and turn to imperative otherwise.
In you example, we could have a nice, purely functional model of an LED that enforces the invariants that make sense. We could then "dispatch" the updated led entity to an imperative shell that actually took the action. All without using the M-word!
I'm probably - unfairly - treating your example more seriously than you intended it, but I think I'm leading to the same conclusion as you at a slightly different place. I want to have a purely functional domain that I wrap in an imperative shell. Trying to model side-effects in a purely functional manner using something like applicative functors just doesn't give the productivity boost that I want.
> I use a purely functional language every day: it's called SQL
This is my favourite way to annoy FP advocates (despite probably being one myself). Every one is a closet mathematician in FP-land but no one wants to admit how beautiful relational algebras are.
Funtional Reactive is a very good way to create that mix. Web Front developers have realized that, and that's the reason why most modern frameworks have been veering towards this model slowly, with Promises and Observers everywhere.
When you represent state as an asynchronous stream that you process with pure functional methods, you get a straightforward model with the best of both paradigms.
Like you, I too think the ML-side of functionnal programming got it right. Sadly, their most popular language commited the unforgivable sin of not being written by Americans and is therefore condamned to never be as popular as Haskell. I console myself by using F# when I can.
Most imperative languages since Fortran contain declarative elements, otherwise we'd be adding numbers with side-effects. Similarly most FP languages offer imperative programming. But the real power from FP comes from it's restrictions and yes, query languages are one such (excellent) application. Config languages and contract languages are others.
A pixel array can be trivially modelled as a pure datastructure and then you can use the whole corpus of transformations which are the bread and butter of FP.
A screen is as IO as it comes for the most average consumers of a screen, we aren't peeking into its internals.
And for me, that's the point of FP - it's not that IO is to be avoided, it's about finding ways of separating your IO from the core logic. I loosely see the monad (as used in industry) as a formalised and more generic "functional core imperative shell"
Now when it comes to pure FP languages, they keep you honest and guide you along this paradigm. That said, it's perfectly possible to write very impure imperative Haskell - I've seen it with my own eyes in some of the biggest proprietary Haskell codebases
But imperative languages don't generally help you in the same way, if you want to do functional core imperative shell, you need a tonne of discipline and a predefined team consensus to commit to this
It was. I still remember the days.
It was nice to be able to put pixels on the screen by poking at a 2D array directly. It simplified so much. Unfortunately, it turned out that our CPUs aren't as fast as we'd like them at this task, said array having 10^5, and then 10^6 cells - and architecture evolved in a way that exposes complex processing API for high-level operations, where the good ol' PutPixel() is one of the most expensive ones.
It's definitely a win for complex 3D games / applications, but if all you want is to draw some pixels on the screen, and think in pixels, it's not so easy these days.
Functional programming is a good way to describe how a system works. By describe the input, the processing, the output. You describe the whole diagram.
However, in reality. People just sucks at thinking systematically. It's likely that the whole education system never taught you about how to do it.
Everyone was taught to do things step by step and smash the results together to see if it works instead of prepare everything before doing any actual work most of time. And if it actually need preparing, there is usually a pre made checklist for you to do it easily.
That type of thinking process isn't that common in our daily live. And of course no one is used to it.
But I think people should at least do it once. Even you are still programming interpretively afterwards. It could benefits you very much and make you a better programmer.
In imperative you can just ignore it and produce objectively worse code, as you are not even aware of all side effects possible. And sure, for the LED project it wouldn't even matter, but the decision FP vs imperative is then more of a design / quality criterion in general - the notion of one being better than the other is just wrong.
Also a monad is much more complicated if you don't really understand it which makes judging it a bit unfair
Has this been done before?
So your LED function in pseudocode looks like
ToggleLeds(leds, t):
for each LED
LED.power = (LED.start + 1s) > t ? ON : OFF
And this is invoked from main() as follows main():
ToggleLeds(this.LEDs, Events.Time)
Where Events.Time is some kind of event stream which allows the runtime to reevaluate main() and any other dependent functions each time it's updatededit: And to sidestep the obvious performance issue with the function being reevaluated every few microseconds :D you would implement something like this
main():
ToggleLeds(this.LEDs, Events.Time(ms=1000))C++ came on the same package as C compilers, some of which it was a compiler switch away.
Both were picked by OS vendors that tried to cater on top of UNIX clones.
Yep, zero marketing.
And yes, it was, though in the intervening years they've improved the language a lot IMHO.
Gabriel Gonzalez – How to market Haskell to a mainstream programmer - https://youtu.be/fNpsgTIpODA
Even in a "functional" task (e.g. compiling) sometimes you just want an imperative algorithm. And while Haskell allows this via state monads, they're clunky.
Meanwhile there's practically no tradeoff to writing functional code in an imperative language. In fact there's lots of functional code in most C++/Java/Python programs. Most "imperative" languages are actually hybrid functional and imperative, supporting "functional" constructs like lambdas and pattern matching.
99% of the time a functional program will get executed sequential and strict anyways. The other 1% of the time, in an imperative language you can explicitly encode functionality (e.g. generic stream operators which work concurrently and on lazy streams).
That being said, I often end up arguing with 'pure functional programmers' at work - they can be incredibly hard to work with and married to the idea that _everything needs to be functional_ because its their preference, even though your average college student is going to be able to pick up and iterate on code that just uses a damn for loop or whatnot.
I _like_ functional, I find it incredibly useful, but like everything else, its just a tool to solve a problem and there are tradeoffs.
But some of the most annoying and must-do-things-academically-because-look-how-cool-monads-are this-is-the-only-proper-way people are very vocal about functional, which is a pretty big turnoff IMO.
Haskell is a good example. There are totally cases where Haskell will make your life easier. But if you're trying to reinvent the wheel and redefine our architecture by injecting haskell everywhere you can because _you like it_, that drives me absolutely bonkers.
I would have to disagree with you there. It partly depends on what you mean, but I quickly give up on functional solutions in, say, Javascript (despite Javascript technically having all the bits I'd need!) just because of the lack of immutable data structures and cheap copying. Not to mention the constant pain point of asking "does this modify in place, or does it return a new" when dealing even with the standard library, let alone -user- code. I either need a library like Immutable.js, or I need to jump through hoops that are both not ergonomic, AND have performance penalties, or I embrace mutation (and at that point frequently rule out a fully recursive approach because the bookkeeping involved far outweighs any benefit).
Writing functional, immutable style in Python is handy but can be maddening when you never know what to expect from libraries.
I don’t see how this is a pain point, JS is pass-by-value for primitives and pass-by-reference objects. Ergonomics, sure, but isn’t the lack of pattern matching a much bigger obstacle the lack of immutable semantic sugar?
Immutability is easy to gain, though, just make your variable object writable: false.
Eh, I think it has more to do with tooling and framework support.
Imagine if the world were flipped and C++/Java/Python had zero IDE support, slow compilers, and weird/unreliable build and package management tooling, and only third-party bindings to any platform or framework. And Haskell had incredible IDE support, a fast compiler, great build and package management tooling, and official support for stuff everywhere you wouldn't have to think twice about using in a commercial context.
You'd use Haskell.
Another is that in a pure language, you can look at a function and know for a fact that nothing you're seeing can ever be mutated. This eliminates a massive source of cognitive load when trying to read and understand code, as well as eliminating all sorts of bugs related to the program being in the wrong state. If you're trying to write functional code in a language not designed for it, you're missing out on those benefits.
Now, it's absolutely the case that the tradeoff may be worth making depending on the person and circumstance. But it does exist.
When you write functional code in imperative languages, these constraints aren't enforced by language, but they should be enforced by convention and code review.
Now using Rust, I think I'm even more functional than ever. One key thing is that I can put a simple loop here and there (inside functions) and still the interfaces are functional.
I’m surprised you are finding Rust good for functional programming to be honest. It lacks a do-notation system, currying by default and guaranteed tail-call optimisation.
This article is pretty terrible. There isn't even a functional implementation, with just a half hearted attempt to even understand how to do it.
Functional languages excel at state machines. The example is pretty terrible too with regards to imperative programming being a good fit, because literally no one enjoys the real life statefulness and mutation when cooking. If you mess up, there's no going back. Why would you want to embrace or repeat that feature in your implementation? Functional state machines just pass explicit state around instead of mutating some random collection of variables.
And god, those C++ examples make me nauseous.
It's just another tool though and there are many places where it's not appropriate. It's also a personal taste thing, I get that. It's worth persisting with it on a deeper level though just for the sheer joy of those 'a-ha' moments that come. Even if you don't use it in your day job, just bending your brain with different programming paradigms is well worth it.
Firstly learning ANY new paradigm is inherently brain bending. By definition right? If you didn't know logic programming or oop you'd have to bend that brain.
Secondly, using FP day-to-day doesn't require any deep insight. You can use various tools, monads and whatnot, and not think too hard about it.
However, when you really grok the paradigm you will get those a ha moments. The same is true of other paradigms too, but there is a particular satisfaction that comes with deep understanding in this domain that I can't explain. It's kind of beautiful to a certain mind I guess...
Just try to implement recursion in MS 8-bit BASIC.
Or maybe it was algol
I'm also not sure the example in TFA is much of an argument. From a glance looks like the programmer got entangled in their own cleverness, but the slice we are shown is too small to show if there's some external reason to do it like that.
story time: once did a compile time parser generator with c++ templates. "Zero-cost" abstraction and all that jazz. Turns out the binary was so large that a type-erased vtable system ran faster, for all its nonzero cost.
Templates are orthogonal to functional or imperative. It's a code generator. Understand what you generate!
I didn't do anything fancy like compile-time parser generation. it was overuse of the standard library features, like `std::variant`.
It was a long time ago and in C++, which is a touch trickier than Java or C#, but "traditional" programming isn't particularly simpler. It's just what's taught in school and what all the examples you google show. It's a self fulfilling prophecy.
Procedural vs functional? Yeah, the former is more intuitive to a beginner. But as soon as you go a little further than that, it's basically all about what you've been exposed to.
They struggled to learn pointers, which makes sense to me. They also struggled to learn OO. And they struggled to learn recursion and state machines. All this stuff seems so simple once you've internalized it.
I think there's something real to the idea that humans have a hardwired instinct for stories. And that makes learning imperative programming easier. But you move beyond your intuitive instincts pretty fast. And when you do, it really matters how well your language or environment helps you to think.
The problem with C++ templates is simply that they aren't very good language for thinking in. They're a mashup of functional concepts in an imperative language with bad syntax. But functional programming doesn't have to be done badly. For a comparison, look at spreadsheets. They're arguably the most popular programming languages in the world. And they're purely functional.
There weren't issues with it. If functional programming was introduced earlier, people would have less trouble learning it.
Meanwhile Stanford does an analogous class in Python. And all of those alumni go on to define the industry trends.
Is there evidence/studies about this?
Is this a fact? I was taught scheme at age 12 and in four weeks I had a working compiler. And I have heard that a team taught middle schoolers erlang and got them writing a chat network in 2 days.
Functional programming is popular because it's intuitive!
Perhaps "immanent" or "diagrammatic" is a better word than functional. I mean that a function _is_ what it does, but not so with a procedure. One cannot bake a value as one does a cake!
See React's dominance. See the usefulness of declarative state machines. See async/await.
Functional programming gets a bad rep. People often fail to see the functional elements in more imperative code and the imperative elements of more functional code. In fact very few language constructs are necessary to open up 99% of functional possibilities in imperative languages (functions as values) or imperative possibilities in functional code (do-notation). Any critique that dismisses one or the other misses the point.
May add more explanation later.
I suppose it really depends on what you've learned first. Myself, I started programming (C++) in my early teens, a bit before I was introduced to the concept of a function in math classes. I didn't have much problems with that part of math, but I knew something doesn't feel right about it. Only many years later I realized that I never fully internalized the concept of mathematical function being a relationship and not a procedure. And I had the same issue with "=" symbol. After being exposed to the concept of assigning values to variables in imperative programming, it took me years before my brain fully internalized the concept of equivalence relationship.
How many times have you heard these two statements from the same individual?
“I don’t need all those functional features”
“I don’t need a functional language because X has taken all the important functional features from language Y”
That’s more useful long-term.
The WISV-IV GAI tests for verbal comprehension and perceptual reasoning. It provides an estimate of general intellectual ability, with less emphasis on working memory and processing speed. My GAI is 124. Which means I'm fairly intelligent (sic. "superior") when it comes to understanding language and reasoning. It makes me a fairly good problem solver. I'm an ok programmer (judged against the hundreds of programmers I've paired with through my consulting work).
That said, I've tried to learn Haskell three times and given up each time. The functional model of D3js kicks my ass. I cannot grok LISP.
It turns out my WMI (working memory index from Wechsler Adult Intelligence Scale) is BELOW AVERAGE. This makes holding lots of recursion or abstraction in my head at once quite difficult. I also suffer from adult ADHD.
I love long functions in a procedural style. I'm quite proficient working that way. There's not too much abstraction and behavior hiding (why I grew to hate OO). You can see how my neurological makeup impacts what "good" code looks like. I also understand that James Gosling has a kind of synesthesia and that impacts the structure of his code to the point of causing problems for others (see Lex Friedman interview).
I said all that to say this. What makes code understandable/readable/clean is in the eye of the beholder. There's quite a bit of neurodivergence in our field and we need to account for that when deciding on how to critique code. After all shouldn't code be written first and foremost for humans to understand and only incidentally for compilers? (paraphrased from SICP)
My son (11yo) is autism spectrum and recently received a WISV-IV evaluation and found him off the charts for spacial reasoning. It immediately had me thinking about object oriented programming and conceptualizing digital processes spatially.
Perhaps functional patterns are preferred by those with faster "processing speed" of thought or "working memory" – basically thinking primarily of "just before" and "just after".
My anecdotal experience has shown that the coworkers who think "the fastest" prefer functional patterns, whereas those who think slower but more comprehensively prefer object oriented.
Also anecdotally, functional programmers rarely leave the keyboard, whereas OOP spend a lot of time on the mouse – both are equally productive at the end of the day.
I think you are entirely correct, FP is both more suitable to certain type of people and to certain types of problems.
I've used FP in some situations and loved it, and had to grapple with FP code that was utterly incomprehensible because the person that produced it seemed more interested in showing off what he could do with FP rather than actually getting the job done (or producing efficient code, for that matter).
Yep that's it! It suits my brain quite well but I totally get that it doesn't suit everyone's. This sort of point is under-appreciated, not just with FP, but with so many things in computing and life more generally.
When you look at programming as a form of communication, it's easier to understand a lot of things. You can't build your tower of Babel with thousands of people if you all talk different languages in different ways, you need something that can be understood easily by everyone. But that's also not the language you would use when writing something deeply personal.
Sure, you can fake all that with other constructs or patterns. But then it really does feel more like a puzzle.
IMO, good FP is good when it is maximally declarative and organized in the linear way in which humans reason best.
Maybe the author should try F# or OCaml instead of Haskell! I love FP and have also tried and hated Haskell three or four times.
I found that I could translate Python to Haskell using Codex and get more real working Haskell code far, far quicker than I could manually.
I think Codex will be a simply incredible tool for learning new programming languages, could definitely make FP easier. It may not be super "smart", but it's smart enough, it knows a ton, and it's got superhuman recall.
Another idea is that you could filter Codex suggestions for those that pass the type-checker, and keep regenerating until it passes.
It's so incredibly fun. It made me want to never stop coding.
not
Functional Programming Is Not Popular-Because-It-Is-Weird.
I just found the ambiguity amusing, in the title of this particular blog post.
I find it’s not the most straightforward langauge for expressing precise thoughts and logic…
“ The Dog Walking Ordinance *
The following transcript of a Borough Council meeting in England illustrates the difficulties of expressing a simple idea in precise and unambiguous language.
From the Minutes of a Borough Council Meeting:
Councillor Trafford took exception to the proposed notice at the entrance of South Park: "No dogs must be brought to this Park except on a lead." He pointed out that this order would not prevent an owner from releasing his pets, or pet, from a lead when once safely inside the Park. The Chairman (Colonel Vine): What alternative wording would you propose, Councillor? Councillor Trafford: "Dogs are not allowed in this Park without leads." Councillor Hogg: Mr. Chairman, I object. The order should be addressed to the owners, not to the dogs. Councillor Trafford: That is a nice point. Very well then: "Owners of dogs are not allowed in this Park unless they keep them on leads." Councillor Hogg: Mr. Chairman, I object. Strictly speaking, this would prevent me as a dog-owner from leaving my dog in the back-garden at home and walking with Mrs. Hogg across the Park. Councillor Trafford: Mr. Chairman, I suggest that our legalistic friend be asked to redraft the notice himself. Councillor Hogg: Mr. Chairman, since Councillor Trafford finds it so difficult to improve on my original wording, I accept. "Nobody without his dog on a lead is allowed in this Park." Councillor Trafford: Mr. Chairman, I object. Strictly speaking, this notice would prevent me, as a citizen, who owns no dog, from walking in the Park without first acquiring one. Councillor Hogg (with some warmth): Very simply, then: "Dogs must be led in this Park." Councillor Trafford: Mr. Chairman, I object: this reads as if it were a general injunction to the Borough to lead their dogs into the Park. Councillor Hogg interposed a remark for which he was called to order; upon his withdrawing it, it was directed to be expunged from the Minutes. The Chairman: Councillor Trafford, Councillor Hogg has had three tries; you have had only two . . . . Councillor Trafford: "All dogs must be kept on leads in this Park." The Chairman: I see Councillor Hogg rising quite rightly to raise another objection. May I anticipate him with another amendment: "All dogs in this Park must be kept on the lead."
This draft was put to the vote and carried unanimously, with two abstentions. ”
For simple loops, I prefer something like map/filter/fold when appropriate. I try to avoid chaining them, because that often turns the code impossible to understand and makes me feel stupid. If the loop can't be modeled naturally as a higher-order function, an iterator-based loop is easier to read than an index-based loop, which is better than tail recursion.
With complex loops, there is no way around concrete loops with comments describing the invariants and what each part of the iteration does.
Many problems can be modeled naturally with recursion. Such problems are particularly common with graphs. However, you should never use explicit language-level recursion unless you can guarantee that recursion depth will be small. It's easy to get a stack overflow, so recursion should usually mean a loop with an explicit stack of states.
nums.iter().map(|x| if x > 10 break(?))
This little thing is the major problem of functional chains (this is only a toy version of the issue). Because lambdas are inside the scope is hard to make them truly part of the current execution flow.So, sometimes I change a chain of iterators to simpler loops for things like this. IF iterators where part of current scope, it will look far more natural, IMHO...
I've grown to sometimes enjoy how a recursive loop looks in code. Sometimes you put the end case first, sometimes you put the end case last, depending on which makes most sense while writing (or perhaps while editing while reading later).
With languages with loops and no pattern matching, I'll always write a loop as a loop, if I used one with both, I dunno, there's probably a few loops I'd do as recursion and most would be loops.
OTOH, Erlang is certainly functional, but it's very pragmatic as well, there's not a mention of monads, and one tends not to do a whole lot of weird stuff with closures (although you can). Sure, there's a good number of functions that take functions as arguements (which you can do in C anyway), and there are anonymous functions, but you get better error reporting if you use functions with names, so at least I tend to name anything with more than a few lines. I also tend to have very short functions, but that is somewhat influenced by stack traces only having function names and not line numbers when I started 10 years ago.
I think it's really just a matter of prolonged exposure to make recursion seem intuitive.
So FP might have a theoretical advantage as a building block of low-level software systems that are not throwaway labor (that is: only valuable in specific historical window / context).
In any case the factors driving programming language popularity have changed dramatically ever since the developer universe got truly "connected". There is a winner-take-all network mechanic that basically inflates whatever initial advantage into a (difficult to explain ex-post) catholic dominance.
Unsurprisingly when I learned functional programming I found that it was a great match for the way I think, or at least the big picture way I approach programming. I doubt it's how all programmers think, but it's definitely the way I do.
And if I had to keep one sentence from it: mutable states are very useful.
FP addicts consider mutable states to be something to be banished at all cost, much like a generation of 70's academics tried to do away with goto's.
The fact is, there are lots of situations in the real world where using mutable state variable is the very best way to model the problem:
- easier to understand
- faster to execute
The same holds for goto's btw.Disclaimer: tongue in cheek
C++ is gaining more and more functional features, and this is quickly becoming the predominant style.
Most go programming I've seen is like python where you are slinging around maps that are getting modified all over the place. Very antithetical to functional programming.
JavaScript isn’t a functional language, neither are Rust nor Go (and Go in general is probably one that the least bets on functional patterns all around), they all just provide constructs from functional languages like mapping arrays.
warning: manual implementation of `Option::map`
--> src/viewer/regionindex.rs:265:13
|
265 | / match vlinkopt {
266 | | Some(vlink) => Some((*vlink).clone()),
267 | | None => None
268 | | }
| |_____________^ help: try this: `vlinkopt.map(|vlink| (*vlink).clone())`
|
note: `#[warn(clippy::manual_map)]` on by default
for further information visit
https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
It wants a map and a closure used on a single value, to replace a simple conditional. I am told that the map and closure will all optimize out, but I haven't looked at the generated code.That being said, once you get used to it it is extremely convenient to just map over any sort of "container" and focus on business logic. The above concept even extends to async programming. You can do things like iterating over a Future and let the runtime handle waiting for the value. Functions returning a Future or a Promise can be composed just like you would iterate over a list.
asyncDbOperationGetUser(userId).flatMap(user => asyncDbOperationGetPosts(user.friends))
There are of course different ways of achieving this (async/await) but the power of learning FP concepts is that you can apply the same fundamentals to almost everything that has a defined notion of iteration (--> is a monad).Hence, calling .map() actually makes the intent of the code more clear.
Remember those light bulbs that went off when you finally incremented an index variable at the right moment, or when you finally figured out the right condition for a while loop? Clearly all styles are puzzle-like when you are first learning them.
I think functional programming has more or less the same learning curve as other styles.
In my view, the reason functional programming isn't more popular is because of Comp Sci curricula.
There are plenty of institutions where there may be one course on functional programming (if you're lucky). Then, many students seem to (incorrectly) internalize the idea that anything their degree doesn't cover extensively must be of lesser importance.
Functional Programming Is Not Popular Because It Is Weird - https://news.ycombinator.com/item?id=11188570 - Feb 2016 (75 comments)
- the idea that readability is not the first thing to pursue
- the idea that macros are great
- the idea that leaving performance off the table is OK
- the idea that objects and subtyping are useless
- the idea that mutability is hard to reason about
- the idea that recursion is super important
- the idea that somehow closures are more readable than objects
- the idea that tuple are somehow better than structs with named fields
It is a style that thrive _in opposition_ to the style of useful programs out there: readables, no macros, fast, with an UI, with mutability, with objects, with names.
But let's take macros: yes, as an average application developer, you should probably avoid macros. But for some problems they're a way better tool than alternatives like runtime reflection or code generation. For instance I'm happy to use macros-based libraries that automatically derive JSON codecs. Unless you're a library developer, macros and their inherent complexity (outside of the Lisp family I guess) aren't an issue in FP languages that I know of.
For example, this Common Lisp cheat sheets covers a large part of the language and isnt hard to follow
https://github.com/ashok-khanna/lisp-notes
I was doing some Haskell and Racket and I found basics not too hard to learn, but I did then too hard to master, but I think thats to do more with the complexities of the problems I tried to solve and less to do with the languages themselves
We get small, specialized fragments of this in Rust's borrow-checker and TypeScript's control-flow analysis, but I'm not aware of something that tries to take the whole of IO-monads and reshape them into a familiar format.
do
putStr "What is your first name? "
first <- getLine
putStr "And your last name? "
last <- getLine
let full = first ++ " " ++ last
putStrLn ("Pleased to meet you, " ++ full ++ "!")
(see: https://en.m.wikibooks.org/wiki/Haskell/do_notation )It became like, "No need to go over there, you can do most of it here, in a language you already know."
As for OOP, I think many people have found ways to circumvent or minimize how much they use it, so are not going over the edge with inheritance and another kind of spaghetti. If they can use a struct, record (Delphi/Free Pascal also have advanced records), prototype (JavaScript, Lua...), OOP without classes (Go, Vlang...), or classes with only data then they do. There is more awareness to try and avoid the traps and excesses of class-based OOP.
Consider a recipe for macaroni cheese. In imperative style it might be written like this:
Thread 1, step 1: melt butter, add flour, gradually beat in milk, simmer.
Thread 1, step 2: remove sauce from heat and stir in cheese.
Thread 2, step 1: bring water to boil, add salt, add 125g of pasta, simmer.
Thread 2: step 2: drain pasta.
Join threads: Fold pasta into sauce and serve.
Nobody thinks about how to make macaroni cheese like this. My internal record for macaroni cheese is more like this:
"Basically it's macaroni and cheese sauce mixed together. You cook pasta in boiling water with salt. Cheese sauce is béchamel sauce with grated cheese. Béchamel sauce is made with a roux and milk. A roux is butter and flour."
In fact, if you look in any decent cookery book, the information will be stored like this. A good book teaches you techniques and builds up raw ingredients into basic building blocks and then final recipes. Only a stupid book tells you how to make béchamel sauce 20 times because 20 different dishes use it.
I really like Haskell, but I think Factor is going to be my next thing (although it needs better type system, like Kitten has attempted to), for the reason that it further simplifies Haskell's (already simple) syntax.
If we take it as a given, then (controversial opinion) it seems possible that popularity of languages and paradigms that are easy and make intuitive sense (and are not weird, like functional languages) could by itself lead to a lowering of average knowledge level across the industry, even if the difficulty of a language has nothing to do with it being objectively somehow better.
[0] Not the way Brainf*^%k does it, though.
2. To learn things on the other branch, people need to unlearn until they reach the common ancestor node.
3. Unlearn is a form of subtraction. Humans suck at subtraction, be it learning things, making products, or just the human civilization itself - it's even easier for it to collapse and start over.
It's the same idea that Japanese is much harder to learn than German: Because it assumes the learner speaks English, while Chinese people that don't speak English would mostly find Japanese is much easier.
I was hoping how it would discuss how building software using functional programming techniques made the paradigm uniquely suitable for popular use.
Anyway, I digress.
I do think the stateless paradigm that serverless cloud has created is a step in the right direction. Most AWS lambdas are by their very nature impure though since you have to send the output somewhere at the final stage of the lambda.
But program analysis and synthesis are different actual endeavors from writing some bog-standard, everyday code.
When I can reason about things in a way that I understand, that makes me happier as a programmer.
I don't think it needs be any more complicated than that.
If it's not popular, so be it. Most of what I am interested in is not popular. I don't care.
In the grand scheme of things, I look at ReactJS today, with its modern incantation of hooks, and that "language" reminds me a lot more of SML (yes, anyone remember SML?), than it does with C or Java. I started programming professionally back in 2005. Back then OCaml was the rage because we thought it would support OO, and bridge FP and OO, and ReactJS did not exist yet. It was taken for granted that OO and "imperative" were practical. So it's interesting to me that modern ReactJS is more like SML than it is like OCaml. And it's actually practical. People for whatever reason don't consider it weird. Meanwhile, "idempotency" is something desired by the masses, even in infra / devops. For example, at least amongst my coworkers, we generally agree that e.g. terraform is "better behaved" than ansible.
If you're looking for a revolution in these sort of things, I don't think you're going to get it, because FP a la Haskell is indeed too "weird." As working engineers, the most important thing at the end of the day is to deliver some working tool or product.
The current generation of actual-FP languages have been too much of a pain to be practical. They had to be. They were breaking new ground as far as what could be done. In my mind, Haskell proved the limits of System F. The language was pretty much a test-bed for what you could do if you eschewed any attempt at "backwards-compatibility" with previous mental methods of programming. From a research perspective, that was hard enough to do.
But meanwhile, Swift is a massively better language to code in because Haskell and SML existed.
Change did not come as a revolution, the way we expected it. People understand FP better than they ever have, on some instinctive level, and that's how the world was able to move further away from C++ / Java, and towards SML. Ironically this happened in UI – and in the web – which in 2005 seemed like the last place where FP could possibly succeed. It was generally understood in 2005 that FP was bad at "state," and UIs, of all things, were highly stateful. (To be clear, Haskell is still a pain as far as state goes, but not as much of a pain as it used to be.) So when you put this all together, we can say that the world works in weird ways. It wouldn't surprise me too much if the languages of tomorrow actually turned out to be weird, but that'll probably take another 15-20 years to see out.
Speak for yourself, I find React is weird as hell! But that's not because it's functional, Elm for example makes a lot more sense in my mind.
Previous discussion: https://news.ycombinator.com/item?id=11188570
If OP had titled his post recursion is weird I think we'd be seeing some different line of comments.
Secondly, his recursion example not really support his opening comments about FP and cakes.
Thirdly, you dont have to do things backwards in FP.
main :: IO ()
main = do
// … followed by imperative recipeHe's wrong and he completely misunderstood functional programming. It's not weird at all. Let's get things straight. First off there's a difference between functional programming and types. Monads are more of a type centric thing and also haskell centric. Haskell is a big part of functional programming but it's only one style. You can do functional programming without monads and also without type checking. Additionally type checking and even haskell-like type checking AND monads can exist in imperative languages as well (like rust.) Does rust having monads make it functional? No.
Second functional programs can be very very very very readable. It depends on how you structure it and your naming conventions. See example below:
list of data primitives (aka ingredients and tools):
cake
grease
flour
small_bowl
large_bowl
pan
whisk
salt
oven
cream
butter_milk
white_sugar
brown_sugar
walnuts
heatedOven = preHeatOven(oven, 175)
panWithGreaseAndFlour = putGreaseAndFlourInPan(grease, flour, pan)
bowlWithWhiskedIngredients = whiskStuffIntoBowl(small_bowl, whisk, salt, flour, baking_soda)
bowlWithSugars = mixStuffInBowl(large_bowl, white_sugar, brown_sugar)
bowlWithSugarsAndBananas = mixStuffInBowl(bowlWithSugars, bananas)
bowlWithAllingredients = mixStuffInBowl(bowlWithSugarsAndBanas, bowlWithWhiskedIngredients, buttermilk, walnuts)
panWithAllIngredients = pourContentsOfBowlIntoPan(bowlWithAllingredients, pan)
heatedPan = heatPan(panWithAllIngredients, heatedOven, 30)
finishedProduct = coolPan(heatedPan)
I mean that's so simple I can literally translate that into english: Create a heated oven by heating an oven to 175 degrees.
Create a pan with grease and Flour by putting grease and flour into a pan.
Create a bowl with whisked ingredients by whisking salt, flour and baking soda into a small bowl with a whisk.
....
You get the picture... It's just real practical english tends to be less pedantic that's it.Think about it this way. Let's say we live in an imperative world where we have an oven. We then heat the oven. Now in the imperative world the oven is destroyed and replaced by a heated oven. We only refer to the "heated oven" as an "oven" for convenience but in fact the original "oven" is actually destroyed as in it no longer exists.
In functional programming THE only difference is that the original oven is NOT DESTROYED. That's it. You have a heated oven, but you still have a reference to the original oven. But you don't ever need to refer to original oven if you don't want to. This makes English describing imperative processes more or less IDENTICAL to functional processes.
Another perspective to think about this is that in the English language we create the heated oven but the original oven is not destroyed but moved! The oven is now moved into a namespace called the past, we can only refer to the original oven by appending or prepending one of the many names and phrases of the "past" namespace to the oven! For example: The oven "before it was heated", the oven "from the past", the "original" oven,... etc.
So from that perspective then the only difference between FP and english is that variables once operated on, are moved to a different namespace. So literally just extra past-tense embellishment on English grammar is the ONLY difference.
It gets crazier than this. Is anything in reality really mutable? What does mutability even mean? We live in a universe where each instance of time is it's own namespace. The universe is a function of time. At t1 there is an oven, at t2 there is a heated oven. So really mutability is just syntactic sugar over immutability where just gloss over mentioning what time namespace we're in for convenience. Yes you heard that right. Everything is actually immutable and mutability doesn't exist. Our concept of "mutability" arises from language shortcuts we take and assumptions of present tense.
Either way from the examples this guy writes it's pretty obvious functional programming didn't click for him. He understands the rules like immutability and he sees all the crazy tricks and nutty types people associate with functional programming but he missed the big picture. Hopefully this explanation will let the concept of functional programming click a bit more with people.
There is in fact a deeper intuition for why functional programming is "better" than imperative programming. But that's another long explanation. Most people don't ever reach this point of realizing how functional programming is better. They could spend years doing functional programming and completely miss it. Instead they reach a state where they think functional programming is sort of a style of programming used to express intellectual arrogance with no practical benefits and they give it up and move back to imperative programming. Well, they are wrong and they missed a deeper understanding of a fundamental concept. If this sounds like you, I'm telling you... you missed the big picture.
Literally the OP is a picture perfect example of this. Completely missed the point of FP but spent enough time with FP to understand what a monad is, even though a monad isn't really technically an FP thing.
I dislike it in Scala, C++ and javascript
I think that they've confused functional and declarative?
This is why Lisp never won. However powerful and elegant it may be, Lisp syntax is downright ugly.
The exact same thing happens with Haskell. However amazing the language is, its freaking unapproachable and the syntax is - like the OP says, weird.
I will even admit that the only reason I never gave Rust a chance was because it reminded me too much of this type of angle bracket monstrosity.