在RankNTypes中forall的目的

时间:2015-04-02 16:27:06

标签: haskell

hlint的帮助下编写一些Haskell代码时,我按照提示向函数添加了一个类型签名。它注入了类似的东西:

{-# LANGUAGE RankNTypes #-}

multipleOf :: forall a. Integral a => a -> a -> Bool
multipleOf divisor n = n `mod` divisor == 0

这种语言扩展对我来说是新的,但据我所知,这与更简单的相同:

multipleOf :: Integral a => a -> a -> Bool
multipleOf divisor n = n `mod` divisor == 0

我读过的所有排名N类的例子似乎都没有对已经可用的多态性添加任何新内容,而且这种forall语法似乎并不像添加任何值。

我错过了什么?是否有一个很好的例子说明forall语法在没有扩展名的情况下可以做什么?

1 个答案:

答案 0 :(得分:11)

您错过了可以限制量化类型变量的范围:

modifyPair :: (Num b, Num c) => (forall a. Num a => a -> a) -> (b, c) -> (b, c)
modifyPair f (b, c) = (f b, f c)

尝试在没有RankNTypes扩展名的情况下编写该函数。特别是,允许​​该对的元素彼此不同类型。

这个特定的示例并不太有用,但总体思路仍然存在。您可以指定函数的参数必须是多态的。

您可以使用此工具执行其他技巧。规范示例来自STST库的目标是允许约束使用可变数据。也就是说,您可以实现一个涉及真实变异的算法,并提供一个纯粹的外部接口。 ST本身通过聪明的类型系统技巧来证明使用是安全的:

newtype ST s a    = ... -- details aren't important
newtype STRef s a = ... -- details still aren't important

-- a standard-ish interface to mutable data
newSTRef :: ST s (STRef s a)
readSTRef :: STRef s a -> ST s a
writeSTRef :: STRef s a -> a -> ST s ()

-- the magic using RankNTypes
runST :: (forall s. ST s a) -> a

额外的类型变量s值得关注。它出现在STSTRef中。每个操纵STRef的函数都会确保sST中的STRef相同的类型变量。因此,当您到达runST时,您会发现类型s必须与类型a无关。 s的范围比a的范围更受限制。最终结果是你不能写runST newSTRef之类的东西。类型检查器将拒绝它,因为类型变量s必须逃避它量化的上下文。

因此,当您指定函数的参数必须是多态的时,您可以提取一些方便的技巧。