Crowdhailer

Crowdhailer

Creator of Raxx

The Gen* behaviours in Elixir (and erlang) provide a pure interface.

One of the benefits of this is business logic is easy to test. No need to start processes just pass the arguments you want to test to handle_call for example and assert on one of the results.

I’m sure I read in a book that a pure interface was an important part of the setup, unfortunately I can’t remember which one so if anyone can point me to that again I’d be grateful.

Unfortunately with GenServer implemented as it is several things can’t be done in a pure fashion. For example sending a reply to a call in response to a later message.

It’s a fairly contrived example but I don’t believe this can be implemented in a pure manner.

defmodule PairUp do
  use GenServer

  def handle_call({:pair, pid}, from, :none) do
    {:noreply, {:waiting, pid, from}}
  end
  def handle_call({:pair, pid2}, from2, {:waiting, pid1, from1}) do
    GenServer.reply(from1, {:other, pid2})
    {:reply, {:other, pid1}, :none}
  end
end

I was thinking with some small changes to the GenServer design purity could be regained.

defmodule PairUp do
  use AltServer

  def handle_call({:pair, pid}, from, :none) do
    {[], {:waiting, pid, from}}
  end
  def handle_call({:pair, pid2}, from2, {:waiting, pid1, from1}) do
    messages = [
      {from2, {:other, pid1}},
      {from1, {:other, pid2}},
    ]
    {messages, :none}
  end
end

The key changes to this interface is that :send/:nosend are replaced by a list of {target, message} pairings, an empty list giving a same behaviour as no send.

I think the structure {[{target, message], state} could be treated as a writer monad. This might even be a helpful model to add type safety to message sending.

This post is really just me musing. my questions are?

  • Is this a great idea or a horrible idea
  • Does something similar exist already

Showing Posts 20 to 11

Crowdhailer

Crowdhailer OP

Creator of Raxx

Not yet but I would very much like too.

mjadczak

mjadczak

It may be the case that the reply never comes if, e.g. the GenServer is (logically) waiting for some other call or cast before it decides to reply to the original caller.

roehst

roehst

Oh i’ll take a look at it. Did you ever try Elm? It is pretty nice to play with

Crowdhailer

Crowdhailer OP

Creator of Raxx

So I have though about this quite a lot.

  • It would be necessary to be able to send more than one message in response to a single incoming message
  • You need to specify the address and the message
  • An address needs to me more generic than a pid, i.e. it could be a http endpoint.

Finally having http as pure instructions where what led me to develop raxx. To model it like this you need a data structure for the request/response but that is a little of the core topic.

I think all the above points can be address with the following signature

spec handle(message, state) :: {[{address, message], state}

for example.

def handle({%HTTP.Request{path: "sign_up", body: email_address, from: client}}, state) do
  email = %Email{body: "Welcome name", target: email_address}
  mailer = state.mailer

  response = %HTTP.Response{status: 201}
  
  {[{mailer, email}, {client, response}], state}
end

Re dialyzer.

Because more than one spec can be added to a function I think you could specify a lot of information about the protocol

spec handle(:m1, :s1) :: {[:a, :m2], :s2}
spec handle(:m2, :s2) :: {[], :s1}

This says that the handle function does NOT handle the message :m1 when in state :s2

Addresses

I think addresses could be the same as you can use for genservers i.e. pid | atom | {:via, module, term}

comms

I have run some very basic experiments in this project. feel free to ignore, i didn’t get very far but would like to revisit the project one day GitHub - CrowdHailer/comms: Explicit message passing for improved reasoning about actor systems · GitHub

roehst

roehst

I stumbled across this a few times while writing Elixir code. I separated the code into processes that return actions, and processes that execute actions… I did not go further because it was a application that integrated GitHub and Slack and what really broke purity was HTTP calls. And writing a framework for doing it went well beyond the scope of the project.

That said I think Elm has a great approach to managing effects purely and could be a good inspiration :slight_smile: I would love to collaborate on something similar for the backend

Now from a theoretical perspective, recursive types offer a basis for purely functional processes, that might be a solid starting point for more academic work, so I thought it is worth mentioning.

Roughly, a process is a function that takes a message and returns a reply and a new process (which is again a function that takes a message and … all the way down):

process :: message -> (message, process)

Does anyone know if dyalizer can check this kind of types?

voughtdq

voughtdq

Couldn’t you use an arbitrary {to, tag}?

i.e.

def test_gen_server_call(msg) do
  ref = make_ref()
  Server.handle_call(msg, {self(), ref}, nil)
  receive do
    {^ref, reply} -> assert_this_reply_is_correct(reply)
  end
end
Crowdhailer

Crowdhailer OP

Creator of Raxx

This quote is pretty close to saying GenServer is valuable because of the ability to test by just calling one of the handle_calls and checking the return values. (Not true in every case, e.g. GenServer.reply/2)

Another interesting thing about what we did when separating the generic from the specific is that we instantly made it much easier to test our individual modules. If you wanted to unit test the old kitty server implementation, you’d need to spawn one process per test, give it the right state, send your messages and hope for the reply you expected. On the other hand, our second kitty server only requires us to run the function calls over the ‘handle_call/3’ and ‘handle_cast/2’ functions and see what they output as a new state. No need to set up servers, manipulate the state. Just pass it in as a function parameter. Note that this also means the generic aspect of the server is much easier to test given you can just implement very simple functions that do nothing else than let you focus on the behaviour you want to observe, without the rest.

Crowdhailer

Crowdhailer OP

Creator of Raxx

Yep that is the kind of thing im looking for

OvermindDL1

OvermindDL1

Hah nice! That’s new! :slight_smile:

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 94592 917
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
heathen
Quite interesting article Google brought me. Didn’t find any mentions about it here. What do you think in general? Would you use togethe...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
AstonJ
Since we have deprecated our Erlang sections (as we have dedicated Erlang Forums now) let’s add this thread for those who’d like to post ...
New
Null-logic-0
What IDE or editor are you using for Elixir development? Personally, I use Zed, and I really like it, but sometimes I wish there were a ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews