1)假设有一个模块
module Int_Sig_1 =
struct
let x = 1
end
;;
2)和该模块的明确签名
module type INT_SIG =
sig
val x:int
end
;;
3)我创建了一个基于上述模块和模块类型的第一类模块
let int_sig_1 = (module Int_Sig_1:INT_SIG)
4)现在我创建了另一个模块,没有明确的签名但是与上面相同的推断签名
module Int_Sig_2 =
struct
let x =2
end
;;
5)正如“真实世界OCAML”一书的第10章所写“如果可以推断,模块类型不需要是构建一流模块的一部分”,我尝试先创建第二个模块类模块使用上面的模块,但没有明确的模块类型
let a2 = (module Int_Sig_2);;
我收到以下错误
错误:无法推断此打包模块的签名。
6)然后我尝试在5中做同样的事情,但这次我把第一类模块没有模块类型创建为列表的元素,其中的头部该列表是一个第一类模块,它是由上面3中的显式签名创建的。
let int_sig= [int_sig_1;(module Int_Sig_2)];;
val int_sig : (module INT_SIG) list = [<module>; <module>] ;;
我的问题是为什么上面的5个给我一个错误而6个没有失败?
答案 0 :(得分:2)
(5)的问题在于,通常有多种模块类型可以被推测。在您的示例中,至少有两种有效的模块类型可用于打包Int_Sig_2
:
module type empty = sig end
module type with_x = sig val x:int end
换句话说,两者都是
let a2 = (module Int_Sig_2: empty)
let a2_bis = (module Int_Sig_2:with_x)
有效。因此,在这种情况下,类型检查器不会尝试推断模块类型。
相反,在您的示例(6)中,列表的类型由其第一个元素确定,其类型为(module INT_SIG_2)
,因此类型检查器可以使用此信息来推断该类型的预期类型列表的第二个元素是(module INT_SIG_2)
。然而,反转这两个元素会产生类型错误。换句话说,这很好:
[(module struct let x = 2 end: with_x); (module struct let x = 1 end)]
然而,反向收益率
[(module struct let x=2 end); (module struct let x = 3 end:with_x)];;
Error: The signature for this packaged module couldn't be inferred.
这是因为类型检查器具有从左到右的偏差,并且首先首先键入列表的第一个元素。