Added $170 bounty to this https://www.bountysource.com/issues/90719678-proc-nil-in-a-method-type-restriction-does-not-work-like-it-does-elsewhere
Proc(Nil)) and have Procs that return non-nil values worksThis allows you to do something like:
class ProcNil
getter proc : Proc(Nil)
def initialize
@proc = -> { 123 }
end
end
pp! ProcNil.new.proc.call # Which will return nil
Using a Proc(Nil) when restricting in a method argument does not work the same. It requires an explicit nil return value in the Proc
class ProcNil
def initialize(proc : Proc(Nil))
end
end
# Error: no overload matches 'ProcNil.new', proc: Proc(Int32)
#
# Overloads are:
# - ProcNil.new(proc : Proc(Nil))
pp! ProcNil.new(proc: ->{123})
To get this to work you'd need to explicitly return nil:
ProcNil.new(proc: ->{123;nil}
I think the reason the version with getter works is that there is an odd behavior when using Proc(Nil) with a method
```crystal
def my_proc : Proc(Nil)
->{ 123 }
end
nilpp! my_proc.call # 123
pp! typeof(my_proc) # Proc(Int32)
````
So it seems like this may be 2 issues. The type restriction in a method incorrectly return a Proc(Int32) even though the restriction is Proc(Nil). This by happenstance allows the first example to work
This is something I would really love to use in my Lucky projects to do stuff like this for generic HTML components where you can add Proc "slots" for passing in HTML https://gist.github.com/paulcsmith/ec853d33d1a0ef9b1eedd070e58136b9
So I've added a $170 bounty to this since I know I'm asking for a potentially complicated fix! :D https://www.bountysource.com/issues/90719678-proc-nil-in-a-method-type-restriction-does-not-work-like-it-does-elsewhere
My hope is this also helps someone during the crazy economy/COVID-19
Hi!
These are two separate issues.
The first one:
Proc(Nil) to a Proc(Nil) restriction should work (also for more number of arguments, this is only about the return type)Proc(Nil) should make the method return Proc(Nil) regardless of whether it returns that, Proc(Int32), Proc(String), etc.I'll fix 2. I'm not sure about 1. The only case where we do something like that is when the return type is Nil. But that's similar to returning void in other languages.
We _can_ implement point 2. What's the use case?
Why do:
def foo : Proc(Nil)
->{ 1 }
end
instead of:
def foo
Proc(Nil).new { 1 }
end
Also, returning procs from methods isn't very common, so I'm very curious about the use case.
Hi Ari!
Sorry about the confusion! The issue I have is really about that first example in the problem section where I’d like to use a proc that has a non nil return value (like Proc(int32)) with a Proc(Nil) restriction. But maybe that’s weird. It appears to work in the first example and I think it is because it uses a getter and instance car with a type restriction instead of putting the type restriction in the method argument
The whole bit about restricting return type in a method to Proc(Nil) isn’t something I’ve ever done or needed. It was just me trying to see if that was maybe why the first example worked.
Does that make sense or did I just make it even more confusing? :P
But now that I think about it it is a bit strange to allow what I’m asking...I wonder why the first example works and the second doesn’t. Almost seems like neither one should work... 🤔
Maybe there is some kind of generic way of to say “I want a Proc and don’t care about the return type”? Like just Proc. And that’d allow a Proc with no args and any return type and will return nil. Maybe we need to chat because I’m not sure if I’m making any sense
I wonder why the first example works and the second doesn’t
The first example works because in #7527 we made the union of Proc(T) and Proc(Nil) to be Proc(Nil). So if you assign Proc(T) to something of type Proc(Nil), the result type is Proc(Nil) and that's exactly the type of the instance variable you are assigning to.
The second example doesn't work because type restriction logic is in a completely separate place in the compiler. Combining union types into other types is one thing. Restricting a type to enter into a method is another thing.
That second thing was overlooked in #7527 (by me ;-))
Also a reminder that #7527 introduced https://github.com/crystal-lang/crystal/issues/7698.
Yeah, I'll try to revert #7527 and only allow assigning Proc(T) to a Proc(Nil) instance/class var, or a var with a declared type. But the compiler won't merge Proc(T) | Proc(Nil) into Proc(Nil) anymore (if I can make it work).
Sounds good and thanks for the explanation Ary!
On Mar 28, 2020, at 1:55 PM, Ary Borenszweig notifications@github.com wrote:

Yeah, I'll try to revert #7527 and only allow assigning Proc(T) to a Proc(Nil) instance/class var, or a var with a declared type. But the compiler won't merge Proc(T) | Proc(Nil) into Proc(Nil) anymore (if I can make it work).—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
@asterite Thanks for fixing this! Remember to claim on https://www.bountysource.com/issues/90719678-proc-nil-in-a-method-type-restriction-does-not-work-like-it-does-elsewhere and then I'll confirm it!
Thank you! It's fine, I don't need the bounty. Maybe give it to help fighting covid? Or maybe give it to Trump and convince him to enter total lockdown?
Trust me I’d love to convince trump of lots of things. Will give it elsewhere. Thanks Ary!
Most helpful comment
Thank you! It's fine, I don't need the bounty. Maybe give it to help fighting covid? Or maybe give it to Trump and convince him to enter total lockdown?