Feature Request (Motoko): Extend or import functionality into actor classes

Let’s say I want to write an actor that has some base functionality, and then I want others to use this actor and build on top of it without breaking any of the abstractions and base logic I have laid underneath.

Currently, Motoko provides no functionality for the following:

import Y "mo:baseActor/Y";

actor X extends Y` {}

This means if I want to create an actor framework, the best I can do is to write modules, and then tell the developer to copy/paste each of the public/shared function code and import the one liner modules. Something like this

import Y "mo:some-module/Y";

shared ({ caller = caller )} actor class X {

  public func f1(t: Text): async() {
    await Y.f1(t);
  };

  // can't define shared functions in modules so need to pass the caller as an argument
  public shared({caller = caller}) func f2(): async() {
    await Y.f2(caller);
  }

  // If I tried making Y.f2() a shared function and importing that directly, I would get the following error:
  // 
  // "type error [M0077], a shared function is only allowed as a public field of an actor
  // (This is a limitation of the current version.)"
}

This works, but if the developer forgets to import an actor API endpoint, it could break their application in unexpected ways.

A few questions:

  1. Are there any “cleaner” ways than what I’ve laid out to extend or import public facing APIs into actors?
  2. If not, would an “extends” or imports/uses functionality similar to what I laid out at the top of this post be feasible? What type of effort/changes would this involve? (This feature would be amazing for abstracting away the complexities of infrastructure frameworks).
2 Likes

I’ve run into this limitation in the past in various young languages and my workaround has always been this;

Anywhere you want to have a user to create a subclass, have them create an instance instead.

It probably requires changing your class constructor to accept a record of options, with some fields possibly containing functions that represent any abstract methods that need to be implemented.

It’s not quite the same but it can get you pretty far in my experience.

As for real extends functionality, I think the class keyword is really just convenience for declaring a type and a function that returns a record (see my talk from Motoko Bootcamp: Motoko Bootcamp V1 2022 | Motoko Variant types - YouTube). We already have this at the type level with intersections so maybe something at the value level would get us most of the way there

If you’re concerned about missing methods, you can always statically enforce that they exist through functions that accept instances of the class as arguments.

1 Like

To clarify; by this I meant when asking users to implement classes on their own. As long as your function argument expects a certain type, then even if a user defines an “invalid” class they won’t be able to pass it to a function that your API provides.

It would indeed be amazing!!