Types must be first defined then called in a module? Why?

module MyModule
    
function myfun(t::MyType)
    println(plusone(t.x))
end

struct MyType
    x::Float64
end

function plusone(x)
    x + 1
end

end
ERROR: UndefVarError: `MyType` not defined 

Your type doesn’t exist yet, but it’s already used. The compiler/interpreter of programming languages typically don’t look ahead of time to see that it’s defined somewhere. This design choice is present in languages like c++ too. I think it has to do with the idea that a code shouldn’t look ahead.

That’s different with the feature of a function?

If you remove the type annotation (of the function argument), it would work.

Yes. But do you think that’s inconvenient? Does that mean it’s better to (or we have to) put all of the type definitions on the top of a module?

That’s a common convention. I myself have been supportive of features requiring the code to look ahead such as attribute inference too but the idea is not very popular.

1 Like

I didn’t realize this problem until today, which confused me very much.

The idea is “All the symbols in the execution context must be defined.” It would be strange behavior if you saw this in a function

function my_func(vec)
    res = map(f, vec)
    f = (x) -> sin(x)
    return res
end 

Because f is reference before assignment.

In global scope, same rules apply.

The first line of a method is its signature and that signature has to be resolved at time of definition so that it can be added to the MethodTable for dispatching. So each Type annotation needs to have been defined already in order to not get a reference before assignment type error.

Now, you CAN have undefined symbol in an unexecuted context, for example, the method’s body. So this works:

f() = MyType()
struct MyType end
f() 

Because the calls inside f don’t get resolved until runtime.

4 Likes