我有一个行为 X 和一个参数类型的回调函数:
%{a: any}
模块 Y 实现行为 X,实现模块 Y 中的回调函数具有参数类型:
%{a: any, b: any}
Dialyzer 不喜欢这样并抱怨:
(#{'a':=_, 'b':=_, _=>_})
is not a supertype of
#{'a':=_}
这意味着透析器尝试确定实现模块 Y 中的回调参数类型是否是行为 X 中的参数类型的超类型。换句话说,它会询问:
行为X的回调参数类型是%{a: any}
的一个子类型
实现模块 Y 的参数类型%{a: any, b: any}
?
为什么透析器期望行为回调的参数类型是子类型而不是超类型?
在编程语言类型理论的背景下,子类型已定义 http://www.cs.pomona.edu/~kim/FOOPL/chap5.pdf as:
类型 S 是类型 T 的子类型,写作 S <: t s>
根据上面的定义,如果行为回调的参数类型是,这对我来说是有意义的T
实现模块是S
。因为实现模块仍然保持行为契约。然而,我不知道为什么透析器期望相反的方式。
请帮助我理解这一点。
注意:这个问题是后续问题,但独立于另一个 SO 问题Erlang (Elixir) Dialyzer - 令人困惑的超类型错误 https://stackoverflow.com/questions/46246998/erlang-elixir-dialyzer-confusing-supertype-error.
透析器是正确的。如果有一种行为X
带有类型的回调%{a: any}
,用户应该能够调用任何声称实现此行为的模块的函数,例如%{a: 1}
。你的模块的功能需要%{a: any, b: any}
这是一个subtype of %{a: any}
,这意味着该函数不能被调用%{a: 1}
不再符合行为。
另一方面,如果行为的回调具有类型%{a: any, b: any}
并且您的模块的函数具有类型%{a: any}
,那本来就很好,因为%{a: any}
是一个超类型%{a: any, b: any}
你的模块可以用%{a: 1, b: 2}
-- 它可以忽略额外的字段。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)