Fl4m3Ph03n1x

Fl4m3Ph03n1x

Background

I am currently reading Unit Testing Principles, Practices, and Patterns to improve at TDD.

The book has object oriented languages in mind and follows the classical school of TDD, but I overall find value in reading. Many of the patterns and ideas the book has, can after all be brought to Elixir and other non-OOP languages. TDD does not adhere to a specific paradigm after all.

Functional architecure

The book mentions two main types of architectures. Hexagonal Architecture and Functional Architecture. The second one (FA), is considered by the author a subset of HA, an extreme where all the side effects and collaborators are pushed to the service layer, thus keeping the core purely functional and easily testable. In reality, his idea of FA is very compatible with what some people know as Functional core, Imperative shell.

Problems with FA

The author mentions that FA works best when the actions to be taken are in the following order:

  1. Get data from external dependencies
  2. Make decision
  3. Apply decision to ugly outside world

This has a major drawback. In many cases, the decision making process (point 2) can’t be done without querying for more external information, aka, the flow is something like:

  1. Get data from external dependencies
  2. Make decision A
  3. Get more data from external dependencies
  4. Make final decision
  5. Apply decision to ugly outside world

Solution?

They author suggests two possible solutions:

  • Use the CanExecute/Execute pattern
  • Send messages to a queue and have external dependencies read from such queue

In the first one, the external dependency, asks the core if it can perform a given action. If so, it performs it.
In the second one the external dependency reads from a queue of actions it needs to perform.

Opinions!

I personally don’t like the first solution. If I have a controller, for example, this means my controller will be littered with if statements. I want my controllers as dumb as possible.

The second one is more akin to what I believe makes sense using in Elixir. But the book mentions no processes nor mailboxes. Just a simple module that has a list of actions that need to be done, it works with mutable state. I am not really sure how to translate this to Elixir.

What do you guys think? What are your opinions?

First Post!

asummers

asummers

Why interact with the world at the core level at all? Push those decisions out as far as humanly possible and wind the functional “units of work” and see if you can remove as many “real world interactions” with the outside world. As an example, rather than do a bunch of queries, perhaps model your program such that it returns a data structure with a plan to run that query. as in e.g. Ecto.Multi. This may not be appropriate for your use case, but you can expand the line of thinking to your own domain.

I think the concerns about not using a process and mailboxes are out the window given you’re on the BEAM =)

As an aside, in general, lean towards more data structures and fewer functions wherever possible. It helps you maintain program agility as it gets larger.

Most Liked

sasajuric

sasajuric

Author of Elixir In Action

If I understand this correctly, I think I did something similar in my To spawn, or not to spawn article. It’s a long article, but a tl;dr is that I’m building a Blackjack as a functional abstraction, and then stick an imperative shell around it. So for example, to start a single round, we do:

{instructions, round} = Blackjack.Round.start([:player_1, :player_2])

Where round is the functional data structure representing the state of the round, while instructions is the list of instructions which the shell has to execute. An example content of instructions:

[
  {:notify_player, :player_1, {:deal_card, %{rank: 4, suit: :hearts}}},
  {:notify_player, :player_1, {:deal_card, %{rank: 8, suit: :diamonds}}},
  {:notify_player, :player_1, :move}
]

I should note that I’m not advocating such approach in all the cases. In the blackjack example it felt natural because the steps are simple (invoke functional core, interpret instructions, rinse&repeat), and also because both concerns are complex enough to justify the separation.

If the protocol is more complex, requiring multiple different invocations to the core, intertwined with different imperative instructions, I don’t think it’s worth it. Such separation would obfuscate the flow, and the complexity of the imperative shell would require more exhaustive testing at this level, at which point the tests of the core are likely not needed anymore.

madlep

madlep

I did a talk about exactly that a few weeks ago at RubyConf AU (not Elixir, but it’s mostly translatable).

The code is sort of usable, but more demo code for talking through. I’ve almost got it polished up (with tests etc), and will blog about it in a bit more detail soon.

(TL;DR - you can use free monads as a solution to separate functional core / imperative shell)

If you’ve got a simple “input → pure logic → output” problem, functional core is easy - because it’s just plain old functions. However in the case where you have to go up and down through the shell/core it gets trickier.

The worked example in my talk was code that gets data from an external API, iterates over it, then for each item has to query the DB to decide on action to take - so something that doesn’t fit the simple functional approach, as you’ve got lots of side effects intermingled with business logic.

The approach I showed was turning side effects into values in the functional core to returning. The effect values have enough data to call the side effect, and a function to then call next with the result of the side effect once it is executed by the shell. This way you can build up a recursive functional data structure representing the chain of actions (with iteration/branching etc as required) without actually having to execute any side effects - this makes texting super easy to decouple side effects from your core business logic.

The functional core only builds those values, but doesn’t execute them. The shell then receives them as the return value from calling the core and actually carries them out. As it executes each one, it calls the next function for that action defined in the core. The shell then only has to be a bunch of independent service functions for executing effects and applying them to the effect values.

The shell has zero business logic, but can do side effects, while the core is nothing BUT business logic, and with no code for executing side effects at all.

LostKobrakai

LostKobrakai

Who said that the mutable shell needs to be constraint to only being in the controller? If you want a dump controller (only doing http related stuff) then you can have a module in between your controller and your functional core doing the mutable work.

Last Post!

LostKobrakai

LostKobrakai

The book doesn’t mention processes and mailboxes, because such constructs are not usually part of programming languages. Personally I treat processes as similar to class instances when reading books with oop examples. Processes come with internal state, which can be similarly mutated as a side effect. The other side effect processes allow for are communication, which in other languages is often done in a more complex manner via some kind of message queue.

With this it’s usually simple to adapt the reasoning of books to code on the beam.

Where Next? Top

Trending in Discussions Top

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
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
axelson
Hi there! :wave: @frigidcode and I (but mostly him) have been running an Elixir Book club, we’re almost done with Designing Elixir Syste...
New
achempion
I’ve been using Emacs as my main code editor for more than a two years. It’s a custom build version although I’ve tried doom emacs and sp...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New
jtormey
Lately I’ve been thinking about how to organize components as a LiveView application grows. One of the pain points I’ve found (for myself...
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
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
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
georgeguimaraes
Just published claude-code-elixir, a plugin marketplace for Claude Code with Elixir support. These are the plugins I’ve been using for my...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews