The Crystal Programming Language Forum

Proc and Inheritance

Hi pals,

I have code like this:

abstract class Expression
end

class IntLiteral < Expression
end

class BoolLiteral < Expression
end

hash = {} of String => -> Expression?

hash["a"] = ->my_method

def my_method : Expression?
	return IntLiteral.new
end

It fails with this error.

Error: no overload matches 'Hash(String, Proc((Expression | Nil)))#[]=' with types String, Proc(IntLiteral)

I was expecting that:

  • Take the method signature : Expression? as type or
  • IntLiteral is an Expression?

What I’m getting wrong? How can I solve this?

Explicitly casting to Expression? in the return do the trick but seems clunky

Hello. I’m not really sure what’s going on here, but I think it should be simply String => Expression?, and accordingly, hash["a"] = my_method. Or, unless there are actually going to be Nils in the hash, just String => Expression.
Hopefully someone else will join in to explain the details, I didn’t even expect -> to work on anything other than blocks and methods.

facepalm. misunderstood syntax and code intent. quietly crawling back under a rock.

The type is String => -> Expression? meaning a String as key and a Proc that doesn’t have parameters and returns Expression? as value

The solution for this is simple, there is no solution as Covariance and Contravariance aren’t fully supported as per documentation

So I ended forcing the type

def my_method  : Expression?
  return IntLiteral.new.as(Expression?)
end

To make it prettier I put the cast inside a macro so it looks cleaner.

macro rexp?(r)
  return {{r}}.as(Expression?)
end


def my_method
  rexp? IntLiteral.new
end

It works ok for me now

Right now a return type is a restriction: the compiler checks that the returned value matches the return type, but the actual returned type might be a subtype.

We should probably change this.

I suggest you send a feature request to the Github repository.