vans163
A hybrid between a cond statement and a multi, does it exist?
So when writing logic that is too complex for a case, (usually with C it will look like a function with return statements scattered throughout) a cond is used.
Now one thing that irks me about cond is, you cannot assign variables in the branches.
Multis for example will fit the bill, but they are a bit heavyweight for the simple usecase of just wanting to assign a variable in a cond.
Here is an example:
inventory = state.inventory
cond do
Enum.find(inventory, & &1.name == "Moon Cake") ->
moon_cake = Enum.find(inventory, & &1.name == "Moon Cake")
{:eat, moon_cake.id}
(Enum.find(inventory, & &1.name == "Space Cake")[:count]||0) > 10 ->
space_cake = Enum.find(inventory, & &1.name == "Space Cake")
{:eat, space_cake.id}
true ->
nil
end
But wish we can do
cond do
moon_cake = Enum.find(inventory, & &1.name == "Moon Cake") ->
{:eat, moon_cake.id}
(space_cake = Enum.find(inventory, & &1.name == "Space Cake"))[:count]||0 > 10 ->
{:eat, space_cake.id}
true ->
nil
end
Is there any good solutions out there? The net win is that, the cond does not go further, so as to prevent multiple iterations of the inventory if an earlier clause succeeded.
Most Liked
sfusato
Wouldn’t Enum.find_value/3 be more appropriate here? Something along the lines:
Enum.find_value(inventory, fn
%{id: id, name: "Moon Cake"} -> {:eat, id}
%{id: id, name: "Space Cake", count: count} when count > 10 -> {:eat, id}
_ -> nil
end)
tfwright
For anyone else like myself who somehow missed this syntax when they were in elixir school: pattern matching on anonymous functions
I am now cringing thinking of all the fn arg -> case arg do end end in my code ![]()
axelson
You’re second example pretty much works as is, I occasionally use a similar pattern.
I just added an extra paren for the “Space Cake” clause:
inventory = [%{id: 1, name: "Space Cake", count: 11}]
cond do
moon_cake = Enum.find(inventory, &(&1.name == "Moon Cake")) ->
{:eat, moon_cake.id}
((space_cake = Enum.find(inventory, &(&1.name == "Space Cake")))[:count] || 0) > 10 ->
{:eat, space_cake.id}
true ->
nil
end
Although if you can structure it as a single reduce it will be more performant (although you’ll need quite a long list before that will begin to matter much), but turning this into a reduce might make it more difficult to follow since it doesn’t seem like the operation you are doing is easily thought of as a transformation. Also if the clause is as complex as the “Space Cakes” clause I would probably think about restructuring the logic away from a cond, or maybe extracting a helper function.
Last Post!
vans163
Yea this can work to an extent but its hard to read. I tried this pattern before but did not like that it reads very poorly and its hard to figure out what is going on since all the implications of the with (else clause and silently returning the match failure by default; I feel the last part causes chaos in a loosely typed language)
its also possible to assign then check in the with as well
inventory = [%{id: 1, name: "Space Cake", count: 11}]
with moon_cake <- Enum.find(inventory, & &1.name == "Moon Cake"),
true <- is_nil(moon_cake) || {:eat, moon_cake.id},
space_cake <- Enum.find(inventory, & &1.name == "Space Cake"),
true <- is_nil(space_cake) || space_cake.count < 10 || {:eat, space_cake.id} do
end
Yea definitely, but the with statement was created for exactly that. So if there is a nice way to solve a pattern it seems plausible to consider it at least as a feature. The reason why creating multiple guard-like functions does not work is that there are just so many variations and combinations that you end up with small functions that get passed 5+ arguments, then you start thinking maybe I should just pass a state object to each, which then leads to nightmare refactors if that state object structure changes. (Same nightmare refactor problem I see with ecto multis, tho the new 1.11 map key precompile checks might remedy this abit)
You can Enum.reduce/3.. yes this is a fine pattern and occasionally we use it but its still not ideal from a readability perspective IMO. Readability and maintainability is always subject to debate tho. On this line maybe to take this a step further, we should redesign our thinking and perhaps always use this type of pattern with a weight assigned to each action result. Then sort by weight and take the first. Kind of a GOAP like pattern.
Popular in Discussions
Other popular topics
Chat & Discussions>Discussions
Latest on Elixir Forum
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









