Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Reducing the maximum latency of a bound buffer in Erlang (theerlangelist.com)
165 points by red_bikeshed on Dec 19, 2016 | hide | past | favorite | 26 comments


As always a very informative post by Saša Jurić. If anyone is looking for quality Elixir study material would highly recommend his "Elixir in action" book.


Another comprehensive and well organized source for learning Elixir I might add: https://elixirschool.com

It covers numerous things: from the basics, all the way to OTP, meta-programming, and even certain popular libraries such as Plug and Ecto.


It's really instructive to just look through and try to grok Saša's code on github. https://github.com/sasa1977


Wouldn't be a thread about BEAM languages without someone complaining that their education was confined to languages that don't look like Erlang and thus Erlang is awful to read.


I learned Erlang before learning (of) Elixir as someone who'd only used C-like languages after an acquaintance recommended it for a project I was working on.

The thing that seemed magical about Erlang was how efficient it's syntax managed to be without being ambiguous. In a weekend I picked up the syntax, and in days I was being productive with it.

I've heard Elixir described as "Rubyfied-Erlang" which would explain why I don't particularly love it, since I don't love Ruby syntax (and I secretly harbor a tiny bit of resentment for Elixir for taking off and essentially cementing Erlang's place outside the spotlight), but it's not bad either and doesn't add that much syntax.

And it's not like the syntax of C-like languages hasn't evolved towards something that looks "different" anyways. Lambdas + Rx/Promises + Streams practically looks like it's own language in most C-like languages...


I'd be programming Erlang for my current project, except that its library position is a bit of a mess - full of 4-year-old projects on github that haven't been touched since they were uploaded. At least Elixir bringing in some Rubyists means there's some decent libraries for connecting to external systems now.


Many of those un-updated Erlang projects on github are reliable, don't overlook them.


Maybe they were not touched because they work? What was missing?


You know, I find callbacks in JavaScript far more painful to read, follow and reason with than Erlang.


I really want to love Elixir, but man, it fundamentally hasn't succeeded in producing a readable, expressive language. Look at his implementations! They're a mess of gobbledegook just like Erlang, plus a bunch of weird magic on top that's not very intuitive.

Syntax aside, the final implementation of this feels like, okay, well, you have access to an in-memory store that's outside of the GC and which works well for your use case here. I'd actually be more interested in further optimizations of the first approach, in-GC, which seem like they'd be more generalizable to other tasks.


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...


Sure, 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.


I 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?


Probably too late to reply, but I mean things like :ets.method or :queue.whatever.


What 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.


I'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.


The atom always means the same thing - a symbolic (named) constant.

The dot operator can be used on an atom type to invoke a function from the corresponding module. A capital Foo.Bar is called an alias in Elixir, and it's the same as an atom :"Elixir.Foo.Bar".

The usage of __MODULE__ is optional. You can also write %Buffer.Ets{...} if you prefer.

There's no hidden magic with %__MODULE__{buffer | ...}. It's an "update" syntax which allows you to take an existing "struct" (essentially a bunch of well-known named fields), and create a transformed version of it with some fields changed. The code %__MODULE__{buffer | size: buffer.size + 1} is therefore the same as %Buffer.Ets{buffer | size: buffer.size + 1}, and it evaluates to a transformed buffer with the size field set to the size of the original buffer incremented by 1.

The original buffer is left intact (since you can't do in-place data mutation in Elixir). However, the transformed version is not a full deep copy of the original. It's going to share as much data as possible with the original version.

Hope that helps :-)


There seems to be confusion here about the language semantics. There is no mutability in the expressions you have shown. buffer.copy() would not make sense since the language is immutable, why would you want an explicit copy mechanism when every update is essentially being performed on a copy.


While 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}}


Many languages use a {obj | prop: new_value} update syntax. Elm is another example.


In my opinion, Elixir is still a more expressive and readable language than Erlang, and if you want to do OTP programming it's the best we have so far. Learning more about OTP has warmed me a little to the syntax of both Elixir and Erlang, because I'm now able to understand the syntactical choices that were made in order to express what's going on under the hood more clearly. As a Rubyist, I also enjoy Elixir's similarities to Ruby, and had to get over how it looked to me for a while before I really understood why it felt so weird to write. It might be a form of Stockholm Syndrome, but these days it makes sense to me so I sort-of enjoy how it works and looks.


I personally find Elixir to be a very readable, expressive and thoughtful language. I agree, Erlang not so much.

Would you care to elaborate which parts you consider to be "gobbledegook"? The code supplied is just a few lines of code.

I don't know how familiar are you with the language, but the magical aspect of it will quickly disappear once your learn a few OTP patterns.

I personally started initially with Erlang, but when I discovered Elixir, I switched, as I found it way more pleasurable.

Python and Elixir are actually the only two languages that make me happy when writing code, and I've tried quite a few of them, from C all the way to Haskell.


> I agree, Erlang not so much.

I thought the same thing once, until I actually sat down and tried to learn Erlang. It turns out that a lot of the decisions that I originally thought were asinine, actually make a lot of sense.

Line terminators for example (, ; .) tend to receive a lot of flak, but they really aren't as crazy as they first seem. Commas come after normal lines of code (you can read them as and), semicolons come at the end of clauses in if or case statements (and you can read them as or), and periods mark the end of a function. It's a little more verbose than something like Javascript or Ruby, but it really isn't as confusing as critics tend to claim!


The things that have always tripped me up for Erlang are capitalized variables, lowercase modules, and bare word atoms. There certainly isn't anything wrong with those choices, but it is counter to everything I'm used to from other languages that it presents some cognitive overhead.

In the end, even as somebody who cares about the look of code a lot, it doesn't matter to me. The BEAM and OTP are elegant enough to compensate for any syntax.


Oh, absolutely. I'm sure the Erlang inventors weren't crazy, and most of their choices have some reasoning behind them. But I personally find Erlang more difficult to read than other languages.


I'm interested in Elixir, but I don't know all that much about it (yet). Could you elaborate on what you mean by "mess of gobbledegook just like Erlang"?

thanks




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

Search: