"Tell, Don't Ask" in Elixir: A Story of Pattern-Matching

“Tell, Don’t Ask” is a well-coveredtopic within
object-oriented programming communities. Its goal? Encourage encapsulation by
having the caller tell an object to do something instead of checking on
state and acting upon it. Almost at odds with the control couple code smell,
our goal is to have the caller issue explicit commands without concerning
itself with object state.

Is Elixir object-oriented? From a paradigm perspective, Elixir is a
functional language when looking at aspects like immutability,
pattern-matching, and functions with inputs and outputs, focused on the sending
of messages to “objects” directly. How does “Tell, Don’t Ask” translate?

Thinking about the goal, let’s do some mental mapping. In OOP, objects are a
blueprint with information containing behavior (methods) and data (state).
In FP, we have functions organized within modules, with state being captured in
various values (e.g. Elixir’s Maps or Structs). We want to avoid having the
caller (a function) dictate paths based on information present in our data.

Let’s write out some non-idiomatic Elixir and see what we can improve.

Game.Lobby.add_player/2 doesn’t feel right. There’s a significant amount of
feature envy as it cares about the various shapes of player and how to
construct a %Game.Player{}. Also, why is Game.Player.generate_id/0 public?
It seems all Game.Lobby.add_player/2 should care about is managing its own
structure (the final two lines of the function).

Instead of having Game.Lobby.add_player/2 care about constructing players,
generating ids, and so on, let’s tellGame.Player to handle that instead:

By moving player creation logic from Game.Lobby.add_player/2 to
Game.Player.new/1, we were able to call a single function to take the
appropriate action based on data. It is important to note that the data it’s
acting upon specifically is behavior to construct a %Game.Player{}.

This becomes more important when using the pipe operator, which shines as a
way to transform data.