首页 > 解决方案 > 类型类函数的显式 forall

问题描述

从 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了扩展,它完全忽略了forall类型变量的顺序TypeApplications,这是一件好事,否则我们最终会得到由实例而不是类决定的行为。

标签: haskelltypeclassforall

解决方案


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 #-}我们不知道的是我的小笑话。


推荐阅读