I’m studying more and more functional programming and Elixir is the language I liked the most. I would like to have static types, but I can leave without it. The biggest issue so far has been the fact that state can happen absolutely everywhere. Isn’t this a huge drawback in terms of reasoning and referencial transparency?
As many of us like to talk about, Erlang is not an academic language and is only functional because it needed to be. Many of us like to say “pragmatic” So it doesn’t come with a lot of things that other functional languages have and it’s up to you do the right thing. Effectful functions will almost always (or, in the standard library I believe always) return a status tuple—usually
{:ok, _}/{:error, _}
which another user around here has affectionately referred to as a ghetto monad ) or have a
!
at the end of its name which signals that it will raise in case of an error. But ya, there is no way to enforce this at compilation, unfortunately. It can definitely be a problem if you’re working with people who don’t know what they are doing. Usually we try and keep side effects at the same “layer” and something like boundary can help with this. It still won’t stop someone from calling File.write
buried away in some poorly named private function but you can, for example, reasonably keep something like database interactions in check.
Another option is fairly new library called Efx. It’s primary purpose is for mocks but it requires that you define any function that needs to be mocked with defeffect
. As it turns out, you could use defeffect
for all effectful functions, even if you aren’t going to mock them. But again, this isn’t something enforced by the compiler.
As a positive, from a pure base language syntax point of view, Elixir is always immutable. We don’t any keywords to make a variable mutable. If we want state, we have to start a server! Or use the built in key/value store or some other such solution that isn’t as assigning a variable.
TL;DR, if you are comfortable with FP, it’s not really a problem. But ya, I do have first hand experience of it becoming a problem.
Every function is referentially transparent if you’re brave enough!
In practice most of us are writing webapps where we hit a DB for our state anyway. Honestly other than that I very rarely write functions that aren’t pure.
It can happen, but in practice it rarely does. Have you actually run into problems here or is this more on the theory-crafting side?
Off the top of my head the only common examples of mutable state that I actually run into are:
:rand.seed/2
- Plug CSRF protection stores the token in the process dictionary
- Ecto transaction state (in a transaction or not) is implicit
And all of these are pretty valid reasons to abuse mutability IMO. There are certain (rare) situations where immutable functions are actually less safe because it would be easy to forget to pass something in. For example, one might forget to pass a seed to Enum.random()
if it worked that way, causing subtle bugs. Likewise with the CSRF token, where such a mistake could be a serious vulnerability.
I think what you meant to say is side-effect can happen absolutely everywhere. State management is actually a strong suit for Elixir/Erlang.
Yes, Elixir and Erlang are not pure. It is a trade-off that the language designer carefully made. There are also Closure, Ocaml and Scala in different places on the pure-ness spectrum; try them and see how you like the trade-off they made.
It turns out my brain, for the large part, translated “state” to “side effect”
The other thing you have to consider is that other functional programming languages handle effects with types because they can fail and the type forces you to handle the error case.
As others have said in Elixir that’s commonly handled with ok/error tuples but not enforced in any sense.
However, I’d say that the runtime mitigates the problem as we’re working with supervised processes that isolate errors and can be restarted when they fail for unexpected reasons.
So it’s basically, you should handle error cases but it’s not enforced. If you forgot something or there is something really really unexpected happening, it won’t crash everything else and you can fix it the next day.