I am used to this syntax for a case structure (my question is about the _ -> part):
case {1, 2, 3} do
{4, 5, 6} ->
"This clause won't match"
{1, x, 3} ->
"This clause will match and bind x to 2 in this clause"
_ ->
"This clause would match any value"
end
A new team member introduced this into our project:
case {1, 2, 3} do
{4, 5, 6} ->
"This clause won't match"
{1, x, 3} ->
"This clause will match and bind x to 2 in this clause"
_other ->
"This clause would match any value"
end
So instead of a _ he/she uses _other. I do know that technically this is no different but I wonder what the majority does/thinks. I donāt want to tell that person in a code review that _other āfeelsā funny in case everybody but me does it. Every time I see it I stumble upon it but I understand why one would use _other.
Just want to +100 this as itās one of my biggest pet peeves. Like, no %#$@ itās āother.ā When it comes to scanning unfamiliar code, this type of thing is just distracting. If I see _some_var, I think that something useful is being communicated. As already said, _other as a catch-all is not at all useful.
And, technically they arenāt equivalent as _other gets bound whereas _ doesnāt. Iām not advocating that is some sort of meaningful optimization, just sayinā
Yeah, I dislike _other as well but many others are valuable i.e. you have a long chain of matching that checks for several types of structs / maps and then ultimately itās fine if below you have _not_a_map -> ... ā and variations of that. A proper unused variable name really helps code readability.
Absolutely, thatās what I was trying to convey. Another way to look at it is that matching is basically (albeit not strictly) assignmentāyou wouldnāt called a variable other (at least I hope you wouldnāt). So long as there is a name that makes sense I name my _s, which is the vast majority of the time.
To take this a bit further, due to Elixir being dynamic Iād vouch for writing OPās example like this (depending on context):
case tuple do
{4, 5, 6} ->
"This clause won't match"
{1, x, 3} ->
"This clause will match and bind x to 2 in this clause"
{_, _, _} ->
"This clause would match any 3-tuple"
end
and if nil is allowed, add a specific nil clause. This makes it very clear what is expected an errors out quickly if it gets junk.
> mix compile
warning: the underscored variable "_other" appears more than once in a match. This means the pattern will only match if all "_other" bind to the same value. If this is the intended behaviour, please remove the leading underscore from the variable name, otherwise give the variables different names
ā
4 ā {_other, _other} -> :doesnt_match
ā ~
ā
āā lib/compile_test.ex:4:16: CompileTest.my_fun/0
Kinda surprised no one else pointed this out but if you use Credo on your project (and you should) then ā unless youāve suppressed the rule ā it will give you a warning for just ā_ā and tell you to give it a more meaningful name.
Now āotherā may not be the most descriptive variable name, but without better context of what sort of thing you are actually matching it will do in a pinch.
The TL;DR though is that Credo is the compendium of idiomatic Elixir choices and a bare underscore for a variable name is not idiomatic.
We practice documentation as code, so we encourage commenting ignored pattern matches like _error to better explain the variables:
case result do
{:ok, data} -> do_action(data)
_error -> fail()
end
If youāre literally talking about writing _other, thatās something we would avoid and would try to come up with a better name since āotherā is what _ -> means. The goal is to document the program, not the programming language.
case result do
{:ok, data} -> do_action(data)
{:error, _} -> fail()
end
This leaves little up to the imagination of an unsuspecting reader: they can see the exact shape and donāt have to look anything up. It also means we get a more meaningful exception if somehow we get nil or something else completely unexpected (the entire case fails the match as opposed to the catch-all failing the match). Even with safety nets we have, Elixir is still a dynamic language so anything is possible!
For me, a bare _ perfectly conveys āI literally donāt care what I get here, even if it falls outside of the subset of things Iām assuming itās going to be.ā Otherwise, if youāve been writing Elixir for more than a minute and you donāt know that the opposing match of an :ok tuple isnāt some form of :error then thatās a whole other problem and naming your matches as such isnāt going to fix that.
I say that if you know the shape of the error, match on the shape! There is confidence in a bare _ that proclaims: āI will literally accept any outcome!ā whereas _error sorta says āWell, you know, Iām assuming this is going to be an error tupleā¦ like, probably.ā
Please note, Iām fully aware Iām being way too overly passionate about a low-stakes stylistic point Iāll do whatever a codebaseās style guide dictates I must do.
Hmmā¦ I use Credo on pretty much everything and _ for placeholders both in function heads and to avoid Dialyzer unmatched return complaintsā¦ But I donāt recall seeing the Credo warning you speak of. Are you sure this is stock/default Credo?
Not quite exact f.ex. Iād also do {:error, _atom} for the sake of the future me (or another dev) who would want to do something with the value sometime in TheFutureā¢. Could save them some time looking up the APIās @spec-s.
case Integer.parse(input) do
{int, ""} -> "all good"
{_int, _string_remainder} -> "oops, only partially an integer"
:error -> "sorry, not an integer at all"
end
Ahh, itās disabled by default. Definitely enabled at my current job (with about a dozen Elixir projects). Pretty sure it was enabled at my last job too though itās been a few years now and hard to remember for sure.
Well, maybe thatās my decade plus in Ruby-land showing through. Even though I left Ruby behind almost 6 years ago, I still have RuboCop PTSD.
Both RuboCop and Credo allow customizing rules but Iāve always felt that there was a strong impetus to adopt the defaults and keep customization to a minimum. I definitely felt that way while working hard to keep RuboCop happy back in my Ruby days.
I mean, if you make Credo or RuboCop a PR check then you have to conform to the rules in order to get your work merged and complete stories. And I donāt think thatās a bad thing (the RuboCop PTSD was because I felt that it went too far and was often a source of a lot of friction). Bike-shedding is best avoided at all costs. Consistency and readability of code saves a lot of mental overhead.
But, still, not everyone gets a say in the linter rules so you have to follow them and it sets a standard. But I think Iām misremembering this one; a) itās not on by default (as I indicated in another response here) and b) like the Phoenix example you shared, there is clearly plenty of important code out there still using bare ā_ā.
Just to make it perfectly clear, this is not an optimization, not even a slight one. The Erlang compiler can see when a variable is unused and will emit the same code for matching _ as when matching _other or some other variable that is never used. Actually, to avoid having to handle _ as a special case, the Erlang compiler replaces _ with a freshly created variable that will never be used.
Ehn, fair, but I donāt think this adds too much. Thatās itās āunwrappingā an error tuple is all the information I need, though I dunno, Iāll think about it next time Iām in this situation.