deriving examples
Pretty printing
The deriving
preprocessor and library provide common
functionality which has an obvious definition at most types. For
example, it's usually easy, although tedious, to write a
to_string
function for a new type you've defined; deriving
will save you the trouble by writing the function for you automatically. To call a deriving
function at a particular
type you use a special notation:
Class.method<type> argument
(Note that the terms "Class" and "method" are taken from Haskell's type classes, and has nothing to do with OCaml's object-oriented class system.)
For example, to call the show
method of the Show
class to convert an integer to a string you would write:
Show.show<int> 3
=>
"3"
You can also specify more complex types:
``` let factors = [(10,[2;5]); (11, []); 12, [2;3;4;6]]
Show.show<(int * int list) list> factors => "[(10,[2; 5]); (11, []); 12, [2; 3; 4; 6]]" ```
To use a deriving
function at a type you've defined, you need to
add the phrase deriving (Class)
to the end of your type
definition. For example,
``` type 'a tree = Leaf of 'a | Branch of 'a tree * 'a * 'a tree deriving (Show)
type point = { x : float; y : float } deriving (Show)
let points = Branch (Leaf {x=0.0; y=0.0;}, {x=2.0; y=2.0}, Branch (Leaf {x=1.0; y=1.0}, {x=1.0; y=0.0}, Leaf {x=0.0; y=1.0}))
Show.show points => "Branch (Leaf {x =193.11; y =132.13}, {x =211.91; y =201.11}, Branch (Leaf {x =113.12; y =1.}, {x =12.7; y =44.1}, Leaf {x =0.; y =13.41}))" ```
If you want to show values of an abstract type defined in a module
outside the module you should add the deriving
annotation to the
signature as well:
``` module IntStack : sig type t deriving (Show)
val empty : t val push : int -> t -> t val top : t -> int val pop : t -> t end = struct type t = Stack of int list deriving (Show) let empty = Stack [] let push item (Stack list) = Stack (item::list) let top (Stack (top::)) = top let pop (Stack (::rest)) = Stack rest end
Show.show (IntStack.push 3 (IntStack.push 4 (IntStack.push 5 IntStack.empty))) => "Stack [3; 4; 5]" ```
You can derive Show for most types, including recursive (and mutually
recursive) types, normal and polymorphic variants, records, tuples and
types containing other types for which Show has been derived. You
can't derive Show for functions because there's usually no meaningful
way to display them. If you have a way to display values of a type
for which Show cannot be derived then you can always write your own
definition and make it available to deriving
; see the section
"Extending definitions".
Dynamic typing
The Typeable' class provides operations for converting between a
universal type
dynamic' and any other type. Converting from dynamic
to another type succeeds only if the type specified in the conversion
matches the type used to create the dynamic value. The upcast
operation is called mk
(or make_dynamic
if you prefer to
be verbose). The downcasts are cast
, which returns an option value, and throwing_cast
, which throws an exception if the downcast fails.
``` type 'a tree = Leaf of 'a | Branch of 'a tree * 'a * 'a tree deriving (Typeable)
let items = [Typeable.mk 3; Typeable.mk 3.0; Typeable.mk (Leaf "three")] => [; ; ]
Typeable.cast (List.hd items) => Some 3
Typeable.throwing_cast (List.hd items) => 3
Typeable.cast (List.hd items) => None
Typeable.throwing_cast (List.hd items) => Exception: Typeable.CastFailure "cast failed". ```
Casts also work between equivalent polymorphic variant types (even if the types used for the upcast and downcast are defined differently):
```
type 'a seq = [Nil |
Cons of 'a * 'a seq]
deriving (Typeable)
let l = Cons (3,
Cons (2, Cons (1,
Nil)))
Typeable.cast<[Cons of int * 'a|
Nil] as 'a>
(Typeable.mk l)
=>
Some (Cons (3,
Cons (2, Cons (1,
Nil))))
```
Casts don't work between record or normal variant types which are defined separately, even if the definitions are identical.
``` type complex = {x : float; y : float} deriving (Typeable)
type point = {x : float; y : float} deriving (Typeable)
Typeable.cast (Typeable.mk {x : -1.0; y : 0.0}) => None ```
However, abstraction using module signatures does not change whether types are interconvertible, so you can use Typeable to access the representation of an abstract type if you know it.
``` module T : sig type t deriving (Typeable) val v : t end = struct type t = int val v = 12 end
Typeable.cast (Typeable.mk T.v) => 12 ```
Equality
There are two polymorphic equality operators in OCaml:
=
tests for structural equality.==
tests for physical equality.
Sometimes neither of these is appropriate. Instead, we want to test for structural equality at immutable types and physical equality (identity) at mutable types (as in SML). This sort of equality tests whether two values can be used interchangeably in a program.
``` Eq.eq (ref 1) (ref 1) => false
let x = ref 1 in Eq.eq x x => true
Eq.eq [1;2;3] [1;2;3] => true
type mpoint = { mutable x : float; mutable y : float} deriving (Eq)
Eq.eq {x = 1.0; y = 2.0} {x = 1.0; y = 2.0} => false
let p = {x = 1.0; y = 2.0} in Eq.eq p p => true
type ipoint = { x : float; y : float} deriving (Eq)
Eq.eq {x = 1.0; y = 2.0} {x = 1.0; y = 2.0} => true ```
Serialisation
The `Pickle' class provides operations for structure-sharing serialisation (marshalling). If any value to be serialised contains two equal subvalues then only one copy of the subvalue will be serialised. Cycles that are created by mutable record fields, including references, are preserved.
All "instances" of Pickle must also be "instances" of Eq and Typeable. (As in Haskell, we use "instance" to mean a set of functions that implement the methods of a class at a particular type.)
``` type 'a tree = Leaf of 'a | Branch of 'a tree * 'a * 'a tree deriving (Eq, Typeable, Pickle)
type point = { x : float; y : float } deriving (Eq, Typeable, Pickle)
Pickle.to_string points => "\007\003\t\128\128\128\128\128\128\128\248?\t\128\128\128\128\128\128\128\128@\001\000\005\000\001\008\000\001\n\001\003\004\t\003\000\001\012\001\003\006\011\005\005\002\002\000\002\000\002\002\000\000\002\001\001\002\002\002"
Pickle.from_string (Pickle.to_string points) => Branch (Leaf {x =193.11; y =132.13}, {x =211.91; y =201.11}, Branch (Leaf {x =113.12; y =1.}, {x =12.7; y =44.1}, Leaf {x =0.; y =13.41})) ```
You can supply a custom definition of equality (see the section
"Extending definitions") to increase sharing: see the file
tests/exp.ml
in the distribution for an example.
There is another class, `Dump', that provides simpler value-oriented serialisation, but doesn't deal with references or cycles.
Map
Given a type ('a1,...,'an) t
, the Functor' class will derive a
map` operation:
val map : ('a1->'b1) -> ... ('an->'bn} -> ('a1,...,'an) t -> ('b1,...,'bn) t
For example,
``` type 'a tree = Leaf of 'a | Branch of 'a tree * 'a * 'a tree deriving (Functor)
Functor_tree.map ((+) 1) (Branch (Leaf 3, 4, Leaf 5)) => (Branch (Leaf 4, 5, Leaf 6)) ```
The <t>
notation is not currently available for Functor.
Enumerations
Enumerations provide several operations for dealing with variant types where all constructors have no argument.
``` Enum.enum_from_to 0 10 => [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
type fruit = Apple | Orange | Banana | Kiwi deriving (Enum)
Enum.enum_from_to Apple Kiwi => [Apple; Orange; Banana; Kiwi]
Enum.succ Orange => Banana ```
Minimum and maximum values
Instances of bounded have maximum and minimum values:
``` type fruit = Apple | Orange | Banana | Kiwi deriving (Bounded)
(Bounded.min_bound, Bounded.max_bound) => ((-1073741824, Apple), (1073741823, Kiwi)) ```
Extending definitions
Instead of deriving a definition automatically you can provide your own by writing a module with the same signature as the standard definitions with a name formed from the class name, an underscore, and the name of the type constructor for which you want to provide the definition.
``` module Eq_fruit : Eq.Eq with type a = fruit = Eq.Defaults(struct type a = fruit let eq l r = match l, r with | Apple , Orange | Orange , Apple | Apple , Apple | Orange , Orange | Banana , Banana | Kiwi , Kiwi -> true | _ -> false end)
Eq.eq [Apple; Orange; Banana] [Orange; Orange; Banana] => true ```