I’m wondering why the second version of handling_nil compiles fine but the first doesn’t.
class A
@a : Array(Int32)?
def from_the_database : Array(Int32)
rand(1) == 1 ? [2] : [] of Int32
end
# This will not compile
# $ crystal build tryit.cr
# Showing last frame. Use --error-trace for full trace.
#
# In tryit.cr:19:17
#
# 19 | if (a = @a).empty?
# ^-----
# Error: undefined method 'empty?' for Nil (compile-time type is (Array(Int32) | Nil))
def handling_nil
@a ||= from_the_database
if (a = @a).empty?
@a = [2]
end
@a
end
# This one is okay
# def handling_nil
# @a ||= from_the_database
# if (a = @a) && a.empty?
# @a = [2]
# end
# @a
# end
end
a = A.new
pp a.handling_nil
The last example in the docs here suggest to me it should be fine. Am I reading something into this that I shouldn’t?
if @a.is_a?(String)
# here @a is not guaranteed to be a String
end
a = @a
if a.is_a?(String)
# here a is guaranteed to be a String
end
# A bit shorter:
if (a = @a).is_a?(String)
# here a is guaranteed to be a String
end
It can’t work. The conditional expression is (a = @a).empty?. It assigns the value of @a (which is nilable) to a and then calls a method on a. This method is not callable for nil value, so you get a compiler error. There is no expression that restricts a to non-nil value.
(a = @a).is_a? can be called because #is_a? is defined on Nil.
Another method that’s defined on Nil is #try. It’s useful for cases like these. You can call it on any nilable value and the methods block is only executed if the value is non-nil. (a = @a). try &.empty? returns true if the value is not nil and empty, false otherwise.
But again, this does not restrict the type of a to non-nil.
Thanks for the replies. They make sense but I needed to have a little think about it as I can see I’m not thinking about things in quite the right way. To be clear:
The compiler checks are against the type, at runtime they’ll be against the value?
if (a = @a) && a.empty? works because && means the Nil type can’t occur on the right hand side?
Sorry if these seem blatantly obvious, I’m trying to shift my lazy thinking to the new paradigm