Was looking at https://github.com/mrwade/multitron/blob/master/lib/multitron/game.ex
def add_player(game, player_id, {_name, _color, _x, _y, _direction} = player) do
%{game | players: Map.put(game.players, player_id, player)}
end
Was looking at https://github.com/mrwade/multitron/blob/master/lib/multitron/game.ex
def add_player(game, player_id, {_name, _color, _x, _y, _direction} = player) do
%{game | players: Map.put(game.players, player_id, player)}
end
I do all the time, along with probably excessive use of when
and plenty of @spec
s. Although for that game specific case Iād probably actually use a record instead of a tuple (basically the same thing, but one has names), or a struct (touch slower, but easier).
I agree. If you were using a struct it would just look like
def add_player(game, player_id, %Player{} = player) do
%{game | players: Map.put(game.players, player_id, player)}
end
Thanx guys Itās not my code I was looking at the talk on youtube and this looked a bit wiered to me so wanted to check with more experienced elixir devs if this is considered a good practice or not.
Seeing your topic title SaÅ”a JuriÄās Elixir in Action immediately came to mind - pp. 110-111:
4.1.4 Abstracting with structs
ā¦
The benefit of pattern matching is that input type is enforced. If you pass anything that isnāt a fraction instance, youāll get a match error.
ā¦
By representing fractions with a struct, you can provide the definition of your type, listing all fields and their default values. Furthermore, itās possible to distinguish struct instances from any other data type. This allows you to place%Fraction{}
matches in function arguments, thus asserting that you only accept fraction instances.
Only upon reading your post did it become apparent that in this case the āstructureā was a mere tuple.
Yep best book Iāve read on Elixir coming from OOP background I am afraid I might have a tendency to overuse structs
I think the best approach is to only match on the structure of something if it matters for that specific function. In some of the earlier code I wrote, I often wrote out some or all fields of a struct I was working on, without these fields actually all being used inside the function. This made it very hard to change the struct around later.
On the other hand I do like to check if my structs have a :fieldname
if I use (structname.fieldname
) inside the function, because in this case the structure is important.
Having done most OOP I think I might have a tendency to over use structs, but how about these guide lines?
In a struct module %Fubar
, use def do_fu_stuff(%Fubar{} = fb)
for functions that operate on the self type. Use further matching if you need some specific field.
In another module Bar
that could takes %Fubar
as arguments, use def def do_bar_stuff(%{f: f, u: u} = fb)
.
This way you get āstrictā local type checking without creating unnecessary coupling to the type from another module that could possibly be useful with some other type.
Like this:
defmodule Fubar do
defstruct f: nil, u: nil
def do_fu_stuff(%Fubar{} = fb), do: nil
end
defmodule Bar do
def do_bar_stuff(%{f: f, u: u} = fb), do: nil
end
I think that would be fine, except that I wouldnāt worry too much about the unnecessary coupling and just do this the external module:
defmodule Bar do
def do_bar_stuff(%Fubar{f: f, u: u} = fb), do: nil
end
If Bar
is a module that provides generic functionality for possibly many data types, I would just define a protocol instead and implement this protocol for the data types that might benefit from Bar
's functionality. That way you have even less coupling, as every type can define/name the fields it needs, without worrying about the field names Bar
expects.
-vincent