自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
类型变量的顺序,这是一件好事,否则我们最终将具有由实例而非类决定的行为。
答案 0 :(得分:1)
如何在类型类方法中为
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'
是在主类中定义的;这就是真实实例定义的所在。)
one method per class当然有很多话要说。
{-# NOOVERLAPPABLE #-}
希望我们不要“开玩笑”。