summaryrefslogtreecommitdiff
path: root/manual/src/refman/extensions/extensiblevariants.etex
blob: 8e76bcac1ab13757a654b28b100f6fad6f7b66d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
(Introduced in OCaml 4.02)

\begin{syntax}
type-representation:
          ...
        | '=' '..'
;
specification:
        ...
      | 'type' [type-params] typeconstr type-extension-spec
;
definition:
        ...
      | 'type' [type-params] typeconstr type-extension-def
;
type-extension-spec: '+=' ['private'] ['|'] constr-decl { '|' constr-decl }
;
type-extension-def: '+=' ['private'] ['|'] constr-def { '|' constr-def }
;
constr-def:
          constr-decl
        | constr-name '=' constr
;
\end{syntax}

Extensible variant types are variant types which can be extended with
new variant constructors. Extensible variant types are defined using
"..". New variant constructors are added using "+=".
\begin{caml_example*}{verbatim}
module Expr = struct
  type attr = ..

  type attr += Str of string

  type attr +=
    | Int of int
    | Float of float
end
\end{caml_example*}

Pattern matching on an extensible variant type requires a default case
to handle unknown variant constructors:
\begin{caml_example*}{verbatim}
let to_string = function
  | Expr.Str s -> s
  | Expr.Int i -> Int.to_string i
  | Expr.Float f -> string_of_float f
  | _ -> "?"
\end{caml_example*}

A preexisting example of an extensible variant type is the built-in
"exn" type used for exceptions. Indeed, exception constructors can be
declared using the type extension syntax:
\begin{caml_example*}{verbatim}
type exn += Exc of int
\end{caml_example*}

Extensible variant constructors can be rebound to a different name. This
allows exporting variants from another module.
\begin{caml_example}{toplevel}[error]
let not_in_scope = Str "Foo";;
\end{caml_example}
\begin{caml_example*}{verbatim}
type Expr.attr += Str = Expr.Str
\end{caml_example*}
\begin{caml_example}{toplevel}
let now_works = Str "foo";;
\end{caml_example}

Extensible variant constructors can be declared "private". As with
regular variants, this prevents them from being constructed directly by
constructor application while still allowing them to be de-structured in
pattern-matching.
\begin{caml_example*}{verbatim}
module B : sig
  type Expr.attr += private Bool of int
  val bool : bool -> Expr.attr
end = struct
  type Expr.attr += Bool of int
  let bool p = if p then Bool 1 else Bool 0
end
\end{caml_example*}

\begin{caml_example}{toplevel}
let inspection_works = function
  | B.Bool p -> (p = 1)
  | _ -> true;;
\end{caml_example}
\begin{caml_example}{toplevel}[error]
let construction_is_forbidden = B.Bool 1;;
\end{caml_example}

\subsection{ss:private-extensible}{Private extensible variant types}

(Introduced in OCaml 4.06)

\begin{syntax}
type-representation:
          ...
        | '=' 'private' '..'
;
\end{syntax}

Extensible variant types can be declared "private". This prevents new
constructors from being declared directly, but allows extension
constructors to be referred to in interfaces.
\begin{caml_example*}{verbatim}
module Msg : sig
  type t = private ..
  module MkConstr (X : sig type t end) : sig
    type t += C of X.t
  end
end = struct
  type t = ..
  module MkConstr (X : sig type t end) = struct
    type t += C of X.t
  end
end
\end{caml_example*}