Crystal: Compiler not smart enough to infer a type variable

Created on 15 Aug 2018  路  13Comments  路  Source: crystal-lang/crystal

class Matrix(N, M)

  def initialize
  end

  def *(other : Matrix(M, P)) : Matrix(N, P)
    Matrix(N, P).new
  end

end

m = Matrix(2, 2).new
m * m

undefined constant P

I'd expect compiler to figure out that both P's are supposed to be of the same value

bug compiler

All 13 comments

try adding forall M, P at the end of the method definition:

  def *(other : Matrix(M, P)) : Matrix(N, P) forall M, P

Same error 馃

It actually works with forall if you omit the return type. That's maybe a bug, not sure.

Yeah, it looks that way. And it should only be forall P because M is already a generic argument.

And you're using 2 as type, you should use a type for N, M and P.

class Matrix(N, M)

  def initialize
  end

  def *(other : Matrix(M, P)) : Matrix(N, P) forall M, N, P
    Matrix(N, P).new
  end

end

m = Matrix(Int32, Int32).new
m * m

https://carc.in/#/r/4qxv

If you omit the return type it works without forall as well.

However, I suspect you are not using generics properly (though I don't know your use case).

class Matrix(T)
  def initialize(@n : Int32, @m : Int32)
  end

  def *(other : Matrix(T))
    Matrix(T).new(@n, @m)
  end
end

m = Matrix(Int32).new(2, 2)
m * m

https://carc.in/#/r/4qz9

@hugoabonizio M and N are not free variables, they're already specified in the generic type arguments and should properly resolve. Only P should be declared explicitly.

I suppose this is just another edge case where the specific implementation for generic type arg as integer is incomplete.

Using integers as generic type arguments to define the dimensions of a matrix is completely legitimate. That's what they're made for.

@hugoabonizio These are just compile-time variables. The main reason I'm using them is to prevent invalid operations at runtime.

A bug in error messages, this should error that the type variable P could not be inferred:

def foo(in : Array(A)) : Array(P) forall A, P
  in
end

foo(Array(Int32).new)

https://carc.in/#/r/4rgo

A bug in numerical generics, this should infer P:

class Foo(A)
end

def foo(in : Foo(P)) : Foo(P) forall P
  in
end

# works
# foo(Foo(Int32).new)

# broken
foo(Foo(1).new)

https://carc.in/#/r/4rgp

Using integers as generic type arguments to define the dimensions of a matrix is completely legitimate.

Really nice to know! We learn something new everyday :smile:

class Foo(A)
  def sum
    A + A
  end
end
puts Foo(1).new.sum

https://carc.in/#/r/4rgz

Was this page helpful?
0 / 5 - 0 ratings