vans163

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

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

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 :man_facepalming:

axelson

axelson

Scenic Core Team

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

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

@bottlenecked

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.

Where Next?

Popular in Discussions Top

PragTob
Hey everyone, this has been on my mind for some time and I’d love your input on it! TLDR: I feel like maps are superioer for storing and...
New
scouten
I’m looking for a host for the server part of a small (personal) side project that I’m working on. It’s currently written in Node.js and ...
New
jesse
Hi everyone, I hesitated to post this here because I don’t want you to think I’m spamming, but I’ve been working on a Platform-as-a-Serv...
New
AlexMcConnell
The reason that Rails is as popular as it is is because it’s very easy for relatively inexperienced developers to get a lot of work done....
588 20142 166
New
AstonJ
If a newbie asked you about Phoenix Contexts, how would you explain the basics to them? Feel free to be as concise or in-depth as you li...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40165 209
New
Fl4m3Ph03n1x
Background This question comes mainly from my ignorance. Today is Black Friday, one of my favorite days of the year to buy books. One boo...
New

Other popular topics Top

dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 31586 112
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New

We're in Beta

About us Mission Statement