Skip to content
Tim Watson edited this page Mar 4, 2013 · 1 revision

The set of operations required by all types are:

  • initial makes an initial, empty or otherwise default value
  • to_string makes a string representation of a value
  • from_string converts a string representation back to a native value
  • size returns the size, length or some similar equivalent measure of a value
  • check takes a restriction and applies it to a value

An example of this, for integral numbers might look something like this:

#'systest.udt'{
    initial     = fun() -> 0 end,
    to_string   = fun erlang:integer_to_list/1,
    from_string = fun erlang:list_to_integer/1,
    size        = fun erlang:length/1
}

It is important to understand how data type definitions work. A data element must specify a base/intrinsic type from which the basic behaviour of the new data type will be derived. Additional behaviour (viz the set of required operations) can override one or all of the inherited defaults from the intrinsic data type, or else a custom module can be written which implements the complete set of operations. To create a User Defined Type you start with the configuration:

{data_type, 'Path', string, [
    {restricted_by, {regex, "^(.*?)([^/\\]*?)(\.[^/\\.]*)?$"}}
]}.

In this scheme, all definitions are supplied as a tuple where the elements have the following meanings: {keyword, name, alias, options} where

  • keyword is either data_type, resource or resource_type
  • name is the name used when referring to the defined thing
  • alias is the name of the intrinsic type upon which the new type is based
  • options contains either overrides of specific behaviour, or provided_by

The restricted_by entry creates a value for the type mapping the required check action to a value restriction API, so we end up with something that works roughly like this at runtime:

check = fun(Value) -> check(Value, create(regex, RxString)) end

Putting aside the naive regular expression, the important thing here is that Path is an extension of the built in string data type, which is just a thin wrapper around Erlang's built-in strings (which are lists of chars). If we wanted to override the complete set of required operations, we would pass the name of a handler to the data_type declaration thus:

{data_type, 'Path', string, [
    {provided_by, my_path_handler_module}
]}.