The || operator and it's respective ||= OpAssign expression work well most of the time, except when we need to preserve Bool values.
In the latter case, we have to write something like:
value = some_expr_which_may_be_bool_or_nil
value = another_expr_which_may_be_bool_or_nil if value.nil?
# ...and so on
It would be nice to have a conditional assignment expression and operator that _only_ checks for nil and let's Bool through, e.g. C#'s ?? operator. That is:
value = some_expr_which_may_be_bool_or_nil ?? another_expr_which_may_be_bool_or_nil # ...
Or,
value = some_expr_which_may_be_bool_or_nil
value ??= another_expr_which_may_be_bool_or_nil
# ...and so on
I have no problem with the functionality - just commenting that it is not a very pretty operator IMO, syntax-wise. ;-)
My personal opinion is that a union with both bool and nil in is a design problem in your code. When true false and nil have different meanings it seems to me that you probably want an enum.
Can you give a practical example where this happens?
Coffescript has exactly the same as ?=.
@RX14 I agree that sometimes it's a "code smell". On the other hand, this is a real world example (more or less extracted from my own code):
struct Column
@required : Bool
def initialize(required : Bool?) # or, **options
@required = required || true # or, options[:required]?
end
# ...
(Which would fail if called as Column.new required: false)
Currently, we have to go:
@required = required.nil? ? true : required
(That's the shortest expression I can think of, and almost non-evident)
Versus
@required = required ?? true
I know it's a corner case, and it's mainly sugar鈥攂ut aren't almost all language features just sugar?
@shevegen I also think it's not very pretty, but both C# or Swift have converged on ??, and || is already taken. On the other hand, redefining ||/||= to only work on nil I think is less "backward-compatible", not to mention it breaks symmetry with &&.
@Sija I personally first thought of ?= but decided to file the ticket to use ??= to preserve symmetry: i.e., || is to ||= and ?? is to ??=
For your first case:
def initialize(@required : Bool = true)
end
In the very rare cases where this is required, required.nil? ? true : required seems concise enough to me.
@RX14 I realized that, that's why I also commented # or **options. Here's a more fleshed out example:
struct Column
@required : Bool
def initialize(options : Hash(String, Bool) = {} of String => Bool)
@required = options.has_key?("required") && !options["required"].nil? ? options["required"] : true
end
end
Which I would love to rewrite as simply:
@required = options["required"]? ?? true
In the Gitter channel, the if !...nil?chain was much longer than that, but I lack context:
value = current_scope[node.name]?
value = __typeof_current_self.call if value.nil?
value = recursive_lookup(current_self, node.name) if value.nil?
if !value.nil?
stack.push(value)
else
__raise_not_found(node.name, current_self)
end
required = options["required"]?
@required = required.nil? ? true : required
required = options.fetch("required", true)
or in the case of the default being hard to calculate
required = options.fetch("required") { calculate_expensive_default_value }
Even if we didn't have fetch, doing what @straight-shoota said isn't terrible.
I vote to close.
fetch only works if the key is missing, not if the value is nil:
{"foo" => nil}.fetch("foo", true) # => nil
@straight-shoota, options : Hash(String, Bool).
If it was a Hash(String, Bool?) that comes back to what I originally said about needing an enum at that point, because that Bool | Nil union is coming from your design decision instead of as a side effect of expression methods.
It's actually a Hash(String, String | Bool | Nil) (among other possible types/values). Yes, in my case, I need a case value; where Bool ... anyway.
I agree that if it was just hashes then .fetch(key, true) would suffice, or for parametersarg : Bool = true.
However, this was really prompted by the discussion and gist in the Gitter channel (provided above) where the computation of value involved a series of expressions that evaluated to Bool?.
I guess I just think a way to distinguish nil from false would be useful, even in corner cases like that.
I guess I just think a way to distinguish nil from false would be useful
#nil?
Having to write a bit more in a few cases is acceptable. Let's not add new features for every little thing.
The proposal has no upvotes after a year, nor any duplicate filings. It doesn't seem to be a pain point in the language thus. The presented usecases have all been given concise alternatives with low bug risks and barely distinguishable mental overheads.
All team members that contributed to this discussion hold the opinion that this feature is unnecessary and I concur.
Let's close this for now :)
Most helpful comment
#nil?Having to write a bit more in a few cases is acceptable. Let's not add new features for every little thing.