Instance variable '@callback' of Foo must be (Proc((T | Nil)) | Nil), not Proc(Nil)
Hi, I cannot wrap my head around below piece of code... What am I doing wrong? https://carc.in/#/r/16r0
reduced
class Foo
@callback : Proc(Bool?)? = -> {}
end
Error in line 2: instance variable '@callback' of Foo must be (Proc((Bool | Nil)) | Nil), not Proc(Nil)
Right now, a proc that returns X isn't implicitly castable to a proc that returns X | Y.
In your case you have ->{}, which is Proc(Nil), but you require the callback to be Proc(Bool | Nil). It's solved if you do Proc(Bool?).new { } instead of ->{}.
I'm still not sure the rules should change. This works but maybe shouldn't:
def foo(block : -> Int32 | String)
end
foo ->{ 1 }
IMO it makes sense that you can use Proc(X) as Proc(X | Y). What would be the downside of that? The returned value is of type X and thus can be assigned to X | Y.
I'm not sure about implicit casting, but Proc(X).as(Proc(X | Y)) should be doable.
The problem is that a proc doesn't know what type it returns, only the compiler knows this when generating the executable. So, if you have a Proc returning X, when you call it the compiler assumes it's X. If you assign a Proc that returns X to a Proc that returns X or Y, when you call it you get a value that's assumed to be X or Y, which has a different memory layout than X. Boom.
Casting between proc types is already possible: use a proc literal, call the other proc and cast the return value.
@asterite Would it be possible for compiler to cast Proc(X) to Proc(X | Y) by appending .as call?
as doesn't work, I was just saying there's a way to fake it. And no, these casts will never be done
This issue has been mentioned on Crystal Forum. There might be relevant details there:
https://forum.crystal-lang.org/t/outsmarted-by-the-compiler/8051/3