使用类型类方法的默认实现来省略参数
Using a default implementation of typeclass method to omit an argument
我希望能够定义一个(多参数-)类型 class 实例,其 class 方法的实现会忽略其参数之一。这可以很容易地完成,如下所示。
instance MyType MyData () where
specific _ a = f a
由于我在多个地方使用此模式,因此我尝试通过添加专门的 class 方法和足够的默认实现来概括它。我想到了以下内容。
{-# 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 #-}
然而,这会产生错误 Could not deduce (MyType a b0) arising from a use of ‘dontCare’
[..] The type variable ‘b0’ is ambiguous
。我不明白为什么后者应该是类型变量 b
的范围从 class 签名到方法声明的情况。你能帮我理解这里出现的确切问题吗?
是否有另一种合理的方法来实现我的意图,即以通用方式允许此类修剪实例?
问题出在 specific
的默认定义中。让我们缩小一秒钟,看看根据您的类型签名,您的方法实际给出了哪些类型。
specific :: forall a b. MyType a b => b -> a -> a
dontCare :: forall a b. MyType a b => a -> a
在 specific
的默认定义中,您在类型 a -> a
处使用 dontCare
。所以 GHC 推断 dontCare
的第一个类型参数是 a
。但是没有任何东西限制它的第二个类型参数,所以 GHC 没有办法 select 正确的实例字典来使用它。这就是为什么你最终需要 AllowAmbiguousTypes
来让 GHC 接受你对 dontCare
的类型签名。这些 "ambiguous" 类型在现代 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 #-}
类型应用指定第二个参数为b
。您可以为第一个参数填写 a
,但 GHC 实际上可以很好地解决这个问题。
我希望能够定义一个(多参数-)类型 class 实例,其 class 方法的实现会忽略其参数之一。这可以很容易地完成,如下所示。
instance MyType MyData () where
specific _ a = f a
由于我在多个地方使用此模式,因此我尝试通过添加专门的 class 方法和足够的默认实现来概括它。我想到了以下内容。
{-# 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 #-}
然而,这会产生错误 Could not deduce (MyType a b0) arising from a use of ‘dontCare’
[..] The type variable ‘b0’ is ambiguous
。我不明白为什么后者应该是类型变量 b
的范围从 class 签名到方法声明的情况。你能帮我理解这里出现的确切问题吗?
是否有另一种合理的方法来实现我的意图,即以通用方式允许此类修剪实例?
问题出在 specific
的默认定义中。让我们缩小一秒钟,看看根据您的类型签名,您的方法实际给出了哪些类型。
specific :: forall a b. MyType a b => b -> a -> a
dontCare :: forall a b. MyType a b => a -> a
在 specific
的默认定义中,您在类型 a -> a
处使用 dontCare
。所以 GHC 推断 dontCare
的第一个类型参数是 a
。但是没有任何东西限制它的第二个类型参数,所以 GHC 没有办法 select 正确的实例字典来使用它。这就是为什么你最终需要 AllowAmbiguousTypes
来让 GHC 接受你对 dontCare
的类型签名。这些 "ambiguous" 类型在现代 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 #-}
类型应用指定第二个参数为b
。您可以为第一个参数填写 a
,但 GHC 实际上可以很好地解决这个问题。