RankNTypes doesn't match return type

梦想与她 提交于 2019-12-10 11:16:28

问题


Using RankNTypes, I define a type that doesn't depend on a type variable. Is this the right way to go around the case below?

I need to define a few functions to be used inside ST s, which, of course, doesn't depend on s. Yet, this causes a problem that an expression of Exp with two Ints applied to it doesn't result in a Block. Why?

Here is a reproducer:

import Control.Monad.ST
import Data.Vector.Unboxed (Vector)
import qualified Data.Vector.Unboxed as U
import Data.Vector.Unboxed.Mutable (STVector)
import qualified Data.Vector.Unboxed.Mutable as UM

type Exp = Int -> Int -> Block
type Block = forall s . STVector s Int -> ST s Int

block :: [Block] -> Block
block [] _ = return 0  -- mapM doesn't work, either - ok, I kinda see why
block (e:es) a = do x <- e a
                    xs <- block es a
                    return $ x+xs

copy :: Exp
copy i j a = do
        aj <- a `UM.unsafeRead` j
        UM.unsafeWrite a i aj
        return 1


f :: Block -> Vector Int -> Int
f blk ua = runST $ U.thaw ua >>= blk

g :: Block -> Int
g blk = f blk $ U.fromListN 12 [1..]

main = print . g $ block [copy 10 1]

The error I get points at the last line:

Couldn't match type `STVector s0 Int -> ST s0 Int'
              with `forall s. STVector s Int -> ST s Int'
Expected type: Block
  Actual type: STVector s0 Int -> ST s0 Int
In the return type of a call of `block'
Probable cause: `block' is applied to too few arguments
In the second argument of `($)', namely `block [copy 10 1]'
In the expression: print . g $ block [copy 10 1]

The difference between the expected and actual type is the forall s. bit, as far as I can tell.


回答1:


While I'd prefer the solution @Oleg posted, I'd like to share an alternative.

Replace

main = print . g $ block [copy 10 1]

with

main = print (g (block [copy 10 1]))

Reason: impredicative types make it hard for the compiler to guess the type of (.) and ($) above.

Another option would be to annotate (.) and ($) with their instantiated type -- this would be rather cumbersome, though.




回答2:


Using newtype for Block will keep s existential. Otherwise it will "leak" out


With original definition:

type Block = forall s . STVector s Int -> ST s Int
type Exp = Int -> Int -> Block

You could simplify failing example (main) to:

g . block

You'd like it's type to be:

g . block :: [Block] -> Int

But as the written out types of the components are:

block :: forall s. [forall s0. STVector s0 Int -> ST s0 Int] -> (STVector s Int -> ST s Int)
g :: (forall s1. STVector s1 Int -> ST s1 Int) -> Int

Then when composed with (.), GHC keeps s general:

g . block :: forall s . [forall s0. STVector s0 Int -> ST s0 Int] -> Int

and tries to unify:

forall s1. STVector s1 Int -> ST s1 Int -- and
(STVector s Int -> ST s Int)

With newtype everything works perfectly (and no need for ImpredicativeTypes):

{-# LANGUAGE RankNTypes #-}
import Control.Monad.ST
import Data.Vector.Unboxed (Vector)
import qualified Data.Vector.Unboxed as U
import Data.Vector.Unboxed.Mutable (STVector)
import qualified Data.Vector.Unboxed.Mutable as UM

type Exp = Int -> Int -> Block
newtype Block = Block { unBlock :: forall s . STVector s Int -> ST s Int }

block :: [Block] -> Block
block [] = Block $ \_ -> return 0  -- mapM doesn't work, either - ok, I kinda see why
block (e:es) = Block $ \a -> do x <- unBlock e a
                                xs <- unBlock (block es) a
                                return $ x + xs

copy :: Exp
copy i j = Block $ \a -> do
        aj <- a `UM.unsafeRead` j
        UM.unsafeWrite a i aj
        return 1


f :: Block -> Vector Int -> Int
f (Block blk) ua = runST $ U.thaw ua >>= blk

g :: Block -> Int
g blk = f blk $ U.fromListN 12 [1..]

main = print . g $ block [copy 10 1]


来源:https://stackoverflow.com/questions/27942828/rankntypes-doesnt-match-return-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!