使用 typeclass 方法的默认实现来省略参数

Ger*_*ood 6 haskell typeclass

我希望能够定义一个(多参数)类型类实例,该实例的类方法的实现忽略其参数之一。这可以很容易地完成,如下所示。

instance MyType MyData () where
    specific _ a = f a
Run Code Online (Sandbox Code Playgroud)

由于我在多个地方使用此模式,因此我尝试通过添加专门的类方法和适当的默认实现来概括它。我想出了以下内容。

{-# LANGUAGE MultiParamTypeClasses, AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

class MyType a b where
    specific :: b -> a -> a
    specific = const dontCare
    dontCare :: a -> a
    dontCare = specific (undefined :: b)
    {-# MINIMAL specific | dontCare #-}
Run Code Online (Sandbox Code Playgroud)

然而,这会产生错误Could not deduce (MyType a b0) arising from a use of ‘dontCare’[..] The type variable ‘b0’ is ambiguous。我不明白为什么后者应该是类型变量b的范围从类签名到方法声明的情况。你能帮我理解这里出现的确切问题吗?

是否有另一种合理的方法来实现我的意图,即以通用方式允许此类修剪过的实例?

dfe*_*uer 2

问题出在 的默认定义中specific。让我们缩小一下,看看根据您的类型签名,您的方法实际上给出了哪些类型。

specific :: forall a b. MyType a b => b -> a -> a
dontCare :: forall a b. MyType a b => a -> a
Run Code Online (Sandbox Code Playgroud)

在 的默认定义中specific,您使用dontCareat 类型a -> a。因此 GHC 推断第一个类型参数dontCarea。但没有任何东西限制它的第二个类型参数,所以 GHC 无法选择正确的实例字典来使用它。这就是为什么您最终需要AllowAmbiguousTypes让 GHC 接受您的类型签名dontCare。这些“模糊”类型在现代 GHC 中有用的原因是我们必须TypeApplications允许我们修复它们。这个定义很好用:

class MyType a b where
    specific :: b -> a -> a
    specific = const (dontCare @_ @b)
    dontCare :: a -> a
    dontCare = specific (undefined :: b)
    {-# MINIMAL specific | dontCare #-}
Run Code Online (Sandbox Code Playgroud)

类型 application 指定第二个参数是b。你可以填写a第一个参数,但 GHC 实际上可以很好地解决这个问题。