Inside type definition is reserved: Defining an array of structures inside another structure - julia

Julia throws an error when i try to have an array of structures inside another structure.
ERROR: LoadError: syntax: "grid = Cell[]" inside type definition is reserved
here is the test code i am trying to run.
struct Cell
color::Int64
location::Int64
Cell(color::Int64,location::Int64) = new(color,location)
Cell()=new(0,0)
end
struct Grid
dimensions::Int64
timestep::Int64
grid=Cell[]
courseGrain=Cell[]
Grid(dimensions::Int64,timestep::Int64,grid_cell::Cell,courseGrain_cell::Cell) =
new(dimensions,timestep,push!(grid,grid_cell),push!(courseGrain,courseGrain_cell))
end

Defining default field values within field declarations is currently not allowed. See https://github.com/JuliaLang/julia/issues/10146
To achieve what you want, define your grid and courseGrain as a 1D array of type Cell, i.e. Array{Cell, 1} or equivalently Vector{Cell}, and handle the default case in your constructors.
struct Grid
dimensions::Int64
timestep::Int64
grid::Vector{Cell}
courseGrain::Vector{Cell}
Grid(dimensions::Int64,timestep::Int64,grid_cell::Cell,courseGrain_cell::Cell) =
new(dimensions,timestep,[grid],[courseGrain_cell])
end
If you want one of your constructors to create an empty grid or courseGrain, you may write Cell[] or Vector{Cell}(undef, 0) in your call to new.

Related

Is it bad practice to provide your own setter or should I use setproperty?

Suppose if I had the following Employee struct:
mutable struct Employee
_id::Int64
_first_name::String
_last_name::String
function Employee(_id::Int64,_first_name::String,_last_name::String)
# validation left out.
new(_id,_first_name,_last_name)
end
end
If I wanted to implement my own setproperty!() I can do:
function setproperty!(value::Employee,name::Symbol,x)
if name == :_id
if !isa(x,Int64)
throw(ErrorException("ID type is invalid"))
end
setfield!(value,:_id,x)
end
if name == :_first_name
if is_white_space(x)
throw(ErrorException("First Name cannot be blank!"))
end
setfield!(value,:_first_name,x)
end
if name == :_last_name
if is_white_space(x)
throw(ErrorException("Last Name cannot be blank!"))
end
setfield!(value,:_last_name,x)
end
end
Have I implemented setproperty!() correctly?
The reason why I use setfield!() for _first_name and _last_name, is because if I do:
if name == :_first_name
setproperty!(value,:_first_name,x) # or value._first_name = x
end
it causes a StackOverflowError because it's recursively using setproperty!().
I don't really like the use of setproperty!(), because as the number of parameters grows, so would setproperty!().
It also brings to mind using Enum and if statements (only we've switched Enum with Symbol).
One workaround I like, is to document that the fields are meant to be private and use the provided setter to set the field:
function set_first_name(obj::Employee,first_name::AbstractString)
# Validate first_name before assigning it.
obj._first_name = first_name
end
The function is smaller and has a single purpose.
Of course this doesn't prevent someone from using setproperty!(), setfield!() or value._field_name = x, but if you're going to circumvent the provided setter then you'll have the handle the consequences for doing it.
Of course this doesn't prevent someone from using setproperty!(), setfield!() or value._field_name = x, but if you're going to circumvent the provided setter then you'll have the handle the consequences for doing it.
I would recommend you to do this, defining getter,setter functions, instead of overloading getproperty/setproperty!. on the wild, the main use i saw on overloading getproperty/setproperty! is when fields can be calculated from the data. for a getter/setter pattern, i recommend you to use the ! convention:
getter:
function first_name(value::Employee)
return value._first_name
end
setter:
function first_name!(value::Employee,text::String)
#validate here
value._first_name = text
return value._first_name
end
if your struct is mutable, it could be that some fields are uninitialized. you could add a getter with default, by adding a method:
function first_name(value::Employee,default::String)
value_stored = value._first_name
if is_initialized(value_stored) #define is_initialized function
return value_stored
else
return default
end
end
with a setter/getter with default, the only difference between first_name(val,text) and first_name!(val,text) would be the mutability of val, but the result is the same. useful if you are doing mutable vs immutable functions. as you said it, the getproperty/setproperty! is cumbersome in comparison. If you want to disallow accessing the fields, you could do:
Base.getproperty(val::Employee,key::Symbol) = throw(error("use the getter functions instead!")
Base.setproperty!(val::Employee,key::Symbol,x) = throw(error("use the setter functions instead!")
Disallowing the syntax sugar of val.key and val.key = x. (if someone really want raw access, there is still getfield/setfield!, but they were warned.)
Finally, i found this recomendation in the julia docs, that recommends getter/setter methods over direct field access
https://docs.julialang.org/en/v1/manual/style-guide/#Prefer-exported-methods-over-direct-field-access

Can a julia struct be defined with persistent requirements on field dimensions?

If I define a new struct as
mutable struct myStruct
data::AbstractMatrix
labels::Vector{String}
end
and I want to throw an error if the length of labels is not equal to the number of columns of data, I know that I can write a constructor that enforces this condition like
myStruct(data, labels) = length(labels) != size(data)[2] ? error("Labels incorrect length") : new(data,labels)
However, once the struct is initialized, the labels field can be set to the incorrect length:
m = myStruct(randn(2,2), ["a", "b"])
m.labels = ["a"]
Is there a way to throw an error if the labels field is ever set to length not equal to the number of columns in data?
You could use StaticArrays.jl to fix the matrix and vector's sizes to begin with:
using StaticArrays
mutable struct MatVec{R, C, RC, VT, MT}
data::MMatrix{R, C, MT, RC} # RC should be R*C
labels::MVector{C, VT}
end
but there's the downside of having to compile for every concrete type with a unique permutation of type parameters R,C,MT,VT. StaticArrays also does not scale as well as normal Arrays.
If you don't restrict dimensions in the type parameters (with all those downsides) and want to throw an error at runtime, you got good and bad news.
The good news is you can control whatever mutation happens to your type. m.labels = v would call the method setproperty!(object::myStruct, name::Symbol, v), which you can define with all the safeguards you like.
The bad news is that you can't control mutation to the fields' types. push!(m.labels, 1) mutates in the push!(a::Vector{T}, item) method. The myStruct instance itself doesn't actually change; it still points to the same Vector. If you can't guarantee that you won't do something like x = m.labels; push!(x, "whoops") , then you really do need runtime checks, like iscorrect(m::myStruct) = length(m.labels) == size(m.data)[2]
A good option is to not access the fields of your struct directly. Instead, do it using a function. Eg:
mutable struct MyStruct
data::AbstractMatrix
labels::Vector{String}
end
function modify_labels(s::MyStruct, new_labels::Vector{String})
# do all checks and modifications
end
You should check chapter 8 from "Hands-On Design Patterns and Best Practices with Julia: Proven solutions to common problems in software design for Julia 1.x"

Stack overflow when I am trying to make a composite type with a matrix as a field

In the following code, I have a composite type, and in my real code several of the fields are matrices. In this example is only 1. I keep getting stack overflow when I try constructing the composite type. Here is the code sample:
struct Tables
eij::Array{Float64,2}
end
Tables(eij::Array{Float64,2}) = Tables(eij) # works if this is commented out
x = 5 # arbitrary matrix dimension
e_ij = Array{Float64,2}(undef,x,x)
for i=1:x
for j=1:x
e_ij[i,j] = i*j/2.3 #dummy numbers, but not the case in real code
end
end
vdwTable = Tables([e_ij[i,j] for i=1:x,j=1:x])
I use the temporary variable e_ij to make the matrix first since I don't want the composite Tables to be mutable. So, my reasoning is that by generating the tables first in dummy variables like e_ij, I can then initialize the immutable Tables that I really want.
If I comment out the outer constructor for the struct Tables it works. However, I actually want to have several different outer constructors for cases where different fields are not passed data to be initialized. In these cases, I want to give them default matrices.
The error I get is the following: ERROR: LoadError: StackOverflowError: on
the line vdwTable = Tables([e_ij[i,j] for i=1:x,j=1:x])
When you define a composite type, an inner constructor is automatically defined, so this:
struct Tables
eij::Array{Float64,2}
end
is equivalent to this:
struct Tables
eij::Array{Float64,2}
Tables(eij::Array{Float64,2}) = new(eij)
end
When you define this outer constructor
Tables(eij::Array{Float64,2}) = Tables(eij)
you get in the way of the inner constructor. Your outer constructor just calls itself recursively until you get a stack overflow.
Doing this, on the other hand,
Tables(eij) = Tables(eij)
is actually equivalent to this:
Tables(eij::Any) = Tables(eij)
so when you subsequently call
vdwTable = Tables([e_ij[i,j] for i=1:x,j=1:x])
it then just ignores your outer constructor, because there is a more specific method match, namely the inner constructor. So that particular outer constructor is quite useless, it will either be ignored, or it will recurse until stack overflow.
The simplest solution is: just don't make an outer constructor. If you do need an outer one to enforce some conditions, make sure that it doesn't shadow the inner constructor by having the same type signature. For example,
Tables() = Tables(zero(5, 5))
should work.
I would probably do it like this, though:
struct Tables
eij::Array{Float64,2}
Tables(eij=zeros(5, 5)) = new(eij)
end
For your second example, with two fields, you can try this:
struct Tables
eij::Array{Float64,2}
sij::Array{Float64,2}
Tables(eij=zeros(5,5), sij=zeros(5,5)) = new(eij, sij)
end
Your inputs will be converted to Float64 matrices, if that is possible, otherwise an exception will be raised.
DNF gave a proper explanation so +1. I would just like to add one small comment (not an answer to the question but something that is relevant from my experience), that is too long for a comment.
When you omit specifying an inner constructor yourself Julia automatically defines one inner and one outer constructor:
julia> struct Tables
eij::Array{Float64,2}
end
julia> methods(Tables)
# 2 methods for generic function "(::Type)":
[1] Tables(eij::Array{Float64,2}) in Main at REPL[1]:2
[2] Tables(eij) in Main at REPL[1]:2
while defining an inner constructor suppresses definition of the outer constructor:
julia> struct Tables
eij::Array{Float64,2}
Tables(eij::Array{Float64,2}) = new(eij)
end
julia> methods(Tables)
# 1 method for generic function "(::Type)":
[1] Tables(eij::Array{Float64,2}) in Main at REPL[1]:3
So the cases are not 100% equivalent. The purpose of the auto-generated outer constructor is to perform an automatic conversion of its argument if it is possible, see e.g. (this is the result in the first case - when no inner constructor was defined):
julia> #code_lowered Tables([true false
true false])
CodeInfo(
1 ─ %1 = (Core.apply_type)(Main.Array, Main.Float64, 2)
│ %2 = (Base.convert)(%1, eij)
│ %3 = %new(Main.Tables, %2)
└── return %3
)
while in the second case the same call would throw a method error.

How do you emit to class that has a 'params' constructor?

Here is the definition of my Package class:
type Package ([<ParamArray>] info : Object[]) =
do
info |> Array.iter (Console.WriteLine)
member this.Count = info.Length
and here is the IL, I'm trying:
let ilGen = methodbuild.GetILGenerator()
ilGen.Emit(OpCodes.Ldstr, "This is 1")
ilGen.Emit(OpCodes.Ldstr, "Two")
ilGen.Emit(OpCodes.Ldstr, "Three")
ilGen.Emit(OpCodes.Newobj, typeof<Package>.GetConstructor([|typeof<Object[]>|]))
ilGen.Emit(OpCodes.Ret)
but this doesn't seem to work. I tried:
ilGen.Emit(OpCodes.Newobj, typeof<Package>.GetConstructor([|typeof<String>; typeof<String>; typeof<String>|]))
a well as:
ilGen.Emit(OpCodes.Newobj, typeof<Package>.GetConstructor([|typeof<Object>; typeof<Object>; typeof<Object>|]))
but it just laughs at me. What am I doing wrong?
The [<ParamArray>] attribute indicates to a compiler that a method accepts a variable number of arguments. However, the CLR doesn't really support varargs methods -- it's just syntactic sugar provided by the C#/VB.NET/F# compilers.
Now, if you take away the [<ParamArray>], what are you left with?
(info : Object[])
That is the signature of the constructor you're trying to call.
So, you'll need to use the newarr and stelem opcodes to create an array, store the values into it, then call the constructor using the array as the argument. This should do what you want (though I haven't tested it):
let ilGen = methodbuild.GetILGenerator()
// Create the array
ilGen.Emit(OpCodes.Ldc_I4_3)
ilGen.Emit(OpCodes.Newarr, typeof<obj>)
// Store the first array element
ilGen.Emit(OpCodes.Dup)
ilGen.Emit(OpCodes.Ldc_I4_0)
ilGen.Emit(OpCodes.Ldstr, "This is 1")
ilGen.Emit(OpCodes.Stelem_Ref)
// Store the second array element
ilGen.Emit(OpCodes.Dup)
ilGen.Emit(OpCodes.Ldc_I4_1)
ilGen.Emit(OpCodes.Ldstr, "Two")
ilGen.Emit(OpCodes.Stelem_Ref)
// Store the third array element
ilGen.Emit(OpCodes.Dup)
ilGen.Emit(OpCodes.Ldc_I4_2)
ilGen.Emit(OpCodes.Ldstr, "Three")
ilGen.Emit(OpCodes.Stelem_Ref)
// Call the constructor
ilGen.Emit(OpCodes.Newobj, typeof<Package>.GetConstructor([|typeof<Object[]>|]))
ilGen.Emit(OpCodes.Ret)
NOTE: In this code, I used the dup OpCode to avoid creating a local variable to hold the array reference while storing the element values. This is only feasible because this code is fairly straightforward -- I strongly suggest you create a local variable to hold the array reference if you want to build something more complicated.

Creating Ada record with one field

I've define a type:
type Foo is record
bar : Positive;
end record;
I want to create a function that returns an instance of the record:
function get_foo return Foo is
return (1);
end get_foo;
But Ada won't let me, saying "positional aggregate cannot have one argument".
Stupidly trying, I've added another dumb field to the record, and then return (1, DOESNT_MATTER); works!
How do I tell Ada that's not a positional aggregate, but an attempt to create a record?
The positional aggregate initialization cannot be used with record having only one component, but that does not mean you cannot have record with one component.
The values of a record type are specified by giving a list of named fields. The correct code for your get_foo function should be as following.
function get_foo return Foo is
return (bar => 1);
end get_foo;
You can also specify the type of the record using the Foo'(bar => 1) expression.
Using the list of named components is better in practice than positional initilization. You can forget the position of the component and it does not change if you add a new field into your record.

Resources