New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Passing a block to a call on a union that uses typeof #5299
Comments
Bump; We're working on an abstract caching API in discordcr/discordcr#136 that allows users to define a custom caching backend, and ran into this issue when implementing The current workaround has been to use # BUG: already has enclosing call
client.cache.each do |value|
p value
end
# OK:
client.cache.as(MemoryCache(Int32, String)).each do |value|
p value
end
Let me know if there's any other info I can provide. |
I checked out this is done and it seems the language can't handle multiple dispatch when captured blocks are involved (either when two methods that capture blocks are involved, or when one captures and the other one yields). Changing this is pretty complex, so for now I would advice not doing that: either yield all the time, or avoid the multiple dispatch (especially when the block type changes, as in the example above with A and B, which makes very little sense). Also, @z64 What's your use case? Why is this a problem? Or do you run into the bug when you try to inspect the block's type using |
@asterite If you'll reference the boiled down carc.in I linked (https://carc.in/#/r/4gsy), I ran into this naturally while developing it: (not a lot of this applies to the issue at hand probably, but I'll paint the full picture as best I can)
A user might chose to store something like channels in memory, while choosing to store users in a database backend on disk.
def each(&block : Tuple(K, V) ->)
@data.each do |key, value|
yield({key, value})
end
end In another implementation for example, like an
I hope that is clear enough.. I realize this is painted with generics, which I know is very much a weak point right now, but sans for this bug/behavior, it results in a very powerful way for our users to control how our client stores data per-type, which is particularly important for large scale deployments of our client. |
@z64 Just replace: @cache.each_value(&block) with: @cache.each_value do |value|
yield value
end and it will work. That way you don't capture the block, the block gets inlined, it's more efficient, and it actually compiles :-) |
@asterite Thank you so much! That works. ❤️ I guess I didn't realize that explicitly writing |
It's more or less explained here: https://crystal-lang.org/docs/syntax_and_semantics/block_forwarding.html It's of course no excuse for the bug :-) |
If we're going to disallow this, we should have a proper error message. |
No, it's a bug but fixing it is hard. |
I think I'm getting this same bug. I found a way around it for now, but I get Using crystal 0.26.1 with Lucky, and using clear. This is the code that throws the error: movie.actors.each do |actor|
link(actor.name, to: path_for_actor(actor))
end Where To fix this, I changed the actors method class NullMovie
def actors
- [] of Actor
+ Actor::Collection.new
end
end This actually makes more sense anyway since this makes |
I think the correct way to fix this would be to have one block instance per def instantiation. That means that for the multi-dispatch case one block would be typed with Alternatively we could just forbid passing a block if it's going to have multiple types, because the block is essentially transformed into a |
Building the following code fails with an error.
This happens both with
Crystal 0.23.1 [e2a1389] (2017-07-13) LLVM 3.8.1
andCrystal 0.23.0+313 [ea4187c57] (2017-10-27) LLVM: 3.9.1
on Ubuntu 16.04 LTS.The following code does compile but the types will be wrong. There are too many
A
s and not enoughB
s. Also, the class types should have unions in them in some of the cases.The text was updated successfully, but these errors were encountered: