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)
Related
Data.Constraint.Forall provides some quantification over constraints, however I fail to see how it can be used. Consider the following:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Forall where
import Prelude
import Data.Constraint.Forall
class Monoid (f a) => MonoidalFunctor f a
testfun :: Forall (MonoidalFunctor f) => (a -> f a) -> [a] -> f a
testfun = foldMap
testfun' :: Monoid (f a) => (a -> f a) -> [a] -> f a
testfun' = foldMap
I thought testfun would typecheck because Forall (MetaMonoid f) would work like forall a. Metamonoid f a, implying forall a. Monoid (f a) because of superclass constraint, but it doesn't.
Why does it not work and is there any workaround? I want to avoid having to write a lot of constraints like MyClass (f MyData) for different MyData types in my function where I know that any useful f will have instances for any f MyData anyway.
Use inst
inst :: forall p a. Forall p a :- p a
inst witness that, if you have forall a. p a, then you can set a to whatever you please and get p a out.
An entailment (:-) is
newtype a :- b = Sub (a => Dict b)
data Dict a = a => Dict
so, by pattern-matching on it, you can reveal the instance within it:
testfun :: forall f a. Forall (MonoidalFunctor f) => (a -> f a) -> [a] -> f a
testfun = case inst #(MonoidalFunctor f) #a of Sub Dict -> foldMap
(type applications/signatures necessary) or you can use (\\):
(\\) :: a => (b => r) -> a :- b -> r
testfun = foldMap \\ inst #(MonoidalFunctor f) #a
which reads "given that a is true and a value of r that also needs b to be true, followed by a value that can prove that b is true given a, make an r. If you rearrange a bit
(\\) :: (b => c) -> (a :- b) -> (a => c)
it looks quite a bit like function composition.
The reason for this dance is simply because it's beyond the reach of GHC to infer that Forall c means it can derive c a for any a; after all, that's why constraints exists. So, you have to be a bit more explicit about it.
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.
When working with a Bifunctor, we gain access to the first and second "map" functions. So basically it is a Functor that allows us to fmap in two different ways.
Is there something like this for Monoid? Some concept that allows us to append in two different ways?
For example, imagine an opaque Matrix type. It is not a list of lists or a vector of vectors, we have no clue how it is structured inside, but we know that we can append rows and columns to it.
Would there be some type class that allowed to do this?
class X a where
firstAppend :: a -> a -> a
secondAppend :: a -> a -> a
instance X Matrix where
firstAppend = appendRow
secondAppend = appendColumn
I guess you could do something like this with indexed Monoids:
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
module IndexedMonoids where
class MonoidIx (m :: k -> *) where
type Null m :: k
type Mult m (i :: k) (j :: k) :: k
nullIx :: m (Null m)
multIx :: m i -> m j -> m (Mult m i j)
class MonoidIx2 (m :: k -> l -> *) where
type Null1 m :: k
type Null2 m :: l
type Mult1 m (i :: k) (j :: k) :: k
type Mult2 m (p :: l) (q :: l) :: l
null1Ix :: m (Null1 m) p
null2Ix :: m i (Null2 m)
mult1Ix :: m i p -> m j p -> m (Mult1 m i j) p
mult2Ix :: m i p -> m i q -> m i (Mult2 m p q)
You'd expect a bunch of laws (identity, associativity, commutativity when you put 4 blocks together). A trivial example of an indexed Monoid: the one where the index does not matter:
newtype Dummy (m :: *) (i :: k) = Dummy { getDummy :: m }
instance Monoid m => MonoidIx (Dummy m :: * -> *) where
type Null (Dummy m) = ()
type Mult (Dummy m) i j = ()
nullIx = Dummy mempty
multIx (Dummy i) (Dummy j) = Dummy $ mappend i j
I'll let you implement the instance for matrices ;)
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))
I'm trying to run the code from:
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.39.8039&rep=rep1&type=pdf
using ghci 7.6.3
{-# LANGUAGE LiberalTypeSynonyms, TypeSynonymInstances #-}
type C m a = (a -> Action m) -> Action m
data Action m = Atom (m (Action m)) | Fork (Action m) (Action m) | Stop
This original form:
instance (Monad m) => Monad (C m) where
f >>= k = \c -> f (\a -> k a c)
return x = \c -> c x
gives this error:
Type synonym `C' should have 2 arguments, but has been given 1
In the instance declaration for `Monad (C m)'
Trying with the additional argument:
instance (Monad m) => Monad (C m b) where
f >>= k = \c -> f (\a -> k a c)
return x = \c -> c x
shows this error:
Kind mis-match
The first argument of `Monad' should have kind `* -> *',
but `C m b' has kind `*'
In the instance declaration for `Monad (C m b)'
How to correct this definition? Thanks
Partially applied type synonyms can't be type class instances, and the only way to avoid that in this case is to make this a data or newtype declaration.
You will have to change the definition of C to make this work to e.g.
newtype C m a = C ((a -> Action m) -> Action m)