leh*_*ins 11 haskell typeclass forall
从ghc-8.0开始,我们有一个很好的扩展名TypeApplications.这允许我们而不是:
?> show (5 :: Int)
"5"
Run Code Online (Sandbox Code Playgroud)
这样做是这样的:
?> :set -XTypeApplications
?> show @Int 5
"5"
Run Code Online (Sandbox Code Playgroud)
这真的很酷.当我们添加更多类型变量时,它会变得更加复杂,但是有一些规则可以用来确定确切的顺序,并且它们有很好的记录:
showFooBar :: (Show a, Show b) => a -> b -> String
showFooBar a b = show a ++ " and " ++ show b
Run Code Online (Sandbox Code Playgroud)
所以在上面的函数中我们首先提供a然后b:
?> showFooBar @Int @Double 3 4
"3 and 4.0"
Run Code Online (Sandbox Code Playgroud)
这很好,但是如果我想改变订单怎么办?没问题,我们可以使用ExplicitForAll扩展(或其他一些暗示它)来指定它:
{-# LANGUAGE ExplicitForAll #-}
showFooBar :: forall b a . (Show a, Show b) => a -> b -> String
showFooBar a b = show a ++ " and " ++ show b
Run Code Online (Sandbox Code Playgroud)
现在我们颠倒了我们要应用的类型顺序:
?> showFooBar @Int @Double 3 4
"3.0 and 4"
Run Code Online (Sandbox Code Playgroud)
问题是我似乎无法弄清楚如何为属于类型类的函数实现相同的效果.考虑这个例子:
{-# LANGUAGE MultiParamTypeClasses #-}
class (Show a, Show b) => FooBar a b where
fooBarClassFunc :: a -> b -> String
Run Code Online (Sandbox Code Playgroud)
我现在不能放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
Run Code Online (Sandbox Code Playgroud)
您只有一种方法,因此驱动类参数顺序的唯一考虑因素是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
Run Code Online (Sandbox Code Playgroud)
(假设otherFooBarClassFunc'在主类中定义;这就是真正的实例定义所在的地方。)
对于每个类的一个方法有很多话要说当然,
{-# NOOVERLAPPABLE #-}我们不知道什么是我的小笑话。