Reputation: 5406
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 Int
s 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.
Upvotes: 2
Views: 135
Reputation: 116139
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.
Upvotes: 3
Reputation: 12123
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]
Upvotes: 3