Why can't I put a anonymous function inside a type parameter of a struct?

    abstract type FieldName{A}  end
    abstract type FieldType{A}  end
    abstract type FieldValue{A} end
    abstract type FieldMutability{A} end

    abstract type Field{A<:FieldName, B<:FieldType, C<:FieldValue, D<:FieldMutability} end

    setfieldname(name)      = Field{FieldName{()->name},B,C,D} where {B<:FieldType, C<:FieldValue, D<:FieldMutability}
    setfieldtype(atyp)      = Field{A,FieldType{()->atyp},C,D} where {A<:FieldName, C<:FieldValue, D<:FieldMutability}
    setfieldvalue(val)      = Field{A,B,FieldValue{()->val},D} where {A<:FieldName,B<:FieldType,D<:FieldMutability}
    setfieldmutability(mut) = Field{A,B,C,FieldMutability{()->mut}} where {A<:FieldName,B<:FieldType,C<:FieldValue}

julia> setfieldname(:mixin)
ERROR: TypeError: in Type, in parameter, expected Type, got getfield(Main, Symbol("##3#4")){Symbol}
Stacktrace:
 [1] setfieldname(::Symbol) at ./REPL[6]:1
 [2] top-level scope at REPL[10]:1

#however this perfectly works fine....

julia> Field{FieldName{()->:mixin}}
Field{FieldName{getfield(Main, Symbol("##11#12"))()},B,C,D} where D<:FieldMutability where C<:FieldValue where B<:FieldType

is it because where the anonymous function is created? if so why?

In general, you can’t use anything as a type parameter. The things you can use are (see here):

  • Both abstract and concrete types can be parameterized by other types. They can also be parameterized by symbols, by values of any type for which isbits returns true (essentially, things like numbers and bools that are stored like C types or struct s with no pointers to other objects), and also by tuples thereof. Type parameters may be omitted when they do not need to be referenced or restricted.

In your case, the underlying problem is that the thing you’re trying to use as a type parameter, mainly ()->name, is a closure with closed over variable, name. The closure is basically just a struct that stores the value name, which in your case is a Symbol, but isbitstype(Symbol) is false, so any struct containing a Symbol (including the closure) is also not bits type. If your closed over variable is bits type though it could work, e.g. you exact code above works with setfieldname(1) because isbitstype(Int) is true.

In any case, storing a closure/anonymous function in a type parameter seems a bit like a misuse of parametric types tbh. Not sure exactly what you’re doing, but maybe traits are a better option? Or if you’re just trying to move some computation to compile time for performance reasons, maybe generated functions are more flexible / cleaner? Ultimatley, for simple functions like your example, I would bet Julia does a great job optimizing the code anyway so there’s no penalty to just having the anonymous function be a field rather than a type parameter.

1 Like

You should store the type of the function in the type parameter. The function itself is “stored” in a field as a formality. Because the function is uniquely defined by its type (that is, each function has its own type) the information is carried by the type parameter, and the field itself requires zero bytes of storage.
Example:

julia> struct FunctionContainer{F}
          f::F
       end

julia> c = FunctionContainer(()->:hello)
FunctionContainer{var"#15#16"}(var"#15#16"())

julia> sizeof(c)
0

julia> c.f()
:hello
1 Like