Reputation: 52029
I have this monomorphic code:
import Data.Array.MArray
import Data.Array.IO (IOUArray)
import Data.Ix (Ix)
push :: IOUArray Int Int -> Int -> [Int] -> IO Int
push stack top [] = return top
push stack top (k:ks) = do
v <- readArray stack k
if v >= 0 then push stack top ks
else do writeArray stack k top
push stack k ks
A fully polymorphic signature for push
would be:
push :: (MArray a e m, Ix e, Num e) => a e e -> e -> [e] -> m e
But I tried to write these partially polymorphic signatures:
push :: MArray a e m => a Int e -> Int -> [Int] -> m Int
push :: MArray a e m => a Int Int -> Int -> [Int] -> m Int
push :: MArray a Int m => a Int Int -> Int -> [Int] -> m Int
push :: (MArray a e m, e ~ Int) => a e e -> e -> [e] -> m e
and I get errors like can't deduce (e ~ Int)
, can't deduce (MArray a Int m) ...
and Non type-variable argument in constraint
, Illegal equational constraint
Is there a way to constrain the type variable e
in the first signature to a concrete type like Int
?
Upvotes: 2
Views: 110
Reputation: 21811
The first signature
push :: MArray a e m => a Int e -> Int -> [Int] -> m Int
doesn't work because you are reading an array element v <- readArray stack k
which has type e
, but then compare it to 0, which is an Int
or Integer
(edit: really any Num
).
The second signature
push :: MArray a e m => a Int Int -> Int -> [Int] -> m Int
doesn't work because you say that a e m
is an instance of MArray
, but then try to use a a Int Int
. GHC tries to unify the required constraint with the given constraint, so it tries to match e
with Int
.
Ganesh's answer resolves the other two signatures.
Upvotes: 3
Reputation: 29100
The third signature works if you enable FlexibleContexts
(as the error suggests):
{-# LANGUAGE FlexibleContexts #-}
import Data.Array.MArray
import Data.Array.IO (IOUArray)
import Data.Ix (Ix)
push :: MArray a Int m => a Int Int -> Int -> [Int] -> m Int
-- push :: IOUArray Int Int -> Int -> [Int] -> IO Int
push stack top [] = return top
push stack top (k:ks) = do
v <- readArray stack k
if v >= 0 then push stack top ks
else do writeArray stack k top
push stack k ks
There's some discussion of this extension on the Haskell Prime wiki.
The fourth signature works with TypeFamilies
to allow the e ~ Int
constraint:
{-# LANGUAGE TypeFamilies #-}
import Data.Array.MArray
import Data.Array.IO (IOUArray)
import Data.Ix (Ix)
push :: (MArray a e m, e ~ Int) => a e e -> e -> [e] -> m e
-- push :: IOUArray Int Int -> Int -> [Int] -> IO Int
push stack top [] = return top
push stack top (k:ks) = do
v <- readArray stack k
if v >= 0 then push stack top ks
else do writeArray stack k top
push stack k ks
In this particular case I think it has exactly the same effect on type checking of calling contexts as the other working signature with FlexibleContexts
, but in general having a type variable with an equality constraint is not quite the same as having a concrete type.
Upvotes: 5