Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Julia Function type annotation

Tags:

types

julia

I`m new to Julia, so this might be a stupid question.

I have a type, with a function as an attribute, like:

struct exampleStruct
    someAtrribute::someType
    theFunction::Function
end

Is there a proper way to further specify the type of theFunction with regards to argument types and return types? In my case I know for sure that the instances of theFunction should only take arguments of type Float64 and have return values of type Float64, e.g. I could write something like theFunction::Function{Float64,...}{Float64}.

I guess you could maybe make a subtype of Function, but I feel like that would require digging a fair bit deeper.

like image 581
keylongx3x0 Avatar asked Oct 29 '25 09:10

keylongx3x0


1 Answers

In Julia every function has its own unique type. For example if you take function sin:

julia> typeof(sin)
typeof(sin) (singleton type of function sin, subtype of Function)

you can see that it has type that is displayed as typeof(sin). Additionally you get an information that this type is a subtype of abstract type Function. You can check this with:

julia> supertype(typeof(sin))
Function

Conversly, you can find all subtypes of Funcion type in your current Julia session by writing:

julia> subtypes(Function)
11259-element Vector{Any}:
 ArgTools.var"#1#11"
 ArgTools.var"#10#20"
 ArgTools.var"#2#12"
 ArgTools.var"#21#31"
 ⋮
 typeof(⊉) (singleton type of function ⊉, subtype of Function)
 typeof(⊊) (singleton type of function ⊊, subtype of Function)
 typeof(⊋) (singleton type of function ⊋, subtype of Function)

(this is the output on a fresh Julia 1.7.2 session)

Now it is crucial to understand that function type is associated with its name and the module in which the function was defined. For example let me import the Statistics module:

julia> import Statistics

julia> typeof(Statistics.mean)
typeof(Statistics.mean) (singleton type of function mean, subtype of Function)

Actually you can extract out type name and module where the function type was defined:

julia> Base.nameof(sin)
:sin

julia> Base.parentmodule(sin)
Base

julia> Base.nameof(Statistics.mean)
:mean

julia> Base.parentmodule(Statistics.mean)
Statistics

(this distinction is important as you can have in Julia many functions having the same name, but be defined in different modules)

So you can think of function type as being defined by function name and module. As you can see there is no argument types or return value type in definition of function type. Why is it so? The reason is that one function in Julia can have many methods. Function type itself does not provide any functionality - it is just a name and module in which the function name is defined. Only after you add methods to a function you have something that can be executed. Since you can have many methods of a given function each of this methods can have different types of arguments and different return value.

Let us check for Statistics.mean what methods it has defined:

julia> methods(Statistics.mean)
# 5 methods for generic function "mean":
[1] mean(r::AbstractRange{<:Real}) in Statistics
[2] mean(A::AbstractArray; dims) in Statistics
[3] mean(itr) in Statistics
[4] mean(f, A::AbstractArray; dims) in Statistics
[5] mean(f, itr) in Statistics

And you can add methods to a function later. Here is an example:

julia> function f end
f (generic function with 0 methods)

julia> methods(f)
# 0 methods for generic function "f":

julia> Base.nameof(f)
:f

julia> Base.parentmodule(f)
Main

julia> f(x::Integer) = 10x
f (generic function with 1 method)

julia> methods(f)
# 1 method for generic function "f":
[1] f(x::Integer) in Main at REPL[26]:1

julia> f(x::String) = x^10
f (generic function with 2 methods)

julia> methods(f)
# 2 methods for generic function "f":
[1] f(x::Integer) in Main at REPL[26]:1
[2] f(x::String) in Main at REPL[28]:1

(in the example I show you that you can even define a function, with a new type that initially has no methods defined)

In summary:

  • no function in Julia has Function type;
  • every function in Julia has its own unique type that is subtype of Function abstract type;
  • every function type is only identified by function name and module in which the function was defined.
  • a single function type can have many methods that take different arguments and have different return value.

For these reasons in Julia it is impossible to specify the type of function with regards to argument types and return types (you can only specify it up to name and module where the function type was defined).

Now regarding the comment of @jling.

Your definition:

struct exampleStruct
    someAtrribute::someType
    theFunction::Function
end

is correct and will accept any function as theFunction argument. However, what is suggested that you create a parametric type instead:

struct exampleStruct{T<:Function}
    someAtrribute::someType
    theFunction::T
end

This recommendation is related to later performance of using the exampleStruct. If field type of theFunction is Function this is an abstract type and using abstract types as parameters in containers will degrade performance of your code as is explained in this section of the Julia Manual. However, if your code is not performance critical then you should be fine with your original definition.

As a final note, bear in mind that in Julia, there are callables that are not a subtype of Function type. Two most important cases are type constructors, e.g. you can call Int constructor, but Int is not a subtype of Function, and the second are functors, as explained in this section of the Julia Manual.

This means that in your definition of exampleStruct, if you restrict the second field type to Function you will disallow passing some callables as its arguments. Maybe this is not a problem in your case, but it is a common issue that is encountered when users try to create data structures that store functions as their fields.

like image 175
Bogumił Kamiński Avatar answered Oct 31 '25 02:10

Bogumił Kamiński



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!