Where Elixir fits in the functional programming world?

I don’t want this thread to become a flamewar, but I was having some huge discussions with a few friends regards functional programming. First of all, my background is in imperative languages, I understand the benefits and value proposition of FP, but I don’t have a math background, nor the academic interest to learn all the bases and proofs of FP.

Do you see Elixir a “less qualified” FP language than something like Haskell? Looks like lately FP means having an effect system and being statically typed, which kinda of rules out most of the languages apart from Haskell and a handful others. Even languages like Scala and Ocaml have lots of escape valves and they can fall into a imperative approach and do effects anywhere without the compiler knowing it, so it’s best effort at the end of the day.

While elixir and the underlying VM is inspired from functional languages, there are not many similarities to a language like haskell. I would say the prominent features that you see in a lot of functional languages that are also present in elixir are: functions as first class citizen, immutability, pattern matching.

Elixir/erlang, compared to haskell does not have a dedicated abstraction for managing side-effects. This means you can have impure functions that do side-effects, just as in any imperative language. The management of side-effects in elixir is very close to what golang does, treat errors as data and use those in your program flow. The way we usually do it in elixir is by wrapping the result/error in {:ok, result} or {:error, reason} tuples.

Scala never aimed to be a strictly functional language. The selling point of scala is that it contains features from all paradigms, such as classes, monads, mutability etc. It’s impossible to set strict compiler rules for a language that has a huge amount of features spanning across different ecosystems.

As a conclusion to make this more clear: Elixir has some features from FP, but the language and the VM it runs on is completely unique and does not fit into one single paradigm. Fault tolerance and distribution are its main unique strengths that are properties of the runtime and not the programming language.

2 Likes

I remember watching a talk from Joe where he stated that Erlang became functional as a side effect of their decisions, the language was not created with the goal of being functional.

I understand why Haskell folks say that Elixir is not functional, but it’s funny when I see the same arguments coming from Scala and Ocaml engineers where they’re on the same situation, and actually their escape valves from FP are way easier to use.

But, do you see any benefit of following the pure fp approach? The only thing I can think of is that it’s easier to test. In theory it produce more correct code, but the effort and layers of complexity, in my opinion, are not worth for the majority of cases.

1 Like

In languages like scala, you could have theoretically pure functional code, but it highly depends on who writes the codebase. There is also the problem that JVM was not optimized to work with immutable data structures, so it might be possible that using purely the functional approach might imply a substantial penalty on performance, even though I cannot confirm this fact as JVM got a huge amount of money as investment throughout the years.

Mainly immutability is superior compared to any imperative language. This is especially true if we talk about mixing that with concurrency, where things become 100x times easier to reason about and less error-prone.

I would say completely the opposite. I will always favor a immutable FP flavored language as the first choice and only resort to imperative languages for situations where immutability cannot be afforded. It’s not about advanced features such as monads, but about not having to deal with broken OOP concepts or mutable global stuff and concurrency.

I know that imperative languages were and still are the most prominent, so most of us are biased towards them, however I’ve taught students FP before they went into the broken languages we have in our industry and the results in their understanding and performance of solving real-world tasks were amazing, much better compared to someone who already worked with imperative languages and needs to shift the mindset.

I can also say the fact that elixir codebases are much more easier to understand and reason about, due the fact that you only have functions and immutability. I remember comparing a somewhat similar java codebase with a elixir one and the differences in complexity and the amount of code were just huge.

1 Like

I may have not expressed myself correctly. I was comparing Elixir with a purely FP language like Haskell. The majority of the benefits, in my opinion, are coming from the base foundation that both languages have, like immutability. The extra bits, like the monad IO, are useful, yes, but I can’t relate it with real life benefits or even if it’s worth the tradeoff given the extra complexity.

1 Like

Sadly I’ve never did anything serious with haskell to either appraise or condemn whether explicit management of side-effects brings a lot of value. Only thing is that I can say is that I don’t like how scala deals with that, it’s like an afterthought added to the language. I am personally a big fan of languages that don’t enforce strictness by default.

For example a lot of folk is criticizing elixir for being dynamically typed, however I see this as a benefit in the prototyping phase or even sometimes in production, where you are not entirely sure on the shape of your data. The beauty of it is that you can make it stricter iteratively, which allows more flexibility compared to statically typed languages, where you are forced to define the contracts upfront.

In the same manner I can see how monads and other dark magic from the FP world can be useful for specific projects. For example there is whichcraft library in elixir that also adds a lot of such features to the language, I’ve never used it and never saw a project that does, but judging by its popularity it gets real-world usage.

1 Like

I agree 100% with you on Scala. I would rather have a well thought system that is not pure, that one that tries to be everything at the same time. I don’t understand when someone say to me that Scala FP has referencial transparency when you can mutate left and write, throw exceptions, and use Java libraries that has tons of mutation.

I think this is a general feeling in the industry, as it seems that scala never got popular. On the other hand, kotlin, that doesn’t claim to be a functional language, but offers various FP features was very well received.

1 Like

I know the kinds of conversations and opinions you’re talking about. It’s important to realize that Haskell has come out of the academic community, and they’re exploring/implementing advanced computer science features. Elixir comes from Erlang, which was created to solve a business problem and focused on pragmatism (resilient systems even in the face of hardware failure) over purity. They have different goals and different language design tradeoffs, but they both treat functions as first class. I wish the academics had some other phrase besides “pure”/“purity” to distinguish their language goals, but then again I suspect that terminology helps them feel superior, so they have little motivation to change it.

7 Likes

IMO, elixir sits in the sweet spot. It’s what I see as FP-lite or pragmatic FP. I have a bias though to keep things simple and easy to reason about. “True” FP with monads and the like always struck me as prioritizing purity to the point where the code becomes difficult to grok. Perhaps there is a nirvana moment after spending substantial time with it.

100%

5 Likes

What’s the expected result of those? Theoretical discussions can go on for literal decades – scientists of yore have been known to be “old rivals” in certain circles – and never go anywhere. So IMO it’s worth asking “What would everyone involved gain out of those discussions?”.

No, quite the opposite. Elixir is pragmatic. While I do want to work with effectful languages like OCaml, the truth of the matter is that the rest of the world is simply not ready. Having side effects be isolated and clearly marked – and the async / multi-threaded / multi-process / multi-node code as well btw – is a clear win and it absolutely is the future but as of today it is only a thorn in the side of the widespread commercial programming.

And with the advent of LLMs and the collective hallucination that programmers are going away and that “AI” is going to write code, I fear that even such pragmatic languages like Elixir are in the danger of being swept under the rug as “too difficult for our new college grads that write the next-gen Facebook” (or w/e is in vogue in these circles). But, another topic. :slight_smile:

“Looks like”, according to whom? :thinking:

True enough, but runtime speed and efficiency given by f.ex. Rust / Zig or even Golang should not be underestimated at all. It can make a gigantic difference especially in situations where the single beefy server cannot take it anymore and people add 100x complexity by starting to cluster stuff; those much more difficult languages will save your hide for another year or two more in the future. Though nowadays with load balancers and everything around them that’s less of an issue, admittedly.

This is a very interesting discussion. We’ve seen effect-systems and immutability as characteristics (and even type systems - huh?).

I do 100% agree, that immutability is a differentiator for functional languages. However, what is also important is that functions are first-class. Passing functions as if they’re values and enabling higher order functions is the game-changer. This is what’s fundamental to implement different effect systems with ease. Monads? Sure just do it. Dependency injection? Free.

There are different ways to control side-effects. The trend in Haskell went from monad-transformers (which don’t compose) to free monads, to imitating an algebraic effect system. Man, the complexity. Scala did the same with catz and scalaz.

Elixir tends to be more pragmatic. What do we want to achieve? Is it for testing? Maybe use mocking or dependency injection? Is it for abstraction? Yeah, you can achieve 99% and implement composable abstraction without knowing category theory. We can do that, since we functions as first-class.

That said, I’d go with what @jam and @dimitarvp said: Elixir is a pragmatic functional programming language (as is e.g. clojure - no one will ever challenge clojure for not being functional).

4 Likes