Julia Error - Constructor not found when defined - julia

Copied this into a jupyter notebook cell but can't get it to run and the message doesn't really help. Everything looks right.
mutable struct CircularArray{T} <: AbstractArray{T,1}
data::Array{T,1}
first::Int
CircularArray{T}(length::Int) where {T} = new{T}(Array{T, 1}(undef, length), 1)
end
a = CircularArray(10)
MethodError: no method matching CircularArray(::Int64)

I think the error is clear: you need to define CircularArray(length::Int). What you implemented, however, is a parametric constructor. To call your parametric constructor, you need to pass the parameter T with your constructor call, e.g.
a = CircularArray{Float64}(10);
You can also implement non-parametric constructor for a default type of your choice. For example;
CircularArray(length::Int) = CircularArray{Float64}(length)
After this your call to this constructor, CircularArray(10);, won't give a MethodError: no method matching CircularArray(::Int64).
Note the ; at the end of the commands. You need to define other methods (like size) for your array type so that display can work. Otherwise, you may get an error in REPL if you omit ; after the evaluations that return a CircularArray.

Related

How is a string assigned to a struct in this Julia code?

I'm looking at the this thread
I'm trying to understand the following line of code:
struct Foo end
(::Type{Float64})(::Foo) = "not a Float64"
It seems like it assigns a string to a variable of type Foo, but I don't see any variable declaration on the left-hand-side.
What is going on?
There's a few things going on here.
The line is a method, not a variable assignment. f(x) = "blah" is one-line syntax for
function f(x)
"blah"
end
The (::Foo) part is actually 1 annotated argument for the method. An argument's name can be omitted, you just can't use the argument in the method body because no variable references it. The argument only dispatches the method. For example, f(::Foo) = "blah" would work for f(Foo()) but not f(Bar()).
The (::Type{Float64}) part instead of a method name indicates this is a functor. (f::F)(x) = x*f.suffix means that instances of struct F can be called like a method F(".net")("something"). The name f isn't a method name and will not exist for you to use like one, it's there to reference the instance of F to be used in the method. The name can be omitted if it's not used in the method (::F)(x) = x*".com".
Type{Float64} is a type whose only instance is Float64, so this means that you can only use this method by calling Float64(Foo()). It seems to me that defining a method Base.Float64(::Foo) = "not a Float64" would be equivalent, I don't actually know why they opted for functor syntax.
When you see annotations surrounded by parentheses, the annotation can only belong to whatever is in the parentheses and to the left. f::Foo means f is annotated with Foo, f(::Foo) means an anonymous argument is annotated with Foo.

Plotting an float array in julia

I have the following code
using Plots
function test()::nothing
A::Array{Float64,1} = rand(Float64,100)
plot(A)
end
Which I run in julia like this
julia> include("main.jl")
test (generic function with 1 method)
julia> test()
ERROR: MethodError: First argument to `convert` must be a Type, got nothing
Stacktrace:
[1] test() at /path/to/main.jl:85
[2] top-level scope at REPL[2]:1
Why do I get the error First argument to convert must be a Type, got nothing ?
Well, this problem is related to the fact that you were using nothing in annotation, but correct type is Nothing (note capital N). nothing is an object, Nothing is a type of this object.
So you should use something like
function test()::Nothing
A::Array{Float64,1} = rand(Float64, 100)
display(plot(A))
nothing
end
Note, that I had to add nothing as return value and explicit display to show actual plot.
But, to be honest, main problem is not the Nothing, but overspecialization. Type annotations in functions do not speed up calculations, you should use them only when they are really needed, for example in multiple dispatch.
Idiomatic code looks like this
function test()
A = rand(100)
plot(A)
end
Note, that I removed all extra annotations and unnecessary Float64 in rand, since it is default value.

Anonymous arguments in julia

Julia permits function and method definitions with unnamed arguments.
This is not mentioned in the functions documentation, nor is it explicitly discussed in the methods documentation. For example:
function myfunc(::Int)
println("Hello!")
end
How should I describe this behavior (I've googled "anonymous arguments" without success), and when is it useful?
This behavior is useful for method dispatch, when you care only about argument type not argument value. Most often this is a case when what you dispatch on is a singleton type.
An example is:
julia> Vector{String}(undef, 3)
3-element Array{String,1}:
#undef
#undef
#undef
This function is defined in the following way:
Array{T,1}(::UndefInitializer, m::Int) where {T} =
ccall(:jl_alloc_array_1d, Array{T,1}, (Any, Int), Array{T,1}, m)
And you can see that we only care that the first argument was of UndefInitializer type, which is in turn defined as:
struct UndefInitializer end
const undef = UndefInitializer()
We see that UndefInitializer is a singleton type, so we do not care about the value of a variable of this type, but only about its type.
Another common singleton type in Base is Missing. Here are example definitions from Base of standard functions getting a Missing as an argument:
for f in (:(acos), :(acosh), :(asin), :(asinh), :(atan), :(atanh),
:(sin), :(sinh), :(cos), :(cosh), :(tan), :(tanh),
:(exp), :(exp2), :(expm1), :(log), :(log10), :(log1p),
:(log2), :(exponent), :(sqrt))
#eval $(f)(::Missing) = missing
end
(again - you can see that we do not care about the value of the variable - we know its type is Missing so we return missing)
In the Julia manual you have examples of such methods e.g. here but admittedly as far as I can tell the manual does not give a name for this style of method definition.

Is there a way to forcefully use a hidden default constructor in Julia?

I want to serialize and deserialize Julia objects from an external package. Some of these objects only have restricted inner constructors, like in the following example:
module ExternalModule
struct SillyType
f::Float64
function SillyType(i::Int)
new(float(i))
end
end
end
To reconstruct objects/structs from the stored values (here, to reconstruct the SillyType using a float), I need to call the default constructor, which is not available:
julia> ExternalModule.SillyType(2.0)
ERROR: MethodError: no method matching Main.ExternalModule.SillyType(::Float64)
The Julia documentation says
If any inner constructor method is defined, no default constructor
method is provided: it is presumed that you have supplied yourself
with all the inner constructors you need.
Is there a way around this to somehow forcefully call the missing default constructor? And what would be the least dirty approach for this?
I think the new function inside the inner constructor of SillyType is what you want to call? It is normally only allowed in inner constructors, but if you forge it using macros, it will still run. Try this:
julia> macro new(args...)
return Expr(:new, args...)
end
#new (macro with 1 method)
julia> #new(SillyType, 4.)
SillyType(4.0)

Call particular method in Julia [duplicate]

The problem is the following:
I have an abstract type MyAbstract and derived composite types MyType1 and MyType2:
abstract type MyAbstract end
struct MyType1 <: MyAbstract
somestuff
end
struct MyType2 <: MyAbstract
someotherstuff
end
I want to specify some general behaviour for objects of type MyAbstract, so I have a function
function dosth(x::MyAbstract)
println(1) # instead of something useful
end
This general behaviour suffices for MyType1 but when dosth is called with an argument of type MyType2, I want some additional things to happen that are specific for MyType2 and, of course, I want to reuse the existing code, so I tried the following, but it did not work:
function dosth(x::MyType2)
dosth(x::MyAbstract)
println(2)
end
x = MyType2("")
dosth(x) # StackOverflowError
This means Julia did not recognize my attempt to treat x like its "supertype" for some time.
Is it possible to call an overloaded function from the overwriting function in Julia? How can I elegantly solve this problem?
You can use the invoke function
function dosth(x::MyType2)
invoke(dosth, Tuple{MyAbstract}, x)
println(2)
end
With the same setup, this gives the follow output instead of a stack overflow:
julia> dosth(x)
1
2
There's a currently internal and experimental macro version of invoke which can be called like this:
function dosth(x::MyType2)
Base.#invoke dosth(x::MyAbstract)
println(2)
end
This makes the calling syntax quite close to what you wrote.

Resources