Disclaimer: I am new to Crystal (and Ruby), so I might be missing something.
I tried to create a recursive block/proc:
def f
g = ->(n: Int){ g n-1 }
g
end
f
That didn't work with an error about an undefined reference to g. Understandable. However, things then start getting odd. I figured I could declare g as nil and then re-assign it:
def f
g = nil
g = ->(n: Int){ g n-1 }
g
end
f
I still got an undefined reference error! But this:
def h
yield n
end
def f
g = nil
g = ->(n: Int){ h &g; g n-1 }
g
end
f
gives:
Error in ./rec.cr:26: instantiating 'f()'
f
^
in ./rec.cr:22: expected a function type, not Nil
g = ->(n: Int){ h &g; g n-1 }
Right. Forgot about Nil checks. However, changing g's body to:
g = ->(n: Int){ h &g if g; g n-1 }
gives the exact same error. I can't put a def inside of a def either. IS THERE SOME WAY TO SOLVE THIS?? I feel at least the errors should be more consistent (why is g not defined when being called but defined when being passed to a function, but still has type Nil even when suffixed with an if guard?).
BTW, I solved this by doing:
g = ->(n: Int) { 0 }
g = ->(n: Int){ g n-1 }
Wait, I think I got it. g is of type Nil in one case because it hasn't been defined yet. The other error is more helpful.
Still leaving this open because:
Nil or undefined.@kirbyfan64 Sorry, but most of the snippets don't compile. For example g n-1 doesn't compile, to invoke a Proc you need to do g.call(n-1).
We discussed this with @waj some days ago, we'll probably need some keyword to refer to the current Proc (other than self, of course) to implement this, but I don't think this is something that's really useful, you can always workaround it with other solutions. But I'll leave this open as an enhancement.
(proposed: clojure's recur)
Thanks to @waterlink in #2218 you can actually do this, and there's no need for a new keyword:
def f
g = uninitialized Int32 -> Int32
g = ->(n : Int32) do
if n <= 1
1
else
n * g.call(n - 1)
end
end
g
end
10.times do |i|
p f.call(i)
end
Not super pretty, you have to use uninitialized, but it gets the job done. And since I think this need isn't very common, it's better to keep things simple for now.
Well, I guess it's not used a lot in some styles of programming, but it's very functional-ish :).
Most helpful comment
Thanks to @waterlink in #2218 you can actually do this, and there's no need for a new keyword:
In play.crystal-lang.org
Not super pretty, you have to use
uninitialized, but it gets the job done. And since I think this need isn't very common, it's better to keep things simple for now.