Search code examples
crystal-lang

Crystal Lang: Can't infer the type of instance variable '@hash_value' of Rule


Here's my crystal code, in snippets, but I think it's all there:

# spec_helper.rb
def fixtures
  rawhash = JSON.parse(File.read("spec/fixtures/state3.json"))
  rules = rawhash["rules"]
  id = rules.as_h.keys[0]
  hash =  rules[id]
  return id, hash
end

# rule_spec.rb
key, hash = fixtures
rule = Rule.new(key, hash)
rule.array(["name"])[0].must_equal "RpsIphone"

# rule.rb
class Rule  < HueResource

  getter :detail, :on, :name, :lights

  def initialize(key, hashvalue)
    super(key, hashvalue)
    @detail = "#{hashvalue["conditions"].length} conds => #{hashvalue["actions"].length} acts"
    @state.merge! ({"on" => hash_value["status"], "name" => @name, "detail" => @detail})
    gen_reskey("r")
  end
...
end

# hue resource.rb
class HueResource
  def initialize(key, hash_value)
    @hash_value = hash_value.as_h
    @lastupdated = @hash_value["state"]["lastupdated"]
    @detail = hash_value["type"]
    @name = hash_value["name"]
    @state = { "key" => key, "lastupdated" => @lastupdated, "detail" => @detail, "name" => @name}
  end

  def gen_reskey(detail)
    @state.merge!({ "id" => detail + @state["key"]})
  end
end

And here's the error:

[Running] crystal "/Users/pitosalas/mydev/crystalplay/spec/rule_spec.cr"
Error in spec/rule_spec.cr:7: instantiating 'Rule.class#new(String, JSON::Any)'

    rule = Rule.new(key, hash)
                [32;1m^~~[0m

in src/rule.cr:7: instantiating 'super(String, JSON::Any)'

    super(key, hashvalue)
    [32;1m^~~~~[0m

in src/hue_resource.cr:3: [1mCan't infer the type of instance variable '@hash_value' of Rule

The type of a instance variable, if not declared explicitly with
`@hash_value : Type`, is inferred from assignments to it across
the whole program.

Now it seems to me that in the constructors, @hash_value is assigned to hash_value.as_h so it would know the type from there on.

Also feel free to point out Crystal stylistic or idiomatic comments!


Solution

  • The compiler can't infer ivar types from the return type of method calls (like hash_value.as_h). The reference of Type inference lists all rules where the compiler can infer ivar types.

    Infering the type from method calls is probably not impossible, but more complicated. It might come to the language at some point, but for now you'll have to use an explicit type annotation such as @hash_value : Hash(JSON::Any, JSON::Any).