Crystal: Bug or Syntax sugar with instance variable and ||=?

Created on 28 Dec 2017  路  6Comments  路  Source: crystal-lang/crystal

I found some little surprise type inference in class with instance variable and ||=.

Test one: instance variable with basic types directly.

Result is it returned same type.

code: https://play.crystal-lang.org/#/r/3aza

class Foobar
  def name
    @name ||= "anonymous"
  end
end

a = Foobar.new
puts a.name #=> anonymous
puts a.name.class # => String

Test two: instance variable with basic types by call another method

Result is it throwed an exception about Can't infer the type of instance variable

code: https://play.crystal-lang.org/#/r/3az9

class Foobar
  def name
    @name ||= default_name
  end

  def default_name : String
    "anonymous"
  end
end

a = Foobar.new
puts a.name #=> Exception: Can't infer the type of instance variable

Test three: instance variable with basic types by call another method but use as to variable.

Result is it returned same type (same as Test one).

code: https://play.crystal-lang.org/#/r/3azb

class Foobar
  def name
    @name ||= default_name.as(String)
  end

  def default_name
    "anonymous"
  end
end

a = Foobar.new
puts a.name #=> anonymous
puts a.name.class # => String

Both Test one and Test three all missing define type of the instance variable, but it could pass the complier.

Most helpful comment

It's a consequence of the syntax sugar expansion of @name ||= "anonymous" ~> @name || @name = "anonymous" So there is a literal been assigned and fall into the rule 1.

It is documented at https://crystal-lang.org/docs/syntax_and_semantics/type_inference.html#other-rules

Test two won't work. A subclass might override the method and currently there is no enforcement that the return type should obey the type restriction of the base class. Note that when the method have params the resolution is even more complex/time consuming.

Any suggestion on how to improve discoverability / error message? Otherwise there is nothing to be done.

All 6 comments

type inference at works. looking at https://github.com/crystal-lang/crystal/blob/master/src/compiler/crystal/semantic/type_guess_visitor.cr seem some undocumented rules. for example ||= and .as.

no bug here, works as expected, only not documented.

when literal, as, etc., compiler can guess. when method call, no.

I think we should be able to infer this: the method has a specific return annotation.

It's a consequence of the syntax sugar expansion of @name ||= "anonymous" ~> @name || @name = "anonymous" So there is a literal been assigned and fall into the rule 1.

It is documented at https://crystal-lang.org/docs/syntax_and_semantics/type_inference.html#other-rules

Test two won't work. A subclass might override the method and currently there is no enforcement that the return type should obey the type restriction of the base class. Note that when the method have params the resolution is even more complex/time consuming.

Any suggestion on how to improve discoverability / error message? Otherwise there is nothing to be done.

Thanks for pointing out my mistake @bcardiff.

thanks @bcardiff and @RX14

@bcardiff The error message is already quite comprehensive and details all the cases where implicit type inference can be applied.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

asterite picture asterite  路  71Comments

HCLarsen picture HCLarsen  路  162Comments

asterite picture asterite  路  60Comments

farleyknight picture farleyknight  路  64Comments

asterite picture asterite  路  70Comments