Given the following code
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE PolyKinds #-}
type family Tagged (m :: * -> *) :: k
class Example (t :: k) (a :: *) where
type Return t a
a :: (Monad m, Tagged m ~ t) => a -> m (Return t a)
data A
data A' a
data B = B
instance Example A B where
type Return A B = ()
a B = return ()
-- This is why I want a PolyKinded 't'
instance Example A' B where
type Return A' B = ()
a B = return ()
I get the type error (pointing to the line a :: (Monad m ...)
• Could not deduce: Return (Tagged m) a ~ Return t a
from the context: (Example t a, Monad m, Tagged m ~ t)
bound by the type signature for:
a :: (Example t a, Monad m, Tagged m ~ t) =>
a -> m (Return t a)
...
Expected type: a -> m (Return t a)
Actual type: a -> m (Return (Tagged m) a)
NB: ‘Return’ is a type function, and may not be injective
The type variable ‘k0’ is ambiguous
• In the ambiguity check for ‘a’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the class method:
a :: forall k (t :: k) a.
Example t a =>
forall (m :: * -> *).
(Monad m, Tagged m ~ t) =>
a -> m (Return t a)
In the class declaration for ‘Example’
I can introduce an argument to a with Proxy t and this will work provided I give the signature at the call site: test = a (Proxy :: Proxy A) B but this is what I'm looking to avoid. What I'd like is
newtype Test t m a = Test
{ runTest :: m a
} deriving (Functor, Applicative, Monad)
type instance Tagged (Test t m) = t
test :: Monad m => Test A m ()
test = a B
I want t to be found from the context Test A m () using the type instance. It seems that it should be possible given the module will compile after removing the kind annotations, PolyKinds, and the instance for A'. Where is k0 coming from?
I suppose the workaround would be to drop PolyKinds and use extra data types like data ATag; data A'Tag; data BTag etc.
This is a partial answer, only.
I tried to make the kind explicit.
type family Tagged k (m :: * -> *) :: k
class Example k (t :: k) (a :: *) where
type Return k (t :: k) (a :: *)
a :: forall m . (Monad m, Tagged k m ~ t) => a -> m (Return k t a)
And, after enabling many extensions, observed this:
> :t a
a :: (Example k (Tagged k m) a, Monad m) =>
a -> m (Return k (Tagged k m) a)
Hence, the compiler is complaining because the instance Example k (Tagged k m) a can not be determined by a,m alone. That is, we do not know how to choose k.
I guess that, technically, we might have different Example k (Tagged k m) a instances, e.g. one for k=* and another for k=(*->*).
Intuitively, knowing t should allow us to find k, but Return being non injective prevents us to find t.
Related
I'm wondering what the appropriate type signature is for g. The one I've got currently doesn't compile. I presume a forall. is needed somewhere but I'm not exactly sure where.
{-# LANGUAGE TypeFamilies #-}
import Control.Monad.ST (ST, runST)
data D
class C t where
type M t :: * -> *
f :: t -> M t D
g :: (C t, M t ~ ST s) => t -> D
g x = runST (f x)
main = return ()
(Added example in response to comment by #cirdec)
{-# LANGUAGE TypeFamilies #-}
import Control.Monad.ST (ST, runST)
data D = D
class C t where
type M t :: * -> *
f :: t -> M t D
data T (m :: (* -> *)) = T
instance (Monad m) => C (T m) where
type M (T m) = m
f _ = return D
main = const (return ()) (runST (f T))
I then replace main with the following:
g x = runST (f x)
main = const (return ()) (g T)
By the looks of it, this should compile, as g T == runST (f T) by definition of g. But it does not. I assume g needs a signature but I'm not sure what it is.
(Added background in response to comment by #cirdec)
Basically in my code C is a class of datatypes that can be treated as monadic disjoint Int sets (I know there are packages on hackage already but my approach has a few more features). C has functions like union and find etc. The actual implementation of these differ depending on whether the user knows their size at creation time or whether they need to dynamically grow, hence the type class. However once these data types are created they can be roughly treated the same. All this occurs in monad code, generally ST or IO, but technically anything that's in the MonadRef will suffice. Then C has a function freeze of result type M t D, where D is some result datatype. For example, for IO freeze will have the type (C t) => t -> IO D but for ST freeze will look more like (C t) => t -> ST s D. In the latter case, one should be able to run runST on the result of freeze to get the raw result data.
The following file compiles for me:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
import Control.Monad.ST (ST, runST)
data D = D
class C t where
type M t :: * -> *
f :: t -> M t D
data T (m :: (* -> *)) = T
instance (Monad m) => C (T m) where
type M (T m) = m
f _ = return D
data Equal a b where Refl :: Equal a a
convert :: Equal f g -> f a -> g a
convert Refl v = v
data Box s where
Box :: C t => Equal (M t) (ST s) -> t -> Box s
g :: (forall s. Box s) -> D
g box = runST (case box of Box eq x -> convert eq (f x))
main = const (return ()) (g (Box Refl T))
{-# LANGUAGE Haskell2010 #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
class PoC m where
type Wrapper m :: * -> *
wrap :: l -> Wrapper m l
I'm working with haskell-src-exts, and I want to upgrade my AST to have freshly generatable labels. Since I want to do it in an extensible fashion, I created an interface like the code above. However, code to upgrade the AST doesn't work. I have the following:
upgrade :: forall f m l. (PoC m, Functor f) => f l -> f (Wrapper m l)
upgrade x = fmap (wrap :: l -> Wrapper m l) x
But with or without the use of ScopedTypeVariables, I get the same error:
/tmp/PoC.hs:10:19:
Could not deduce (Wrapper m ~ Wrapper m0)
from the context (PoC m, Functor f)
bound by the type signature for
upgrade :: (PoC m, Functor f) => f l -> f (Wrapper m l)
at /tmp/PoC.hs:9:12-69
NB: `Wrapper' is a type function, and may not be injective
The type variable `m0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Expected type: l -> Wrapper m0 l
Actual type: l -> Wrapper m l
In the first argument of `fmap', namely
`(wrap :: l -> Wrapper m l)'
In the expression: fmap (wrap :: l -> Wrapper m l) x
In an equation for `upgrade':
upgrade x = fmap (wrap :: l -> Wrapper m l) x
But I don't understand where GHC binds this m0. Is "may not be injective" the core of the problem here?
Non-injectivity is the problem, indeed. Injectivity would allow GHC to know that if f a ~ f b then a ~ b but since you can have two PoC m instances with the same choice of Wrapper m type it's hard to know what m was after you've solved for what Wrapper m is.
You should be able to fix it by adding an input term to wrap which contains the m type parameter. This can be a proxy argument which is never evaluated
data Proxy a = Proxy
class PoC m where
type Wrapper m :: * -> *
wrap :: proxy m -> l -> Wrapper m l
instance PoC () where
type Wrapper () = Maybe
wrap _ = return
aUnit :: Proxy ()
aUnit = Proxy
upgrade :: (PoC m, Functor f) => proxy m -> f l -> f (Wrapper m l)
upgrade aProxy x = fmap (wrap aProxy) x
I'm messing around with the SPECIALIZE pragma while trying to find a solution to this problem.
I came up with this example:
{-# LANGUAGE FlexibleContexts, GeneralizedNewtypeDeriving #-}
import Data.Vector
import qualified Data.Vector.Generic as V
class Foo a
newtype Phantom m = T Int deriving (Show)
instance (Foo m) => Num (Phantom m)
f :: (Num r, V.Vector v r) => v r -> v r -> v r
{-# SPECIALIZE f :: (Foo m) => Vector (Phantom m) -> Vector (Phantom m) -> Vector (Phantom m) #-}
f x y = V.zipWith (+) x y
main = print "hello"
which fails to compile (GHC 7.6.2) because
Forall'd constraint `Foo m' is not bound in RULE lhs.
Googling only turned up a couple of GHC bug reports from years ago. I didn't see anything about "forall'd constraints" while reading about SPECIALIZE or RULE. My specialize signature does seem less polymorphic than the original, and it satisfies the "if-and-only-if" rule.
replace with
{-# SPECIALIZE f :: (Num (Phantom m)) => Vector (Phantom m) -> Vector (Phantom m) -> Vector (Phantom m) #-}
and it will work. The r in Num r is Phantom m not m, thus you can't add the constraint Num m. This is logical--Num (Phantom m) does not imply Num m and you could get other instances under the open world assumption.
EDIT:
You actually don't need any constraint at all in this case
{-# SPECIALIZE f :: Vector (Phantom m) -> Vector (Phantom m) -> Vector (Phantom m) #-}
anyway, the basic problem if I understand what you are trying to do is that you can't constrain when you perform an optimization based on phantom type parameters.
I have some contrived type:
{-# LANGUAGE DeriveFunctor #-}
data T a = T a deriving (Functor)
... and that type is the instance of some contrived class:
class C t where
toInt :: t -> Int
instance C (T a) where
toInt _ = 0
How can I express in a function constraint that T a is an instance of some class for all a?
For example, consider the following function:
f t = toInt $ fmap Left t
Intuitively, I would expect the above function to work since toInt works on T a for all a, but I cannot express that in the type. This does not work:
f :: (Functor t, C (t a)) => t a -> Int
... because when we apply fmap the type has become Either a b. I can't fix this using:
f :: (Functor t, C (t (Either a b))) => t a -> Int
... because b does not represent a universally quantified variable. Nor can I say:
f :: (Functor t, C (t x)) => t a -> Int
... or use forall x to suggest that the constraint is valid for all x.
So my question is if there is a way to say that a constraint is polymorphic over some of its type variables.
Using the constraints package:
{-# LANGUAGE FlexibleContexts, ConstraintKinds, DeriveFunctor, TypeOperators #-}
import Data.Constraint
import Data.Constraint.Forall
data T a = T a deriving (Functor)
class C t where
toInt :: t -> Int
instance C (T a) where
toInt _ = 0
f :: ForallF C T => T a -> Int
f t = (toInt $ fmap Left t) \\ (instF :: ForallF C T :- C (T (Either a b)))
What is the type-class equivalent to the following existentially quantified dictionary, inspired by the Pipe type:
{-# LANGUAGE ExistentialQuantification, PolymorphicComponents #-}
data PipeD p = forall cat . PipeD {
isoI :: forall a b m r . Iso (->) (p a b m r) (cat m r a b),
categoryI :: forall m r . (Monad m) => CategoryI (cat m r) ,
monadI :: forall a b m . (Monad m) => MonadI (p a b m) ,
monadTransI :: forall a b . MonadTransI (p a b) }
The rough idea I'm going for is trying to say that given the (PipeLike p) constraint, we can then infer (MonadTrans (p a b), Monad (p a b m) and (using pseudo-code) (Category "\a b -> p a b m r").
The CategoryI and MonadI are just the dictionary equivalents of those type-classes that I use to express the idea that Category, Monad, and MonadTrans are (sort of) super-classes of this PipeLike type.
The Iso type is just the following dictionary storing an isomorphism:
data Iso (~>) a b = Iso {
fw :: a ~> b ,
bw :: b ~> a }
If this is indeed a type class, then the dictionary value is determined solely by the type p. In particular, the type cat is determined solely by p. This can be expressed using an associated data type. In a class definition, an associated data type is written like a data definition without a right-hand side.
Once you express cat as a type, the other members can easily be changed to type classes, as I've shown for Monad and MonadTrans. Note that I prefer to use explicit kind signatures for complicated kinds.
{-# LANGUAGE TypeFamilies, FlexibleInstances, UndecidableInstances #-}
class Pipe (p :: * -> * -> (* -> *) -> * -> *) where
data Cat p :: (* -> *) -> * -> * -> * -> *
isoI :: forall a b m r. Iso (->) (p a b m r) (Category p m r a b)
categoryI :: forall a b m. Monad m => CategoryI (Category p m r)
instance (Pipe p, Monad m) => Monad (p a b m)
instance Pipe p => MonadTrans (p a b)