SimpleStructs.jl

Easy to use struct definition with defaults and value constraints, as well as auto-defined user-friendly constructors
Popularity
7 Stars
Updated Last
1 Year Ago
Started In
November 2015

SimpleStructs

Build Status Windows Build status SimpleStructs SimpleStructs GitHub license

This is a simple utility of defining structs by specifying types, default values and value constraints for fields, with an automatically defined user-friendly constructor. This code is extracted from Mocha.jl and MXNet.jl.

This utility is useful to define structs holding specifications or hyperparameters. The following is an example of specifications of a stochastic gradient descent optimizer used in MXNet.jl:

@defstruct SGDOptions <: AbstractOptimizerOptions (
  (lr                :: Real = 0.01, lr > 0),
  (momentum          :: Real = 0.0, momentum >= 0),
  (grad_clip         :: Real = 0, grad_clip >= 0),
  (weight_decay      :: Real = 0.0001, weight_decay >= 0),
  lr_scheduler       :: Any  = nothing,
  momentum_scheduler :: Any  = nothing
)

And this is an example of the definition of a Dropout layer in Mocha.jl:

@defstruct DropoutLayer <: Layer (
  name       :: AbstractString = "dropout",
  auto_scale :: Bool = true,
  (ratio     :: AbstractFloat = 0.5, 0 < ratio < 1),
  (bottoms   :: Vector{Symbol} = Symbol[], length(bottoms) == 1),
)

API

The main utilities provided by this package are two macros: @defstruct and @defimmutable. They are almost the same, except that the latter defines a type that is immutable. The macros can be called in the following way

@defstruct StructName (
  field_name :: field_type,
  (fname2    :: ftype2 = default_val2, fname2 > 0 && fname2 < 5),
  (fname3    :: ftype3 = default_val3, fname3 <= fname2),
)

The StructName can be StructName <: SuperTypeName if the struct needs to be a subtype of SuperTypeName. Each field should have

  • field name: the name of the field.
  • field type: the type used to store the field value. Note the constructor accept any value type, and calls convert explicitly on the user supplied values. So there is no frustration about Julia types being not covariant. For example, for a field of type Vector{AbstractString}, it is OK if user call with ["foo", "bar"], which will be of type Vector{ASCIIString}.
  • field default value: this is optional. When a default value is not presented, it means the field is required. An AssertionError will be thrown if the user does not provide a value for this field.
  • field value constraints: this is optional. Value constraints can be used to ensure the user supplied values are reasonable. Note the constraints are asserted in the order as the fields are defined. So in the example above, the constraint for fname3 can use the value for fname2 and safely assume the constraints for fname2 is already satisfied.

A constructor will be automatically defined, where each argument should be provided as keyword arguments:

struct = StructName(field_name=7, fname3=8)

Please see the unit tests for more examples.