Elixir / Erlang Dialyzer:为什么行为回调的param类型应该是子类型而不是超类型?

时间:2017-09-20 06:25:13

标签: erlang elixir phoenix-framework dialyzer

我有一个行为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

1 个答案:

答案 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}调用 - 它可以忽略额外的字段。