我有一个行为X和一个带参数类型的回调函数:
%{a: any}
模块Y实现行为X,实现模块Y中的回调函数具有参数类型:
%{a: any, b: any}
透析师不喜欢并且抱怨:
(#{'a':=_, 'b':=_, _=>_})
is not a supertype of
#{'a':=_}
这意味着dialyzer尝试确定实现模块Y中的回调参数类型是否是行为X中的param类型的超类型。换句话说,它要求:
行为X的回调参数类型
%{a: any}
是否为子类型 实现模块Y的参数类型%{a: any, b: any}
?
为什么透析器期望行为回调的参数类型是子类型而不是超类型?
在编程语言类型理论的背景下,subtype is defined为:
类型S是类型T的子类型,如果表达式为,则写入S<:T type S可以在任何需要类型为T的元素的上下文中使用。 另一种表达方式是S类型的任何表达式都可以 伪装成T型的表达。
根据上面的定义,如果行为回调的参数类型为T
且实现模块的参数类型为S
,则对我有意义。因为实施模块仍然保持行为合同。但是,我对于透析器为什么还有其他方法感到无能为力。
请帮助我理解这一点。
注意:这个问题是一个后续行动,但独立于另一个SO问题Erlang (Elixir) Dialyzer - confusing supertype error。
答案 0 :(得分:2)
透析器是正确的。如果行为X
具有类型为%{a: any}
的回调,则用户应该能够调用声称实现此行为的任何模块的该函数,例如%{a: 1}
。您的模块的函数需要%{a: any, b: any}
这是%{a: any}
的子类型,这意味着不能再使用%{a: 1}
来调用函数,这不符合行为。
另一方面,如果行为的回调类型为%{a: any, b: any}
且您的模块函数的类型为%{a: any}
,那就没问题了,因为{{1是%{a: any}
的超类型,您的模块可以使用%{a: any, b: any}
调用 - 它可以忽略额外的字段。