3 ms·
I'm curious about what aspects of the syntax you consider to be unreadable. Elixir code tends to be terse (though less terse than something like Haskell), but I
by ShaneWilton 10y ago
I'm curious about what aspects of the syntax you consider to be unreadable. Elixir code tends to be terse (though less terse than something like Haskell), but I wouldn't call it gobbledegook.
The server implementation [0], for example, is 25 lines and handles a lot behind the scenes. Those 25 lines are all you need to define a process that can leverage all of the supervision and hot code reloading benefits offered by OTP. There's a lot going on, but it's mostly just pattern matching tuples and maps, and it only takes a few hours of messing around with Erlang or Elixir before that kind of thing stops looking so scary.
[0] https://github.com/sasa1977/erlangelist/blob/master/examples/buffer/lib/buffer/server.ex https://github.com/sasa1977/erlangelist/blob/master/examples...
- aetherson 10y agoSure, like this: %__MODULE__{buffer | size: buffer.size + 1, queue: :queue.in(item, buffer.queue)} So that involves a bunch of operators that are very specialized to Elixir -- the %, the | are both very unclear in my opinion. And the __MODULE__ construction is clumsy. Or worse: {:ok, { :ets.lookup_element(buffer.ets, pull_index, 2), %__MODULE__{buffer | size: buffer.size - 1, pull_index: rem(pull_index + 1, buffer.max_size) } }} Same problem as above, plus the nested set structures that Erlang loves, but which rapidly become super difficult to decipher. Oh, and also the overloading of atoms to work like pseudo-objects.
- ShaneWilton 10y agoI agree that your second code example is difficult to read, but I think that is more a result of a lot of logic being inlined into one call, than it is a property of the language. % and | are going to be unclear if you're an outsider looking in on the language, but that is true for any sort of specialization. I'd be interested to know what languages you find to be readable, because I can all but guarantee that there's aspects of that language that I'd find arcane -- not because they are, but because I don't know the language. I'm not terribly interested in discussing whether a language is readable or not, because a lot of that is going to come down to subjective opinion, but at the end of the day, I find that Elixir lets me quickly write maintainable code. I'm not entirely sure what you mean by "overloading of atoms to work like pseudo-objects", do you mind elaborating?
- aetherson 10y agoProbably too late to reply, but I mean things like :ets.method or :queue.whatever.
- karmajunkie 10y agoWhat you might be missing is that modules names are just atoms, and thats how you get to erlang libraries/applications in elixir. In "MyApp.whatever" MyApp is also an atom. It's not any kind of overloading or object fakeout at all. The use of __MODULE__ just refers to the current module, and its use is quite common. It's perfectly idiomatic and straightforward to read if you've written enough erlang or elixir to recognize the syntactic elements without the translation hiccup. At this point I find Elixir code a great deal easier to read than a lot of ruby, php, or C++ code I've had to work with in the past. While it may not really be your cup of tea, you can't fairly accuse it of something that has more to go with the reader than the writer.
- aetherson 10y agoI'm not missing it, I just don't like it. It makes the language difficult to understand when any given time you run into an atom, maybe it's being used like a ruby symbol (such as the way the initial :ok gets used in the examples I pasted), but maybe it's being used as a module. My problems with __MODULE__ are, first, it's just super clumsy to have heavily used language constructs with lots of underscores around them, and I don't really understand why anyone ties themselves to that particular semantic. I hate it in Python, too. Almost any other way of distinguishing it from other language elements would be less jarring to read. But second, %__MODULE__{ buffer | size: buffer.size + 1, queue: :queue.push(buffer.queue, whatever) } does some kind of magic that I'd have to look up the details of, but basically copies buffer but overrides some of its elements, right? That's why you don't have to set max_size explicitly? It's a hack that lets you have actually immutable objects but deal with a logically mutable buffer. It's a weird combination of verbose (to copy buffer, I have to write %__MODULE{ buffer, where in another language it might be like buffer.copy()), and compact to the point of overly dense information (I'm putting a lot of expressions on one line), and it's implicit, too -- what parts of buffer are being copied over unchanged? Sorry, better go and look somewhere else. I think this is legitimately hard to get right -- the immutability constraint means that a lot of the standard idioms of programming are a poor fit -- but ultimately this is Elixir's job.
- xiaoma 10y agoMany languages use a {obj | prop: new_value} update syntax. Elm is another example.
- qaq 10y agoWhile if you play a bit with Elixir it will likely grow on you, there is nothing preventing you from writing it as something like element = :ets.lookup_element(buffer.ets, pull_index, 2) pi = rem(pull_index + 1, buffer.max_size) buffer = %Buffer.Ets{buffer | size: buffer.size - 1, pull_index: pi} {:ok, {element, buffer}}