Erlang(Elixir)Dialyzer - 令人困惑的超类型错误

Ahm*_*ous 4 erlang elixir erlang-otp

我已经定义了Elixir行为X.回调start_link被指定为:

@callback start_link(
  args :: producer_args,
  opts :: GenServer.options
) :: GenServer.on_start
Run Code Online (Sandbox Code Playgroud)

其中producer_argstype被定义为:

@type producer_args :: %{job_queue_name: String.t}
Run Code Online (Sandbox Code Playgroud)

Y实现该行为的客户端代码中,start_link定义为:

def start_link(args = %{job_queue_name: _job_queue_name, redis_url: _redis_url}, opts) do
  GenStage.start_link(__MODULE__, args, opts)
end
Run Code Online (Sandbox Code Playgroud)

透析器不喜欢它.它说,

(#{'job_queue_name':=_, 'redis_url':=_, _=>_}) 
is not a supertype of 
#{'job_queue_name':=binary()}
Run Code Online (Sandbox Code Playgroud)

问题#1:

在继承方面,子类型扩展了超类型.因此,定义的行为(X)应该被认为是超类型.实现行为(Y)的模块应被视为子类型.显然Dialyzer应该问这个问题:

#{'job_queue_name':=binary()}超类型 (#{'job_queue_name':=_, 'redis_url':=_, _=>_})

相反,它以相反的方式提出问题.为什么?

问题2:

supertype透析器中的定义与OOP继承的讨论相同吗?如果没有,那么它是什么?我试图在透析器的上下文中找到超类型的定义,但没有找到.

Mik*_*hot 6

错误消息基本上是这样说:您不能要求额外的redis_url密钥,因为它没有在行为的类型规范中声明.

Dialyzer不会将行为和实现模块视为类型.它专门查看回调的参数.

将匹配的值集合是将匹配#{'job_queue_name':=_, 'redis_url':=_, _=>_}的值的子集#{'job_queue_name':=_}.

所以#{'job_queue_name':=_, 'redis_url':=_, _=>_}是子类型#{'job_queue_name':=_}.

Dialyzer将允许您使用参数来实现回调,这些参数是回调中声明的内容的超类型,因为这可确保任何依赖于行为契约的代码在运行时都不会因匹配错误而失败.