0
class Foo
  def initialize(@foo : String | Nil)
  end
  def foo
    @foo
  end
end

a = Foo.new "213"
if !a.foo.nil?
  puts a.foo, typeof(a.foo)
end

get output of

213
(String | Nil)

but shouldn't type of a.foo be narrowed to String? Is this another design limit?

Austaras
  • 901
  • 8
  • 24

1 Answers1

2

The compiler doesn't know that @foo doesn't change. Say your class Foo has a setter for @foo. If some concurrently running code uses that setter to set @foo to nil, the second call to Foo#foo inside the if condition might return nil now even though the check passed before.

You can remedy by introducing a local variable which the compiler can reason about:

if foo = a.foo
  puts foo, typeof(foo)
end
Jonne Haß
  • 4,792
  • 18
  • 30
  • The documentation about this behaviour can be found at https://crystal-lang.org/reference/syntax_and_semantics/if_var.html#limitations – Johannes Müller May 20 '19 at 16:33
  • thanks for the explanation. I thought crystal shares same run to completion semantic but seem like it doesn't – Austaras May 20 '19 at 23:02