I’m struggling to understand why a block is a captured block i.e. why i get Error: can’t use yield
inside a proc literal or captured block.
My code is similar to this, which also gives the same error:
https://play.crystal-lang.org/#/r/c3ky
I’m struggling to understand why a block is a captured block i.e. why i get Error: can’t use yield
inside a proc literal or captured block.
My code is similar to this, which also gives the same error:
https://play.crystal-lang.org/#/r/c3ky
TreeNode#each
captures the block, while TreeNodeUser#each
yields. You can’t yield from inside a captured block.
See Capturing blocks - Crystal for more details on capturing blocks.
I realize that the differentiation between captured and yielded blocks can be difficult. See Continue with anonymous block arguments · Issue #8764 · crystal-lang/crystal · GitHub for a proposal to introduce more explicit syntactic differences in method signatures.
Hm, i thought that forwarding it wouldn’t capture it. But ok, I see now.
And I guess I can’t fix this recursive case easily:
https://play.crystal-lang.org/#/r/c3lo
Maybe I can use some kind of visitor pattern to get around this.
I think I’ve read "To forward non-captured blocks, you must use yield
" multiple times in the docs, but now i’ve actually read it.
I think I’m starting to get this now :)
To capture a block you must specify it as a method’s block parameter, give it a name and specify the input and output types.
I think this tricked me. I thought i had to explicit specify input and output type for the block. Or maybe I’m missing something? Wouldn’t it be enough to say that one must specify it as a method’s block parameter? hm
Yeah, that’s a bit misleading. If you omit the type restriction, it defaults to Proc(Nil)
.
I don’t think it’s misleading, it’s just wrong. The docs should say that a captured block is one where you mention the name of the block inside the method’s body. The signature doesn’t matter at all for this to happen.
Today another question was raised in my head.
Is this block captured or not?
def f(&block : String -> _)
yield "hello"
end
According to the docs it should be, but it feels like it’s not since I’m just yielding and never accesses the block parameter.
If I change the block parameter definition to
def f(&block : Int32 -> _)
yield "hello"
end
i get a compiler error, so the block parameter isn’t ignored which indicates that it’s captured?
Will the compiler change yield
to block.call
?
A captured block is one where the block argument is mentioned inside the method definition, not just in the arguments list. Like this:
def f(&block)
block # here I'm using it
end
In any other case, it’s not a captured block, even if you give it a name in the arguments list.
The current docs are wrong and should be updated accordingly.
Thanks, that confirms my findings after comparing the llvm-ir output!