
Reducing the maximum latency of a bound buffer in Erlang - red_bikeshed
http://theerlangelist.com/article/reducing_maximum_latency
======
qaq
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.

~~~
plainOldText
Another comprehensive and well organized source for learning Elixir I might
add: [https://elixirschool.com](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.

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

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

~~~
vertex-four
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.

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

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

~~~
ShaneWilton
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...](https://github.com/sasa1977/erlangelist/blob/master/examples/buffer/lib/buffer/server.ex)

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

~~~
ShaneWilton
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?

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

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

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

~~~
sasa555
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 :-)

