说我有一堂课
class T where
tag1 :: String
tag2 :: String
Run Code Online (Sandbox Code Playgroud)
启用模糊类型后,我可以在实例中指定每个类型:
instance T A where
tag1 = "tag1"
tag2 = "tag2"
Run Code Online (Sandbox Code Playgroud)
如果我想对tag2追加内容tag1,则可以定义
instance T A where
tag1 = "tag1"
tag2 = tag1 @A ++ " suffix"
Run Code Online (Sandbox Code Playgroud)
这很好用,但是如果我想tag2始终附加suffix到tag1每个实例,则似乎必须为每个实例指定模糊的调用。
我了解此需求,因为tag1从任何情况下,每个呼叫都可以使用。但是,haskell中是否有任何技巧可以让我仅指定一次?
就像是
tag2 :: T a => String
tag2 = tag1 @a ++ " suffix"
Run Code Online (Sandbox Code Playgroud)
由于类型类没有类型参数,因此您的代码当前无法编译,因此我将假设您的代码实际上是(假设AllowAmbiguousTypes已启用)
class T a where
tag1 :: String
tag2 :: String
Run Code Online (Sandbox Code Playgroud)
现在,您可以为以下内容提供默认实现tag2:
class T a where
tag1 :: String
tag2 :: String
tag2 = "tag2"
Run Code Online (Sandbox Code Playgroud)
但这不满足将后缀添加到的要求tag1。
我们可以尝试一下(假设TypeApplications已启用):
class T a where
tag1 :: String
tag2 :: String
tag2 = tag1 @a ++ "suffix"
Run Code Online (Sandbox Code Playgroud)
现在,它将无法编译,并且编译错误将是
error: Not in scope: type variable `a'
Run Code Online (Sandbox Code Playgroud)
没错,这个类型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
Run Code Online (Sandbox Code Playgroud)
输出为:
> ./foo
tagA
tagA suffix
tagB
tagB overriden
Run Code Online (Sandbox Code Playgroud)
是的,您可以完全执行- tag1 @a,但是您需要进行两项修改:enable ScopedTypeVariables并添加一个explicit forall,如下所示:
tag2 :: forall a. T a => String
tag2 = tag1 @a ++ " suffix"
Run Code Online (Sandbox Code Playgroud)
显式的forall是为类型变量创建作用域的作用a,从而使整个变量都可以访问tag2。没有它(即按照Haskell 2010的标准规则),类型变量将仅作用于类型签名,并且无法在主体中访问。
如果您希望将其tag2作为类方法而不是独立函数,则可以为其添加默认实现,如下所示:
class T a where
tag1 :: String
tag2 :: String
tag2 = tag1 @a ++ " suffix"
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您无需提供明确的forall。相反,类型变量的范围将是整个类实例。但是您仍然需要ScopedTypeVariables,否则将根本没有范围。