mikejm
I am just learning Elixir. The purpose is to build a server API to interact between clients and a separate database server. I am coming from C#/C++.
I have now been reading a variety of tutorials on GenServer, as that seems to be the best point to start. It has been the first thing I’ve read about that truly makes sense in that it explains how to do truly practical tasks.
If I am understanding correctly, something that says use GenServer becomes not much different once started than an object of a class. The main differences I see is are:
- All internal variables must be stored within
state, typically as%{}so you can store numerous data points as key-value. - Thus the only mutable variable inside the GenServer is
state(but this is really not any big difference as you can store as many variables of any type as you like within that). - Every time one of the major functions of a GenServer type process is called (
start_link,cast,call), this just triggers the callback to referencestateagain and allow you to mutate it or run other functions. - GenServer as a
Behaviorbasically acts as a standard basic Elixir version of a Class you can derive from to accomplish things needing internal state maintained.
Am I wrong? It seems once you start understanding it, it’s just a different way of accomplishing the same thing.
I guess this design of storing all functions inside state and just having very few simple functions to start a process helps keep it more generic or maybe contributes to things being better able to regenerate itself? Plus the Supervisor system to let each run more autonomously?
It also seems like pid is the same as having an object reference. As I understand it every process (ie. object) gets a pid by which it can be referenced and that is how the system tracks these things. You can also give them names which have to be unique, again like objects.
Am I correct that these things are not that different and analogous in the ways I described?
If I am not wrong, I think Elixir would be well served by having an explanation of how concepts and roles are analogous to in object oriented programming. For example, I found this tutorial:
https://medium.com/@roydejong/elixir-a-primer-for-object-oriented-programmers-fd5ef0206943
Although he explains some things well, I think he makes it overall even more confusing. For example, he states that modules are “stateless”. At no point then does he explain how it is possible for anything in the language to maintain any type of state (which is obviously necessary for anything to get realistic done - how can an application or server or process or object do anything without storing its state?).
It would be much better in my opinion to say that the primary differences in these regards are that in Elixir:
- “All functions take arguments as values not references.”
- “Elixir at its simplest uses what are essentially derivations of GenServer to handle states in the same way you usually would with an object of a class. All variables of the process/object should be lumped inside
stateinside there.”
Something like that (if remotely true) makes a lot more sense than just saying it is “stateless” or has nothing analogous to “references” which obviously makes no sense. It obviously must hold/transform variables/state in some way and we must have some way of referring to all our processes (objects) in some way.
Am I understanding the basic analogies? Any thoughts?
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #elixirconf-eu
- #metaprogramming
- #hex










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
kokolegorille
Joe Armstrong, creator of Erlang
sodapopcan
In short, they are similar—but different. I’m not an expert but perhaps my layman explanation could help.
The key thing is to let go of the idea that modules and processes are in any way related. While they do work together, modules are indeed just stateless bags of functions, and processes are used to run those functions and store state. While an object has its state encapsulated within its instance and only the methods defined on its class can operate directly on that state, processes can call functions from any module and have its state altered by any of them. So a process is like an instance, yes, but there isn’t a single module that controls its state. The gen server pattern does indeed associate a single module with a process, but there is nothing forcing that—you could put functions in that same module that are called by unrelated processes. All
use GenServerdoes is metaprogram some functions into your module that are called by a process.For me it helped a lot to build a very basic GenServer-type thing with primitives (
spawn,send, andreceive). There are lots of blog posts about this (kind of too many, heh).I dunno, does that help? It’s a bit mind bending to think about sometimes because they are very similar but ya… basically what @kokolegorille said
benwilson512
This observation is a good one! This article here elaborates on some of the key differences in terms how when you use processes vs functional structures The Erlangelist - To spawn, or not to spawn?
andyleclair
adw632
Not wrong, only slightly inaccurate. The state that a process maintains via the process runloop is never mutated. The runloop is a recursive function, returning a new value into itself each time around the receive “loop”.
Also note that we don’t actually have loops in Elixir, Erlang or BEAM languages since valid bytecode is not allowed to “jump backwards” and therefore it cannot get “stuck” without yielding in a potentially infinite loop inlike almost all other languages I am aware of. The BEAM bytecode can only call another function and that is also how preemption is meticulously enforced through precise “reduction accounting” and why the BEAM provides such low latency. Even bad code cannot block work and processes can always be killed without cooperating and ruining a global “OO state graph” and leaving locks everywhere.
The important thing is that both state and execution context is bound together with real encapsulation. That is not the case in “OO languages”, as Joe’s Armstrong famously said in the book Coders at work:
Yes you are correct. BEAM languages like Erlang and Elixir are the epitome of object oriented.
Alan Kay the father of the term object oriented said that Erlang is the most object oriented language.
The fact is that object oriented languages are not object oriented according to Alan Kaye’s definition.
Alan said he imagined objects as biological cells communicating only via messages like computers on a network.
Well that describes an Elxir or Erlang process cluster communicating using only messages. Don’t you think?
Joe Armstrong thought so…
I think perhaps there could be some room for object-splaining in order, however…
… it might be confusing, confronting and potentially create congnitive dissonance for many to say Elixir is a true object oriented language to those who have been conditioned to what they believe OO is.
dimitarvp
Apart from the excellent answers you already received, I’ll also add that the “mutation” of state (which is not really a mutation, only the observed effect looks like mutation as @adw632 said) is serialized. Which means only one process at a time will have the message they sent to a given
GenServerprocessed (namely the message that changes theGenServerinternal state).Which means that the BEAM VM has been made to make race conditions impossible.
jhogberg
One thing I don’t see mentioned in this thread is agency: a GenServer (or rather its process) is not a passive entity that is acted upon like in “traditional” OO languages, but an active one that can act entirely on its own.
This is very useful when working in domains where you have lots of objects that act independently of each other. Trying to manually juggle tons of different passive objects to simulate them being active is not an easy task.
D4no0
Indeed, this is the crucial thing when we talk about client side and server side logic from “generic server”.
It is confusing when callback functions are mixed with client functions calling them, and this is a general pattern we tend to implement.
The generic examples specified by
__MODULE__callback don’t help at all, as you can have the following definition for global genservers:You literally need to know metaprograming features in order to understand these definitions.
dimitarvp
Oh absolutely, agreed. Though nowadays I wonder if Elixir shouldn’t have made two variants of
GenServeri.e. aStateHolder(can only receive messages that change state) andBackgroundTaskor something. But that would require much stronger static typing and almost formal-proof level of checking and compiling so I see this as one of the reasons why it wasn’t done like this.I’ve mentored novices, both free and for a fee, and a good part of them eventually said that a
GenServeris too generic a tool and it took them some getting used to to it.I am 50/50 about that though, it’s good that we have building blocks on top of which we can make better tools. And, every language and framework has its learning ramp. We can’t make it a 100% smooth and completely painless ride.
dimitarvp
Oh yeah, don’t get me started on this, I had to write two small mini libraries for myself just to make the distinction between
GenServerID and name in my head.