2013-08-17 3 views
5

Извините за ужасное название. Я пытаюсь сделать экземпляр Applicative для Monad обертывания типа Monoid.Аппликационная инстанция для (Monad m, Monoid o) => m o?

instance (Monad m, Monoid o) => Applicative (m o) where 
    pure x = return mempty 
    xm <*> ym = do 
     x <- xm 
     y <- ym 
     return $ x `mappend` y 

Это не работает; GCHi жалуется на:

Kind mis-match 
The first argument of `Applicative' should have kind `* -> *', 
but `m o' has kind `*' 
In the instance declaration for `Applicative (m o)' 

Я понимаю, что то, что я написал выше, не имеет смысла. Вот контекст: я пытаюсь использовать абстракцию compos, как описано в статье A pattern for almost compositional functions. Исходя из этого дерева (используя версию GADT из compos, я упростил это много):

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 

Я собираюсь написать много функций, которые спускаются по дереву и возвращает список ошибок, говорят или набор строк в то время и требует состояние, как она идет вниз (например, связующей среды), таких как:

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) 

Я думаю, что они все должны быть в состоянии абстрагируются путем composFoldM использования compos для (Monad m, Monoid o) => m o структуры. Поэтому использовать его с GADT Applicative версии compos см. На странице 575/576 из the paper. Я думаю, что мне нужно создать экземпляр этой структуры. Как мне это сделать? Или я полностью опускаю неправильный путь?

ответ

5

Вы хотите получить Constant от Data.Functor.Constant в пакете transformers, в котором вы найдете here.

Это Applicative имеет следующий пример:

instance (Monoid a) => Applicative (Constant a) where 
    pure _ = Constant mempty 
    Constant x <*> Constant y = Constant (x `mappend` y) 

Вы можете сочинить Constant с любым другим Applicative с помощью Compose из Data.Functor.Compose (также в transformers пакете), который вы можете найти here.

Compose имеет этот Applicative экземпляр:

instance (Applicative f, Applicative g) => Applicative (Compose f g) where 
    pure x = Compose (pure (pure x)) 
    Compose f <*> Compose x = Compose ((<*>) <$> f <*> x) 

Вы можете Compose ваш Constant аппликативны с любым другим Applicative (как State), чтобы сохранить как некоторое состояние и работает Monoid бирку.

В более общем плане вы должны прочитать статью The Essence of the Iterator Pattern, в которой более подробно рассматриваются эти шаблоны.

+0

Это похоже на то, что мне нужно! Но как я действительно использую его? Я пытался делать такие вещи, как 'composFoldM f = getCompose. составьте (Compose. WrapMonad. Const. f) ', но это не сработает. Есть ли примеры/объяснения того, как объединить функторы? –

+0

Мой бог. Я, наконец, проработал это через испытание и совершенствование. Наверное, так вы учитесь! Правильная вещь - 'compFoldM f = liftM getConst. распаковатьМонад. getCompose. Состав (Compose. WrapMonad. liftM Const. f) '. : D –

+1

@CallumRogers Это точно! Это одна из приятных вещей в Haskell: контролер типов всегда будет направлять вас к правильному решению. –