Hey, I've recently been exploring the effect system as a way to stop people from calling coroutine-only functions outside of a coroutine. This _seems_ like a prime use-case for effects. # coro.nim type YieldEffect = ref object of RootEffect proc jield*() {.tags: [YieldEffect].} = discard # todo: implement me Run
In theory, I could annotate my application's `main()` proc with `{.tags: [].}` which would guarantee `jield()` is never called on the main code path. This unfortunately falls apart because _callbacks_ and _forward-declared procs_ are assumed to have any and all effects, unless their effects are explicitly annotated. # foo.nim var callback: proc() proc setCallback*(f: proc()) {.effectsOf: [].} = callback = f proc doCallback*() = callback() Run # main.nim import foo proc main() {.tags: [].} = setCallback(proc () = echo "hello world!" ) doCallback() # Error: doCallback() can have an unlisted effect: RootEffect main() Run Library authors aren't gonna put `{.tags: [].}` on their callbacks (nor should they, as they can't assume that all effects are bad), therefore most libraries will accidentally bestow RootEffect on you at some point. I can get closer to a workable solution by using a non-inherited type. This way I can permit RootEffect and anything that inherits from it, but anything else is still an error. # coro.nim type YieldEffect = object # ... Run # main.nim import foo proc main() {.tags: [RootEffect].} = setCallback(proc () = echo "hello world!" ) doCallback() # ok! # jield() # correctly prevented :) main() Run But now the opposite problem occurs, it's easy for the effect to be "lost", for example by assigning to a proc variable, or calling a foward-declared proc, or using `effectsOf` from Nim 1.6: # main.nim import foo proc bar() proc main() {.tags: [RootEffect].} = setCallback(proc () = echo "hello world!" jield() ) var f: proc() f = proc() = echo "hello world!" jield() doCallback() # allowed :( f() # allowed :( bar() # allowed :( main() proc bar() = jield() Run I believe what I'm really looking for is a "forbidden" or "must be explicit" effect which is _always_ an error when assigned-to, passed-to or called-within any proc whose effects are left unspecified. The effect would only be allowed if it appears in the tag list. The onus is then on the propagators of the effect to always list it, rather than every possible reciever of the effect to reject it. Maybe it would look something like this? type YieldEffect = ref object of ExplicitEffect YieldEffect {.forbidden.} = object # or like this... YieldEffect {.explicit.} = object # or like this... YieldEffect = distinct object # or like this? Run Does this sound like a good idea or am I overlooking something?