5

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?

Callum Rogers
  • 15,630
  • 17
  • 67
  • 90

1 Answers1

5

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.

Gabriella Gonzalez
  • 34,863
  • 3
  • 77
  • 135
  • This looks like the thing I need! But how do I actually use it? I tried to do things like `composFoldM f = getCompose . compos (Compose . WrapMonad . Const . f)` but this doesn't work. Are there any examples/explanations of how to combine functors? – Callum Rogers Aug 18 '13 at 05:37
  • My god. I finally worked it out through trial and improvement. I guess this is how you learn! The right thing is `composFoldM f = liftM getConst . unwrapMonad . getCompose . compos (Compose . WrapMonad . liftM Const . f)`. :D – Callum Rogers Aug 18 '13 at 06:05
  • 1
    @CallumRogers That's exactly right! That's one of the nice things about Haskell: the type-checker will always steer you towards the correct solution. – Gabriella Gonzalez Aug 18 '13 at 15:09
  • Indeed, one of my favourite things about Haskell. – Callum Rogers Aug 19 '13 at 01:18
  • `Constant` also exists as `Const` in `Control.Applicative`. Which one should be used? – ipsec Aug 19 '13 at 18:29
  • @ipsec I'm not completely sure. If I had to pick then probably `Const` since ` Control.Applicative` is in `base`. – Gabriella Gonzalez Aug 19 '13 at 19:02
  • I used `Const` as it's part of base library. They have exactly the same semantics. – Callum Rogers Aug 19 '13 at 22:44