这是作用域类型变量GHC 扩展。点击链接了解更多信息。
基本上,您定义了一个关于模式必须满足的类型才能匹配的断言。所以,是的,它类似于警卫,但并非完全如此。
这个特定的例子是如何工作的?深入研究“基础”库的来源,找出:
class (Show e) => Exception e where
toException :: e -> SomeException
fromException :: SomeException -> Maybe e
data SomeException = forall e . Exception e => SomeException e
instance Exception IOException where
toException = IOException
fromException (IOException e) = Just e
fromException _ = Nothing
instance Exception ArithException where
toException = ArithException
fromException (ArithException e) = Just e
fromException _ = Nothing
我们看到了,IOException并且ArithException是实现 typeclass 的不同类型Exception。我们还看到这toException/fromException是一种包装/展开机制,它允许将 typeException的值转换为 types 的值或从 types 的值转换IOException,ArithException等等。
所以,我们可以这样写:
f = expr `catches` [Handler handleArith,
Handler handleIO]
handleArith :: ArithException -> IO ()
handleArith ex = ....
handleIO :: IOException -> IO ()
handleIO ex = ....
假设发生这种IOException情况。当catchesHandler处理处理程序列表的第一个元素时,它调用tryHandler,它调用fromException. 从tryHandler它的定义来看, 的返回类型fromException应该与 的参数相同handleArith。另一方面,e属于Exception类型,即-(IOException ...)。所以,类型以这种方式发挥作用(这不是一个有效的 haskell,但我希望你明白我的意思):
fromException :: (IOException ...) -> Maybe ArithException
从instance Exception IOException ...它立即得出结果是Nothing,所以这个处理程序被跳过。出于同样的原因,将调用以下处理程序,因为fromException将返回(Just (IOException ...)).
因此,您已经使用了handleArith和的类型签名handleIO来指定它们中的每一个何时被调用,并fromException/toException确保它以这种方式发生。
如果您愿意,您还可以使用作用域类型变量来约束 的定义handleIO和handleArith内部的类型。f可以说,这可以为您提供更好的可读性。
最终确定,作用域类型变量不是这里的主要参与者。它们只是为了方便而使用。玩这种花样的主要机器是fromException/toException和朋友。作用域类型变量只允许您拥有更类似于保护模式的语法。