从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
而言,这是一件好事,否则我们最终会得到由实例决定的行为,而不是类。
为了
TypeApplication
在类型类方法中如何更改类型变量的顺序?
我想,@ luqui的答案已经足够好了。但为什么不这样:
class (Show b, Show a) => FooBar b a where
fooBarClassFunc :: a -> b -> String
您只有一个方法,因此驱动参数顺序的唯一考虑因素是方法中的TypeApplication
。
如果你有两个或两个以上的方法,你想要TypeApplication
的顺序是不同的(@ chi的点,但为什么?),那么对于其他方法要么luqui的建议,要么(等效地)另一个具有超类约束和a的类默认实现。
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'
是在主类中定义的;那就是真实实例定义所在的位置。)
当然,one method per class还有很多话要说。
qazxsw poi我们没有的东西是我的小玩笑。