VarStructs
Features
VarStructs are similar to structs but have extra features:
- You can add fields after their definition.
- They can be defined inside a function or a local scope.
- They can be redefined.
- Fields can be uninitialized, have default values, etc.
Similar to structs
- They can be used for dispatching (zero-cost)
- They can have custom constructors
- They have type conversion/checking for the fields that are declared
VarStructs removes the limitations of structs, and so in addition to all the applications of structs, you can use structures in new applications! For example, it can be used to define a "Schema" for your type, or you can used it for serialization of unknown data.
SharedVarStructs also allows having shared instances for one struct.
Install and Usage
using Pkg; Pkg.add("VarStructs")
using VarStructs
If you want to see a full example see here.
Declaration
There are two ways to declare them:
Struct Syntax
In this syntax, providing initial values and types for the fields are optional.
@var struct Animal
name::String
number::Int64
end
You can redeclare the struct later, but in redeclaration you will not get type checking based on the previous declaration.
Call Syntax
In this syntax, you should provide the initial values for the fields. Providing type for the fields are optional (if not provided, it is considered as Any
).
@var Person(
name = "Amin",
number::Float64 = 20.0,
)
Getting an Instance
Use the following syntax for getting an instance:
julia> person = Person(name = "Amin", number = 20.0)
Person(
name::Any = Amin,
number::Float64 = 20.0,
)
# Type conversion for the fields that were declared
julia> person2 = Person(name = "Amin", number = 20) # number is converted to Float64
Person(
name::Any = Amin,
number::Float64 = 20.0,
)
# Type checking for the fields that were declared
julia> person2 = Person(name = "Amin", number = "20")
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Float64
# new field added
julia> person2 = Person(name = "Amin", number = 20.0, initial = "A")
Person(
initial::String = A,
name::Any = Amin,
number::Float64 = 20.0,
)
The two syntaxes that are used for declaration also return an instance of the VarStruct. So if you need an instance right away, you can use the following.
animal = @var Animal(
name = "lion",
number::Int64 = 10,
)
# redefinition of `Animal` returns a new instance:
animal2 = @var Animal(
name = "dog",
number::Int64 = 1,
)
Accessing, Setting, Adding Fields
# Accessing
julia> person.name
"Amin"
# Setting
julia> person.name = "Tim"
# Adding Fields
julia> person.initial = "T"
Dispatch
info
function dispatch for Person
and Animal
type:
function info(x::Person)
println("Their home is city")
return x.name
end
function info(x::Animal)
println("Their home is jungle")
return x.name
end
julia> info(person)
"Their home is city"
"Amin"
julia> info(animal)
"Their home is jungle"
"lion"
Custom Constructor
To define a custom constructor return an instance using keyword method:
function Person(name, number)
return Person(
name = name,
number = number,
initial = name[1],
)
end
Person("Amin", 20.0)
Shared VarStruct
Using @shared_var
, you can define make the struct shared. That means calling the constructor for creating a new instance will use the already defined instance as its declaration.
person = @shared_var Citizen(
name::String = "Amin",
number::Float64 = 20.0,
)
person2 = Citizen(name = "Not-Amin", number = 1) # Will make "person" the same as "person2"
If you are a geek see here too: ReadmeGeeks