I know the difference between strict (and, or) and relaxed (&&, ||) operators, but when should I use them? Using && and || seems to be more versatile, but maybe it isn’t a good practice. What’s the convention?
docs state below:
As a rule of thumb, use
and
,or
andnot
when you are expecting booleans. If any of the arguments are non-boolean, use&&
,||
and!
.
One special trick which you can do with ||
is to assign default value when something is nil:
def id(opts) do
id_prefix = opts[:id_prefix] || "default_prefix"
#... do something.
end
||
becomes tricky for booleans when it is used for assigning default values :
def verify(id, opts) do
# this will result in a bug - validate_prefix? will not ever be false (for booleans)
validate_prefix? = opts[:validate_prefix] || true # this will result in a bug - validate_prefix? cannot be false
# do something
end
Calling verify/2
with opts , opts2, opts3:
# works good - validate_prefix? will be true
opts = %{validate_prefix: true}
verify("abc-123", opts)
# works good - true will be assigned (default value)
opts3 = %{}
verify("abc-123", opts1)
# bug - disabling prefix validation is not possible due to use of ||
opts2 = %{validate_prefix: false}
# this will be ignored and default value true will be assigned
# default value true will be assigned as false || true is always true
verify("abc-123", opts2)
One situation where and
and or
are required: &&
and ||
aren’t allowed in guard clauses.
Like ||
can be used to check for nil and assign default values - can &&
be used for anything else?
One thing it can be used for raise /throw.
true && raise(ArgumentError)
false || raise(ArgumentError)
nil || raise(ArgumentError)
you could possibly use it to perform an action that doesn’t require the value
value = get_some_value()
value && perform_some_action_only_if_value_is_not_nil()
value
something like this, I guess
Thanks for all replies. So which one do you prefer or which one do you use more often?
As both (strict, relaxed) are not substitutes for one another - it depends on the restrictions they come with ?
Something I’ve done recently with :gen_tcp
:
# The terminate callback in a GenServer
def terminate(_reason, socket) do
socket && :gen_tcp.shutdown(socket, :read_write)
end