Iama elixir/erlang developer here to preach the ways of BEAM

iama elixir/erlang developer here to preach the ways of BEAM.

ama.

i work on a large scale distributed voice/text chat app that some'ya probably use.

Other urls found in this thread:

github.com/niahoo/elixir-curry
twitter.com/NSFWRedditImage

Why didn't you use forth instead?
What does BEAM have over JVM?

Not familiar with forth.

Mainly for our usecase, the built in distribution, actor model, per-process heaps (meaning no STW GCs), supervision trees & isolated failures, very easy hot code reloading, excellent tools for debugging app in production, pre-emptive scheduling...

Discord?

What would you recommend for someone who knows nothing about the FP paradigm except the map/reduce/filter functions in Swift?

I'm pretty sure Whatsapp is what OP's referring to.

Oh I didn't know WhatsApp also used Elixir, I was only aware of Discord. I wonder what makes it so suitable for large scale chat systems, IIRC League of Legends also used to use ejabberd which is Erlang based (not sure if they still do).

I am not an erlang specialist, but i do quite some FP. What i've seen from erlang, if you want to learn FP you shouldn't choose it, because in essence that's not what erlang is about, the fundamental unit in erlang is a process. If you want to learn FP dive into haskell or Scheme.Haskell enforces pure FP and gives you genius-cred, so it might be the better choice. Seriously, if people hear you program haskell they instantly treat you like Einstein.

Have you installed Source Mage? Of no os your answer then you should!
It will improve your life in every aspect!

Yes. We use Elixir pretty heavily for our real time stuffs.

elixir is very FP lite (relative to haskell/scala). not really much to wrap your head around, you just have to think of everything as state transitions, as in order to mutate an object you need to create a copy of it.

i.e. in python, say you have a dict:

x = dict(a=1)
x['b'] = 2


In elixir this looks more like:

x = %{a: 1}
x = Map.put(x, :b, 2)


More precisely, we are creating a copy of x and re-binding it to x. Likewise we could have done:


x = %{a: 1}
y = Map.put(x, :b, 2)


x now is not mutated, and y is a copy of x, with the key b set to 2.

You can then think of data transformations in terms of mapping and reducing. That stuff is pretty tame actually.

OTP/ERTS part of BEAM is what is a bit harder to learn.

Elixir, and by extension, erlang, are not really functional. Yeah, they use some functional paradigms, and have some syntaxes borrowed from other functional languages (pipes and pattern matching), but you can write functional ruby and have it look virtually identical to elixir.

Elixir, for example, lacks currying and pretty much every other form of functional composition, save procs/lambdas. It also doesn't tend to rely on monads too much, if at all.

If you really wanna deep-dive into FP, haskell is a decent place to start, but it can and will make you miserable at times.

If you want to learn something completely different than most programming languages you've ever used, I'd recommend trying Factor or Forth. They're stack based and use RPN.

here's a simple recursive fibonacci in factor, for example:
: fib ( n -- m )
dup 2 < [
[ 1 - fib ] [ 2 - fib ] bi +
] unless ;

riot uses erlang heavily too.

Erlang was written for telecommunications, so it's no surprise it excels at soft real-time applications (i.e. chat systems).

BEAM was designed for distributed systems. Each chat room could be modeled as a single process. Processes in beam are very light weight. They generally run a routine called "GenServer". Processes communicate with eachother by passing messages. A process has a mailbox, and reads messages from its mailbox, mutates its state, and maybe does some other side-effects.

Given that communication is done via message passing, it lets you distribute processes across multiple nodes, and pass messages between nodes over the network.

Additionally, given BEAM's soft-real-time guarantees, and it's cost-based scheduling, the VM is able to prevent loaded processes from slowing down the rest of the system. For example, you could have a handful of processes busy looping and consuming CPU without slowing down the rest of the system, as each process is fairly scheduled and pre-empted if other work must be done by other processes.

This beats go, where if N many goroutines busy loop and don't yield to the scheduler (either explicitly or implicitly via doing IO), where N >= gonumprocs, the program stalls out.

Also since each process holds its own stack, GCs can be done on a per process basis, rather than having to GC the entire VM heap (like what JVM might do). This means that GC pauses are very tiny, and only affect a few processes at a time (while not impacting the work of the other processes on the BEAM VM).

I'm currently learning erlang in college for concurrency, am I in for a painful ride?
So far I've only worked with pthread.h in C.

You can kinda curry with elixir, but it's not as nice.

iex(1)> sum = fn (a, b) -> a + b end
#Function
iex(2)> sum_5 = &sum.(5, &1)
#Function
iex(3)> 15 = sum_5.(10)
15


iex(4)> summer = fn a -> fn b -> a + b end end
#Function
iex(5)> summer.(5).(10)
15

concurrency in erlang is very easy. since mutation is done via message passing, stuff like data races are harder to come by. if you can describe a problem, i can describe how it feels to do on BEAM!

wow this fibonacci looks insanely bad. I never had haskell make me miserable, i only ever had being stupid...

here's the haskell way of getting the full fibonacci sequence ( not the most trivial way to do it, but its faster than recursion^^) :

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

if you want a specific one of them use this :
fib :: Integer -> Integer
fib n = fibs!!n

somehow i like this one better :P

for reference here's doing it by using recursion and pattern matching instead of an infinite list :

fib :: Integer -> Integer
fib 0 = 0
fib 1 = 1
fib n = fib ( n - 1) + fib ( n - 2)

Fib in elixir is kinda easy too:

defmodule X do
def fib(0), do: 0
def fib(1), do: 1
def fib(n), do: fib(n - 1) + fib(n - 2)
end

Yeah, you can sort of hack together a curry, due to elixir actually supporting higher-order functions, in a fashion. You can clean that up even further using a macro that defines a curry keyword, i.e.: github.com/niahoo/elixir-curry

But elixir's pipes are fairly hobbled in how limited they are. You can't do some of the cooler things that you could do with Needles or Arrow syntax in Haskell, for example this:

{-# LANGUAGE QuasiQuotes #-}

fNeedle :: (Int, Int, Int) -> (Int, Int, Int, Int)
fNeedle = [nd|
}=={(+1)}=\==========================>
\
}===\ \ /============>
\ \ /
}=) \ (==={uncurry div}=/={negate}===>
\
\=={(*2)}========================>
|]


or its pure arrows equivalent:

{-# LANGUAGE Arrows #-}

f :: (Int, Int, Int) -> (Int, Int, Int, Int)
f = proc (a,b,c) -> do
d

The power of elixir isn't in the functional nature.

Here's the code required to make a process that will receive a message, compute fib, and then reply with the result to another process.

Yeah, OTP is incredible. Elixir could be a lot better with some first class functional paradigms injected in though

My advice:

Learn Erlang first.
It's a pretty small language and has a very intesting philosophy about wht errors are and how to handle them.

Also OTP is almost as big as the Erlang core language itself, basically it's the other half of Erlang..


It's difficult to comnpare Erlang and Haskell, since they are coming form very different angles.

Haskell comes form an academic background, it's very mathematical. Erlang comes from a "egineering" background, therefore it's first and foremost about reliabilty. Erlng being functional is simply because it makes sense, but it's not a goal.

But when it comes to error handling Haskell has nothing on Erlang, because Erlang avoids "defensive programming", where errors are intruders on the pure white snowplanes of your functional programm. No, errors are rather part of everyday business whe signals are getting lost or whatever. I always foudn Erlang way more "down to earth" and fun, even though the type system of Haskell is a class of it's own.

Long story short:
Don't compare apples and oranges, Erlang and Haskell are both great languages.

To expand on this, let's parallelize some work with a very simple strategy:


# Define our FibServer module, and an entry point 'loop' which will receive messages and
# reply with the result of the computation.
defmodule FibServer do
def fib(0), do: 0
def fib(1), do: 1
def fib(n), do: fib(n - 1) + fib(n - 2)

def loop() do
receive do
{from, num} ->
send(from, {num, fib(num)})
loop()
:stop -> :ok
end
end
end

# Build a pool of processes, by spawning a bunch of them that are running FibServer.loop()
processes = for _

Oh absolutely, i'd rather write erlang/elixir any day over haskell. But in terms of things that would just save a shitload of time, I want currying and left-pipes and composition operators.

And i've yet to see another language handle errors quite as well as elixir/erlang

>And i've yet to see another language handle errors quite as well as elixir/erlang

That's because the philosophy of elixir/elrang is to let it crash! The process supervisor will restart the crashed process from a known good state.

Supervision trees are incredibly powerful, but they have a lot of semantics you need to understand to use them effectively. Stuff like supervision strategy, restart intensity, and how failures propagate up the tree.

Of course this is using more lower level primitives. We can just as easily use the `Task` module.

For my next demo, I'll go ahead and run 3 nodes, and use the :pool module to coordinate spawning work amongst them.

Here I am starting 3 seperate beam VM's in distributed mode, giving them the name, a, b, and c.

Pool module was overkill. So instead I'll just spawn a process on each "node" after connecting to them, using the code I pasted above, but using processes spawned on remote nodes, instead of local node.

In this minimal example, I've

a) started 3 nodes which would be doing the work "a", "b", and "c"
b) started a master node called "master"
c) connected to node a, b and c, forming a distribution of nodes.
d) use the Node module to spawn processes on the remote node.
e) use the strategy I outlined earlier to distribute the work between the spawned processes.

Imagine doing this in another language with such ease and finesse. I've basically written 5 lines of code that distributed work across remote nodes over the network.

And the code that did the work on the local node, vs remote node, was identical. The distributed version just takes a list of processes spawned on other nodes.

Each process is referred to as a PID, which contains a process identifier + node that it's on, so BEAM can route the message to the correct node. Sending messages to local processes is basically same as sending it to a process running on a remote node.

In this more contrived example, instead of limiting the concurrency of the computations by limiting them to a pool of spawned processes, what if we just spawned a hundred processes to compute the fib of numbers 0 to 100.

Will this freeze the beam VM? Thanks to the pre-emptive scheduling of processes, a bunch of CPU bound processes is not going to starve other processes from doing work.

Nice examples, thanks!