crabonature

crabonature

Multiple guards in Elixir

I’m still quite new to Elixir.

As I understand we got in Elixir “multi guards” as convention to simplify one large guard with or’s?:

def boolean(value) when map_size(value) < 1 or tuple_size(value) < 1 do
  :guard_passed
end

is almost the same as:

def multiguard(value)
    when map_size(value) < 1
    when tuple_size(value) < 1 do
  :guard_passed
end

There are multiclause anonymous functions as well:

larger_than_two? = fn
    n when is_integer(n) and n > 2 -> true
    n when is_integer(n) -> false
  end

But why there is no multi guard functions? As I understand instead of them I need to write cond expression or use multi clousers, but if I got in a function definition a lot of parameters or complicated pattern matching, only cond stays quite readable.

For example in Haskell:

bmiTell :: (RealFloat a) => a -> String  
bmiTell bmi  
    | bmi <= 18.5 = "You're underweight!"  
    | bmi <= 25.0 = "You're supposedly normal."  
    | bmi <= 30.0 = "You're little too much!"  
    | otherwise   = "No way!"  

It can be handy if we got something like this in Elixir, functions with multi guard bodies? What you think? Was there some talks in past about it?

@spec bmi_tell(float()) :: String.t()
def bmi_tell(bmi)
  when bmi <= 18.5, do: "You're underweight!"
  when bmi <= 25.0, do: "You're supposedly normal."
  when bmi <= 30.0, do: "You're little too much!"
  when true,        do: "No way!"

Most Liked Responses

NobbZ

NobbZ

or and multiple whens are not equivalent, especially when there are exceptions in the game:

iex(1)> defmodule M do                                                          
...(1)> def guard_or(x) when x <> "foo" == "xfoo" or is_integer(x), do: true    
...(1)> def guard_or(_), do: false                                              
...(1)> 
...(1)> def guard_when(x) when x <> "foo" == "xfoo" when is_integer(x), do: true
...(1)> def guard_when(_), do: false                                            
...(1)> end
{:module, M,
 <<70, 79, 82, 49, 0, 0, 4, 172, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 105,
   0, 0, 0, 11, 8, 69, 108, 105, 120, 105, 114, 46, 77, 8, 95, 95, 105, 110,
   102, 111, 95, 95, 9, 102, 117, 110, 99, ...>>, {:guard_when, 1}}
iex(2)> M.guard_or(1)                                                           
false
iex(3)> M.guard_or("x")
true
iex(4)> M.guard_when(1)             
true
iex(5)> M.guard_when("x")
true

When using or an exception makes the whole guard false, while with multiple whens only the sub-expression is considered false.

peerreynders

peerreynders

I think you may be fixating on guards too much. It’s important to keep in mind that guards only allow a restricted set of expressions,

The reason for restricting the set of valid expressions is that evaluation of a guard expression must be guaranteed to be free of side effects.

Expressions — Erlang System Documentation v29.0.2

and that guards are a conditional extension to pattern matching - i.e. they can appear whenever a pattern match is possible. There are other alternatives for conditionals such as case/2 (which is based on pattern matching and therefore also supports guards) and cond/1.

To me cond seems most appropriate for your bmi_tell/1 example:

defmodule Demo do

  def bmi_tell_cond(bmi),
    do: (
      cond do
        bmi <= 18.5 -> "You're underweight!"
        bmi <= 25.0 -> "You're supposedly normal."
        bmi <= 30.0 -> "You're little too much!"
        true        -> "No way!"
      end)

  def bmi_tell_case(value),
    do: (
      case value do
        bmi when bmi <= 18.5 -> "You're underweight!"
        bmi when bmi <= 25.0 -> "You're supposedly normal."
        bmi when bmi <= 30.0 -> "You're little too much!"
        _                    -> "No way!"
      end)

end

IO.inspect(Demo.bmi_tell_cond(18.0))
IO.inspect(Demo.bmi_tell_cond(24.0))
IO.inspect(Demo.bmi_tell_cond(29.0))
IO.inspect(Demo.bmi_tell_cond(31.0))

IO.inspect(Demo.bmi_tell_case(18.0))
IO.inspect(Demo.bmi_tell_case(24.0))
IO.inspect(Demo.bmi_tell_case(29.0))
IO.inspect(Demo.bmi_tell_case(31.0))
OvermindDL1

OvermindDL1

Literally even!

Something like this:

def blah(a) when a<0, do: 0
def blah(1), do: 1
def blah(a) when a>0, do: 2

Quite literally compiles ‘almost’ identically to (‘almost’ meaning it is more lambda’ish internally):

def blah(a) do
  case a do
    a when a<0 -> 0
    1 -> 1
    a when a>0 -> 2
    _ -> raise %MatchError{...}
  end
end

Actually it compiles into a simple dispatch tree. It’s not like ‘way’ smart in that it can combine all parts possible, but it is pretty decent. In general you can think of it as ‘testing’ the first branch, if that fails it tests the second, if that fails it tests the third, and so on. It is a linear slowdown with the number of cases (though linear more in C terms so it is still blazing fast compared to anything you could write explicitly, this is also why guard calls are restricted).

Precisely. Multiple when’s compiles to different branches in the dispatch tree, where or’s get compiled into the same dispatch branch.

Where Next?

Popular in Discussions Top

laiboonh
Hi all, I am trying to convince my team to use liveview over the current react. What are some of the points where one should consider us...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
arpan
Hello everyone :wave: Today I am very excited to announce a project that I have been working on for almost 3 months now. The project is...
New
Crowdhailer
I’ve been hearing much about the new formatter and it’s something I have been keen to try. I find examples buy far the most illuminating...
248 19204 150
New
marciol
Please, let me know if this kind of discussion already took place in another topic . Hi all, how do you consider if is better to build ...
New
sergio
There’s a new TIOBE index report that came out that shows Elixir is still not in the top 50 used languages. It also goes on to call Elix...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
und0ck3d
Hello everyone! A few days ago I’ve created a topic here about how people were creating CMSs with Elixir and Phoenix. I’ve been studying...
New
acrolink
How does the two languages compare when it comes to server side application development? Any experiences or ideas? Thank you.
New
slashdotdash
Phoenix Live View is now publicly available on GitHub. Here’s Chris McCord’s tweet announcing making it public.
New

Other popular topics Top

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
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
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
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 126479 1222
New

We're in Beta

About us Mission Statement