2013-08-17 46 views
5

Przepraszamy za okropny tytuł. Próbuję wykonać instancję Applicative dla zawijania Monad typu, który jest Monoid.Applicative Instance dla (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 

To nie działa; GCHi narzeka na:

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

Zdaję sobie sprawę, że to, co napisałem powyżej, może nie mieć sensu. Oto kontekst: próbuję użyć abstrakcji compos, jak opisano w artykule A pattern for almost compositional functions. Biorąc to drzewa (przy użyciu wersji GADT z compos; uprościłem to dużo):

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 

zamierzam napisać wiele funkcji, które schodzą z drzewa i zwróci listę mówią błędów lub zestaw strun podczas gdy również wymagający stan jak to idzie w dół (takich jak środowisko wiązanie), takich jak:

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) 

myślę, że to powinny być w stanie być wydobywane z dala poprzez composFoldM korzystanie compos dla struktury (Monad m, Monoid o) => m o. Aby więc użyć go z wersją GADT Applicative z compos znalezioną na stronie 575/576 z the paper. Myślę, że muszę utworzyć instancję Applicative tej struktury. Jak to zrobić? Czy też idę całkowicie niewłaściwą ścieżką?

Odpowiedz

5

Chcesz aplikatora Constant z Data.Functor.Constant w pakiecie transformers, który można znaleźć here.

Ten Applicative ma następujący przypadek:

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

można następnie skomponować Constant z jakimkolwiek innym aplikacyjnych z wykorzystaniem Compose z Data.Functor.Compose (również w pakiecie transformers), które można znaleźć here.

Compose ma ten Applicative instancję:

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

Można wtedy Compose Twój Constant aplikacyjnych z jakimkolwiek innym Applicative (jak State), aby utrzymać zarówno jakiś stan i uruchomioną Monoid zgadzają.

Bardziej ogólnie, należy przeczytać artykuł The Essence of the Iterator Pattern, który omawia te wzory bardziej szczegółowo.

+0

To wygląda na to, czego potrzebuję! Ale w jaki sposób mogę go używać? Próbowałem robić rzeczy takie jak 'composFoldM f = getCompose. compos (Compose, WrapMonad, Const. f) 'ale to nie działa. Czy są jakieś przykłady/objaśnienia, jak łączyć funktory? –

+0

Mój Boże. W końcu udało mi się to osiągnąć dzięki próbom i ulepszeniom. Chyba tak się uczysz! Właściwą rzeczą jest 'composFoldM f = liftM getConst. odwijaj Monoad. getCompose. compos (kompilacja: WrapMonad. liftM Const. f) '. : D –

+1

@CallumRogers Dokładnie tak! To jedna z miłych rzeczy na temat Haskella: kontroler zawsze poprowadzi cię do właściwego rozwiązania. –