【发布时间】:2018-03-01 01:56:23
【问题描述】:
我有一个行为 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}?
为什么透析器期望行为回调的参数类型是子类型而不是超类型?
在编程语言类型论的上下文中,subtype is defined 为:
类型 S 是类型 T 的子类型,写作 S <: t s>
根据上面的定义,如果行为回调的参数类型是T,而实现模块的参数类型是S,对我来说是有意义的。因为实现模块仍然保持行为契约。但是,我不知道为什么透析器期望相反。
请帮助我理解这一点。
注意:此问题是后续问题,但独立于另一个 SO 问题 Erlang (Elixir) Dialyzer - confusing supertype error。
【问题讨论】:
-
Because implementing module still keeps the behaviour contract.我不这么认为。现在你有一个模块,你说它实现了X,但实际上没有人可以用%{a: any}调用函数,因为你的实现还需要映射中的b: any。 -
尽管实现模块 Y 需要在映射中额外添加
b: any,但最好允许对行为 X 指定的原始类型执行任何操作。它遵守宽度子类型规则:en.wikipedia.org/wiki/Subtyping#Width_and_depth_subtyping -
"它仍然允许对行为 X 指定的原始类型的所有操作" 根据行为,我应该能够将
%{a: 1}传递给 X 的所有实现者,但我不能这样做Y 因为它需要一个额外的字段:b。反过来也可以——如果 X 需要%{a: any, b: any}和Y需要%{a: any},那很好,因为我可以像行为所说的那样调用它,b将被Y忽略。 -
刚刚度过了愉快的时光!没有从那个角度思考。非常感谢。这非常有用。您能否将其放在答案中以便我接受?与阅读 cmets 相比,我们更有可能阅读已接受的答案。
标签: erlang elixir phoenix-framework dialyzer