Hacker News new | ask | show | jobs
by halostatue 766 days ago
We’ve had Elixir in production since 2017 and have not found any of the items you have mentioned to be issues.

- two different string types: You have undercounted (three types in Erlang, and Elixir adds a fourth), and suggested that something which is a non-issue for the vast majority of Elixir code. Most Elixir code deals with `String.t()` (`"string"`), which is an Erlang `binary()` type (`"string"` in Elixir, `<<"string">>` in Erlang) with a UTF-8 guarantee on top. A variant of the Erlang `binary()` type is `bitstring()` which uses `binary()` to efficiently store bits in interoperable ways. Code interacting directly with Erlang often needs to use `charlist()`, which is literally a list of integer values mapping to byte values (specified as `'charlist'` in Elixir and `"charlist"` in Erlang; most Elixir code would use `String.to_charlist(stringvar)` in the cases where required.

Compare and contrast this with the py2 to py3 string changes and the proliferation of string prefix types in py3 (https://docs.python.org/3/reference/lexical_analysis.html#li...).

- three different types of exception: true, but inherited from Erlang and the difference is mostly irrelevant. The three types are exceptions (these work pretty much as people expect), throws (non-local returns, see throw/catch in Ruby; these are more structured than `goto LABEL` or `break LABEL` for the most part), and process exits. In general, you only need to worry about exceptions in most code, and process exits only if you are writing something outside of your typical genserver.

- return AND throw versions for almost every func: trivially untrue, but also irrelevant. Elixir is more sparse than Ruby, but still comes more from the TIMTOWTDI approach so most libraries that offer bang versions usually do so in terms of one as the default version. That is, `Keyword.fetch!` effectively calls `Keyword.fetch` and throws an exception if the result is `:error`. It also doesn't affect you if you don’t use it. (Compare the fact that anyone who programs C++ is choosing a 30–40% subset of C++ because the language is too big and strange.)

- slow compiler without much in the way of compile-time checking: I disagree with "slow", even from a 2020 perspective, and "compile-time checking" is something that has only improved since you decided that Elixir wasn't for you. Even there, though, different people expect different things from compilers, and not every compiler is going to be the Elm compiler where you can more or less say that if it compiles it will run as intended. (I mean, the C++ compiler is both slow and provides compile time checks that don't improve the safety of your code.)

- constant breakages over trivial renamings "just because": false‡. Elixir 1.0 code will still compile (it may have deprecation warnings). To the best of my knowledge, nothing from 1.0 has been hard deprecated. ‡If you always compile `--warnings-as-errors`, then yes you will have to deal with the renaming. But that is a choice to turn that on, even though it is good practice.

- having to burrow into erlang errs half the time since many elixir funcs are just wrappers: not an issue in my experience, and I can only think of a handful of times where I have had the Erlang functions leak out in a way where I needed to look at Erlang error messages.

Elixir isn't suitable for everything, but frankly your list of so-called shortcomings is pure sour grapes.

1 comments

I guess I will just have to stay sour with my sub-second compile times, actual compile-time type checks, stable naming of built-ins, single-binary deployments, and uniform error handling.
Better to say that whatever your resulting platform is (probably Go based on what you’ve said), it suits your constraints.

We've found that our constraints are better met for our most important project by Elixir. We are building something else in Go (mostly because it will be easier to find cheaper contractors to build these boring things once we have the first version done) and we still deliver a number of things in Ruby, and there's Typescript for some things. Use what works at the time you need it.

I personally find Go to be a tedious language and think it could use a fair number of DX improvements, ranging from adopting some form of Rust's terminal `?` sugar to just better recommendations on project structure and layout. It took far too long for Go to get generics, and the platform-specific build files being based on filename (e.g., `foo_aix.go`) is clever with all of the hatred I can put into that.

I have shipped software in ~40 different programming languages and distinct dialects over my career. Go does a number of things well, but some of the choices made are frankly bizarre. There are restrictions on how you can name things because of the platform bit and `internal/`, but then there's no recommendation on how to lay out a project otherwise. I’ll still use it any time over most other lower-level languages, but the language itself does not spark joy. The compiler is good and fast, but I have less confidence that I have built the right thing from it than when I use Rust.