VarStructs.jl

Variable Julia Structs with dispatching
Author aminya
Popularity
7 Stars
Updated Last
2 Years Ago
Started In
April 2020

VarStructs

CI

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