automatic inference of parametric types - julia

I am using the code below and it seems Julia should be able to infer the type parameters by itself, however this is not the case. Any ideas, maybe I'm doing something wrong?
abstract type ABS{A,B} end
struct MyStruct{A,B,K<:ABS{A,B}}
a::A
b::B
MyStruct{A,B,K}(a::A,b::B) where {A,B,K<:ABS{A,B}} = new(a,b)
end
MyStruct{Int64,Float64,ABS{Int64,Float64}}(1,2.1) # <<-- works
MyStruct(1,2.1) # <<-- doesn't work

I forgot the outer constructor, as #gnimuc pointed out. This code works:
abstract type ABS{A,B} end
struct Myk <: ABS{Int64,Float64} end
struct MyStruct{A,B,K<:ABS{A,B}}
a::A
b::B
MyStruct{A,B,K}(a::A,b::B) where {A,B,K<:ABS{A,B}}= new(a,b)
end
# this is the outer constructor:
MyStruct(a::A, b::B, ::K) where {A,B,K<:ABS{A,B}} = MyStruct{A,B,K}(a,b)
# now this works:
MyStruct(1,2.1,Myk())

Related

Easy initialisation of empty containers

Consider the following code.
struct MyType
data::Dict{Int, Float64}
end
MyType() = MyType(Dict{Int, Float64}())
Having to repeat the type of data is a bit clumsy, and the problem gets out of hand very quickly if you have more variables and/or your types get more complicated. Can I avoid this?
If the type in question is default-constructible (i.e., it has a no-args constructor), you can use the following trick.
struct Default; end
Base.convert(::Type{T}, ::Default) where T = T()
struct MyType
data::Dict{Int, Float64}
end
MyType() = MyType(Default())
You can do:
Base.#kwdef struct MyType
data::Dict{Int, Float64} = Dict{Int, Float64}()
end
And now you can just write MyType():
julia> MyType()
MyType(Dict{Int64, Float64}())
I personally more like the package Parameters which is additionally affecting how struct is displayed:
using Parameters
#with_kw struct MyType
data::Dict{Int, Float64} = Dict{Int, Float64}()
end
And now in REPL you can see:
julia> MyType()
MyType
data: Dict{Int64, Float64}
The following hack-ish method works (as of Julia 1.8):
MyType() = MyType((t() for t in MyType.types)...)
It doesn't include Int or Float64, so it passes OP conditions. And also, it generalizes to types with many fields, and complex ones because it is essentially recursive.

Get struct property from inside struct function errors out

Recently started to try and learn Julia through examples. I am basically trying to figure out how to access a struct property from within a function inside the struct itself. E.g:
struct Test
a::Int
foo::Function
function Test()
return new(777, xfoo)
end
function xfoo()
println(a)
end
end
t = Test()
t.foo()
I get:
ERROR: LoadError: UndefVarError: a not defined
Stacktrace:
[1] (::var"#xfoo#1")()
# Main /tmp/j.jl:10
[2] top-level scope
# /tmp/j.jl:15
in expression starting at /tmp/j.jl:15
Am I using Julia wrong or am I missing something?
Julia is not object oriented language so object oriented patterns are usually not a good idea.
Hence xfoo should be outside of Test:
function xfoo(t::Test)
println(t.a)
end
There are packages that try to emulate OOP with Julia (however this is not a Julian pattern): https://github.com/Suzhou-Tongyuan/ObjectOriented.jl
You can also easily find quite a lot of discussion behind the design decision no to make Julia OOP. Start with: https://discourse.julialang.org/t/why-there-is-no-oop-object-oriented-programming-in-julia/86723
Workaround
Just out of curiosity one can find some workaround to attach a function to a struct (not a recommended design pattern!). For an example:
mutable struct MyTest
a::Int
foo::Function
function MyTest()
s = Ref{MyTest}()
s[] = new(777, () -> println(s[].a))
s[]
end
end
And some sample usage:
julia> t = MyTest();
julia> t.foo()
777
julia> t.a = 900;
julia> t.foo()
900

How to hard code struct variables in Julia?

I have a Julia struct:
struct WindChillCalc
location::Tuple;
w_underground_url::String;
WindChillCalc(location, wug) = new(location, w_underground_url);
end
How do I hard code w_underground_url to contain "someString" upon the constructor of WindChillCalc being called?
Try something like below
struct testStruct
x::Real
y::String
testStruct(x,y) = new(x,"printThis")
end
test = testStruct(1,"")
test2 = testStruct(2,"")
println(test.y)
println(test2.y)
It prints "printThis" for any object.
Just write for example:
struct WindChillCalc{T}
location::T;
w_underground_url::String;
WindChillCalc(location::T) where {T <: NTuple{2, Real}} =
new{T}(location, "some string");
end
and now Julia automatically creates a concrete type for you:
julia> WindChillCalc((1, 2.5))
WindChillCalc{Tuple{Int64,Float64}}((1, 2.5), "some string")
Note that I have restricted the type of the parameter to be a two element tuple where each element is a Real. You could of course use another restriction (or use no restriction).
With this approach your code will be fast as during compile time Julia will know exact types of all fields in your struct.

Check if a type implements an interface in Julia

How to check that a type implements an interface in Julia?
For exemple iteration interface is implemented by the functions start, next, done.
I need is to have a specialization of a function depending on wether the argument type implements a given interface or not.
EDIT
Here is an example of what I would like to do.
Consider the following code:
a = [7,8,9]
f = 1.0
s = Set()
push!(s,30)
push!(s,40)
function getsummary(obj)
println("Object of type ", typeof(obj))
end
function getsummary{T<:AbstractArray}(obj::T)
println("Iterable Object starting with ", next(obj, start(obj))[1])
end
getsummary(a)
getsummary(f)
getsummary(s)
The output is:
Iterable Object starting with 7
Object of type Float64
Object of type Set{Any}
Which is what we would expect since Set is not an AbstractArray. But clearly my second method only requires the type T to implement the iteration interface.
my issue isn't only related to the iteration interface but to all interfaces defined by a set of functions.
EDIT-2
I think my question is related to
https://github.com/JuliaLang/julia/issues/5
Since we could have imagined something like T<:Iterable
Typically, this is done with traits. See Traits.jl for one implementation; a similar approach is used in Base to dispatch on Base.iteratorsize, Base.linearindexing, etc. For instance, this is how Base implements collect using the iteratorsize trait:
"""
collect(element_type, collection)
Return an `Array` with the given element type of all items in a collection or iterable.
The result has the same shape and number of dimensions as `collection`.
"""
collect{T}(::Type{T}, itr) = _collect(T, itr, iteratorsize(itr))
_collect{T}(::Type{T}, itr, isz::HasLength) = copy!(Array{T,1}(Int(length(itr)::Integer)), itr)
_collect{T}(::Type{T}, itr, isz::HasShape) = copy!(similar(Array{T}, indices(itr)), itr)
function _collect{T}(::Type{T}, itr, isz::SizeUnknown)
a = Array{T,1}(0)
for x in itr
push!(a,x)
end
return a
end
See also Mauro Werder's talk on traits.
I would define a iterability(::T) trait as follows:
immutable Iterable end
immutable NotIterable end
iterability(T) =
if method_exists(length, (T,)) || !isa(Base.iteratorsize(T), Base.HasLength)
Iterable()
else
NotIterable()
end
which seems to work:
julia> iterability(Set)
Iterable()
julia> iterability(Number)
Iterable()
julia> iterability(Symbol)
NotIterable()
you can check whether a type implements an interface via methodswith as follows:
foo(a_type::Type, an_interface::Symbol) = an_interface ∈ [i.name for i in methodswith(a_type, true)]
julia> foo(EachLine, :done)
true
but I don't quite understand the dynamic dispatch approach you mentioned in the comment, what does the generic function looks like? what's the input & output of the function? I guess you want something like this?
function foo(a_type::Type, an_interface::Symbol)
# assume bar baz are predefined
if an_interface ∈ [i.name for i in methodswith(a_type, true)]
# call function bar
else
# call function baz
end
end
or some metaprogramming stuff to generate those functions respectively at compile time?

Julia: why must parametric types have outer constructors?

The following works:
type TypeA
x :: Array
y :: Int
TypeA(x :: Array ) = new(x, 2)
end
julia> y = TypeA([1,2,3])
TypeA([1,2,3],2)
This does not:
type TypeB{S}
x :: Array{S}
y :: Int
TypeB{S}( x:: Array{S} ) = new(x,2)
end
julia> y = TypeB([1,2,3])
ERROR: `TypeB{S}` has no method matching TypeB{S}(::Array{Int64,1})
In order to get the second case to work, one has to declare the constructor outside of the type declaration. This is slightly undesirable.
My question is why this problem exists from a Julia-design standpoint so I can better reason about the Julia type-system.
Thank you.
This works:
type TypeB{S}
x::Array{S}
y::Int
TypeB(x::Array{S}) = new(x,2)
end
TypeB{Int}([1,2,3])
which I figured out by reading the manual, but I must admit I don't really understand inner constructors that well, especially for parametric types. I think its because you are actually defining a family of types, so the inner constructor is only sensible for each individual type - hence you need to specify the {Int} to say which type you want. You can add an outer constructor to make it easier, i.e.
type TypeB{S}
x::Array{S}
y::Int
TypeB(x::Array{S}) = new(x,2)
end
TypeB{S}(x::Array{S}) = TypeB{S}(x)
TypeB([1,2,3])
I think it'd be good to bring it up on the Julia issues page, because I feel like this outer helper constructor could be provided by default.
EDIT: This Julia issue points out the problems with providing an outer constructor by default.

Resources