Readit News logoReadit News
goto11 · 4 years ago
Languages do not becomes successful due to their intrinsic qualities. Languages become successful when they are coupled to a successful platform. E.g. C became popular because Unix became popular, JavaScript became popular because of the browser, Objective-C became popular because of the iPhone and so on.

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.

smokey_circles · 4 years ago
>Languages do not becomes successful due to their intrinsic qualities. Languages because successful when they are coupled to a successful platform.

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

ajuc · 4 years ago
Java was the only way to write applets and code for feature-phones early on. Nobody thought of writing application servers in java in early 90s, java was supposed to be the way to write code for internet of things.

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 :)

virgilp · 4 years ago
For Java, JVM was the "platform". I still remember learning Java in university - what left an impression on me was that my first ever university homework where I used Java (i.e. non-trivial project) ran perfectly on first successful compilation [+]. GC is huge help, especially if you don't use smart pointers (which were far less prevalent at the time).

[+] 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.

goto11 · 4 years ago
It is almost forgotten now, but the platform which carried Java to critical mass was the browser. Java successfully pivoted to server-side, but initially it was considered a client-side language.

C++ was the preferred application development language on Windows.

Python is popular because of the numeric and ML ecosystem.

dragonsh · 4 years ago
Just a minor nitpick Rust still has miles to go before it catches with Python or Go or Java, so not yet in the same league. Most of the rust crates today depends on C, so it’s not going to replace C in next 2 decades, but will still depend on C.

Hopefully it will start replacing C going forward but in that space Rust is competing with Zig and Nim.

pharmakom · 4 years ago
> And I have to agree: State management is a huge part of CRUD apps.

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.

pjmlp · 4 years ago
UNIX became popular because it was free beer with source tapes.

Had UNIX been written in PL/I, Mesa or BLISS, those languages would be "C" today.

krageon · 4 years ago
> Go did that with it's own properties

Go got popular because it was a Google thing and everyone fanboyed hard over that.

rmbyrro · 4 years ago
Java and the JVM had considerable marketing investiment to become a popular choice among the enterprise segment.

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.

twistedpair · 4 years ago
The Spark data processing platform was/is popular, and is written in Scala, a.k.a. FP.
roenxi · 4 years ago
Java & Go are sponsored by megacorps; their success or otherwise has little to do with the language's strength in isolation. They are more examples in favour of successful platforms being leveraged to promote a language.
ronenlh · 4 years ago
> 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.

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/

CraigJPerry · 4 years ago
Two counter points spring to mind:

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?

virgilp · 4 years ago
I would argue that python's popularity comes with numpy/pandas, jupyter notebooks, and the recent popularity of AI/ML/data science. Yes it's useful in other areas (flask and django are somewhat popular) but nowhere near as popular as its use for ML.

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.

somejan · 4 years ago
Java nowadays is mostly used for business applications, where developer productivity is more important than runtime performance. Java being memory safe is a huge part of that, which is the JVM platform. Java the language had the major feature of being superficially similar to C++, that helped take over the business market but is now no longer relevant. When Java was first marketed as a business application language there were few competitors. Nowadays there are, but nowadays the major reason to choose Java is because it is entrenched and good enough.

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.

_dain_ · 4 years ago
There is a functional programming language that is coupled to a platform, that most people on HackerNews wouldn't think of: the M language for PowerQuery. It is used to make ETL pipelines for Excel (and I think other Microsoft apps?). It is very popular, although among people who don't consider themselves "programmers" or "software engineers", but rather analysts.

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.

rkangel · 4 years ago
It has just occurred to me that Excel is an excellent example of a functional language to use to explain to people who don't understand what functional languages are.
codetrotter · 4 years ago
> 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.

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.

goto11 · 4 years ago
Microsoft introduced VBScript support in Internet Explorer. VBScript was a variant of Visual Basic and therefore a lot more familiar than JavaScript. VB was already used in MS Office and a whole generation had learned programming starting with BASIC. But it didn't matter.

Nobody would use a language which wasn't supported by the browser with the largest market share, regardless of the merits of the language.

jcelerier · 4 years ago
I disagree, at school we studied both C and LISP during the same semester, writing a lot of similar things in procedural Vs functional styles... I can assure you that most people (who didn't have any programming exposure) really preferred C
Banana699 · 4 years ago
This data point is confounded by lisp's weird syntax, I would expect people not used to programming to hate the excessive indentation and nestedness that lisp's syntax does to expressions.
deepstack · 4 years ago
> JavaScript became popular because of the browser

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.

blacklion · 4 years ago
> and Douglas Crockford insight on that JS has closure and function as first class citizen

«Insight» is like «RTFM»? :)

goto11 · 4 years ago
> and Douglas Crockford insight on that JS has closure and function as first class citizen

That is not an "insight". Anyone who had written an event handler in JavaScript would already know that.

_the_inflator · 4 years ago
I think that we must see C always in the context of assembly language. Pascal and C really helped things moving forward in terms of abstractions. C seems hard now and it is, however, assembler is way harder and when things started to evolve from 8bit to 16 and 32bit computers, we were all glad that machine language was something from the past. Same goes in most part to BASIC.
blablabla123 · 4 years ago
Not sure if it's implied but JavaScript was basically ideated as Scheme with C syntax in the Browser. That concept didn't take off for quite some time but pure functional programming within React actual got quite some popularity. Even "plain React" is borrowing a lot of concepts from FP, especially when combined with Flux and other add-ons. During that time also Clojure due to Om (React with Clojure) gained some popularity.

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.

coldtea · 4 years ago
>Languages do not becomes successful due to their intrinsic qualities. Languages become successful when they are coupled to a successful platform.

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).

malkosta · 4 years ago
> Functional programming is less popular because no major successful platform has adopted a functional language as the preferred language.

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....

austincheney · 4 years ago
> JavaScript became popular because of the browser

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.

juki · 4 years ago
> Unfortunately this sentiment is disqualified by data.

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.

zone411 · 4 years ago
This shows JavaScript as being popular since at least 2004 (beginning of the dataset) https://trends.google.com/trends/explore?date=all&q=javascri... and this is closer to my recollection.
kryptiskt · 4 years ago
Javascript became mainstream with the Web 2.0/AJAX hype of 2004. It was still Google's "fault", but it was the slick Google Maps site (and also GMail) and not Chrome that was the inciting factor. With an assist from Microsoft's Web Outlook and XMLHttpRequest.

Really, the reason Chrome invested heavily in optimizing Javascript was because it had started to be widely used in more than trivial scripts.

antris · 4 years ago
> I would argue functional programming is incredibly popular but less common in the workplace

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.

erostrate · 4 years ago
How does your theory explain Rust or Python?
valenterry · 4 years ago
Despite Rust being around for quite some time now, highly liked (hyped) by its users and being a good language with many features that make it stand apart while having major commercial support, it's still a very niche language.

I think it rather supports the OPs claim.

goto11 · 4 years ago
People are learning Python because they want to do ML or scientific computing - not the other way around. Python has its current popularity because of NumPy and the whole numeric and ML ecosystem.

I love Python as a language, but realistically it could just as well have been Ruby or some other language.

tgv · 4 years ago
For Python, the platform is education.
dijksterhuis · 4 years ago
Python is quite popular with the data / machine learning crowd. Think pandas, tensorflow etc.
js8 · 4 years ago
Python came with a platform of sorts, its motto was "batteries included", after all.
57844743385 · 4 years ago
I personally find functional languages highly unintuitive.
goto11 · 4 years ago
Intuitive just mean familiar. A programming language is intuitive if it is similar to another language you are already familiar with.
menotyou · 4 years ago
I personally find object oriented languages highly unintuitive.
amelius · 4 years ago
This is exactly the reason why people are trying to push their favorite language down everyone's throat.
dna_polymerase · 4 years ago
Hi, do you have a minute to talk about our lord and saviour, Rust?
tdy_err · 4 years ago
Chicken or egg?
patrick451 · 4 years ago
Why do you assume the browser would have become as popular as it did if Netscape had gone with Scheme? Or Unix with C?

It seems more likely there is a feedback between the popularity of a platform and it's anointed language.

goto11 · 4 years ago
Netscape already had total dominance of the browser market when JavaScript was introduced.
cardanome · 4 years ago
For me OOP is weird. I never understood what people mean when they say it is close to how they think. It feels like a way to obfuscate the flow of code and requires to make decisions about what thing should have which responsibility and relation with which other thing and building weird hierarchies that will bite you back in the long term.

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.

GuuD · 4 years ago
Oh man. Are you me? Thanks for that comment.

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

TacticalCoder · 4 years ago
Amen!

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.

ladyattis · 4 years ago
I tend to mix OOP and functional together as I use objects as a way to relate functionality or steps within the process. I almost never use objects as a means to model the actual data. At most, I use objects in this instance as glorified structs. The only real benefit that OOP as implemented in most languages has is the ability to write to an interface whether you use an explicit interface type or an abstract class which concrete implementations derive from. Like Bob C Martin said in many lectures the inversion of control is really where OOP shines. Anything else most other paradigms do it better.
Tainnor · 4 years ago
I find that OOP languages (as long as they support FP and immutability) have the benefit of using classes as nothing more that first-class parameterised modules (without any mutable state) so I don't have to repeatedly pass arguments from one function to the next but can look them up from a shared context. Haskell doesn't really have that option, although people have told me that OCaml can do that.
magicalhippo · 4 years ago
> I can refactor without any fear of side effects.

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.

cardanome · 4 years ago
> 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.

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.

matt_kantor · 4 years ago
> 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.

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)

Tainnor · 4 years ago
Confidence in a program's behaviour is not an all-or-nothing proposition. You can statically verify certain properties of your program (through immutability, type systems, linters etc.) without having to write a full formal proof of your program's entire behaviour, and you can gain more confidence in the entire program working correctly.

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.

lambdatronics · 4 years ago
> building weird hierarchies

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.

kaba0 · 4 years ago
There are problems (or parts of problems) that are more about behavior and others that are more about data. The latter of course is benefited by a FP mindset/language, but I wager that OOP is a better fit for the previous.

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.

Dead Comment

6gvONxR4sf7o · 4 years ago
Some of the weirdness is so completely avoidable that it frustrates me, but languages and libraries dig in on ‘you get used to it.’ I’m talking about the f(g(h(x))) vs x | h | g | f. Right to left chains of functions seem more popular than chains of pipes in FP, but even being someone who has drunk the kool-aid deeply, I always prefer pipes.

‘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.

davidatbu · 4 years ago
I'm relatively an FP newbie, and I have a few questions.

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 num

lostcolony · 4 years ago
Elixir, Elm, Ocaml, Haskell, and F# all have a pipe like operator for function composition (though the actual operator varies).
kjeetgill · 4 years ago
> which is not an FP right? > Aren't dependent types orthogonal to whether a language is functional?

You'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.

mdoms · 4 years ago
F# and Ocaml are good examples from my limited functional experience.

https://stackoverflow.com/questions/2177110/in-f-what-does-p...

dwohnitmok · 4 years ago
Dependent types are not totally orthogonal of FP. They generally require immutability and function purity to work well and become extremely difficult to work with in the absence of those features.

See e.g. https://shuangrimu.com/posts/language-agnostic-intro-to-depe...

gpderetta · 4 years ago

  h(x).f().g() 
Is also very common and I would say it counts, especially if the language support something like extension methods.

chadrs · 4 years ago
(2) No, dependent types are not the same as overloading. Basically it's when the type depends on the values; so like having a type for odd numbers or having an array that knows its length at compile time (for example to avoid array out of bounds exceptions at compile time)
6gvONxR4sf7o · 4 years ago
1. Others have answered it well. Tons of languages have pipe operators. I used | in my example because I figured more readers will recognize bash piping than other notations.

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.

1-more · 4 years ago
The pipeline operator |> exists in Elm[0]. It is available in bash as & and the popular (in my limited experience) package flow[1] also implements it as |> for readability. It is a proposed addendum to Javascript[2] but I have no idea how seriously the JS powers-that-be are taking that.

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

[1] https://hackage.haskell.org/package/flow

[2] https://github.com/tc39/proposal-pipeline-operator

agumonkey · 4 years ago
outside `|` there's the threading macro in lisps (or else)

(-> x f g h ...)

chromatin · 4 years ago
Some languages like OCaml have “pipeline” operator like `|>`, others like D allow you to chain function application `f.g.h()` instead of `h(g(f()))`

[0] https://www.cs.cornell.edu/courses/cs3110/2019sp/textbook/ho...

[1] https://tour.dlang.org/tour/en/gems/uniform-function-call-sy...

Deleted Comment

LinAGKar · 4 years ago
That sort of chaining seems a bit like the method chains you have in some languages, like Java streams, C# LINQ and Rust iterators. That sort of "functional constructs when it fits" is getting popular, even if pure functional isn't gaining much popularity.
scns · 4 years ago
In D, Flix [0] and Nim you can use UFCS [1] to chain functions.

[0] https://flix.dev/principles/

[1] https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax?w...

FeepingCreature · 4 years ago
D doesn't have pipes, being a C-like (it probably could, but it's untypical), but it has something almost as good: "uniform function call syntax", meaning that a function that takes X as its first parameter can be called as if it was a method of X. `foo(x)` => `x.foo()`. This makes functional programming in D a lot closer to the pipe example, in that you're usually passing a lazy iterator ("range") through a sequence of chained calls.

Every C-like language should copy this idea, imo.

sk0g · 4 years ago
That sounds amazing, but I wonder if it could get unwieldy as well.

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.

_dain_ · 4 years ago
Nim has this as well, it's lovely.
sullyj3 · 4 years ago
This is why every time this stuff comes up I plug the haskell flow package. Imo, it should be in the prelude.

https://hackage.haskell.org/package/flow-1.0.22/docs/Flow.ht...

grapist420 · 4 years ago
I’ve hacked pipes in python many times just because it’s so much better. And no, “a=f(a);\n b=g(a);\n c=h(b);\n a=i(c)” isn’t “more declarative” or “more expressive” than a | f | g | h, and even in python a |pipe| b |pipe(blah)| c |pipe(blah, blah)| d is so much nicer than nesting the parens.
lpapez · 4 years ago
This sounds like a debugging nightmare. Is it not easier to name each intermediate result so you can inspect it in a debugger?
thaumasiotes · 4 years ago
> ‘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.’

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.

pnut · 4 years ago
I'm always surprised how unpopular perl (and even more, its spiritual successor raku) is with the HN crowd. It does not square with my experience of various languages' ergonomics.

It's not at all surprising that perl put thought into things like this, the entire language concept optimises for elegance of expression.

turkeygizzard · 4 years ago
I think maybe part of it is that most people are introduced to FP through haskell, if I had to guess. And haskell (at least the tutorials) is IMO guilty of the top-down approach where the final value is written first, referencing variables that haven't appeared yet. ocaml, on the other hand, has you define your components before the final value which is more familiar to most programmers

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

scns · 4 years ago
I concur, in Haskell you have to import the pipe operator (&) before using it, kinda second class, no snark intended.
theCodeStig · 4 years ago
A lot of the difficulty in learning FP is in unlearning imperative programming. I hypothesise that for someone whom has never programmed before, FP and declarative paradigms are easier to reason about.
kjeetgill · 4 years ago
> FP has enough great ideas that I’d recommend everyone learn pure functional solutions just to put into their tool belt

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.

Deleted Comment

fiddlerwoaroof · 4 years ago
I’ve “drunk the koolaid” and I mostly went the opposite direction on left-to-right pipes vs. right-to-left compose: with a(b(c(d))), order of evaluation will always be arguments->function call (in most languages) and, so d is the first thing evaluated. Pipes make it seem more consistent, but it also introduces an inconsistency.
grapist420 · 4 years ago
isn’t the first thing you are being the first thing evaluated just better? we read ltr, so having to see and remember a before c(d) but then having some of a’s arguments at the very end is just annoying vs pipes
qsort · 4 years ago
Java streams are a (limited) version of what you propose. Something like this is very idiomatic:

    myhashmap.entrySet()
        .stream()
        .map(Map.Entry::getValue)
        .sorted()
        .distinct()
        .toList()

kilotaras · 4 years ago
Difference with pipe operator would be

1. can't be extended.

    myhashmap.entrySet()
       .stream()
       .flattenBars()
       .toFoo()
2. (As a result of 1) - applies only to streams

carlmr · 4 years ago
I was going to say C# LINQ, too!
the_gipsy · 4 years ago
For the order of fn composition, I found the pipe operator found in e.g. Elm really nice.
Siira · 4 years ago
Most FP-heavy languages also have good support for metaprogramming, and have macros that imitate pipes (e.g., all lisps and Julia).
rg111 · 4 years ago
I recently watched this talk-

Why 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...

endymi0n · 4 years ago
It's way simpler actually.

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.

dwohnitmok · 4 years ago
I have my share of gripes about Haskell (which I'm assuming is the language you have in mind when you're talking about a pure FP language), but even with the sarcasm disclaimer, this is a pretty extreme strawman.

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).

chii · 4 years ago
> 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

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.

stepbeek · 4 years ago
I'm at a very similar place to you at this point. It make sense for FP to be good at "describing relationships of actions" since the base unit of reasoning is a function, or an action.

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.

grumpyprole · 4 years ago
People need to be familiar with both approaches. And there's sillyness on both sides. I've seen influential imperative OOP programmers on stackoverflow model a bank account using a single mutable variable, even when they surely know accountants use immutable ledgers.

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.

yomly · 4 years ago
Your LED example is an interesting one. In the basic model of a computer architecture, the screen is abstracted as a pixel array in memory - set those bits and the screen will render the pixels. The rest is hand waved as hardware.

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

mmis1000 · 4 years ago
I think functional programming is less popular simply because people just aren't good at it.

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.

go_elmo · 4 years ago
I agree with you but see another relevant reason: in FP, you HAVE to consider side effects 1) from the beginning and 2) completely, which as anyone can guess is quite a task.

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

jb_s · 4 years ago
It's been ages since I've used a "real" functional language but wouldn't it be nice to parameterise externalities like time, and have events occur "spontaneously" that force application state to update? Kind of like interrupts. Or now that I think about it... it sounds a bit like React where DOM events etc force application state to update (in a pure fashion)

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 updated

edit: 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))

dnautics · 4 years ago
there are FPs that have and embrace side effects, not everything is haskell.
shaklee3 · 4 years ago
C++ and C have spent $0 on marketing and are more popular, so I don't think this is a good indicator of success.
pjmlp · 4 years ago
Indeed, C came for $0 with UNIX that AT&T wasn't allowed to sell and provided source tapes alonside a symbolic price, in a time where systems cost several hundreds $$$$.

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.

kaba0 · 4 years ago
Never heard of any conference starting with Cpp or the like, nor does it have a website I guess..
deepsun · 4 years ago
I thought Java was once the most popular language before Oracle bought it from Sun Microsystems.
Nursie · 4 years ago
They bought the whole of sun!

And yes, it was, though in the intervening years they've improved the language a lot IMHO.

platz · 4 years ago
this is also a good talk in a similar vein, although aimed at haskell programmers, is really about any technology looking to grow into the mainstream

Gabriel Gonzalez – How to market Haskell to a mainstream programmer - https://youtu.be/fNpsgTIpODA

armchairhacker · 4 years ago
Fully functional programming (e.g. Haskell) isn't popular because it isn't ideal and computers work imperatively.

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).

cheeze · 4 years ago
Functional programming absolutely has its use cases, and I love a bit of functional style thrown into imperative language one hundred percent.

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.

jimmaswell · 4 years ago
What are the use cases you've seen functional programming in real business?
theCodeStig · 4 years ago
Have you tried to see the pure FP viewpoint, rather than argue?
lostcolony · 4 years ago
"Meanwhile there's practically no tradeoff to writing functional code in an imperative language."

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).

macintux · 4 years ago
Agreed. Constraints (primarily in this context immutability) are invaluable.

Writing functional, immutable style in Python is handy but can be maddening when you never know what to expect from libraries.

eurasiantiger · 4 years ago
> 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 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.

nwmcsween · 4 years ago
Why would you worry about low level details for a high level language, optimization is an implementation detail. Javascript in my opinion lends itself functional style programming
phillipcarter · 4 years ago
> isn't popular because it isn't ideal and computers work imperatively.

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.

sullyj3 · 4 years ago
There are tradeoffs to writing functional code in langauges designed primarily for imperatively. Another comment mentioned efficiency.

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.

Ginden · 4 years ago
> 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.

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.

mamcx · 4 years ago
I use F# a lot and like it, but imperative code was problematic to do.

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.

pharmakom · 4 years ago
F# has mutable and while loops also.

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.

bmitc · 4 years ago
It really isn't. Just see Racket, Clojure, Elixir, and F# for practical functional programming languages.

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.

TuringTest · 4 years ago
Concur about the C++ examples, but that's not just on the language. The author seems to be averse to whitespace to separate one chunk of code from the next.
jmfldn · 4 years ago
Functional programmer here. It's a different way of thinking is all, I don't see it as solving puzzles exactly. It's unfortunate if people see it as being about complexity or being clever. Done well, it should be the opposite. Once you understand the key abstractions, it can make code a lot easier to write and reason about in some cases. That's the joy of it for me. It furthers my day to day software engineering concerns of readability, maintability, testability etc. Nothing more.

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.

mdoms · 4 years ago
I feel like your first and second paragraphs are directly at odds. It's not about "solving puzzles" and you decry cleverness and complexity, but you also think we should persist for joyous "a-ha" moments and brain-bending?
jmfldn · 4 years ago
Ha, yes OK that sounds a bit contradictory so let me clarify.

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...

rbanffy · 4 years ago
It makes expressing some solutions so elegant and easy it’s totally worth using a functional approach. Other things may be more elegant to express using an imperative approach.

Just try to implement recursion in MS 8-bit BASIC.

Akronymus · 4 years ago
AFAIK basic doesn't do stackframes. So a recursive function would overwrite itself.

Or maybe it was algol

vnorilo · 4 years ago
I don't know. I don't write functional because it's hard and I'm super smart, I write functional because it's easy and I'm lazy.

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!

pharmakom · 4 years ago
I have settled on the immutable core + imperative shell pattern, because it’s the easiest way to write robust software in my experience. It’s much easier to do this in a functional language like F# or OCaml than it is in a mainstream OOP language, even one with all the bells and whistles like C#.
logicchains · 4 years ago
It's getting much easier in C++ due to constexpr becoming more and more powerful. A constexpr function is a mathematical function, in that the compiler guarantees that its output is a deterministic function of its input.
vnorilo · 4 years ago
True. Especially when guiding a team C# makes life hard because so much of immutability is by convention only. I suppose it will get better once we can move to C#9 with Records, but even so, some discipline is required.
garethrowlands · 4 years ago
It's a great pattern. It's also the default in Haskell because you can't normally invoke imperative actions from a pure function.
tooltower · 4 years ago
I've had this experience too. It doesn't take a lot of template use for both binary size and build time to blow up. When iterating with `make` starts taking over a couple of minutes even on a smallish-medium-sized project, it becomes a bit much.

I didn't do anything fancy like compile-time parser generation. it was overuse of the standard library features, like `std::variant`.