How to implement mixins in the OCaml module system.
The closest I achieved is to use the following methodology, which I illustrate on a MixinFormat which adds usual output functions print, output and to_string on a mixin able to format.
Assume that we implement a module MixinFormat with the following signature:
module MixinFormat :
sig
(** Formatable mixin. *)
(** Input signature of the functor [MixinFormat.Make]. *)
module type Basis =
sig
type t
(** The type of formatted elements. *)
val format : Format.formatter -> t -> unit
(** [format fft a] pretty prints [a] on [fft]. *)
end
(** Output signature of the functor [MixinFormat.Make]. *)
module type Methods =
sig
type t
val to_string : t -> string
(** Convert to string. *)
val output : out_channel -> t -> unit
(** Output on the given output channel. *)
val print : t -> unit
(** Output on the standard output channel. *)
end
(** Functor implementing output mixins based on a format definition. *)
module Make(B:Basis): Methods
with type t := B.t
(** Signature of formatable mixins. *)
module type S =
sig
type t
include Basis with type t := t
include Methods with type t := t
end
end
We can now use it to add common output functions to a module able to format, as in:
module Date =
struct
module Prototype =
struct
type t = int * int * int
let format ppt (y,m,d) =
Format.fprintf ppt "%04d-%02d-%02d" y m d
end
include Prototype
include MixinFormat.Make(Prototype)
end
This works very well but has the convenient that it is not easily iterable: if a second mxin expects functions added to Prototype by MixinFormat.Make then we need to pack Prototype and MixinFormat.Make(Prototype) in Prototype2 which is a bit clumsy and hinders readability.
Is there an alternative implementation of mixins which could avoid to introduce a Prototype2 when iteratively using mixins?
Firstly, to avoid multiple definitions of the type t, your functor should probably be defined as:
module Make(B:Basis): Methods with type t := B.t
To avoid having to create inner modules, you can use recursive modules like this:
module rec Date : sig
type t = int * int * int
include Basis with type t := t
include Methods with type t := t
end = struct
type t = int * int * int
let format ppt (y,m,d) =
Format.fprintf ppt "%04d-%02d-%02d" y m d
include Make(Date)
end
However, it is worth noting that recursive modules can be a bit tricky. For example, if any of the values in your module (e.g. format) were not functions then this definition would not work.
It is also worth noting that OCaml's object system has excellent support for mixins. For examples, see Chapter 12 of Real World OCaml.
Related
I have this code:
cnf.mli
type literal
type clause
type cnf
type l_diff
val l_compare: literal -> literal -> l_diff
cnf.ml (partial)
type l_diff = Same | Negation | Different
checker.ml (partial)
open Cnf
type solution = (literal * bool) list
let rec solve_literal sol l =
match sol with
| [] -> false
| (hl, b)::rs when (l_compare l hl) = Same -> b
| (hl, b)::rs when (l_compare l hl) = Negation -> not b
| _::rs -> solve_literal rs l
This works in utop using:
#mod_use "cnf.ml";;
#use "checker.ml";;
But if I try to compile checker I get the following error:
compile command:
ocamlbuild cnf.cma
ocamlbuild checker.cma
error:
+ /home/user/.opam/4.05.0/bin/ocamlc.opt -c -o checker.cmo checker.ml
File "checker.ml", line 7, characters 42-46:
Error: Unbound constructor Same
Hint: Did you mean Some?
Command exited with code 2.
Compilation unsuccessful after building 6 targets (2 cached) in 00:00:00.
Am I using a variant the wrong way, or using the compiler incorrectly?
Neither, you are using abstract types in signature in the wrong way. When you write in cnf.mli
type l_diff
this declares the type l_diff as an abstract type, in other words a black box whose content is hidden.
Contrarily, when using #mod_use "cnf.ml" the toplevel infers a signature by itself and makes all type declarations transparent:
type literal = …
type clause = …
type cnf = …
type l_diff = Same | Negation | Different
val l_compare: literal -> literal -> l_diff
(If you want to see the full inferred signature ocamlbuild cnf.inferred.mli should work.)
With this signature, the constructor of l_diff are visible and it becomes possible to construct directly or pattern match values of type l_diff.
More generally, your signature cnf.mli is far too restrictive:
With this signature, the only way to create a value of type l_diff is to call l_compare. However it would be then impossible to observe the content of the type. Similarly, with the interface cnf.mli that you provided, it is impossible to create a value of type literal.
Using the OUnit unit testing framework in OCaml, I would like to test that the result of evaluating a function is an instance of a specified type.
Defining such a test in Python's PyTest would be done as follows:
def test_foo():
assert isinstance(foo(2), int)
How can this logic be translated to OUnit? That is, how are assertions of type membership specified?
I'm aware that, assuming the function under test is annotated with the proper type signature, this testing might be unnecessary.
This is the job of a type checker, and it is made automatically during the compilation (at static time). The type checker (i.e., the compiler) guarantees that all values that are created by a function has the same type, and the type is defined statically at the compilation time. You will not be able to compile a function, that creates values of different types, as you will get a type error during the compilation. This is an essential property of all statically typed languages, e.g., Java, C and C++ also has the same property.
So, probably, you're using are confusing terminology. It might be the case, that what you're actually trying to test, is that the value belongs to a particular variant of a sum type. For example, if you have a sum type called numbers defined as:
type t =
| Float of float
| Int of int
and you would like to test that function truncate, defined as
let truncate = function
| Float x -> Int (truncate x)
| x -> x
always returns the Int variant, then you can do this as follows:
let is_float = function Float _ -> true | _ -> false
let is_int = function Int _ -> true | _ -> false
assert (is_int (truncate 3.14))
I have the following code that compiles fine in OCaml 3.11:
module type T =
sig
type test
val create : int -> test (* line 44 *)
...
end
...
type test = (string, clause list) Hashtbl.t
let create = Hashtbl.create (* line 332 *)
But when I try to compile it with OCaml 4.01, it gives me the following error:
Error: Signature mismatch:
...
Values do not match:
val create : ?random:bool -> int -> ('a, 'b) Hashtbl.t
is not included in
val create : int -> theory
File "test1.ml", line 44, characters 2-28: Expected declaration
File "test1.ml", line 332, characters 6-12: Actual declaration
make[1]: *** [test1.cmo] Error 2
make: *** [byte-code] Error 2
What changed in OCaml 4 so that it now can't compile it? I am sure it has a very easy explanation but I am still learning the inner workings of OCaml types.
The type of the function has changed --of course! Since the addition is of an optional argument, it will affect anyone who is aliasing the function (which will carry over the type, inc. the optional parameter). You will have to eta-expand the arguments of the create function to fix this issue, as in...
let create i = Hashtbl.create i
In fact it should be noted that you only need to eta-expand one argument to remove the optional arguments from the inferred type signature as in...
let create ?random1 ?random2 x y z = Hashtbl.create (x+y+z);;
(* ?random1:'a -> ?random2:'b -> int -> int -> int -> ('c, 'd) Hashtbl.t *)
let create7 = create 7;;
(* create7 : int -> int -> ('_a, '_b) Hashtbl.t *)
It seems like defining types in SML isnt that helpful:
type point = int * int
val origin : point = (0, 0)
But I could easily just use int * int for typing methods, no? Compared with datatype, with which it seems you can do more interesting things like:
datatype Point = PlanePoint of (int * int) | SpacePoint of (int * int * int)
val origin : Point = SpacePoint(0, 0, 0)
Out of curiosity, what are situations where you really just gotta have a type defined?
The reason is mostly type safety. I will try to explain with a simple example.
Say you have a module that uses 2 types that are represented with real * real
For example, a 2d point like in your example, and a line represented by a slope and a y intercept. Now if you're writing a function like lies_on_line which takes a a point and a line and returns a boolean whether the point lies on the line you have 2 choices for a signature:
val lies_on_line : (int * int) * (int * int) -> bool
Ord
val lies_on_line : point * line -> bool
It's obvious that the 2nd example makes it harder to make mistakes.
Also, while it's more of a benefit for modules, naming a type allows you to change its representation without changing code that uses the type (indirectly through the module).
It makes sense to define aliases for your types in the context of your problem domain. That way you can think in your design in terms of more relevant and meaningful types.
For instance if you are writing a word processor program then you have types like:
type Word = string
type Sentence = Word list
which may make more sense than string and string list.
This is my error:
Error: This expression has type nfa but is here used with type nfa
What could possibly be happening to cause this? I'm using emacs tuareg, and loading evaluating files one by one. Sometimes this happens, and other times it doesn't.
There's a good description of this in the ocaml tutorial. What's happened is you have shadowed a type definition with a new definition:
type nfa = int
let f (x: nfa) = x
type nfa = int
let g (x: nfa) = x
Restarting the top-level will clear out the old definitions.
Update:
Since OCaml 4.01.0 (released Sept. 2013) the general problem is the same, but the error message adds a number to the type definition, to make evident the types are internally different.
Full example from the old OCaml FAQ in the toplevel:
type counter = Counter of int;; (* define a type *)
type counter = Counter of int
# let x = Counter 1;; (* use the new type *)
val x : counter = Counter 1
type counter = Counter of int;; (* redefine the type, use it *)
type counter = Counter of int
# let incr_counter c = match c with Counter x -> Counter (x + 1);;
val incr_counter : counter -> counter = <fun>
# incr_counter x;; (* now mix old and new defs *)
Error: This expression has type counter/1029
but an expression was expected of type counter/1032
#