What is the meaning of <: in julia? [duplicate] - julia

This question already has answers here:
Julia: what does the "<:" symbol mean?
(2 answers)
Closed 2 years ago.
Can anyone help me with this code:
struct GenericPoint{T<:Real}
x::T
y::T
end
What is meaning of <: in {T<:Real} in Julialang?

Let me begin by saying that the punctuation page of the manual is a handy way to search for such operators, which are otherwise very difficult to look for using a search engine.
In the specific case of <:, we find this page, with the relevant documentation for essential operators.
There are (at least) 3 contexts where A <: B might be used, and in all of them this expresses the idea that A is a subtype of B.
as a predicate, A <: B returns true if and only if all values of type A are also of type B:
julia> Int <: Number
true
julia> Int <: AbstractString
false
in a type definition, this declares that the newly defined type is a subtype of an existing (abstract) type:
# `Foo` is declared to be a subtype of `Number`
struct Foo <: Number
end
as a type-parameter constraint (like in your example), T <: Real expresses the idea that type parameter T can be any subtype of Real:
julia> struct GenericPoint{T<:Real}
x::T
y::T
end
# Works because 1 and 2 are of type Int, and Int <: Real
julia> GenericPoint(1, 2)
GenericPoint{Int64}(1, 2)
# Does not work because "a" and "b" are of type String,
# which is not a subtype of Real
julia> GenericPoint("a", "b")
ERROR: MethodError: no method matching GenericPoint(::String, ::String)
Stacktrace:
[1] top-level scope at REPL[5]:1
Note that the use for type-parameter constraints is not restricted to the definition of parametric types, but also applies to function/method definitions:
julia> foo(x::Vector{T}) where {T<:Number} = "OK"
foo (generic function with 1 method)
# OK because:
# - [1,2,3] is of type Vector{Int}, and
# - Int <: Number
julia> foo([1, 2, 3])
"OK"
# Incorrect because:
# - ["a", "b", "c"] is of type Vector{String}, but
# - String is not a subtype of Number
julia> foo(["a", "b", "c"])
ERROR: MethodError: no method matching foo(::Array{String,1})

Related

How to lock the variable type in Julia?

I want to lock the type of a variable in Julia, how to do? For example, I define an array called weight,
weight = Array{Float64,1}([1,2,3])
Now I want to lock the type of weight as Array{Float64,1}, is it possible?
I mean if do not lock the type of weight, then if I mistakenly or casually do
weight = 1
Then weight will become an Int64 variable, so it is not longer a 1D array. This is obviously not what I want.
I just want to make sure that once I defined weight as 1D Float64 array, then if I change the type of weight, I want Julia gives me an error saying that the type of weight has been changed which is not allowed. Is it possible? Thanks!
This is useful because by doing this, it may preventing me from forgetting weight is an 1D array, and therefore preventing bugs.
For global variables use const:
julia> const weight = Array{Float64,1}([1,2,3])
3-element Vector{Float64}:
1.0
2.0
3.0
julia> weight[1]=11
11
julia> weight=99
ERROR: invalid redefinition of constant weight
Note that redefining the reference will throw a warning:
julia> const u = 5
5
julia> u=11
WARNING: redefinition of constant u. This may fail, cause incorrect answers, or produce other errors
You can circumvent it by using the Ref type:
julia> const z = Ref{Int}(5)
Base.RefValue{Int64}(5)
julia> z[] = 11
11
julia> z[] = "hello"
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
In functions use local with type declaration:
julia> function f()
local a::Int
a="hello"
end;
julia> f()
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
You'd normally write:
weight::Vector{Float64} = Array{Float64,1}([1,2,3])
...but this doesn't seem to be possible in global scope:
julia> weight::Vector{Float64} = Array{Float64,1}([1,2,3])
ERROR: syntax: type declarations on global variables are not yet supported
Stacktrace:
[1] top-level scope
# REPL[8]:1
However, you can do it in local scope or in a struct:
julia> function fun()
weight::Vector{Float64} = Array{Float64,1}([1,2,3])
weight = 1
end
fun (generic function with 1 method)
julia> fun()
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Vector{Float64}
Closest candidates are:
convert(::Type{T}, ::AbstractArray) where T<:Array at array.jl:532
convert(::Type{T}, ::LinearAlgebra.Factorization) where T<:AbstractArray at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.6/LinearAlgebra/src/factorization.jl:58
convert(::Type{T}, ::T) where T<:AbstractArray at abstractarray.jl:14
...
Stacktrace:
[1] fun()
# Main ./REPL[10]:3
[2] top-level scope
# REPL[11]:1
You could use const, but then redefinition with a value of the same type will cause a warning:
julia> const weight = Array{Float64,1}([1,2,3]);
julia> weight = [2.]
WARNING: redefinition of constant weight. This may fail, cause incorrect answers, or produce other errors.
1-element Vector{Float64}:
2.0

Generalise heterogeneous tuple of variable-length MVector's to a variable number of MVector's

Is there a way to create a parametric type which represents a heterogeneous tuple of variable length, where each element of the tuple is a MVector, such that the MVector's can have different length but they contain the same numerical type. So in pseudo code I would like to have something like this:
using StaticArrays
struct MyType{T, N1, N2, N3, ...}
data::Tuple{MVector{N1, T}, MVector{N2, T}, MVector{N3, T}, ...}
end
I can of course make a more specific type like:
struct MyType3{T, N1, N2, N3}
data::Tuple{MVector{N1, T}, MVector{N2, T}, MVector{N3, T}}
end
But I would like to generalise to an tuple of arbitrary length.
If you don't need the "variadic type arguments" N1...Nk explicitely, you can use a tuple of Vararg of some UnionAll types:
julia> MyType3{T} = Tuple{Vararg{MVector{M, <:T} where M}}
Tuple{Vararg{MArray{Tuple{M},#s14,1,M} where #s14<:T where M,N} where N} where T
julia> (MVector{2}([1,2]), MVector{3}([1,2,3])) isa MyType3{Int}
true
julia> (MVector{2}([1,2]), MVector{3}([1,2,3])) isa MyType3{Number}
true
julia> (MVector{2}([1,2]), MVector{3}([1,2,3])) isa MyType3{String}
false
julia> (MVector{2}([1,2]), MVector{3}([1,2,3])) isa MyType3
true
julia> (MVector{2}(["a", "b"]), MVector{3}([1,2,3])) isa MyType3
true
Or maybe like this, where you have an explicit tuple of the size types:
julia> struct MyType7{T, Ns<:Tuple, D}
data::D
function MyType7(vecs::Vararg{MVector{M, T} where M}) where {T}
Ns = [typeof(v).parameters[4] for v in vecs]
new{T, Tuple{Ns...}, typeof(vecs)}(vecs)
end
end
julia> MyType7(MVector{2}([1,2]), MVector{3}([1,2,3]))
MyType7{Int64,Tuple{2,3},Tuple{MArray{Tuple{2},Int64,1,2},MArray{Tuple{3},Int64,1,3}}}(([1, 2], [1, 2, 3]))
julia> typeof(ans)
MyType7{Int64,Tuple{2,3},Tuple{MArray{Tuple{2},Int64,1,2},MArray{Tuple{3},Int64,1,3}}}
But this is now a kind of a hack, since we use parameters to reflect on the type arguments at runtime. If you use this variant, be sure to know what you're doing (which I wouldn't quite say about myself), and to benchmark it (or maybe make it #generated?).

What is the difference between fields and properties in Julia?

Julia has the setter functions setproperty! and setfield! and the getter functions getproperty and getfield that operate on structs. What is the difference between properties and fields in Julia?
For example, the following seems to indicate that they do the same thing:
julia> mutable struct S
a
end
julia> s = S(2)
S(2)
julia> getfield(s, :a)
2
julia> getproperty(s, :a)
2
julia> setfield!(s, :a, 3)
3
julia> s
S(3)
julia> setproperty!(s, :a, 4)
4
julia> s
S(4)
fields are simply the "components" of a struct. The struct
struct A
b
c::Int
end
has the fields b and c. A call to getfield returns the object that is bound to the field:
julia> a = A("foo", 3)
A("foo", 3)
julia> getfield(a, :b)
"foo"
In early versions of Julia, the syntax a.b used to "lower", i.e. be the same as, writing getfield(a, :b). What has changed now is that a.b lowers to getproperty(a, :b) with the default fallback
getproperty(a::Type, v::Symbol) = getfield(a, v)
So by default, nothing has changed. However, authors of structs can overload getproperty (it is not possible to overload getfield) to provide extra functionality to the dot-syntax:
julia> function Base.getproperty(a::A, v::Symbol)
if v == :c
return getfield(a, :c) * 2
elseif v == :q
return "q"
else
return getfield(a, v)
end
end
julia> a.q
"q"
julia> getfield(a, :q)
ERROR: type A has no field q
julia> a.c
6
julia> getfield(a, :c)
3
julia> a.b
"foo"
So we can add extra functionality to the dot syntax (dynamically if we want). As a concrete example where this is useful is for the package PyCall.jl where you used to have to write pyobject[:field] while it is possible now to implement it such that you can write pyobject.field.
The difference between setfield! and setproperty! is analogous to the difference between getfield and getproperty, explained above.
In addition, it is possible to hook into the function Base.propertynames to provide tab completion of properties in the REPL. By default, only the field names will be shown:
julia> a.<TAB><TAB>
b c
But by overloading propertynames we can make it also show the extra property q:
julia> Base.propertynames(::A) = (:b, :c, :q)
julia> a.<TAB><TAB>
b c q

accessing types within a type union

Suppose I have a DataFrame in Julia and typeof((df[:,:col])) returns Array{Union{Missing, Float64},1}. How do I check the types within Union{Missing, Float64} to, for example, see if Float64 is in that Union, or to make sure that there are no String values in that Union?
You can use the subtype operator:
T1 = Union{Missing, Float64}
Float64 <: T1 # true
String <: T1 # false
This is because Float64 is a subtype of the union, whereas String is not (since it's not in the union).
If you're defining a method to dispatch on it, you could go a step further:
function doSomething(arr::Vector{Union{Missing, T}}) where T <: Float64
# do something
end

(Julia 1.x) Getting type of #undef variable

I am looking to get the types of the fields within a struct in order to set the field values correspondingly. Some data types initialise values on instantiation (e.g. Int64, Float64), whereas other types initialise to #undef (e.g. String, Array). Whilst typeof(getfield()) works for the former types, it throws UndefRefError for the latter:
julia> mutable struct MyStruct
a::Int64
b::String
MyStruct() = new()
end
julia> foo = MyStruct()
MyStruct(0, #undef)
julia> typeof(getfield(foo, :a))
Int64
julia> typeof(getfield(foo, :b))
ERROR: UndefRefError: access to undefined reference
Stacktrace:
[1] top-level scope at none:0
Is there is way to get the type of an uninitialised variable or does #undef indicate the distinct lack of a type? Alternatively, is it possible to initialise default values using the inner constructor? e.g.
julia> mutable struct MyStruct
a::Int64
b::String
MyStruct() = new(b = "")
end
You're looking for the fieldtype function:
julia> fieldtype(MyStruct, :a)
Int64
julia> fieldtype(MyStruct, :b)
String
To your other question, you surely can initialize fields.
mutable struct MyStruct
a::Int64
b::String
MyStruct() = new(0,"") # will initialize a as 0 and b as ""
end
Just a follow-on, you can get a tuple of all field types with fieldtypes:
julia> fieldtypes(MyStruct)
(Int64, String)

Resources