Cascading <system> tags in Libraries with Events

There may be no way around this, but I found a fun new Motoko game called “cascading tags during library integration.”

What happens is that my library lets the user pass in functions that it wants called during certain things(ie. handleTokenTransfer(block) : ()). The issue is that I don’t know what the user might do, and generally, I want them to be able to do whatever they want to do. Say they want to create a game where upon a token transfer they start a timer and 1 minute later a move occurs.

Well, not until the user tries to implement this function do they get the error that their fancy new function requires system capabilities. Boo.

As a result, I’m going in and annotating ANY function that may be user-defined to have system capabilities and that is making almost all my libraries public functions that change state end up having system capability.

Which makes me wonder what the point of having system capability is.

I would imagine this would come up with almost any library that does some kind of event notification system.

Like I said…I don’t know that there is a solution to this, but I’d like to know a bit better what the point of doing the whole system thing was. Was it to make sure a library you use isn’t draining your cycles or setting timers? It seems that you should know what you are using…I guess I get the safety being provided, but I’m finding myself having to route around it most of the time.

cc @claudio , @ggreif

3 Likes

Ya, i would imagine that the goal would be to have the user add the system type IF they are using anything that requires , and in @skilesare 's situation, I would imagine he wouldnt need define anywhere if he is not using system capabilities himself. Since from what Ive gathered the goal being around the safety of calling 3rd party libraries right? Or is there some other special magic going on?

The system capabilities were introduced to prevent third-party libraries (and their future versions) from doing stuff you never wanted to let them do.

I can see it might be annoying, but it’s only synchronous functions that need to be declared with explicit system capabilities, async functions already have them. @skilesare can you point at a PR where you are adding these - perhaps many are actually redundant.

1 Like

Declare a listener in a library here:

Instantiate here:

Call it here in the library:

Registered in an actor here:

So it can be called here:

Hmm, that doesn’t look that terrible to me, unless all the other listeners also need to grow a parameter.

Another option might be to declare

All async functions have system capability implicitly, but then you need to propagate the change in the type elsewhere too, in a similar way.

That gives the listeners the power to directly send messages too, which might be ok if you want to avoid using that Timer, but that depends on your application, of course.

Ok…interesting. So all async functions, including async* have system capabilities.

But if I do this I need to be ready for my user to make a state commit. So there are some trade offs.

I didn’t mean to imply it was bad, and if premeditated, it is likely easy to use…but maybe not intuitive? Perhaps there is a compiler warning:

If a module has a public item that is a function an it is not marked as system…you get a warning? Or what’s the opposite of system? Restricted? Perhaps public function or variable containing function types need to marked with one or the other?

If a library function is not declared as a system function then you get the guarantee that it can not do system stuff.

If it is declared as a system function then it may do system stuff, but you have to grant it system capability to call it and be in a context where you have that capability.

The compiler will warn if you implicitly grant the capability. You can silence the warning by explicitly granting it.

This is so you don’t accidentally call a system function without intending to.