在类型类功能上明确说明



由于GHC-8.0,我们有一个非常不错的扩展名,称为TypeApplications。这使我们而不是:

λ> show (5 :: Int)
"5"

这样做类似的事情:

λ> :set -XTypeApplications
λ> show @Int 5
"5"

这真的很酷。当我们添加更多类型变量时,它会变得更加涉及,但是有一些规则可以用来确定确切顺序,并且记录了很好:

showFooBar :: (Show a, Show b) => a -> b -> String
showFooBar a b = show a ++ " and " ++ show b

因此,在上面的功能中,我们将首先提供a,然后供应b

λ> showFooBar @Int @Double 3 4
"3 and 4.0"

太好了,但是如果我想更改订单怎么办?那里没问题,我们可以使用ExplicitForAll扩展名(或其他暗示它)来指定它:

{-# LANGUAGE ExplicitForAll #-}
showFooBar :: forall b a . (Show a, Show b) => a -> b -> String
showFooBar a b = show a ++ " and " ++ show b

现在,我们扭转了要应用的类型顺序:

λ> showFooBar @Int @Double 3 4
"3.0 and 4"

问题在于,我似乎无法弄清楚如何对类型类的函数实现相同的影响。考虑此示例:

{-# LANGUAGE MultiParamTypeClasses #-}
class (Show a, Show b) => FooBar a b where
  fooBarClassFunc :: a -> b -> String

我现在无法将forall放在函数上(例如fooBarClassFunc :: forall a b . a -> b -> ..,导致会改变该功能的含义,并且显然不会编译。

因此,问题是,如何将类型变量的顺序更改为TypeApplication的目的?

编辑

以防万一,我尝试过InstanceSigs扩展名,并且就TypeApplications而言,它完全忽略了forall类型变量的顺序,这是一件好事,否则我们最终会以实例确定的行为,而不是班级。

如何将类型变量的顺序用于类型类方法中的TypeApplication的目的?

@luqui的答案足够好,我想。但是为什么不这样做:

class (Show b, Show a) => FooBar b a where
  fooBarClassFunc :: a -> b -> String

您只有一种方法,因此唯一考虑驱动类参数的顺序是为了TypeApplication,方法是在方法内。

如果您有两种或多种方法,您希望TypeApplication的顺序不同(@Chi的观点,但是为什么?),那么对于其他方法,Luqui的建议或(等效地)带有超级类别的其他类约束和默认实现。

class (Show a, Show b, FooBar b a) => OtherFooBar a b where
  otherFooBarClassFunc :: a -> b -> String
  otherFooBarClassFunc = otherFooBarClassFunc'  -- default
instance {-# NOOVERLAPPABLE #-} OtherFooBar a b  where {}  -- take default

(假设otherFooBarClassFunc'是在主类中定义的;这就是真实实例定义继续进行的。)

当然,对于每个课程的一种方法,有很多话要说。

{-# NOOVERLAPPABLE #-} wot我们不是'ave是我的小笑话。

相关内容

  • 没有找到相关文章

最新更新