Hacker News new | past | comments | ask | show | jobs | submit login
Why am I interested in Elixir? (underjord.io)
341 points by muhic on July 4, 2019 | hide | past | favorite | 178 comments



I'm currently building a video course hosting platform with Elixir / Phoenix and all I can really say is this has been the nicest tech stack I've ever used in 20 years of web development.

IMO it really does feel like you get the best of everything (developer productivity, developer happiness, great language for creating maintainable code, OTP and the BEAM bring a lot to the table, it's memory efficient, tracing code without a ton of experience is very doable, it's fast as balls and Phoenix channels / LiveView is a great abstraction on websockets). I find myself fist pumping all over the place as I make progress towards this app while learning as I go.

I don't think I've liked a technology this much ever and I'm honestly surprised it's not already more popular than it already is. I've been around for the release of a lot of major frameworks and technologies (Rails / Django, Node, Go, etc.). Nothing has impressed me this much. It truly feels like it's a zero compromise environment to write code in and I hope I can still be using it in 10+ years.


I love hearing stories like yours. I wonder how Elixir would compare to Clojure, which I've used and liked for web development for many years. I hear nothing but good things about Elixir but I wonder if it would bring something new to the table for me in the "really liking it" category.


I think you will find Elixir itself very similar to Clojure. They are both functional, dynamic languages with immutable data structures. They both use protocols/behaviors to achieve polymorphism in similar ways (e.g. functions working with collections (Clojure) and enumerations (Elixir)).

While there is a superficial syntactical difference I think the most important differentiation is the runtimes, the JVM versus the BEAM. JVM will outperform BEAM with regards to computations, while the BEAM has better support for true immutability and concurrency.

With regards to web development specifically the Elixir community seems to more or less have converged around a framework approach with Phoenix, while Clojure (too me) seems more geared towards a collection of libraries approach, á la Node.js.

I don't have much experience with Clojure's tooling story, but the tools that come come bundled with Elixir, like Mix (build tool) and ExUnit (testing framework), are very good. Compared to the languages I have experience with (Python, Ruby, JS, Java, C#) Elixir provides the best developer ergonomics out of the box.


I haven't worked with Clojure, but one of my subscribers wrote me contrasting exactly that:

Wanted to thank you again for making these videos, I learned a ton and I'm really into Elixir now. If only there was a way to use it at work!

One of my favorite things about Elixir is Erlang/OTP. It's as if you're in Disneyland and everything's nice and colorful. But sometimes you go into a basement and you discover the entire thing is built on top of ancient Jedi ruins and these immortal, powerful forces lurk just beneath the surface, at your disposal.

Clojure is the opposite. It's all nice and fine as long as you're in Clojureland, but then you look behind the curtain and it's a huge pile of cards and matchsticks that hold everything together, and the glue is made of mutable state and classes.

And man, the tooling. I can't say this enough. It took me about 3 seconds to create a new project with mix new to get the unit tests and everything set up for the flatten challenge. I don't even know how to set up unit tests in Clojure, and I've been playing with it for 5 years.

So basically OTP == "ancient Jedi ruins" :D


IMO BEAM is a better VM than JVM despite the number of libraries.

It provides a more robust, performant and observable platform for Web development. (Technically JVM is faster batch operations but for Web, most Elixir apps win)

More importantly, it's a perfect fit for immutable functional languages. Everything is immutable and proper tail-recursion are provided. Clojure is great but it has several different ways to define state and type, and sometimes I found they're a little bit awkward. With BEAM process, there's no need for language to have constructs for state/reference/async handling, which could makes the language itself more simpler and more compact.


Relatively, JVM and Clojure ecosystem is much more mature and performant.

I would any day prefer Clojure and framework Pedestal.

https://github.com/pedestal/pedestal


More mature? Both Erlang and BEAM predate Java and JVM. The Erlang ecosystem is super mature and performant.


It's a great stack. I've been primarily using it for the last 5 years although for a largish .5 million unit IOT backend not web development.


> I don't think I've liked a technology this much ever and I'm honestly surprised it's not already more popular than it already is.

I think a big part of it has been a lack of learning resources. A major part of why I started my site and YT channel was the frustration I had learning Elixir in 2016. Phoenix was even tougher. At that time, even the paid resources were mostly out of date. I found about 70% of everything I came across (books, videos, blog posts) wouldn't compile on Elixir version > 1.0! The changes in Phoenix 1.2 and 1.3 were a similar story.

The one exception to this was Dave Thomas's Programming Elixir book. I bought version 1.2 and he religiously updated it (for free!) all the way through its current version. I never found anything in it that didn't work on the current version of the language. Well done, Dave!


Are there no bad parts?


Of course they are, but thé article seems to be from someone interested by elixir, rather than someone who used it long enough to discover them (although I might have misunderstood the article.)

From own experience, the problematic parts are :

- deployment being a bit messy if you try to follow 12 factors (might be improving in 1.9)

- absence of a decent debugger

- younth of the ecosystem and size of the community (aka hard the find a maintained lib that works for a few common issues.)

- compiler being a bit slow to my taste (YMMV)

- then, of course, the elephant in the room: the absence of the kind of static type checking that facilitate refactoring ord relatively large codebases.

Nothing unbearable or permanent, but some things to keep in mind. Go try it !


BEAM based languages lack "good" debuggers, because they historically depend on tracing instead of debuggers. I recently wrote a blogpost about the tracing landscape in Elixir. https://www.erlang-solutions.com/blog/a-guide-to-tracing-in-... But the main reason is that BEAM languages are mainly used for concurrent computing which is a very difficult problem to debug with classical debuggers. I know it is not a mainstream way to "debug" but a very useful one once one learns it.


Wow, since moving over to Elixir/Phoenix I have been doing all my bug fixing this way but wasn't sure if I was doing it right!


Author here. You are right in that I haven't had a chance to see what a few-years-old Elixir project feels like to run and maintain. I've been following and experimenting with Elixir for a few years and I'm doing more stuff with it recently.

And the post was very much intended to be enthusiastic about cool things, rather than nuanced and looking for problems. So I think your take-away is entirely fair.


Using a combination of IO.inspect and IEx.pry, I've never found myself wanting or needing any other debugging tools.


Can you describe a situation where you would have wanted a debugger that io.inspect couldn't get you out of? I haven't found one yet.


- deployment is terrible like really bad

- average performance ( like 10x slower than Java even worse for CPU intensive tasks )

- dynamic language ( this is the worst part ), working on large code base means problems ahead

- lot of features from BEAM / OTP that are not that useful and done better on modern cloud platforms ( Kubernetes for instance does a lot of similar things but better and more flexible, apply to any languages ) People like to talk about hot code update which imo is a terrible idea, you should have a proper CI/CD pipeline to do that and not rely on dangerous features like that.

- lack of ecosystem / libraries

- it's FP, I count that as a personal cons


> - dynamic language ( this is the worst part ), working on large code base means problems ahead

IMO, I the fact that Elixir is a dynamic language is not that much a problem thanks to pattern matching.

Elixir's pattern matching is really powerful, allowing for things like:

    def move(_state, _x, _y, kind) when kind not in ~w(forward backwards) do
      {:error, :invalid_move_kind}
    end

    def move(state, x, y, kind) do
      get(state, x, y)
      |> do_move(state, x, y, kind)
    end
In this case, I am asserting that "kind" is either string "forward" or "backwards". If not, I return the an error :invalid_move_kind. I don't need to check if pos is nil or not string, since I can assert using pattern matching that everything with my inputs are right.

Also, there are things like structs:

    defmodule RobotVsDinosaurs.Robot do
      alias RobotVsDinosaurs.Robot

      @enforce_keys [:pos]
      defstruct name: nil, pos: nil

      def set(state, x, y, robot = %Robot{}) do
        case Matrix.elem(state, x, y) do
          %Robot{} -> {:ok, Matrix.set(state, x, y, robot)}
          _ -> {:error, :invalid_type}
        end
      end
    end
What I am saying in the code above is that %Robot{} must have a :name and a :pos entries, and by default they're nil. However, to create a new %Robot{}, :pos must be different them nil. And of course, I can pattern match if my input is actually a %Robot{} and not a simply dict.

You can also do crazy things like pattern matching if a byte array have some specific characteristics, like a magic number.

So while I think it would be nice for Elixir to have types (there is Dialyzer however it is kinda a PITA to use), it is less necessary than Python/Ruby/Javascript thanks to the above.


I agree, and what's interesting about this technique is that you can guard against specific values instead of just types.


Regarding Elixir being a dynamic language, I've programmed in Java, C#, and other strongly typed languages as well as Ruby, Python, Elixir, and other dynamic languages since the mid 90s. Throughout my entire career, I cannot think of a single time when I've thought, "It would be so much nicer if this language was statically typed." It just hasn't ever been an issue for me.

If typing is something you really want to catch at compile time, and pattern-matching and guards aren't enough for you, you can always define Dialyzer specs and run Dialyzer during your build process. I'm currently working on an Elixir codebase that does this, but I've worked on others that don't, and I haven't seen much of a difference either way. Personally, I think strong typing is overrated.


Not to mention that for a decade and more, HN was full of "dynamic languages are the best! Who needs types! Dynamic FTW!" comments and articles. It wasn't fully accurate, and neither is the about-face to "Types FTW! Typing is the best thing ever!".

I have come to enjoy the more self-documenting nature of static typing (in PHP of all languages). Yesterday I worked on a legacy project with no typing, and to determine what was passed to an untyped method (with no documentation) required running the interactive debugger and inspecting what was passed in different scenarios...


Well I mean there are a class of bugs they help to catch but with pattern matching it really don't come up very often. Just don't match the values you don't want. Sure it's a runtime error but you'll catch them pretty fast and elixir is fault tolerant.


> deployment is terrible like really bad

I've seen this stated many times as one of the big drawbacks of Beam languages. But using Distillery with Elixir, it's very easy to generate a release that's completely self contained. Just extract a tarball on a freshly installed system and you are ready to go. If your target platform differs from the one you are compiling your project on, there are various options for cross compiling too


We just bumped to elixir 1.9 and I'm enjoying seamlessly deploying to our dev environment with a single command from Linux and my juniors are working on getting it to go for Mac via vagrant.


Distillery is a pain to set up, but once done, deployment is as easy as:

    mix edeliver build release production && mix edeliver deploy release production
I've set this up as an alias in mix.exs to I just have to type:

    mix deploy
1.9 makes configuration easier than Distillery, I think.


1.9 does some of, but not all of what Distillery does. I'm still using Distillery because it supports upgrades instead of just full releases.


> average performance ( like 10x slower than Java even worse for CPU intensive tasks )

Urgh, that's a disaster.


How confident are you that what you are doing is CPU intensive?


There might be (and probably are), but I haven't really come across anything yet that made me think I chose the wrong tech or I'm deeply upset about a certain design decision.

On the other hand, there's so many nice things that you can take advantage of without being a functional programming / Elixir god and every time you figure something out you think "how the heck was I able to survive before being able to do this?". I'm hardly an Elixir veteran but I'm past the Kool-aid phase. I still get amazed almost every day working on this project for 2-3 hours a day.


The needlessly dynamic typing.

In practice, Elixir data structures are rarely dynamic in nature. All structs have the same fields. Virtually all lists and maps and whatnot you end up writing will have the same type for all elements. Unlike in the JS ecosystem, there is no culture of having functions with, say, optional arguments in the middle, or arguments that can be either a number or a function or a Date object or stuff like that. Unlike the Ruby ecosystem, there's no runtime monkey patching, runtime attribute generation, runtime anything generation. Everything is as robustly typed as your average C# program, but there's no way to write that down.

Or, well, there is, typespecs. And typespecs are imprecise, hard to write, borderline impossible to typecheck (dialyzer errors are shit), and generally just barely worth it because they're so hard to use.

It just feels like.. such a waste. Dynamic typing is nice if it lets you iterate faster, but that's nearly never the case in practical Elixir code, because there's so little dynamism and magic abound (this is a good thing!). At the same time, not being able to see what values a parameter can be, not being able to ever make proper IDE support, for a language and ecosystem that is so static in nature, is just a missed opportunity.

I still really like Elixir, and I agree with the GP's love letter, but I wish it had a friendlier way for me to communicate data structures to the program, future me, and my colleagues. I still hold hope that someone (future me, maybe?) will bolt types onto Elixir in the same way TypeScript bolted them onto JavaScript. It should be easier than TS because of how static-y the entire Elixir ecosystem already is.

(Sidenote: the usual argument against static types in Elixir/Erlang is that static type checking across process boundaries is hard, if not impossible. Genius Haskellers tried it and failed. My answer is that not having type checked messages would solve 95% of my itch. In the end, most lines of Elixir code is single-process, just transformations and function calls like in any other language. I'll be happy to tell the compiler what type I expect a received message to be and crash at runtime if it isn't - just like I do when I get sent a JSON payload over a REST API or IPC or whatever)


I've never used a proper statically typed language for web development but with Elixir can't you solve the 90% with pattern matching and guards?

For example (taken from the docs on guards):

    def foo(term) when is_integer(term), do: term
    def foo(term) when is_float(term), do: round(term)
If you tried to pass a string into foo at run time, no function pattern would be matched and it would blow up. The above should also give IDEs a way to potentially say "hey, when you call foo, it accepts a term and expects an int or float", and it's also very human readable to figure out what you need to provide as arguments without IDE support.

What's interesting about guards is you can also put expressions in there. Like `when foo > 3` and now suddenly you have a new type of integer that only accepts integers greater than 3, except it's not an explicit IntegerGT3 type you have defined somewhere.

In practice, how much worse is that vs a "real" statically typed language, other than putting guards on things is optional?


I've used it sparingly, I think deploying is pretty bad with it.

I like the convenience of deploying a golang app, just one executable.


OTP releases let you build something akin to this, where the runtime and all the dependencies are bundled in; so long as you built it for the right architecture (much like a binary), it's a single command to run your app.

This used to be done with Distillery[1], but I suspect its use will go down now that Elixir has native OTP releases.[2].

In either case, there's still work behind it, but it's not like deploying Ruby, Python, or Node, where you have to be pretty careful about library paths and runtime versions.

   [1]: https://github.com/bitwalker/distillery
   [2]: https://elixir-lang.org/blog/2019/06/24/elixir-v1-9-0-released/


> I like the convenience of deploying a golang app, just one executable.

This is crazy. What about configuration, systemd unit file / initscript, documentation, manpage, shell completion. And the ability to tell what is installed, where, and at which version.

HN forgot how to do software distribution to the hype of statically compiled languages.


Deployments are a mess and as it is interpreted, performance is closer to Ruby than Go. I found the community to be pretty disappointing (but YMMV obviously) — it seemed more alternative oriented than the Rust community (which seems more solutions oriented).


Performance is definitely not close to Ruby, this is just blatantly incorrect. Elixir and Erlang can easily hold their ground vs Go.

The BEAM was originally designed to run software for telephone switches. It's often referred to as soft realtime because it is so responsive. It has been battle-tested for now over 3 decades.

Where the BEAM falls flat is pure number crunching but it's blazing fast with binary processing (e.g. string parsing).

A fresh phoenix project where you render a nontrivial template easily reaches sub millisecond response times, without any kind of optimization.


For CPU intensive tasks, performance _is_ close to Ruby, while both Ruby and Elixir are far behind Go. Soft realtime doesn't mean fast, it just means good std deviation on response time and not a lot of missed deadlines. Fortunately as the article points out, there's always Rustler if you need to optimize while still keeping BEAM crash proof.

That being said, I agree that performance for certain IO intensive applications like web servers is very good, way better than Ruby. Subjectively Phoenix feels way, way faster than Rails in development and is much more performant in production.


>>For CPU intensive tasks, performance _is_ close to Ruby, while both Ruby and Elixir are far behind Go.

Yeah, but how many companies do you know of that perform heavy computations in such large scale that their choice of programming language has a noticeable effect on the result?

I mean, it might matter for tech giants like Google, who need to squeeze every bit of processing power out of their CPUs. For almost everyone else, it will be a non-issue.


It's just that when you say "performance on par with Go", people might imagine you could write a 3D engine in Elixir, which obviously isn't the case.


A fresh phoenix project where you render a nontrivial template easily reaches sub millisecond response times, without any kind of optimization.

Going from Phoenix to Rocket.rs I saw a pretty dramatic speedup on even simple pages. Phoenix was faster than the Rails version of the app, but still slower than a compiled language.


It's not a mess. Mostly It was just unclear mixed of runtime and compile time configuration that contribute the inconvenience. Elixir 1.9 just comes up with a simple built-in release mechanism with more clear on how you put configuration.


It's not a mess. Mostly It was just unclear mixed of runtime and compile time configuration that contribute the inconvenience. Elixir 1.9 just comes up with a simple built-in release mechanism with more clear on how you put configuration.

From the sound of it things have gotten better (by throwing out oft-touted features), but Elixir deployments are inherently far too complicated. With Go and Rust you get a single binary and a single program to run. With Java (and other languages targeting the JVM) you get a single jar archive. With Elixir you get to bundle the whole runtime and spawn a few separate supervisor processes to run even a hello world app. Lord help you if the only maintainer for the deployment tooling takes an unannounced vacation or if the various BEAM processes stop talking to each other because the stars are misaligned (and the deployment tools are minimally tested) and you can't even run an Elixir based database migration as a result.

When I started toying around with Elixir and Phoenix I'd just come off of a Clojure kick. You can get most, if not all, of the syntactical sugar that Elixir promises with other languages that all offer infinitely simpler deployments with more mature tooling. In the case of Clojure you get whatever JVM tooling you'd like.


Sounds compilicated.


Aren't most things that actual survive encounters with the real world?

Make it as simple as possible, but no simpler.


It was a pun.


For web development the performance is within a breath of Go and a kilometer ahead of Ruby on Rails.


Elixir is compiled, not interpreted.


Compiled into bytecode to be interpreted by the Erlang (BEAM) VM.


Are we doing semantics now? Java is also compiled into bytecode, executed by the JVM. Do you consider Java an interpreted language also?


I think most people consider Java to be an interpreted language, it's interpreted by JVM. It's obviously somewhere in between, in the same way that JIT languages are, but it's still not native.


Not sure about the BEAM, but Java isn't considered an interpreted language at all.

First, discerning the compilation step to IR is relevant, which is why for example, Python can be compiled or interpreted. That distinction matters. Even though it is compiled to intermediate representation, it is still compiled.

Now, with regards to the intermediate representation, Java Byte Code is also not interpreted. Java Byte Code is compiled to native machine code either Just in Time by the JVM, or Ahead of Time by the SubstrateVM. Both of these make it a compiled language.

An interpreted language never gets translated into native machine code, it gets executed by a native interpreter, and that's very different. It is more akin to using a language where you read and parse texts, and based on the text, you might execute one or more things. Now if that parsing to execution branching is powerful enough to allow Turing complete behavior, you have yourself a full blown interpreted computer language. This is not what happens in the JVM. The JVM translates Java Byte Code to native machine code Just in Time, and then the native code is run.


We should be more precise: the JVM may or may not have an interpreter. This is implementation dependent. There are JVMs that are interpreted fully and AOT compilers as well.

Hotspot includes an interpreter -- your code may be interpreted until it gets hot and gets compiled.

Still, I don't think it's fair to call Java an interpreted language since the parts that are interpreted are only during warm up or slow enough not to matter.


Yes you are right. Someone could argue Java Byte Code is interpreted, but I think colloquially, that's leading people to misunderstand the nature of modern JVMs, which as you say, will choose to either run the code in an interpreted manner, or compile it first (possibly with optimizations) dynamically at runtime. Those choices are made based on what will result in best performance and safety.

Java on the other hand is 100% compiled (to bytecode). And byte code is machine code for a non existent machine. Actually, I think Sun had built machines that could natively execute Java Byte Code. Someone could build an interpreter for it, but I'm not aware of one.


Java is an interpreted language because you always need a JVM to run it. I even if you distribute the runtime with your code as one package, that’s just an artificial distinction.

Regardless, interpreted does not mean bad, or poor performance for your task.

It’s a technical notion.

Most people would say Python is interpreted, even if you are only running compile python bytecode on a JIT like PyPy. That’s exactly the same situation as Java.


Java is compiled to Java byte code. It cannot be interpreted in its source form. There's nothing preventing it to be, but as far as I know, there are no interpreters for it. Python on the other hand can be interpreted from source, and that's the default behavior, making Python an interpreted language.

This makes Java a compiled language, even though it is compiled to machine code for a machine that doesn't exist. Java Byte Code is an assembly language, and in fact, Sun had at one point machines that were natively using Java Byte Code instruction sets.

Now Java Byte Code is trickier. You could consider it to be interpreted or compiled or both. It is fair to say that in general it is interpreted, but it is just as fair to say it is in general compiled.

What matters most though is to understand that modern JVMs make use of a JIT compiler and optional AOT compilers.

In the latter mode, you can compile Java Byte Code to native machine executables ahead of time or pre-runtime and no JVM beyond that point is required. So you can distribute the app as a self-contained executable. In most cases though, this will be less performant in the average and peak performance, but it will speed up the worst case, such as start time.

In the former mode, the JVM will analyze runtime behavior and based on the frequency of use of various code paths, it will either compile the code to native machine code (cache the compiled code), and then run the freshly compiled code block, or it will choose to interpret the byte code directly. This allows aggressive optimization from information which is only available or easily available at runtime when performing compilation.


That is incorrect. I don't know of any mainstream Python implementation that has ever interpreted the code directly, except perhaps in a context like evasl(). CPython has all ways compiled to byte-code first.

Actual sourced-based interpretation is _very_ slow, and pretty rare in anything that sees meaningful real world use. Ruby was, back when it was an order of magnitude slower than Python, but no other example jumps readily to mind, and Ruby went bytecode with the release of 1.9 in 2007.


Ah you are right, I didn't know that about Python. Since it can compile to IR on the fly though, I think it gets more tricky as well. Probably fair to consider that interpreted or compiled both. Like they say in their doc:

> Python is an interpreted language, as opposed to a compiled one, though the distinction can be blurry because of the presence of the bytecode compiler. This means that source files can be run directly without explicitly creating an executable which is then run.

OpenJDK can't do that. You need to pre-compile the source to byte code first.


There is nothing about Java that requires a VM. That is just how Sun decided to implement it.

GCJ, for instance, allows AOT compilation of a Java program that requires no JVM.


It can be both. .ex files are compiled, .exs files are interpreted.


> I don't think I've liked a technology this much ever and I'm honestly surprised it's not already more popular than it already is.

To be honest, this can probably be almost 100% attributed to the fact that it is not backed and heavily promoted by a tech giant.


I love Elixir, but the learning curve was steep. Async everything, dialyzer, macros, Erlang errors, charlist vs string vs iolist... It was a lot to take in.

Edit: 4 upvotes in 4 minutes. I guess people are reading this... for anyone who hasn't already discovered it, the saving grace for me was the Elixir Slack channel. I also learned while working with someone who really knows Erlang, which helped quite a bit, too. If anyone learning Elixir gets to the point where they want to scream, feel free to email me. If I can help, I will! Email in profile.


Hmm, I'm primarily a C# dev and recently spent a morning getting to know Elixir, and was actually surprised with how flat the learning curve was.

Now, writing idiomatic functional code while coming from an OO background, now that is difficult!

I have to say though, I loved the short time I spent with Elixir so far - it's hard to put a finger on it, but it just feels right. But... I do wish it was statically typed :(


> Now, writing idiomatic functional code while coming from an OO background, now that is difficult!

This is one of my favorite parts. I mostly program in Python and Ruby and am very very wired to write programs in an imperative way.

So when you finally "get" things like Enum.reduce and you use it twice in 1 day to solve 2 different problems, it's a real rush.


> Async everything

What do you mean by that? Most code in Erlang/Elixir is synchronous, meaning it will block the process (but not the scheduler). Maybe you meant concurrent (or message-passing) everything?


Yeah I'm curious too.

I've written about 2,000 lines of code on this course platform so far and everything except for 1 function is synchronous from a "this is my code" standpoint. I only used Task.start once to launch something in the background.


This is what I like about it: my junior had an ask from management to profile a customer task, as a single process, and as a concurrent job to simulate more than one simultaneous client. I'm nonzero suspicious it was asked to try to get him to trip up and have an excuse to can him. I instructed him to do the job in elixir. He got it done, then I wrapped it in task.async one-line, and it just worked and he understood what was going on, and was able to generalize the concurrency rules.

The elixir standard library is just full of things that make your concurrent programming seamless, easy, and bulletproof, even for a junior that's never done things that way.

I can't imagine him getting this correct with python async, JavaScript promises and callbacks, or having to create go channels.


In the Erlang VM, process execution can stop at an arbitrary point and resume later on. This time slicing is done to ensure that no process, takes up too much time. So you must treat your code as asynchronous since if you get a value from another module, then get it again in the next line it might have changed because your process was suspended in between those two lines.


While that is true it does not make a difference for day to day programming, unless you are super accustomed to think about locks.

You can get very far in Elixir without considering the nitty gritty parts of OTP.


It's interesting that people would like Elixir without understanding OTP.


Some of us like to have productive junior devs that can ease into an understanding of otp.


All of the GenServer stuff is built on top of regular Erlang processes. It's all async. You'll might find out when your server gets busy and GenServer calls start timing out


I don't think it's a good idea to worry about dialyzer, iolists or especially macros for someone just learning. In fact, I don't think OTP should be the first step, either.

You can be very productive with Elixir before learning the things that it's famous for!


.... and no vectors/arrays.


You've got:

http://erlang.org/doc/man/atomics.html and http://erlang.org/doc/man/counters.html for cheap, mutable uint64 arrays;

• The http://erlang.org/doc/man/array.html module for a functional persistent vector abstraction (similar to the one in Clojure);

• ETS ordered_set tables, for mutable AVL trees (for when you want to cheaply apply a sequence of random mutations to a collection of terms without building a lot of garbage);

• The process dictionary (https://www.erlang.org/course/advanced#dict), a mutable per-process hash-map data-structure, for when ETS ordered_set tables have too much inter-process copying overhead.

All that, plus Erlang lists (singly-linked lists) are really cheap the way Erlang does them. (Each Erlang actor-process gets its own heap that gets independently GCed; short-lived processes never experience even a single GC, so allocating linked-list nodes (by consing to the beginning of a list) is just a matter of incrementing the actor-process's heap's free pointer—about the same level of optimality you'd hope to get from pushing to the end of a vector.)


Thanks! Didn't know about some of those.



There is List, which is similar (but not quite the same).

Other than that, you may want to give this a read: https://elixirforum.com/t/arrays/8850/13

tl;dr: the type of data structure that would satisfy your use case almost certainly exists in Elixir, but you need to look around a bit.


you know the best framework is?

is the one that's in production and doing its work. Most developers including me, get bored of doing something in x,y,z framework, and we gladly go try something else for a while.

I have a good friend, that jumped on the band wagon of elixir. he openly now admits, he wish he just used python.

I hate band wagoners.. no language , no framework is the cure all.


My current side project is in Elixir/Phoenix and it's sweet. Currently on 1.8.

I'm hoping to move to 1.9. The language just fit my ideology better, Jose Valim just stated that the language is mostly complete. All major planned features are completed unless something come up. The language isn't bloated, it's small and sweet, and it doesn't go out of the way to add random unnecessary features to the core.

I was fullstack since ~2008 and it is becoming pretty hard to be fullstack when frontend moves too dang fast for me. So the pace of Elixir and Phoenix is amazing. I know these two technology are relatively new but compare to its peers it's boring tech; as in it's battle proven and it's not going to radically change that often (at least not for the sake of hype or reinventing the wheel).

The community over at elixirforum is nice. There seems to be quite a few camps. The ones that stands out to me are the web dev and embedded camps.


The important thing to add here is that since it has powerful, hygienic, macros, there is nothing stopping you (or library writers) from adding new constructs and abstractions with zero runtime performance hit.


My startup uses Phoenix, along with a Vue front-end. The combo is an absolute joy to use. The benefit that has had the most business impact for us so far is developer productivity.

A couple of months ago we met with a prospective client to discuss their use cases and conduct requirements gathering. They really liked what we showed them, but the CFO wanted a dashboard that displayed the data in the app in a specific way. We shook hands, told them we would contact them soon, and parted ways. It was around 9 AM when we stepped outside the client's office and got back in our cars.

By 5 PM that evening, we had a stunning, fully functional dashboard built and deployed to the demo environment. It utilized a few new database tables that aggregated data, some data processing done by Elixir, passing that data to the Vue front-end and displaying it using Charts.js and a few other UI libraries.

I sent a quick email to the CFO with the URL. He responded ten minutes later with, "holy cow, why didn't you show this during our meeting if you had it already?!" :)

We are meeting with them again later this month, hopefully to sign them on as a client.

(For reference, I've used Rails and ASP.NET with C# before Elixir, along with JQuery back in the day, and then a bit of React. Can't speak about other frameworks and languages.)


Same experience here, it truly is an absolute joy. In the front-end, we are using TypeScript with vue-property-decorator [1], it made me like Vue even more. I wasn't much of a fan of developing frontends before, but I actually have been having fun using TypeScript + Vue + Tailwind CSS. I'd also like to try out Svelte in the future.

I'm also doing an startup -- we've been working on the product for only two weeks and we just released access for our first pilot customer, with an initial feature set we are very happy with. Apart from the productivity, it's been a long time since I loved programming. Keep in mind I was doing embedded C development for a few years, so my web-fu is a bit rusty lately.

The only thing I miss sometimes is types, especially when refactoring. Java with IntelliJ will spoil you, I guess. I was thinking about going with Kotlin because of this, but I was really digging the Phoenix project structure and I love Ecto.

All in all, a great experience.

[1]: https://github.com/kaorun343/vue-property-decorator


Great story, and best of luck! BTW, if you haven't tried it yet, the new Phoenix Live View makes those one-off dashboards a joy to make in my mind. Vue is fantastic but it still ends up with two layers which need data piping.


FWIW, I feel like I he this kind of productivity with server-side rendered ASP.NET Core web apps.

Something that is great about .NET is the available tooling - the debuggers in both Visual Studio and Rider are amazing, and it's difficult to imagine living without them.

AFAIK, you're relegated to rather primitive "println debugging" with Elixir - has this been an issue for you in practice?


> AFAIK, you're relegated to rather primitive "println debugging" with Elixir - has this been an issue for you in practice?

Not at all. The traditional BEAM way of debugging is the tracing modules. For little things an `IEx.pry()` is often good enough though.


> The traditional BEAM way of debugging is the tracing modules

Excuse my ignorance, but by "tracing modules", do you mean "println debugging"?

I had a quick look at IEx.pry(), which at a glace I think gives you a repl at a given breakpoint? It looks like it also requires you to instrument your code with breakpoints, which TBH feels a bit ugly (but could presumably be solved with the right IDE)


I just finished writing my own in-house static site builder in Elixir. I come from the Ruby land. If you thought Ruby was awesome, then you would LOVE Elixir. I was hesitant at first, thinking "not another language.." but once I gave it a shot, I stopped looking back.

I'm an individual consultant with a very small team. Without Elixir, many projects that I've finished in weeks would have taken me months. This is a seriously under-rated language that would make you wonder why didn't you give it a shot earlier. It's that good.

My static site is now way faster than Jekyll or Middleman. I didn't re-write it for speed, speed was just a nice side-effect. I wrote it mostly for the language's flexibility.

With Ruby, which Middleman and Jekyll both use, you need to think in the traditional OOP way and sooner or later, you will hit the limits of the language itself. Whereas, with Elixir, everything is taken care of by guards. This is really one of the nicest codebases I've ever written in a while. This is in comparison to my earlier codebase which was written in Ruby, and I had to do all sorts of things like install a library to make the language more scalable. That was the ugliest hack I've ever done. [1]

Imagine, a Wordpress clone, only it runs on your computer and generates static files (this was my goal) but, written in less than a month by one person, working a full time job, with the same fit and finish of Wordpress.

Elixir is amazing.

[1] https://github.com/aetherknight/recursive-open-struct


> My static site is now way faster than Jekyll or Middleman.

What? You mean the generation step is faster?


Yes. Correct.


That's cool. Why didn't you use Wordpress?


With WordPress, you need a server at all times and the HTML is generated each time you try to access your site. Sure, you can use caching plugins, but it only makes things complex. Eg. You need to now keep those plugins updated at all times, along with WordPress itself. A static site doesn't need a server and can work right off a CDN. It's blazingly fast and it's virtually unhackable. There are no security updates whatsoever you need to keep track of.


Thanks. Not sure why I got downvotes for asking a question though.

Also, I recently built http://blog.winricklabs.com for example which uses my own static site generator. I didn't use wordpress just because it'd be overkill.


Precisely! Good job, looks interesting, similar goals.


We are building a sales automation app using Elixir and Vue.

Our dev team seem really happy with this tech stack.

Productivity is awesome - we are four months in and already have similar functionality to a (sort of) competitor that has spent several years on their dev. This is the first dev project I have experienced where we are ahead of our planned milestones. Got to find some more features to quickly add!

My original concern was getting hold of developers as Elixir lacks the pure numbers of people that other server environments enjoy, but this has not been an issue. If anything we have got more senior devs then we would otherwise be able to get as they are excited to be able to use Elixir. Oskar in Poland and Dan in London, hats off, you are awesome!

Not sure if I agree with others that the broader environment is underdeveloped - yes there may be less versions of the same thing compared to a large JavaScript framework, but what is there is absolute quality.

Only (small) gripe is that Elixir is not a fast language. At one stage I thought this was going to be a real issue, but great support from the community and improved understanding on how to do things the Elixir way (let it fail!) seems to have resolved this for us.


> Only (small) gripe is that Elixir is not a fast language.

Which is interesting because Elixir isn't well known for being super fast when it comes to CPU bound tasks, but for a lot of semi-CPU intensive things you'd end up doing in a web app, it's still very very efficient.

For example I wanted to generate 5,000x 19 random character discount codes and my first attempt took 730ms to generate the codes while being a complete newbie to the language and cobbling together Enum.reduce / Enum.random while concatenating strings. Within an hour of asking if someone had a better way of doing it, I got multiple solutions from community members that were able to produce the same results in 25ms while still having very readable code (arguably more readable than my original implementation), and even one person came up with a solution to do it in 3ms.

It's almost hard to imagine being in a position in a web app where 3ms would be considered too slow to generate 5,000 unique discount codes with a custom character set.


Would love to see the 3ms solution. Post a gist if you have it available.


Sure. Here's a gist with 3 versions (original, the 25ms version and the 3ms version): https://gist.github.com/nickjj/99ea84f460f41dae4139d0610ce80...

The reason I didn't use the 3ms version was due to it having too many concepts that are unknown to me. At the end of the day I would still need to maintain the code and the 25ms version is a lot easier to change (for me at least).

But, if I had very strict time requirements, I could just drop in the 3ms version at any time to get the speed boost which I think is reasonable. I only need to bring in the operational complexity when the demand calls for it. Otherwise the more human readable version is fast enough for my use case.


I suspect the 3ms version is still hamstrung by the system rng :) I might try to make it even faster if you don't mind!!


Sure, go nuts. Feel free to ping me on Twitter at @nickjanetakis and / or reply here if you can improve it.


Thanks.


Totally agree with what you said, but there is a caveat no one tells you about when you start Elixir. Coming from CUDA the extra speed you might get with concurrency in Elixir seemed very enticing for me so naturally I began experimenting. In Erlang/Elixir processes are really lightweight, but they do still bring some overhead and, in most cases I experimented with brought no speed advantage whatsoever and were in fact slower.

For example, lets say you want to compute the first million fibonacci numbers using the series' nth number formula. The function below calculates the nth fib number in the sequence; Then, to keeps things simple, we look around in the Elixir documentation and find Task which accepts a closure, is async and spawns a new process. Great you think, with the extra speed from parallelising in blocks of lets say 1k fib numbers we'll surely be faster.

def nthFib(n) do round(:math.pow(@phi, n) / @sqrt5) end

1..1000000 |> Task.async_stream(MyModule.nthFib) |> Enum.map(fn({:ok, result}) -> result end)

Nope. In fact, doing it naively with [0, 1, ... fib[i-1]+fib[i-2] ...] is faster. This was a bummer for me.

If you're interested and want to test something on hackerrank yourself, take a look here for a post I made while first experimenting with Elxir's processes on overoptimizing the first Euler problem. Noob thread here: https://elixirforum.com/t/hackerrank-optimizing-euler-proble...

Of course it might be just me, trying to apply the same principles I learned using CUDA. In my defence, those principles served me well over the years no matter if I was using CUDA or solving concurrency problems somewhere else. If anyone is interested in pitching in, I'm open to ideas, but for now I'm going to continue thinking that Elixir is fast enough for most use cases, just don't get your hopes up thinking its a silver bullet.


The Elixir ecosystem offers a lot of interesting things. I'd like to underline those:

- LiveView (which I use in production & have recommended for upcoming projects too) is a complete game changer, not because it allows to remove javascript, but because it removes a boundary (between the client & the server), making development & maintenance much faster since you only have one layer, and also making very rich features easier, because you can remain stateful during the processing (if you have interactive rich UIs with e.g. file upload & processing then live reporting as you process the file, this will remind you of desktop programming, in a good way)

- While the initial setup of apps can be a bit cumbersome to my taste (like the SwitchTower period of Rails - e.g. you'll need a build server or a Docker image typically), the mental model of programming is quite simple in a lot of cases afterwards. I would say that junior developers can be onboarded quite easily (I'm starting to train some), and maintenance is quite sweet at this point.

I can warmly encourage you to try Elixir out (a nice way to get started by the way is to code ExUnit tests to try out the language, see https://github.com/thbar/elixir-playground/blob/master/test/... as a boilerplate to get started).


I can't tell what your description of LiveView is suggesting, I will need to read about it, sounds interesting. Is it like a codebase that is implicitly split on a back-end and a browser, without the need to write JavaScript?


Phoenix LiveView is the Google Stadia of web app. Browser is just like a console for sending commands over an established connection (stateful) to server. It's pushing computation and state management to servers, and eliminating a lot of steps e.g. encoding huge internal data structure to json, api endpoints design, huge javascript downloading and parsing, re-rending that data with template on client side. If you use a compile-to-js language on frontend, the extra step is to decode json into internal types as well.

LiveView is the haven for apps that are focusing on B2B and internal tools in organizations. Because it doesn't have to support offline usage, and cost effective since you don't have to have a frontend team.


Phoenix LiveView is basically server side rendering in real time.

The user's actions are sent to the Backend via WebSocket, and the Backend rerenders (parts of) the view and sends them to the user.


But ... why? Sends it back as ... HTML?


Not really, the frontend caches the static parts of the template and the backend only needs to resend the dynamic, changing, parts.

So "Welcome to $mysite, $username." would initially send the full rendering but if the data changes it would only send "Hacker News,lawik" over the wire. Somewhat simplified.

This covers it quite well: https://www.youtube.com/watch?v=8xJzHq8ru0M


pretty much like .NET Blazor and SignalR


All the backend code for my current startup is in Elixir (switched from Ruby, my previous love).

Elixir/Phoenix/Ecto are an absolute joy.

It took a bit to get used to immutability and FP but it feels so "cozy", for lack of a better word, to know exactly what my code is doing and to not be bit by obscured mutability and magic anymore.


From your comment I gather you're new to FP. Same here, but with around 20 years of OO experience, I really struggle to understand what idiomatic FP code should look like - was wondering how you'd coped on that front?


I don't have a direct answer to your question, but once I've played with Elixir a bit a couple years ago on simple projects, the whole FP paradigm clicked, and now I'm writing Elixir full time and immutability/functional programming has become second nature. Everything's just a matter of transforming data from one shape to another.


Coming from Ruby and Python and a bit of node, Elixir encourages a really convenient mental model for thinking about small and large parts of your app in the same way. Net result is fewer bugs created, easier to write tests so you write more, and easy to debug when they do happen. And because of the fault tolerance, most production bugs are so transient they're not causing problems. I've run several small webapps with a few thousand users and the maintenance is minimal because of how solid the language is.


Elixir is great. It's the most fun I've had programming. I wrote Erlang for a little more than a year, and I grew to like the idea, but found it hard to work with. Elixir is bringing me back around.


My experience with Elixir has been great but one area of improvement is debugging. Maybe it's because I'm so used to using something like Chrome's developer tools, but I wish there was something as easy to use for setting breakpoints and inspecting the environment. Would love to know how others are debugging currently!


During a recent developer meetup in my country, I went to an elixir workshop where the host (an independent consultant) confided in us that several of her main clients had asked her to not reveal that they use elixir - they're apparently so happy with their choice that they treat it as a competitive advantage and claim to use a different stack when asked by competitors.

I can't know for sure if she was bluffing, but the idea really was interesting enough to make me wonder.

Edit: sorry, this was supposed to be a top level comment.


I've heard that a few times now, so I'm not sure if its a sort of community marketing meme or an actual truth. And I'm in the community :P


I'm surprised there are so many comments surrounding debugging in this thread. Did you use `require IEx; IEx.pry()` in your code at all? This gives you almost the exact same experience as calling `debugger;` within javascript. If so what was lacking with that experience?


Regarding debugging, I left a comment higher in this thread: https://news.ycombinator.com/item?id=20359699


Try IEx.pry. It's everything breakpoints and watches are and more, in my opinion.


Has anyone who previously preferred static typing converted (at least for certain projects) to using Elixir? I won't even put plain Node into production anymore, TypeScript at the minimum, but otherwise OCaml or Go.


I generally prefer static types. Elixir is the exception. Immutable variables and structs, coupled with OTP, make it arguably as safe for me as Typescript (whose types can lie if you get unexpected data from an api that doesn't match your type definition, for example). Refactors kind of blow though. Refactors are where I actually miss types, otherwise, I don't really notice.


I'm all in static typing, but writing Elixir has ways less anxiety than any dynamic typed languages I've written.

These are features that help eliminating uncertainties:

1. Function head pattern matching

This also includes anonymous functions used in Enumerable functions like map, reduce. Which is my most favorite way of using pattern matching

2. Guard clause

It's part of function definition so it's visible in very nice ways; not mess with the body.

3. Process supervision strategies

Let it crash! The elegant way of dealing with exceptions and unexpected errors.

It's a fair trade with static typing.


Came from Java. Was an adherent to static typing everything. Dialyzer via Dialyxir is imperfect but helps you bridge the gap, especially with the error message improvements available via my Erlex library. You can turn on an optional Credo rule to require @specs for every public function, and adapt the rule trivially to require on private functions. Dialyxir is notably imperfect, but it catches a surprising amount of bugs, especially with some of the optional flags.


> Dialyzer via Dialyxir is imperfect

Imperfect is putting it lightly. I loathe it. Aside from useless errors ("here's what the success typing looks like! Ignore the any()s, and fix it! But I won't tell you what's _wrong_!"), it doesn't run against tests (which bit my ass in production last month due to some return values being ignored, and only actually matched in tests.)

> especially with the error message improvements available via my Erlex library

My interest is piqued. Does it help unfuck Dialyzer error messages?


I agree, Dialyzer is just... horrible. It' hard to find anything good to say about it, unfortunately. Not that it's a trivial problem to solve, I get that.


As someone who quite likes dialyzer, it is most certainly not optimized for user experience :P


There is a `mix dialyzer.explain no_return` and such, which gives you at least an intuition about what the errors are looking for. Re: my library, the Erlang error messages are just wholly inappropriate so it lexes, parses and pretty prints them to give a much better user experience, due to an upstream decision to not offer the errors in AST form.


I actually started off with Elixir but now working my way up with statically typed langs, most notably Haskell. I've found Haskell's type system allows me to express complex business logic far better than anything else I've ever gotten my hands on. It's clearly superior to Elixir in that regard in my opinion, although it does come at a price (steep learning curve).

That said, in a lot of other ways Haskell is not as ideal for web development, particularly if you need clustered stateful stuff.

Typescript and friends I've never really managed to develop any keen interest on, even though Typescript itself is an upgrade over JS.


Sounds like F# on dotnetcore or Reason may suite you


What makes you say that? I mean what would they do better than I could do with Elixir or Haskell? I use Elixir when typical APIs or stateful soft real-time stuff is needed and Haskell whenever I have some complex data structures and logic to work on.

I'm unfamiliar with F# and .NET stuff. C# I don't like one bit, if that's anywhere near that avenue. I've tried Reason but didn't quite click with it. Ended up preferring Elm for the frontend side, although Purescript could be even a better fit for me there.



Thanks, excellent resources!


Not sure why you don't like C#, as to me it's the most sane and powerful of the Java-esque languages. I'm familiar and have used all the usual suspects, and IMHO I like the .net stack. I said F# because you said you liked both Haskell (ML-style languages) and ELM. (F# however of course eager-eval like OCaml/Reason and doesn't quite have higher-order types.) And you said, "Haskell is not as ideal for web development" - and you can't really beat asp.net core on the back-end side of things. With F# you get back-end code and front-end code - the SAFE stack, ELMish, transpilation of F# to front-end JS using Fable or Xamarin WebSharper (which creates transparent proxies to exec code client-side purely from a meta-data annotation on your fn), Electron on the client, mobile (droid, iOS...) using web-tech, bindings to ReactNative, or the new Fabulous, etc.


Perhaps I've managed to somehow develop an inherent allergy to Java'ish languages. I use plenty of Dart nowadays and as a language it's not something I like to use. However, other things count too and the platform (Flutter) is awesome.

Most importantly, I've grown into immutable data a little too much to willingly give that up.

> you can't really beat asp.net core on the back-end side of things

I guess you could argue that about Elixir too, particularly with soft real-time stuff. And Haskell does a good job in its own right where I find Elixir lacking.

Unfortunately still not seeing the benefits of really putting the effort to picking up something new when there's nothing clear to gain from it.


Elixir is a language that I can very productive in day one. Only Ruby+Go enable me to be productive like that. Lot of people get turn down by so many thing in Elixir like OTP and other cool stuff that you never used and think it's complex.

I literally learn it on the job.

Lack of typing is the biggest issue for me, but pattern matching enable to get the shape of object and to a certain extent and I'm quite sasitify with it.


Where are people getting their documentation for Phoenix? The stuff on the website is awful for grasping the bigger picture and how the parts of Phoenix combine. It gave me the impression of being an immature version of Rails when I tried it recently which doesn’t gel with the attitude people seem to have of it.


The actual API reference is top notch: https://hexdocs.pm/phoenix/Phoenix.html

But I'd suggest reading through "Phoenix In Action" to get a feel for how everything composes together, but its surface area is much smaller than Rails (though I haven't used Rails in a decade)

Phoenix only deals with the web framework component. If you want to talk with databases, the official library is Ecto. For GraphQL there's Absinthe.


I would say Phoenix doesn't attempt to be quite as magical as Rails and Django-style frameworks. It is a bit more explicit and as such hopefully easier to follow. The goal doesn't seem to be Rails for Elixir but rather that every new backend language needs a good web framework or it may be dead in the water.

As someone suggested, check out the Hex docs. That is where you get the better in-depth stuff and API reference. I've found the Getting started-stuff very helpful but also a bit limited in scope.


I've found that it really helps to start with the high-level overview of Phoenix if you don't have experience with 'Railsy' frameworks.

Then look at the 'magic' bits. <app_name>_web.ex is an important one, and the view/template distinction can be a bit confusing at first.

From there I suppose endpoint.ex covers pretty much all the rest of it (and then hands things off to router.ex).

With Rails I was always uncomfortable that I couldn't quite follow or understand the path taken through my codebase, but with the exception of a few 'magic' things, Phoenix is wonderfully transparent in this regard.


For the bigger picture I would say grab the Programming Phoenix 1.4 book. It was written by the authors of Elixir and Phoenix.

It focuses on building a web app and shows how everything fits together while solving real issues, which is a much better learning experience than the documentation IMO. If you come at it with a Rails background you'll probably walk away with a completely different impression.


Programming Phoenix 1.4 by Chris McCord, Bruce Tate and José Valim & Elixirforum

Lots of trials and errors. The one thing that I think was pretty dang unclear for phoenix is authentication.

I went through 3 libraries and scrap many codes to figure out what's what. I settle on POW, the programmer is very nice and awesome he answers a lot of questions on elixirforum.


I'm currently working on my side project the backend for which I've chosen elixir to go with. Looking at performance and long term maintainability, it doesn't seem to be a sucker like rails.

No magic, no hacky patterns. No mess like the one you see with Node. For what it does the platform is hella stable. I drifted towards Go for a little bit but man, code in Go felt too verbose with weird patterns. Go is a solid platform as well. But it's not just my cup of tea for web apps right now.

Scalability is in reach if you need it. Hell, even if you need ridiculous background job processing power, use OTP already. And I forgot, liveView is also rock solid. Honestly, if anyone would ask me what the best platform for web apps right now is, I'd just say Phoenix/Elixir.


I really, really enjoyed Rails when I discovered it and I still like it for some quick prototypes. Since finding Elixir, I haven't ever wanted to maintain a Rails app again, though.

The immutability and the lack of magic you mention are probably the two main reasons Elixir projects are easier to jump into. Even when macros are involved, you can still scroll to the top of the file, look at line with "use" or "using" in it and know exactly what is affecting your module.

With a lot of other languages, it's completely unknowable from inside one file to know where or if external metaprogramming is affecting your module.


The most interesting part of Nerves for me isn't rpi/embedded/resource-limited environments, but rather the ability to remove most/all of the security surface area of the userspace (say, in the cloud).


Oh yeah, that is an interesting thought. I've considered that too. Didn't cover it in the post because it isn't quite the focus of Nerves and I already had a lot to say.

I've seen a few different nerves-systems targeting AWS EC2. Have you tried doing anything in particular with it?


I will definitely be aiming to make my next career move to somewhere where I can be using as much elixir as possible.


Great language and importing other technologies though NiF to integrate c and rust (wrapping c or not) with Elixir. My old company build many systems on it and hope another will in the future.


Elixir has a lot going for it nowadays! My side projects are all using Elixir in some way nowadays.


Elixir is an absolute joy to use. I love to use it whenever I can and coming from Erlang, boy its a step up!

It's just the Phoenix framework for which the initial setup of the project generator feels so opinionated. You have no options, no way to make a template for the generator. Phoenix's LiveView is a great feature and I want to use it. It's just Phoenix itself that feels off for me, not as straightforward as it should be. Does anyone else get this feeling?


You can still make a phoenix project without the generator.

I agree though that phoenix feels almost a little too heavy and opinionated.


coming from erlang elixir is much nicer than that. exunit is miles ahead of eunit and i feel like that would be enough to convince me to switch.

the with syntax is nice and helps to deal with early exit on errors but still feels a bit awkward compared to imperative control flow.

like you can do something like this in an imperative language:

    foo, err = func()
    if err != nil {
      return nil, Err("bad")
    }
    bah, err = func2(foo)
    if err != nil {
      return nil, Err("blah")
    }
    return bah
whereas in elixir you have to do something like the following in order to avoid the nesting of doom:

    with {ok, foo} <- func() |> Error.or({:error, "bad"}),
         {ok, bah} <- func2(foo) |> Error.or({:error, "blah"}), do
      bah
    end
like the with blocks kind of work until you have to transform the errors you receive then you need helper functions. if you are just doing static transforms like above it is not too bad but it starts to get hairy if you want to transform based on the returned error or variables in the function. whereas the imperative style you can inline your error handling logic nicely into the function.

for example what if i want to log something on the error path that includes some contextual information and custom formatting. probably, the easiest way is going to be to use with() and pipe to a custom function that triggers on the error path to do the logging because the code is going to start getting really messy. whereas if i was writing it imperatively i could just inline the logging statement most of the time because it is just a few lines of code.

    foo, err = func()
    if err != nil {
      return nil, Err("bad")
    }
    bah, err = func2(foo)
    if err != nil {
      Logger.error("err: " + err + " when processing: " + foo.name)
      return nil, Err("blah")
    }
    return bah
like i feel this is a bit messy but maybe it is actually not that bad:

    with {ok, foo} <- func() |> Error.or({:error, "bad"}),
         {ok, bah} <- func2(foo) |> Error.or_else(fn err ->
           Logger.error("err: " <> err <> " when processing: " <> foo.name)
           {:error, "blah"} 
         end), do
      bah
    end


The idiomatic way to do that is to match errors in the else branch of the with statement.

I've been using Elixir and Phoenix for a customer for a couple of years. It's ok to great, especially when spawning jobs, with some stains.

I'm not a great fan of the with syntax. I wish they implemented it as a native statement of the language instead of as a macro. In that way they probably could let us write the same code inside and outside a with, instead of having to transform = into <- and add a comma.

But the worst offenders are GenServers. They should really have the syntax of OO classes instead of the incomprehensible handle* functions. After all that's what they are, objects with their own CPU. (Remember Armstrong about Erlang being the only true OO language?)

By the way, that would make it easier to code, to understand and to migrate people from imperative languages.


I think one of the big points about being a true OO language is that the only way to talk to a process is via message passing. To me, the handle concept makes sense when you consider that it's handling a message in its mailbox.


Yes, I see that but it's an unnecessary complication. All I want to do client side is calling Module.func() and GenServer side I wish I could only def func() instead of all those incantations.

Furthermore GenServers mix client side and server side code in the same module. It's very confusing even after years. It's one of the most unpleasant coding experience of the decade for me.

On the other side I'd steal Elixir's pattern matching implementation and add it to every language. It's everywhere in Elixir and IMHO it's its strongest point.


I think Dave Thomas has a bunch of comments on the elixir forums, and perhaps blog articles and videos too that express a similar problem with GenServers. might be interesting to look those up.

IIRC, one approach he encourages is to at the very least keep most of your logic in a separate module rather than have just a GenServer module that contains a bunch of regular functions and a bunch of handle_<x> functions (which is kind of idiomatic, or at least what I've always been taught).

That said, it's been a while since I followed this discussion so I'd be happy to be corrected.

EDIT: calling a 'public' function in a GenServer and having a handle_<x> function actually deal with it does seem to have an explicitness about it that I think I like. Learning about processes and how they work, I imagine I might've gotten confused if a seemingly normal functional call somehow magically handed things off to a different process.


Yeah you may be on to something here. Looking at GenServer callbacks as some sort of pure independent functions would not make a lot of sense, they're tightly coupled to processes/GenServer anyway.


You need to do it this way, it's more idiomatic:

    with {:user_created?, {:ok, user}} <- create_user do
      # do something with `user`.
    else
      {:user_created?, {:error, errors}} -> 
        # something
      _ ->
        # some unhandled error
    end
And so on, use an atom on the fly to identify branches and error conditions. But honestly most of the time you worry about happy paths and Let It Crash.


yeah this is kind of horrible once you get to high levels of nesting. in an imperative language you can return early in the error paths and your function will still be understandable. this doesn't work with a single return. you can easily have 6 guard statements in an imperative language and your function will be comprehensible try doing 6 levels of nesting in elixir and it will be a complete mess.


Maybe the “OK” library [1] could be useful for you.

I would also welcome a better built-in idiom for early returns.

[1] https://hexdocs.pm/ok/readme.html


You also have to consider that the whole BEAM philosophy is "let it crash".

On a "normal" language you have to deal with all error paths or your app goes down. On the BEAM you handle the happy case and common and unexceptional errors. Otherwise, let it crash.

Example: if your app depends on a database that once in a blue moon is unreachable, don't test for connection errors. Just assume the connection was successful, and if it wasn't, most of the time once the process restarts everything is fine.

I have an Elixir app in production, I get Sentry errors once in a while about some weird state outside my control (we work with external APIs) that caused a process to crash, I just ignore them and go on with my life.

EDIT: I highly recommend this video from Saša Jurić: https://www.youtube.com/watch?v=JvBT4XBdoUE


I'm a big fan of the 'let it crash' approach. A lot of code I write is hacker-ish stuff where I just want to get something done and I don't need it to be pretty. I've been amazed at how many of my projects in Elixir just kind of keep chugging along despite the fact that I mostly bothered coding only the 'happy path'.

A while ago I wrote some code that retrieves data from various online sources (cryptocurrency exchanges) at regular intervals. A friend of mine wrote essentially the same code in Python. His code was littered with try/catch statements and even with those the app would often crash because these API's sucked ass. As a result, there'd be gaps in the data, or he'd have to restart the app or set up whatever restart mechanism one uses in the python ecosystem.

Meanwhile my elixir project just kind of kept running and retrieving data, and barring permanent changes in the various API's, it would keep collecting data even in every once in a while the endpoints would misbehave.

My typical approach to projects is to tinker and cobble things together but then also overly worry about all the things that can go wrong. I know I'm not alone in being this way; I've met plenty of developers who seem to be a combination of just wanting to get results but then worrying excessively about everything that could go wrong.

Elixir is singular in how it allows me to approach projects in this manner while still feeling confident that it'll work well enough. And while that might not be the most common use-case, it's really helped reinvigorate my enjoyment of programming.


Not sure I understand completely your problem and I apologize if this is a stupid question, but can’t you just pattern match the errors in with’s else block?


this depends on errors being unique to the line they come from. if any of your errors overlap then it doesn't work. for example what if you have a bunch of functions that return {error, :notfound} and you want to handle each differently. also you don't have access to any previous values instantiated in the else block. if you could have an else block for each <- that short-circuited and had access to previously <- assigned values it would be perfect.


One technique I use to solve this is to name each stage of a complex pipeline with tuple pairs. Then I can pattern match on a specific error. I've found that I haven't ever written crazy error handling and so this has worked well for me


Yeah, if your functions return the same error it's quite difficult to use the "else" pattern. I also dislike the fact that the error handling is not near the function that provokes it, but in a separate block, so you have to remember which function in the pipeline returns which error. I've never written a with-pipeline with more than 4-5 calls, so it's been easy to avoid its limitations, but I can understand it's not optimal if you're writing complex stuff.


Great write up, Elixir definitively deserves all the attention is getting and likely more. I recently helped my team so solve a huge blocker on a project, we had to create a few proxies to handle large amounts of data.

With very little effort or development time we where able to put together a highly scalable solution that is robust and very low maintenance.


Spot on! I recently wrote a blog how we've been super happy with elixir/phoenix at AgentRisk: https://blog.agentrisk.com/how-having-a-non-typical-tech-sta...


Big fan of elixir, FP was something to get used to, but feels so much easier to reason about now that I’m used to it.


What are its promising uses outside of web development? From the little I've seen it looks like a beautiful functional programming language, but I'm not even tangentially involved in web development.


* Hardware, from arduino and raspberry pi to embedded chips (https://nerves-project.org/) * Multimedia streaming (https://www.membraneframework.org/) * Financial services backend (divvy) * IoT (http://farm.bot)

The erlang Vm’s roots go back to telephony switches, so there are many cases beyond web.


The post only covers the web stack briefly.

My current favorite things in Elixir are not particularly web-related:

- Nerves, embedded Linux + the BEAM VM to do IoT-style things in a nicer way. The workflow compared to something like working from Raspbian seems a lot more sane to me.

- Scenic, OpenGL-rendered UI, for embedded hardware or other purposes. I recently used it to get text-rendering and geometries onto an eInk display with Nerves. One big reason for the creation of Scenic was to give IoT/connected devices UI that doesn't require a web stack. For many reasons, with security definitely being high on the list.


People preferring static typing, what's your opinion about Elixir? Do big projects become difficult to reason about and maintain like with Python and friends?


Is it possible for someone to create a statically typed language that works on OTP/BEAM, or for an existing language to interface with them somehow?

I tried learning Elixir a while back and just couldn't get myself to like the syntax/ergonomics. It seems like most of the benefits people list for Elixir are actually attributed to BEAM, not the language itself.


You could look into Gleam [1] and Alpaca [2].

[1] https://gleam.run

[2] https://github.com/alpaca-lang/alpaca


Erlang is strongly typed.

In my opinion OTP/BEAM is the killer feature of Elixir; however, there are a lot of other niceties as well. Macros, pattern-matching, tooling, readability, similarity with Ruby (a plus for Ruby developers) all spring to mind.


Something like https://gleam.run/?


I really, really wish Elixir/OTP/BEAM would take hold in the soft real time / large embedded space. Such a nice fit.


I am learning Elixir and build small library with it. Looking to integrate the Phoenix sometime near. I have asked feedback for this project on elixirfourm or on its slack channel. And people have been nice and helpful.

https://github.com/navyad/moviematch




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: