I recently integrated a few third-party services and I think the story around doing that as it relates to Candid could be improved.
The current process seems to be to copy and paste from READMEs and .did files to your own .did file.
I think introducing a module system to Candid could provide a solution to ever-growing and unwieldy Candid files, as well as provide a better experience when integrating with third-party services.
Introduce an import keyword to Candid. Everything in a .did file would be implicitly exported.
e.g.
import canistergeek from "./canistergeek.did";
...
// Access types like canistergeek.LogMessagesData here if necessary
...
service : {
...
collectCanisterMetrics: canistergeek.service.collectCanisterMetrics;
getCanisterLog: canistergeek.service.getCanisterLog;
getCanisterMetrics: canistergeek.service.getCanisterMetrics;
}
This would be backwards-compatible with how things work today.
Alternatives Considered
To approximate the above proposal, service providers could follow a convention of separating the service declaration into a separate file from the rest, and then concatenating the files together using a custom build step.
Consumers would then add third-party .did files to the list of files they include in their own custom build step.
Interesting, I wasn’t aware of that – sounds a bit fishy :). An alternative would be to make trying to import something that has a service declaration an error. Of course, then people would have to split up their Candid sources into “libraries” and main files where reuse is intended, but that may be a good thing.
Or we can introduce scoping as Paul suggested and import the main service as well. There is an open issue in the spec about whether we want to have qualified names for imports.
OTOH, import can only appear in local development. When we attach the did file as canister metadata, import cannot appear there, because there is no notion of file system. We can only dump the typing environment when exporting did file.
type collectCanisterMetrics = func () -> ();
type getCanisterLog = func (opt CanisterLogRequest) -> (opt CanisterLogResponse) query;
type getCanisterMetrics = func (GetMetricsParameters) -> (opt CanisterMetrics) query;
The above works, but is it supported in ic-repl?
I have tests that do import codebase = "<canister id>" as "candid/codebase.did"; that are now failing with Error: Unbound type identifier collectCanisterMetrics