First the code:
module Boolean = struct
exception SizeMismatch
type boolean = T | F | Vec of boolean array
let to_bool v = match v with
T -> true
| F -> false
| _ -> raise SizeMismatch
end
module Logic = struct
type 'a var_t = { name: string; mutable value: 'a }
type 'a bexp = Const of 'a
| Var of 'a var_t
let eval exp = match exp with
Const x -> x
| Var x -> x.value
let make_var s v = { name = s; value = v }
let set v n = v.value <- n
let get_var_name v = v.name
let get_var_val v = v.value
end
module type EXP =
sig
type 'a var_t
type 'a bexp
val eval_exp : 'a bexp -> bool
val get_var_name : 'a var_t -> string
val get_var_val : 'a var_t -> 'a
end
module LogicExp =
struct
include Logic
let eval_exp exp = Boolean.to_bool (Logic.eval exp)
end
module FSM ( Exp : EXP ) =
struct
let print_var v = Printf.printf "%s = %d\n" (Exp.get_var_name v)
(Exp.get_var_val v)
end
module MyFSM = FSM(LogicExp)
let myvar = Logic.make_var "foo" 1;;
MyFSM.print_var myvar ;;
When I compile it I get the following error:
File "test.ml", line 57, characters 19-27:
Error: Signature mismatch:
Modules do not match:
sig
type 'a var_t =
'a Logic.var_t = {
name : string;
mutable value : 'a;
}
type 'a bexp = 'a Logic.bexp = Const of 'a | Var of 'a var_t
val eval : 'a bexp -> 'a
val make_var : string -> 'a -> 'a var_t
val set : 'a var_t -> 'a -> unit
val get_var_name : 'a var_t -> string
val get_var_val : 'a var_t -> 'a
val eval_exp : Boolean.boolean Logic.bexp -> bool
end
is not included in
EXP
Values do not match:
val eval_exp : Boolean.boolean Logic.bexp -> bool
is not included in
val eval_exp : 'a bexp -> bool
What I don’t understand is how the more specific type isn’t included in the more general type?
The error message is actually quite accurate:
The
MyFSMfunctor expects a module argument that, amongst other things, should contain a functioneval_expof type'a bexp -> bool. That means that given a value of type'a bexpfor whatever choice of'athe function should produce a value of typebool. You are, however, supplying a module that contains a function that only does this for one particular choice of'a, i.e., the one where'ais the typebooleanfrom the moduleBoolean.This quickest fix is to define your signature
EXPasso that
eval_expnow operates on Boolean expressions over a fixed typeband then defineLogicExpasso that it fixes
btoBoolean.boolean.Implementing these changes will make your code compile.
Now, let us look at your question on “how the more specific type isn’t included in the more general type?”. This assumes that
'a bexp -> boolis indeed more general thanboolean bexp -> bool, but in fact it isn’t. A function typeA -> Bis considered more general than a function typeC -> DifCis more general thanAandBis more general thanD:Note the “flipping” of
CandAin the premise. We say that the function-space constructor... -> ...is contravariant in its argument position (versus covariant in its result position).Intuitively, a type is more general than another if it contains more values. To see why the function-space constructor is contravariant in its argument position, consider a function
fof typeA -> Cfor some typesAandC. Now, consider a typeBthat is strictly more general thanA, that is, all values inAare also inB, butBcontains some values that are not inA. Thus, there is at least one valuebto which we can assign typeB, but not typeA. Its type tells us thatfknows how to operate on values of typeA. However, if we were to (incorrectly!) conclude fromA <: BthatA -> C <: B -> C, then we could usefas if it had typeB -> Cand, hence, then we could pass the valuebas an argument tof. Butbis not of typeAandfonly knows how to operate on values of typeA!Clearly, covariance of
... -> ...in argument positions wouldn’t work. To see that contravariance does work, consider the same typesA,B, andCand now also consider a functiongof typeB -> C. That is,gknows how to operate on all values of typeB. Contravariance of the function-space constructor in its argument position allows us to conclude thatgcan also be safely assigned the typeA -> C. As we know that all values inAare also inBandgknows how to deal with all ofBthis poses no problems and we can safely pass values inAtog.