为什么这个等效程序无法编译?

时间:2015-09-05 20:47:09

标签: haskell impredicativetypes

这个程序:

{-# LANGUAGE RankNTypes, ImpredicativeTypes #-}

import qualified Data.Vector.Mutable as MV
import qualified Data.Vector as V
import Control.Monad.ST
import Control.Monad.Primitive

unsafeModify :: [(forall s . MV.MVector s Int -> ST s ())] -> V.Vector Int -> V.Vector Int
unsafeModify mods vec = runST $ do
    mvec <- V.unsafeThaw vec
    (mods !! 0) mvec
    V.unsafeFreeze mvec

编译。这个计划:

{-# LANGUAGE RankNTypes, ImpredicativeTypes #-}

import qualified Data.Vector.Mutable as MV
import qualified Data.Vector as V
import Control.Monad.ST
import Control.Monad.Primitive

unsafeModify :: [(forall s . MV.MVector s Int -> ST s ())] -> V.Vector Int -> V.Vector Int
unsafeModify mods vec = runST $ do
    mvec <- V.unsafeThaw vec
    ($ mvec) (mods !! 0)
    V.unsafeFreeze mvec

无法使用以下错误进行编译:

Muts.hs:10:15:
    Couldn't match type ‘forall s1. UV.MVector s1 Int -> ST s1 ()’
                  with ‘UV.MVector s Int -> ST s a0’
    Expected type: [UV.MVector s Int -> ST s a0]
      Actual type: [forall s. UV.MVector s Int -> ST s ()]
    Relevant bindings include
      mvec :: UV.MVector s Int (bound at Muts.hs:9:5)
    In the first argument of ‘(!!)’, namely ‘mods’
    In the first argument of ‘$ mvec’, namely ‘(mods !! 0)’

为什么?

2 个答案:

答案 0 :(得分:3)

注意:这篇文章是用文字Haskell编写的。您可以将其保存为Unsafe.lhs并在GHCi中尝试。

让我们比较不同行的类型:

 mods                ::     [(forall s . MV.MVector s Int -> ST s ())]
(mods !! 0)          ::      (forall s . MV.MVector s Int -> ST s ())
(mods !! 0)  mvec    ::       forall s. ST s ()


($ mvec)             ::     (MV.Vector s Int -> b) -> b
         (mods !! 0) ::     (forall s . MV.MVector s Int -> ST s ())
($ mvec) (mods !! 0) ::     ????????????????????????

由于$的类型,它们并不相同:

($) :: forall a b. (a -> b) -> a -> b

然而你需要一些东西

($)  :: (a ~ (forall s . MV.MVector s Int -> ST s ())) =>
      (a -> b) -> a -> b

这是不合法的。

但是,让我们来看看你真正想做的事情。

> {-# LANGUAGE RankNTypes #-}

> import qualified Data.Vector.Mutable as MV
> import qualified Data.Vector as V
> import Control.Monad.ST
> import Control.Monad.Primitive

  unsafeModify :: ??? -> V.Vector Int -> V.Vector Int

> unsafeModify mods vec = runST $ do
>   mvec <- V.unsafeThaw vec
>   mapM_ ($ mvec) (mods !! 0)
>   V.unsafeFreeze mvec

由于unsafeModify的多态第一个参数mods导致事情变得混乱。你的原始类型

[(forall s . MV.MVector s Int -> ST s ())]

告诉我们它是一个函数列表,其中每个函数都是参数s的多态函数,因此每个函数都可以使用另一个s 。但是,这太过分了。如果s在整个列表中共享,那就没关系了:

(forall s. [MV.MVector s Int -> ST s ()])

毕竟,我们希望在相同的ST计算中使用所有函数,因此流状态标记s的类型可以是相同的。我们最终得到了

> unsafeModify :: (forall s. [MV.MVector s Int -> ST s ()]) -> V.Vector Int -> V.Vector Int

现在,无论您使用($ mvec) (mods !! 0)(mods !! 0) mvec还是mapM_,您的代码都会愉快地编译,因为s现在已被runST正确修复整个清单。

答案 1 :(得分:3)

(这应该是评论,但我需要更多空间。)

可悲的是,正如@dfeuer所指出的那样,不可预测的类型在GHC中不能很好地运作。 考虑这个例子:

{-# LANGUAGE ImpredicativeTypes, PartialTypeSignatures #-}
import qualified Data.Vector.Mutable as MV
import Control.Monad.ST

-- myIndex :: [forall s. MV.MVector s Int -> ST s ()] 
--         -> Int
--         -> (forall s. MV.MVector s Int -> ST s ())
myIndex = (!!) :: [forall s. MV.MVector s Int -> ST s ()] -> Int -> _

它成功编译,尽管由于类型漏洞而发出警告:

VectorTest.hs:9:69: Warning:
    Found hole ‘_’ with type: forall s. MV.MVector s Int -> ST s ()
    Relevant bindings include
      myIndex :: [forall s. MV.MVector s Int -> ST s ()]
                 -> Int -> forall s. MV.MVector s Int -> ST s ()
        (bound at VectorTest.hs:9:1)

我们可以尝试删除PartialTypeSignatures扩展名并使用其类型forall s. MV.MVector s Int -> ST s ()填充该漏洞。但这种情况非常糟糕:

VectorTest.hs:9:11:
    Couldn't match type ‘forall s2. MV.MVector s2 Int -> ST s2 ()’
                   with ‘MV.MVector s1 Int -> ST s1 ()’
    Expected type: [forall s. MV.MVector s Int -> ST s ()]
                   -> Int -> MV.MVector s1 Int -> ST s1 ()
      Actual type: [MV.MVector s1 Int -> ST s1 ()]
                   -> Int -> MV.MVector s1 Int -> ST s1 ()

最后forall被提升到顶级,现在GHC推断(!!)的第一个参数必须是单态元素列表[MV.MVector s1 Int -> ST s1 ()] 尽管< / em>我们的注释!基本上,GHC有两种选择:

-- Note the hoisted forall s1
myIndex = (!!) :: forall s1. [forall s. MV.MVector s Int -> ST s ()] -> Int 
               -- ^ first choice for instantiating the type of (!!)
               -> MV.MVector s1 Int -> ST s1 ()
               -- ^ second choice

GHC选择第二个,但失败了。只有部分类型签名我才能删除第二个选择,以便GHC被迫做正确的事情。

如果我们只有像GHC Core这样的显式类型应用程序,我们可以编写(!!) @ (forall s. ...),但是我们没有。{/ p>