Let’s say I have a module with a class, and a collection of static functions.
I’m currently unable to use one of these static functions in the class constructor if this static function lies in the same file as my module.
module {
public func somethingStaticAndUseful(counter: Nat): Nat {
counter + 1;
};
public class Clazz(counter: Nat) {
// shows error: "cannot infer type of forward field reference somethingStaticAndUseful"
var counter = somethingStaticAndUseful(counter);
}
}
However, if I put the somethingStaticAndUseful() function in another file and import it, the code compiles without any errors.
Ideally, if I’m publishing a module, I’d like to include both the class and the static function in the same file and not have to copy/paste the code from the static function into the logic of the constructor.
For example, I don’t want to have to do this:
module {
public func somethingStaticAndUseful(counter: Nat): Nat {
counter + 1;
};
public class Clazz(counter: Nat) {
// if I copy the logic from the static function in here, it compiles
var counter = counter + 1;
// this is a simple example, but it's frustrating if there's more
// complicated logic in the static function that may call other
// "helper" functions located in the same module
}
}
@rossberg Thanks for the suggestion! Unfortunately this doesn’t fix the error described.
I just updated the code example and the Motoko playground link in the original post to demonstrate that this error is still occurring after adding the Nat return type.
There are two problems: (1) the inner counter shadows the outer (such that the argument refers to the wrong one), and (2) in classes, the types of var declarations cannot always be inferred early enough. This works for me:
public class Clazz(c : Nat) {
var counter : Nat = somethingStaticAndUseful(c);
}
It’s generally good practice to put types on var declarations (I remember we have even discussed making them mandatory, at least in classes).
Thanks for the help, the example above was somewhat contrived, but it ended up being that case that putting a type on the instance variable with the class fixes the issue.
// What I was previously doing
public class Clazz(arg : Nat) {
let instanceVariable = staticFunction(arg)` // <- forward reference error
};
// The Solution
public class Clazz(arg : Nat) {
let instanceVariable: Nat = staticFunction(arg) // Note the type added (`: Nat`)
}
This seems somewhat arbitrary that the instance variable would need a type (can just look at the return type of staticFunction(). Also that the error message is a bit misleading.
The problem is that everything can be mutually recursive, i.e., the type annotations on functions may themselves refer to class types. To unravel possible recursion, type checking proceeds in stages, the first one being collecting all named type definitions in a scope. A class is a form of type definition, but its meaning in turn has to be derived from its fields – before it has looked at functions, which is where the example failed. The type system could of course be more clever (e.g. require a dependency analysis or a fixpoint iteration), but this aspect is already pretty complicated and costly as is.
I agree the error message could be improved, though.