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<point tree> 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.t>
(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<int> 3;
Typeable.mk<float> 3.0;
Typeable.mk<string tree> (Leaf "three")]
=>
[<abstr>; <abstr>; <abstr>]
Typeable.cast<int> (List.hd items)
=>
Some 3
Typeable.throwing_cast<int> (List.hd items)
=>
3
Typeable.cast<float> (List.hd items)
=>
None
Typeable.throwing_cast<float> (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<int seq> 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<point> (Typeable.mk<complex> {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<int> (Typeable.mk<T.t> 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<int ref> (ref 1) (ref 1)
=>
false
let x = ref 1 in Eq.eq<int ref> x x
=>
true
Eq.eq<int list> [1;2;3] [1;2;3]
=>
true
type mpoint = { mutable x : float; mutable y : float}
deriving (Eq)
Eq.eq<mpoint> {x = 1.0; y = 2.0} {x = 1.0; y = 2.0}
=>
false
let p = {x = 1.0; y = 2.0} in Eq.eq<mpoint> p p
=>
true
type ipoint = { x : float; y : float}
deriving (Eq)
Eq.eq<ipoint> {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<point tree> 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<point tree> (Pickle.to_string<point tree> 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<int> 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<fruit> Apple Kiwi
=>
[Apple; Orange; Banana; Kiwi]
Enum.succ<fruit> 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<int * fruit>, Bounded.max_bound<int * fruit>)
=>
((-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<fruit list> [Apple; Orange; Banana] [Orange; Orange; Banana]
=>
true