Recursive canister generation

Hello… I did this code as an example to see if it is possible to create a canister of the same type as the canister from where it is created. Example I have a Counter class actor and through one of its methods another Counter type canister can be instantiated. From what I see here it has not thrown me errors but I have not tried it yet. Can work? Thanks

1 Like

This works in the Motoko interpreter but it will be rejected when compiling to the IC. There is a workaround, but it’s complicated. Do you need a workaround or were you just exploring?

1 Like

Hi Claudio, I was exploring with the intention of implementing that pattern in a project. It is not something that I need urgently, but when I reach a certain stage of the project I will have to opt for the complex solution.

Hello!! At Kyle’s suggestion on discord, I wrapped the “selfplaying” actor class in a module and then import it into main.mo, which will have the function of creating the initial instance of the “selfplaying” actor. Which got me pretty close to a solution, or at least to better identifying the problem. The error I got after that is the one discussed in this thread:

At first glance I understand that for some reason the compilation process prevents the possibility of making recursive calls of this type from being open, perhaps as a prevention of problems related to an erroneous implementation. I’m digressing. Here are the codes of both files with which I wanted to test this creational pattern.

autogen.mo

import Cycles "mo:base/ExperimentalCycles";
import Principal "mo:base/Principal";

module {
    public shared ({ caller = super }) actor class Counter(_owner : Principal, _init : Int) {
        stable var count = _init;
        public func incCount() : async () { count += 1 };
        public func decCount() : async () { count -= 1 };

        public shared ({ caller }) func mkNewCounter(_fee : Nat) : async Principal {
            Cycles.add(_fee);
            let client = await Counter(caller, count);
            Principal.fromActor(client);
        };
    };
};

main.mo

import autogen "autogen";

actor {
  public shared ({caller}) func create(): async autogen.Counter{
    await autogen.Counter(caller, 0); 
  };
};

This should actually work fine, if you replace autogen by:

import Cycles "mo:base/ExperimentalCycles";
import Principal "mo:base/Principal";

shared ({ caller = super }) actor class Counter(_owner : Principal, _init : Int) {
        stable var count = _init;
        public func incCount() : async () { count += 1 };
        public func decCount() : async () { count -= 1 };

        public shared ({ caller }) func mkNewCounter(_fee : Nat) : async Principal {
            Cycles.add(_fee);
            let client = await Counter(caller, count);
            Principal.fromActor(client);
        };
};

I.e. don’t wrap the imported class in a module.

Compare with the very similar: examples/motoko/classes at master · dfinity/examples · GitHub

1 Like

Hi, apparently that’s how I had it at first. I was getting a different error, I think [039], I’ll confirm that later. As I recall it said: “The variable counter is accessible… but it is not accessible in the compiled file” something like that. When I can I’ll check well, I’m from the mobile

Ah, sorry I missed the recursive call to Counter in mkNewCounter.

Recursion is indeed not support at the moment.

The reason has to do with the way we compile actor classes for the IC: the client of the class needs to include the wasm of the class in its image, so that it can install the wasm on the IC when instantiating the class. To support recursion, the wasm of the class would need to include a copy of itself, an infinite regress. We could solve this, like most problems in CS, with an extra indirection, but don’t at the moment.

1 Like

Oh I understand. Thank you very much anyway Claudio. I will stay tuned for future updates.