case with nil and not nil conditions

6,148 views
Skip to first unread message

Brad O'Hearne

unread,
Feb 10, 2016, 11:56:33 AM2/10/16
to elixir-lang-talk
I am trying to handle a simple case where I want to use the value of a variable if it is not nil, but I want to set the value of the variable if it is nil. There have been two questions that have arisen from this: 

  1. What's the proper (or recommended, most concise, etc.) approach for doing this? 
  2. I had started with an if/else, and the compiler recommended this: 

atom = 
 case int do
    1 -> :one
    2 -> :two
  end

I changed my code to look as such: 

atom = 
 case int do
    nil   -> :one
    ! nil -> :two
  end

The "! nil" caused the following compiler error: 

invalid expression in match

What is the proper way to handle nil and non-nil conditions in a case statement? 

Thanks in advance for your help. 

Brad

José Valim

unread,
Feb 10, 2016, 12:00:08 PM2/10/16
to elixir-l...@googlegroups.com
Everything after the first clause already means it is not nil. :)

atom = 
 case value do
    nil   -> :one
    _ -> :two
  end



José Valim
Skype: jv.ptec
Founder and Director of R&D

--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/c3f0eaeb-4ec7-49cf-9638-c796f679d960%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Message has been deleted

Brad O'Hearne

unread,
Feb 10, 2016, 12:49:38 PM2/10/16
to elixir-lang-talk, jose....@plataformatec.com.br
Thanks for the response and solution, Jose! It was the "_" I needed to get the ! nil issue resolved, so thanks again. 

However, this has raised another issue related to the bigger thing I was doing, which is essentially trying to initialize some variables based on whether an object is nil or not (forgive my "object" reference, I'm still adjusting from the OO world -- it's "module" I suppose). Basically I have an object with fields of its own -- if that object is nil, I want to use default values, otherwise I want to use the object's values. In order to do this, I started with an if/else which set two variables depending on whether the object was nil or not, similar to this: 

if ! my_object do
    id = nil
    update_timestamp = Date.now |> DateFormat.format!("{ISOz}")
else
    id = my_object.id
    update_timestamp = my_object.update_timestamp
 end 

This code received two separate compiler warnings for both id and update_timestamp which said they were unsafe and recommended the change of syntax, i.e.:

atom = 
 case value do
    1  -> :one
    2 -> :two
  end

The problem is, that this is setting one value, not two. Because of the structure recommended, I cannot set two variable values off of one test condition. If I were to follow this structure, I have redundant code as such: 

id = 
case my_object do
    nil -> "UNKNOWN"
_   -> my_object.id
end

id = 
case my_object do
    nil -> Date.now |> DateFormat.format!("{ISOz}")
_   -> my_object.update_timestamp
end

If I try to combine these into the same case statement, I get the original compiler warning all over again (because it is essentially the same as the original if/else but in a case).

It is probably obvious what a nuisance this would be if you had more than two variables to intiialize. What is the proper way to accomplish this? 

Thanks, 

Brad

Chris McGrath

unread,
Feb 10, 2016, 12:52:48 PM2/10/16
to elixir-l...@googlegroups.com
Use pattern matching to assign both at once

{id, update_timestamp} =
  case my_object do
   nil -> { “UNKNOWN”, Date.now |> DateFormat.format!("{ISOz}”) }
   _ -> {my_object.id, my_object.update_timestamp}
end

HTH,

Chris

--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
signature.asc

José Valim

unread,
Feb 10, 2016, 12:54:09 PM2/10/16
to Brad O'Hearne, elixir-lang-talk
Or alternatively:

{id, update_timestamp} =
  case my_object do
    %{id: idupdate_timestamp: update_timestamp} ->
      {id, update_timestamp}
    _ ->
      {nil, nil}
   end 



José Valim
Skype: jv.ptec
Founder and Director of R&D

Gilbert Kennen

unread,
Feb 10, 2016, 12:54:18 PM2/10/16
to elixir-l...@googlegroups.com
You get this warning because 'if' is a macro which compiles into a
'case' statement. The way that case statements and scoping works, you
*can* assign to variables outside of the case statement from inside, but
it is generally considered not-good practice to do so, thus the compiler
warnings.

If you had written your code like this:

{id, update_timestamp} =
if !my_object do
{ nil, Date.now |> DateFormat.format!("{ISOz}") }
else
{ my_object.id, my_object.update_timestamp }
end

You would get the same effect, but without the warning.

You could also rewrite this using pattern matching inside a case statement:

{id, update_timestamp} =
case my_object do
nil ->
{ nil, Date.now |> DateFormat.format!("{ISOz}") }

%{id: id, update_timestamp: update_timestamp} ->
{ id, update_timestamp }
end

which might read better depending on one's preferences. I would also
recommend moving the timestamp initializing to a separate function to
keep things clean and readable.

Gilbert

Brad O'Hearne

unread,
Feb 10, 2016, 7:10:21 PM2/10/16
to elixir-lang-talk, gil...@firewatcher.org
 Thanks all for your responses and solutions....you are all right, and solved my problem!

Cheers, 

Brad 

Gleb Arshinov

unread,
Feb 10, 2016, 7:38:42 PM2/10/16
to elixir-l...@googlegroups.com
I can't tell exactly if it's applicable or not, but on the off chance
it might, we use a pipe-friendly helper with similar semantics:

def nil_coalesce(left, right) do
if left == nil, do: right, else: left
end

E.g.:

post =
MyRepo.get_by(Post, title: ^title)
|> nil_coalesce(%Post{title: "New Post"})
|> Post.changeset(changes)
|> MyRepo.insert_or_update!

I don't know if this does exactly what you need, but you might be able
to define a similar helper function which does.

Best regards,

Gleb
> --
> You received this message because you are subscribed to the Google Groups
> "elixir-lang-talk" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to elixir-lang-ta...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/elixir-lang-talk/84897f45-b4f5-4485-8df5-0a0ccdb71061%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages