Reputation: 15829
Sorry for the terrible title. I'm trying to make an instance of Applicative
for a Monad
wrapping a type that is a Monoid
.
instance (Monad m, Monoid o) => Applicative (m o) where
pure x = return mempty
xm <*> ym = do
x <- xm
y <- ym
return $ x `mappend` y
This doesn't work; GCHi complains with:
Kind mis-match
The first argument of `Applicative' should have kind `* -> *',
but `m o' has kind `*'
In the instance declaration for `Applicative (m o)'
I realise that what I've written above may make no sense. Here is the context: I am trying to use the compos
abstraction as described in the paper A pattern for almost compositional functions. Taking this tree (using the GADT version of compos
; I've simplified it a lot):
data Tree :: * -> * where
Var :: String -> Expr
Abs :: [String] -> Expr -> Expr
App :: Expr -> [Expr] -> Expr
class Compos t where
compos :: Applicative f => (forall a. t a -> f (t a)) -> t c -> f (t c)
instance Compos Tree where
compos f t =
case t of
Abs ps e -> pure Abs <*> pure ps <*> f e
App e es -> pure App <*> f e <*> traverse f es
_ -> pure t
I'm going to write a lot of functions which descend the tree and return a list of say errors or a set of strings whilst also requiring state as it goes down (such as the binding environment), such as:
composFoldM :: (Compos t, Monad m, Monoid o) => (forall a. t a -> m o) -> t c -> m o
composFoldM f = ???
checkNames :: (Tree a) -> State (Set Name) [Error]
checkNames e =
case e of
Var n -> do
env <- get
-- check that n is in the current environment
return $ if Set.member n env then [] else [NameError n]
Abs ps e' -> do
env <- get
-- add the abstractions to the current environment
put $ insertManySet ps env
checkNames e'
_ -> composFoldM checkNames e
data Error = NameError Name
insertManySet xs s = Set.union s (Set.fromList xs)
I think these should all be able to be abstracted away by making composFoldM
use compos
for the (Monad m, Monoid o) => m o
structure. So to use it with the GADT Applicative
version of compos
found on page 575/576 of the paper. I think I need to make an Applicative
instance of this structure. How would I do this? Or am I going down completely the wrong path?
Upvotes: 5
Views: 370
Reputation: 35099
You want the Constant
applicative from Data.Functor.Constant
in the transformers
package, which you can find here.
This Applicative
has the following instance:
instance (Monoid a) => Applicative (Constant a) where
pure _ = Constant mempty
Constant x <*> Constant y = Constant (x `mappend` y)
You can then compose Constant
with any other applicative using Compose
from Data.Functor.Compose
(also in the transformers
package), which you can find here.
Compose
has this Applicative
instance:
instance (Applicative f, Applicative g) => Applicative (Compose f g) where
pure x = Compose (pure (pure x))
Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)
You can then Compose
your Constant
applicative with any other Applicative
(like State
) to keep both some state and a running Monoid
tally.
More generally, you should read the paper The Essence of the Iterator Pattern, which discusses these patterns in more detail.
Upvotes: 5