6 ms·
I like Elixir and the community is amazing, especially for such a young language. Particularly the quality of the standard library as well as the more popular o
by skrebbel 8y ago
I like Elixir and the community is amazing, especially for such a young language. Particularly the quality of the standard library as well as the more popular open source libraries is really high.
That said, there's one design choice that I simply never understood: the dynamic typing. Bear with me, I'm not trying to start another holy war here, I have an argument. Thing is, Elixir very seldomly uses its dynamic typing. Ruby has method_missing and monkey patching, JavaScript (finally) has Proxies, but Elixir has none of these. In fact, most Elixir code is particularly undynamic in nature.
All Elixir code that I've seen looks more like a non-OO version of typical Java or C# code. Notably, variables and seldomly change type. Function arguments are seldomly "a string or an array" like is common in JavaScript. Structured arguments are typically either used as a dictionary, or as a struct (the Elixir word for object, record, whatever), but not really ever "a little bit of both". Modules always have the same set of functions, the functions always have the same signatures.
Elixir replaces Ruby's monkey patching with macros. They feel a bit similar, but notably macros are entirely compile time. There's nothing dynamic about them.
I feel like if Elixir had unambitious static typing, more akin to C# or TypeScript than to Haskell, it'd have significantly better tool support, code would be much much easier to read and follow, and virtually all code that works now would still, well, Just Work except that you'd need to specify types on function arguments of course.
Of course, there's typespecs, but anyone who's ever tried to use them knows how abysmal the development experience is. Dialyzer error messages are impossible to figure out, often showing errors multiple functions down the stack from where the typing or programming error actually is. The syntax is weird, minimally documented and not integrated with the rest of the language. I'm impressed by how well most bigger libraries are typespec'ed, given how hard it is to get that right.
All that said, I'm well aware that even an "unambitious" type system would be hard to accomplish. Making a dynamically typed language surely must be easier than an ergonomic statically typed one - especially if the underlying VM does not depend on static types. Maybe the simple reason Elixir lacks a good static type system is that it's a lot of work.
- di4na 8y agoThe main reason is that no research in static typing have found how to type the function self() in an actor model yet. There are some typed language on the BEAM but far from being reliable on prod. Quite simply, noone knows how to efficiently type the actor model on the BEAM yet. It is that simple
- yawaramin 8y agoThere's a bigger reason than that: https://learnyousomeerlang.com/types-or-lack-thereof#for-type-junkies https://learnyousomeerlang.com/types-or-lack-thereof#for-typ... To recap, one of the main Haskell designers tried to come up with a type system for Erlang but couldn't cover the inter-process communication, probably because a process which has another process' pid is allowed to send it literally any type of message. In Elixir, you notice the dynamic typing mostly when you're defining callbacks that take MFA-style function specs, e.g. def handle_click(m, f, a) ... Last point, you are right about Dialyzer error messages, but people are working on it right now. Look for messages in this discussion from _asummers.
- bjz_ 8y agoMost systems aren't really at the scale where the BEAM really excels. I'd still contend that they would be far better off with a good type system to make it easy to refactor and iterate as requirements change. Apparently Typed Akka is finally starting to arrive at a good design, after a number of iterations over the years, but seems like there is still lots of interesting research to be done in distributed type systems. > In Elixir, you notice the dynamic typing mostly when you're defining callbacks that take MFA-style function specs, e.g. def handle_click(m, f, a) I really noticed it in the `Enum` module - so many `any`s, on simple things like `map`. Kind of ridiculous. And protocols are not type directed, so you don't get the huge win of return type polymorphism. Kind of kills lots of useful patterns :/
- nathan_long 8y ago> Most systems aren't really at the scale where the BEAM really excels. BEAM's concurrency model can be helpful even at small scale. Eg, suppose you have a web app where requests to one endpoint are computationally expensive (by design, or only for certain input, or because there's a bug). If you use (eg) Rails with Unicorn, you have a smallish, fixed number of processes available to serve requests. If N is 10 and you get 10 requests to that endpoint, every other request to your site is now standing in line, waiting to be served. By contrast, Cowboy (the web server a Phoenix app would use) will spawn a new, tiny BEAM process per incoming request, and each process gets to run a fixed number of instructions before it is paused by the scheduler and goes back to the end of the line. So even on a single-core system, a few expensive requests wouldn't affect typical requests noticeably, and the effect would be gradually increasing latency as you got larger numbers of expensive requests, not a sudden and dramatic spike. Maybe this won't matter for your site, but it's something that you don't have to worry about.
- nickjj 8y agoI'm still new to Elixir but we have some type of half baked typing system already available right? For example if you wanted some function to work with both a string and list you could use pattern matching and "when". Something like: def myfunc(input) when is_binary(input) do: # ... def myfunc(input) when is_list(input) do: # ... Of course it's not the same and you lose a bunch of useful static analysis tools, but from a usability POV, you could skim function heads and quickly see what types are supported. To me, this is a big win compared to having a massive elseif or case statement buried into a single function where at call time you have no context on what's expected.