这是作用域类型变量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
和朋友。作用域类型变量只允许您拥有更类似于保护模式的语法。