On "Why Elixir?"

The view in the Erlang/OTP/elixir world has always been that no language is good at everything, get over it. And in any real system different parts of the system will have different requirements so the best solution is generally to use different languages, use the right language for each part and then glue them together. I would hate to try bit fiddling in raw memory in Erlang, I would use C for that in a NIF so I could control it from Erlang/Elixir. And I would hate to use C to build large, concurrent, scalable, fault tolerant systems. Of course it could be done but the pain. And if I really has to do it I would use the right library for it, the BEAM :wink:

26 Likes

It’s pretty interesting how @josevalim explain exactly what means “you may not need Redis with Elixir” in this post: https://dashbit.co/blog/you-may-not-need-redis-with-elixir

Even the @whatyouhide talks more about it on twitter

4 Likes

What I have to say about Elixir pro/con

Here I am after… what? 2 years? of almost exclusively using Elixir. Maybe more? It’s hard to keep track of when we retired other programming languages for good.

I was the driving force behind going full on Elixir, and I am ready to take to full blame for everything that goes wrong in our apps. But I am feeling pretty good - most of our problems come from server issues (except for one memory leak we could not pin down yet…)

But here is the story.

We started with 100% Ruby on Rails, and I would say I went through all the states of love/hate with Rails. At first I really loved it. I did not know Ruby, I just knew a little bit of JavaScript, and Java (the stuff they taught in Uni) - but I still did not have a real idea how a web-app works.
Along comes Rails… You just need to google what helpers to use, and stuff works!

But oh oh oh! Don’t try to do things that are not included in the “Rails and go yaaaaaaaaaa” package. After 3 years with Rails, and struggling with their lackluster real-time support (what we did back then was make a poll request to the server and return javascript that had to check if anything changed) I started working with a highschool friend of mine, and I was hoping for some insight into how to work the Web 2.0!

I had to learn EmberJS - which is kind of Rails for frontends, probably emerged a year or more after the first real frontend JS frameworks became popular. And I ran into the same problem… “but what if I want to do something not in your text-book, that seems to be written in Web 1.0 times?”
And additionally, we had to maintain the Rails API server (written before rails_api was a thing, so we had to manually remove gems), and the EmberJS frontend.

My predecessor started fooling around with Elixir early on, and we had one actually very important app running on Phoenix pre 1.0 and Elixir (beta version?)

At first I hated Elixir… I did not understand how to use it, given my Ruby on Rails background, and basically used to things just happening magically.
And then I had to actually use Elixir. And I realized that everything was so much more explicit.
Want to know where something comes from? Do a project wide search for something and you see everything where it’s passed down to the view etc., instead of being some magic shoot you can never find if you don’t know how Rails works.

But the real kicker for me was when Phoenix made the switch from the MVC style to Domain Driven Design. I read the blog post (help me out here, probably by Chris McCord?) about how DDD works in Phoenix and I immediately thought “this is so much better than RoR!” I went throught the redesign for Phoenix with Elixir, and I am just in love with the barebone design of the language. But it also has all those web development packages, that follow the same design principle: as barebone as possible, if you want more, add dependencies.

I have updated Ruby, React, Ember, and Elixir server for years now, and I have to say Elixir is the easiest to update (except for the one error we run into every now and then with hackney! why tf does it actually prevent me from ignoring ssl for crappy APIs.)

So, for me personally the context focused Phoenix structure was the reason why I wanted to use Elixir more. But I can also attest for the performance and reliability.
We had a Elixir/Phoenix server running for almost 4 years before I first looked into it - and the reason I did was Heroku updating stuff, and we moved it to a cloud server. Almost no problems, super easy with edeliver (once you understand how it works, admittedly.) And another server I updated w/o understanding Elixir yet, also on a version that was probably still alpha, maybe beta. Updated the server and app, and all the errors were very descriptive and easy to understand. I would honestly say the hardest update in Elixir happened just recently, and it’s just because of hackney!

Another reason why to use Elixir: we recently had a few spikes from a customer who wanted to do a bulk interaction. A few thousand requests/minute + usual traffic. I saw the spike on the server, but no problems other than the database server sometimes crapping out.

PROs:

  • Lightweight stlib, once you know Elixir you know it (unlike Ruby, where they add aliases for methods every 3 monts)
  • Once it is deployed, it will just run… and run… and run…
  • Phoenix is a great web dev framework for intermediate developers
  • NEW The language is still quite new, so sometimes you can implement packages for everyone. I have a package a few people use, and it feels very good to be part of the community.
  • EXPLICIT Elixir/Phoenix values explicity over implicity, so unless you screw up, you always know what is going on.

CONs:

  • first deploy can be a pain the A**
  • erlang is slow to install, and the community seems to be stuck in the 90s when you look at their docs
  • NEW the language is still quite new - opens opportunities, but means you will have to implement stuff yourself where other languages have packages ready.
  • EXPLICIT There is more code to type, more data to pass, and most things don’t just work magically.

I put 2 things in caps, because I think they can be a blessing or a curse, depending on how you look at it…

Overall Elixir, Phoenix, and especially PhoenixLiveView have changed the way how I do my poopy web apps, and gave me the feeling of finally having “one app” that does almost everything, instead of a bunch of “micro services” in Ruby/PHP/Python and a separate client.

Also, we developed an app in ReactNative - guess how hard it was to establish a websocket connection to the server from react native. Right, ridiculously easy.

So, why Elixir?

  1. I just love @chrismccord for Phoenix (great name, great technology!) and of course @josevalim for developing Elixir and still being active in the community!
  2. Because for me it is a modern take on an old, battle tested technology. Erlang syntax is a crime against humanity IMO - so thank you again Jose to make the technology actually usable.
  3. Phoenix LiveView - we have been using it in production since alpha, because I just wanted to use it! Every update is an improvement, and usually once we see bugs, they already fixed it in the next version. I forgot who the guy for the Javascript was, but if you tell me his elixiforum name, I will include him here, because that s**t is just incredibly cool.
  4. You can easily use Elixir w/o Phoenix or anything, and it’s still incredibly good in spawning lightweight processes all over the place (the orchestrator)
  5. honestly, I am just attached to this programming language/community now. And as long as I am in charge, we will keep using it! Keep up the great work!
16 Likes

As someone who tries to implement the TreeSitter for Elixir, I beg to differ :wink: It is more pleasant to some eyes, but on the technical side Elixir syntax is enormous PITA (no offence @josevalim, just writing alternative parser isn’t the easiest job).

I think that it depends on your background. C#/Java developers will feel mostly there, but Ruby/Python developers can get lost due to completely different approach.

The first look can be terrifying, but when you get past the aesthetics then it is one of the best documentations I have pleasure to work with. However the default documentation generator in the OTP (which seems to not be used for OTP docs, don’t ask me why) should be burned to the ground. Hopefully EEP48 adoption may change that one day.

2 Likes

Elixir is my favourite language but it took me time to make the switch from my previoulsy favourite language, Erlang, because of the rubyesque syntax ; notably optional parentheses and the dot_call.() nonsense.

Then I understood the tradeoffs that were made, the beauty of the macro system, and that syntax should not matter in the end as it is just a way to express an AST.

I still think it is kind of ugly but I could not care less :slight_smile:

I was in the process of learning Docker, as we use that in my company, when I had to deploy my first Elixir app, and I found it was really easy. It is not distributed though.

I love that this thread is still going on :smiley:

I think “crime against humanity” in terms of Erlang syntax is a little harsh. So I’ve never actually written anything in Erlang but I’ve looked at a lot of code in my quest to grok Elixir. I actually think some things are nicer in Erlang—function definition comes to mind. I also prefer the explicit function exporting—defp doesn’t standout quite so well for me… I should probably change the syntax highlighting.

I’m back from a 2.5 month break from learning Elixir. I was able to take a lot of my learnings and apply them to our messy functional frontend React code at work. I was starting to make my peace with React but got stuck on some refactorings, so I came back to Elixir for some inspiration. I’m now back to being bitter than I’m forced to work in React every day. Ah well. That’ll change some day, I’m sure—I just like the company I work for too much right now to make a change.

I agree - I think maybe we should ban all criticism about the Erlang syntax until people have read Programming Erlang :043:

Seriously tho, a lot of people, myself included, have grown to appreciate the Erlang syntax - although I am going to reserve full judgement (about which I prefer) until I’ve finished the book (a few of us have a Programming Erlang book club going on Devtalk if anyone is interested in joining us).

Generally I am of the opinion that it just boils down to preference. For me at least the important thing is that people are using a BEAM language, whether that’s Elixir, Erlang, Gleam, LFE or any of the many that we have now - so long as people are happy with their choice that’s all that really matters :blush:

5 Likes

From the perspective of time I came to similar conclusion. Either explicit export list as it is in Erlang or swap the meaning of def and defp, so “by default” all functions will be private and exported more explicitly.

3 Likes

I don’t know much about Erlang syntax and I’ve just glanced Erlang code. One thing I would say, is that my gut feeling is that for lot people it’s much simpler to jump into Elixir from a mainstream language than it would be to Erlang just because it looks more alien compared to them. Also like to add that difference is not just about syntax and I understanding that one of the biggest differences is that Elixir has powerful macro system that Erlang currently doesn’t have.

2 Likes

Yep. IMO the Elixir macro system made certain things possible that weren’t before.

As for syntax, I quite like Rust’s way, it’s explicit, concise and easy to grep at the same time:

fn a1() {} // private
pub fn a2() {} // public for everyone
pub(crate) fn a3() {} // only public in the project it's defined in

But I agree Erlang forces you to be explicit and that’s a good thing. I would find it annoying during iteration and prototyping however.

5 Likes

Ya, that’d be much nicer, actually. That’s kind of like es6 or other languages that have an export keyword. But at the end of the day, I don’t really get too hung up on syntax, unless it’s particularly horrible, and there is nothing I find horrible about Elixir syntax.

@wanton7 At least as it pertains to me it’s was way easier for me to jump into Elixir than Erlang. I remember looking at Erlang many years ago and just thinking, “nope”, though I wasn’t as open minded back then. But once you learn Elixir, Erlang code starts to make a lot of sense. And yes, macros are a huge factor in making Elixir more digestible, at least to those of us used to more “modern” syntax. If you see some of the Erlang equivalents to certain patterns, Elixir is removing a lot of boilerplate with macros.

One last note on syntax, the one thing that really annoyed me when getting started with Elixir was actually how much it looked like Ruby. As a Rubyist, I kept thinking I was in Ruby and kept thinking modules were classes and had to keep reminding myself I was not writing Ruby. At this point I don’t even think about it, though.

1 Like

Erlang had parse transforms which can provide similarly powerful features, just usage of them is much harder and less obvious. Also implementation of these is much more complex than Elixir macros.

In this case it is not about syntax, but about semantics. Public functions in Elixir cannot be inlined and can slow down your code. Additionally exposing functions, that are meant for internal usage, in public API can sometimes cause maintainability problems, when people will keep assuming existence of such functions. Of course you can say that people shouldn’t use that functions, but making something impossible is better than making it discouraged.

2 Likes

I was just talking the syntax of def and defp vs something like defpublic and def or whatever, I wasn’t suggesting eliminating private functions. But that is good info I wasn’t aware of! I do remember hearing private functions are faster in some OO languages but I didn’t remember the explanation, so thanks!

I wish we could define “compiler modules” that would be automatically injected into any other modules in the same otp application on compilation to define macros.

We could then override the def macro and implement an export macro :smiley:

Erlang’s syntax is fine, in some area even nicer than Elixir. Erlang’s core library and the documentation of them could learn some lessons from Elixir’s counterpart.

2 Likes

Then you add:

-compile(export_all)

to the module while prototyping and remove it for production. Or even have that as a compile directive for the development environment.

I find the export list preferable to function annotation as you can see a list of all exported functions on top of the module. It is much easier to see rather than scan the entire file to look for public functions.

I’ve seen erlang code where the export list has a comment with a short description
of function signature and description. This is nice when reading but probably but risky to get them out of sync with the real functions and function specs.

-export([
    add_something/2, % (#object{}, Incrementor) -> #object{} Add to object
    foo/3, % (X, Y, X) -> integer. do foo
    bar/2, % (...) -> bar
 ]).

Perhaps tooling could automate the export list functions.

3 Likes

I prefer to have modules with a single responsibility, like handling one action per resource. So, you have a CRUD resource, then you have a module for each of the create, read, update and delete actions. This leads to modules with one single public function, and all the remain functions are private.

I am using this approach for all my projects, and it makes them much easier to understand and read, then all that fat files, that give you the issue you mention of scanning an entire file for public functions.

Elixir use reaches far beyond implementing Phoenix controllers …

I am not talking about controllers per se, but I see that everyone associates CRUD with controllers because of the MVC style that is a doctrine I don’t like to use.

I used the CRUD as an example to make it easier to understand, but I was not referring to controllers at all.

Example:

Screenshot from 2021-01-12 22-12-32

See how many actions I have for the todos resource, and they are not inside of the tasks_web folder, instead they are in the core, that can be reused with any web, api or cli implementation of it. They are also inside an api folder, because anything inside this folder is only accessible through the todo_api.ex file, therefore I can easily change my core without affecting the clients consuming it.

1 Like

Along the lines of documenting exports, I really love how defdelegate requires the args. That way, if you’re doing DDD, your aggregate root will contain a whole bunch of delegates that document, in code, how the entire context works (this is in a scenario where you’d have nested contexts). And it’s hard for them to get out of sync (save for a variable name) as changing the number of arguments or pattern matching will crash.

2 Likes