annad

annad

Why is it bad to check for values you don't want (eg nil values)?

I was reading an earlier post: Pipe obsession anti-pattern which recommended an article entitled Good and Bad Elixir.

I’m confused by one of the recommendations in the Good and Bad Elixir article. It says: “State what you want, not what you don’t.”

EXAMPLE GIVEN:

# Don't do...
def call_service(%{req: req}) when not is_nil(req) do
  # ...
end

# Do...
def call_service(%{req: req}) when is_binary(req) do
  # ...
end

I use guards quite a bit to ensure that the function is not being passed a nil value in error. I gracefully manage the error, sometimes sending a message to the user to try again. I don’t understand why that would be bad. The article says, “You’d prefer to raise or crash if you receive arguments that would violate your expectations.”

Could someone explain why that is the case?

Most Liked

tfwright

tfwright

I feel like it needs to be emphasized that this is really not primarily an issue with guards or even “excessive” negations/not, it’s just a logic error. If your function accepts the category “binaries” and you define that category as “not nil” this is simply incorrect. You could use all negative guards and solve the logic error, and then you might have a style issue that makes your code “confusing” etc. Conversely you can make category errors like this without using not at all (if say you guard on is_list but actually only keyword lists are expected).

dimitarvp

dimitarvp

You already had excellent answers and did a good summary yourself.

I’ll add one more thing: make full use of pattern-matching to your own advantage. Example with the code from your OP:

def call_service(%{req: req}) when is_binary(req) do
  # ...
end

def call_service(other) do
  # Simplified, we did it much more fine-grained with all the `is_*` guards.
  YourMonitoringService.warning(
    "Unexpected value for AwesomeModule.call_service/1: #{inspect(other)}"
  )
end

Note the second variant of the same function. In Elixir you can have catch-all clauses for fallback actions. In this case: report an unexpected value. This is not an error condition but it’s undesirable and the devs want to be informed so they can monitor closely who and when is sending those invalid values.

I had such requirements in a few projects, one of them was a legacy API provider where the company announced an API upgrade shortly after I joined but many users of the API still didn’t understand or read the docs and upgrade guides and kept calling the new API with the old parameters.

Thanks to the catch-all / fallback mechanism Elixir gives us we managed to track down the customers who used the API wrongly and helped them off-board their previous code and write a new one.

Just one production example for you. Obviously you can do that with any programming language but Elixir made it very easy to achieve.

tfwright

tfwright

If you are expecting a string, then there is no advantage to specifying not nil instead of binary. If the function actually expects any value except nil then the first variation is not incorrect.

sodapopcan

sodapopcan

Because this is considered Defensive Programming (I didn’t fully vet that article but it has a good TL;DR). Unless nil is a meaningful value in your design, you should rewrite your code so that the function would never receive nil anyway (Parse, Don’t Validate) and if through some circumstance it did, it would be considered an “exceptional” circumstance in which case we just fail (throw an exception). You could also look at it like: Why stop at defending against nil? If you’re going to defend against one type you should theoretically be defending against every known type in your system.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Relatively simply: If I do call_service(%{req: %UnexpectedThing{}}), %UnexpectedThing{} isn’t nil, but it also probably isn’t what you want. So if what you actually want is a binary, then ask for it. If literally everything is valid except nil, then do the nil check.

EDIT: And to be clear the emphasis is on avoid not checks, it isn’t so much on nil.

Where Next?

Popular in Questions Top

SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
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
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
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
makeitrein
Hey all, just started picking up Elixir last week and am writing a scraper as a learning project. Baby step #1 is extracting the number ...
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New

Other popular topics Top

belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
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

We're in Beta

About us Mission Statement