Definedness error when trying to pass functions to classes

I’ve run into a definedness error and I’m not sure that it should be an error…and if it is I need a strategy to fix it! I have an instantiation of a class that takes an argument that is a ?func. That func uses the class, but it would never be called before the class was instantiated.

func myfunc(){ myClass.doSomething() };

let myClass = MyClass({onDoSomething = ?myFunc});

definedness error [M0016], cannot use myfunc before myClass has been defined

If I flip things around and do the class first I get two definedness errors! cannot use myFunc before myFunc has been defined and cannot use myFunc before myClass has been defined.

This might work, but I’m not sure how useful it is since it will probably just loop when you try to call myClass.doSomething():

class MyClass({onDoSomething = fo: ?(() -> ())}) {
  public func doSomething():() { 
    switch fo {
      case null {};
      case (?f) { f(); };
    }
  }
};

var temp = MyClass({ onDoSomething = null});

func myFunc(){ temp.doSomething()};

let myClass = MyClass({onDoSomething = ?myFunc});

temp := myClass;

I ended up making the class a var, setting my function to null, and creating an init function that runs at the end of my actor class instantiation to set it back the way I want it. So the questions are, what am I giving up by making it a var? Memory? And should it be an error at all if I’m not calling the function, but just referencing it?

I think you are giving up some safety (you could forget to set the var) and a little perf (you need to dereference the var) but not much else.

The compiler rejects the original code because, like all type systems, it is conservative, and assumes that MyClass could use its argument, and apply myFunc, and thus read myClass, before myClass is actually defined.

If this was translated directly from the example you gave in your original post here, what would it look like?

Take a look at how I solved it my to_sync and to_async branches. I think we should at least take it to async*/await*, but I know with mops I had to hack in tests with async*/await*. I was thinking sync, but with the IC architecture, it may be nice to know we can interrupt at a particular point and continue on in a future round, we may just need keep track of a lock at the top level so that we don’t allow simultaneous execution. In theory, the scheduler could intermingle calls if they come in on the same round.

I didn’t have to use classes, but instead a function returning the engine.