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")
If you want to see a full example see here.
There are two ways to declare them:
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.
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
@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"
info function dispatch for
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"
To define a custom constructor return an instance using keyword method:
function Person(name, number) return Person( name = name, number = number, initial = name, ) end Person("Amin", 20.0)
@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