Those types look complicated, but sometimes come out naturally when programming, because of nested struct constructs. They end up in the type fields of the outer type because this is the easiest way to avoid abstract field. For example:

This has a very simple type signature, but all fields are abstract, and will be very bad for performance:

julia> struct A
x::Function
y
end
julia> struct B
a::A
b
end
julia> b = B(A(sin, 1), true)
B(A(sin, 1), true)
julia> typeof(b)
B

Now if you use parametric types to annotate the types of each field, the signatures get complicated quite quickly:

julia> struct C{F,T}
x::F
y::T
end
julia> struct D{A,B}
a::A
b::B
end
julia> d = D(C(sin,1), true)
D{C{typeof(sin), Int64}, Bool}(C{typeof(sin), Int64}(sin, 1), true)

But now all fields are concrete, and performance of operations on these types will be better.

You do not need to parameterize every field (you can use for instance b::Bool, c::Float64, etc, but when the fields contain more complicated objects it starts to be easier just to use a parametric type than to get the correct signature for the field.

thx, I finally understood why this complex struct comes out. So in practice how can we judge an instance with such complicated struct match which method? And developer how to organized structs in pkg?

The functions donâ€™t need to be annotated with all parameters for dispatch. For example:

julia> using StaticArrays
julia> f(x) = 1 # generic
f (generic function with 1 method)
julia> f(x::SVector) = 2
f (generic function with 2 methods)
julia> f(x::SVector{3}) = 3
f (generic function with 3 methods)
julia> f(x::SVector{3,T}) where T<:Float64 = 4
f (generic function with 4 methods)
julia> f([1,2,2])
1
julia> f(SVector{2,Float64}(1,2))
2
julia> f(SVector{3,Float32}(1,2,3))
3
julia> f(SVector{3,Float64}(1,2,3))
4

the type of sin is a Function, why it reports typeof(sin)?

â€śThe functions donâ€™t need to be annotated with all parameters for dispatch.â€ť. Maybe I understand what you mean, because the specific parameterized types are subtypes of the type itself, so if I want to pass d to a method, I donâ€™t have to care about the type structure inside D, as long as that method can receive the type D

julia> typeof(sin) === Function
false
julia> typeof(sin) <: Function
true
julia> x = rand(3);
julia> typeof(x) === AbstractArray
false
julia> typeof(x) <: AbstractArray
true

Function is abstract.

Note that this means you can dispatch on particular functions, and that a function receiving another function as an input can specialize on that particular function (note that as a heuristic, specialization will generally only occur if the function is called, or specialized is forced via making the type a parameter, e.g foo(f::F) where {F}.

For example

julia> foo(::typeof(sin)) = "sin"
foo (generic function with 1 method)
julia> foo(::Function) = "some other function"
foo (generic function with 2 methods)
julia> foo(cos)
"some other function"
julia> foo(sin)
"sin"