Ocaml 函子编译问题:签名不匹配:模块不匹配
首先是守则:Ocaml 函子编译问题:签名不匹配:模块不匹配,ocaml,functor,Ocaml,Functor,首先是守则: 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 = {
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 ;;
我不明白的是,更具体的类型如何不包含在更一般的类型中?错误消息实际上相当准确:
Values do not match:
val eval_exp : Boolean.boolean Logic.bexp -> bool
is not included in
val eval_exp : 'a bexp -> bool
MyFSM
functor需要一个模块参数,除其他外,该参数应包含类型为'a bexp->bool
的函数eval_exp
。这意味着,对于'a
的任何选择,给定类型为'a bexp
的值,函数应生成类型为bool
的值。但是,您提供的模块包含一个函数,该函数仅对'a
的一个特定选项执行此操作,即'a
是模块布尔
的类型布尔
最快的修复方法是将签名定义为
module type EXP =
sig
type b (* added *)
type 'a var_t
type 'a bexp
val eval_exp : b bexp -> bool (* changed *)
val get_var_name : 'a var_t -> string
val get_var_val : 'a var_t -> 'a
end
module LogicExp =
struct
type b = Boolean.boolean (* added *)
include Logic
let eval_exp exp = Boolean.to_bool (Logic.eval exp)
end
因此,eval_exp
现在对固定类型b
上的布尔表达式进行操作,然后将LogicExp
定义为
module type EXP =
sig
type b (* added *)
type 'a var_t
type 'a bexp
val eval_exp : b bexp -> bool (* changed *)
val get_var_name : 'a var_t -> string
val get_var_val : 'a var_t -> 'a
end
module LogicExp =
struct
type b = Boolean.boolean (* added *)
include Logic
let eval_exp exp = Boolean.to_bool (Logic.eval exp)
end
因此它将b
固定为Boolean.Boolean
实现这些更改将使代码得以编译
现在,让我们看看您关于“更具体的类型如何不包含在更一般的类型中”的问题。这假设一个bexp->bool
确实比布尔bexp->bool
更一般,但事实并非如此。如果C
比A
更一般,B
比D
更一般,则认为函数类型A->B
比D
更一般:
A <: C D <: B
--------------------
C -> D <: A -> B
A…
在其参数位置是逆变的(而在其结果位置是协变的)
直观地说,如果一个类型包含更多的值,那么它比另一个类型更通用。为了了解函数空间构造函数在其自变量位置的原因,请考虑函数类型<代码> f>代码>类型<代码> -> c>代码>某些类型<代码> < <代码> >代码> C/<代码>。现在,考虑一个类型<>代码> b>代码>,它严格地比<代码> > <代码>,也就是说,<>代码> a <代码>中的所有值也在<代码> b>代码>中,但是<代码> b <代码>包含一些不在<代码> < < /代码>中的值。因此,至少有一个值b
,我们可以为其分配类型b
,但不能分配类型A
。它的类型告诉我们f
知道如何对A
类型的值进行操作。但是,如果我们(错误地!)从acc
得出结论,那么我们可以使用f
,就好像它有类型B->C
,因此,我们可以将值B
作为参数传递给f
。但是b
不是A
类型,而f
只知道如何对A
类型的值进行操作
显然,的协方差…->处于参数位置的代码>不起作用。要看到逆变是有效的,请考虑相同的类型<代码> < <代码> > <代码> b>代码>和<代码> c>代码>,现在还考虑类型<代码>代码> g >代码> >函数>代码> b> c>代码>。也就是说,g
知道如何对B
类型的所有值进行操作。函数空间构造函数在其参数位置的反差允许我们得出结论,g
也可以安全地分配类型A->C
。正如我们所知,A
中的所有值也都在B
中,g
知道如何处理所有B
这不会带来任何问题,我们可以安全地将A
中的值传递给g