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.
An alternative solution to what Brian says is to use sum
with a block:
http://play.crystal-lang.org/#/r/ein
z = (1..2).map do
v = nil
v = 1 if true
v
end
puts z.sum { |x| x || 0 } #=> 2
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.