The issue happens when the variable, that the array was built from, was a nil
initially.
y = (1..2).map do
v = nil
v = 1
v
end
p y # => [1, 1]
p y.class # => Array(Int32)
p y.sum # => 2
When v
stops being nil
on a condition, that is potentially computational and not solvable while compiling:
z = (1..2).map do
v = nil
v = 1 if true
v
end
p z # [1, 1]
p z.class # => Array(Nil | Int32)
The array gets more complex type, that isn't compatible with current sum
implementation, so p z.sum
causes compile time error:
undefined method 'zero' for Nil:Class (compile-time type is (Nil | Int32):Class)
def sum(initial = T.zero)
^~~~
How am I supposed to fight this properly?
Or maybe it waits for some better implementation of stdlib sum
method or anything else?
UPD: inject
gives the same:
p z.inject{ |i, j| i + j }
undefined method '+' for Nil (compile-time type is (Nil | Int32))
You can use Iterator#compact_map
to select non-nil values. The compiler will be able to infer a Array(Int32)
in that case.
http://play.crystal-lang.org/#/r/e85
z = (1..2).map do
v = nil
v = 1 if true
v
end
pp typeof(z) # => Array(Nil | Int32)
pp z # => z = [1, 1]
y = z.compact_map(&.itself)
pp typeof(y) # => Array(Int32)
pp y # => y = [1, 1]
Also, notice that typeof(Expr)
and Expr.class
might lead to different results. The first is the compile time type and the later is the runtime type.