说我上课
class T where
tag1 :: String
tag2 :: String
启用模糊类型后,我可以在实例中指定它们中的每一个:
instance T A where
tag1 = "tag1"
tag2 = "tag2"
如果我想将tag2
附加到tag1
上,我可以定义
instance T A where
tag1 = "tag1"
tag2 = tag1 @A ++ " suffix"
这很好用,但是如果我想tag2
总是将suffix
附加到每个tag1
上,我似乎必须为每个实例指定模糊的调用。
我知道需要这样做,因为来自任何实例的tag1
对于每个呼叫都适用。但是,haskell中是否有任何技巧可以让我仅指定一次?
类似
tag2 :: T a => String
tag2 = tag1 @a ++ " suffix"
答案 0 :(得分:8)
由于类型类没有类型参数,您的代码当前无法编译,因此我假设您的代码实际上是(假设启用AllowAmbiguousTypes
)
class T a where
tag1 :: String
tag2 :: String
现在,您可以为tag2
提供默认实现:
class T a where
tag1 :: String
tag2 :: String
tag2 = "tag2"
但这不满足将后缀添加到tag1
的要求。
我们可以尝试一下(假设启用了TypeApplications
):
class T a where
tag1 :: String
tag2 :: String
tag2 = tag1 @a ++ "suffix"
现在该将不会进行编译,并且编译错误将为
error: Not in scope: type variable `a'
,因此,a
类型在任何地方都没有定义。但是,我们要引用类开头的a
,为此,我们需要语言扩展ScopedTypeVariables
,并且将编译该代码,并且您将获得期望的结果(我建议阅读链接的文档)
这是一个演示用法的完整程序:
{-# LANGUAGE TypeApplications, AllowAmbiguousTypes, ScopedTypeVariables #-}
class T a where
tag1 :: String
tag2 :: String
tag2 = tag1 @a ++ " suffix"
data A = A
data B = B
instance T A where
tag1 = "tagA"
instance T B where
tag1 = "tagB"
tag2 = "tagB overriden"
main = do
putStrLn $ tag1 @A
putStrLn $ tag2 @A
putStrLn $ tag1 @B
putStrLn $ tag2 @B
输出为:
> ./foo
tagA
tagA suffix
tagB
tagB overriden
答案 1 :(得分:7)
是的,您可以完全做到这一点-tag1 @a
,但是您需要进行两项修改:启用ScopedTypeVariables
并添加一个明确的forall
,如下所示:
tag2 :: forall a. T a => String
tag2 = tag1 @a ++ " suffix"
显式forall
是为类型变量a
创建范围的对象,使它可以在tag2
的整个主体中访问。如果没有它(即按照Haskell 2010的标准规则),则类型变量的作用域仅限于类型签名,并且在正文中不可访问。
如果您希望将tag2
作为类方法而不是独立函数,则可以为其添加默认实现,如下所示:
class T a where
tag1 :: String
tag2 :: String
tag2 = tag1 @a ++ " suffix"
在这种情况下,您无需提供明确的forall
。相反,类型变量的范围将是整个类实例。但是您仍然需要ScopedTypeVariables
,否则根本就没有作用域。