3

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

为什么透析器期望行为回调的参数类型是子类型而不是超类型?

在编程语言类型论的上下文中,子类型定义为:

类型 S 是类型 T 的子类型,写作 S <: T,如果类型 S 的表达式可以在任何需要类型 T 的元素的上下文中使用。另一种说法是任何类型 S 的表达式都可以伪装作为类型 T 的表达式。

T根据上面的定义,如果行为回调的参数类型是,实现模块的参数类型是,对我来说是有意义的S。因为实现模块仍然保持行为契约。但是,我不知道为什么透析器期望相反。

请帮助我理解这一点。

注意:这个问题是后续问题,但独立于另一个 SO 问题Erlang (Elixir) Dialyzer - 令人困惑的超类型错误

4

1 回答 1

3

透析器是正确的。如果有一个X带有回调类型的行为%{a: any},用户应该能够调用任何声称使用例如实现此行为的模块的该函数%{a: 1}。您的模块的函数采用%{a: any, b: any}子类型%{a: any}这意味着不能再调用%{a: 1}不符合行为的函数。

另一方面,如果行为的回调具有 type%{a: any, b: any}并且您的模块的函数具有 type %{a: any},那会很好,因为%{a: any}它是的超类型,%{a: any, b: any}并且您的模块可以被调用%{a: 1, b: 2}- 它可以忽略额外的字段。

于 2017-09-20T10:40:08.407 回答