2014-09-12 8 views
8

Wo sind die Applicative Transformatorklassen? Ich wollte Transformator Klassen für die applicative transformer stack in a previous answer verwenden, aber sie scheinen nicht zu existieren.Applikative Transformatorklassen

Das transformers Paket und viele andere sind voll von Transformatoren, die Applicative Struktur erhalten, auch wenn die zugrunde liegende Struktur kein Monad ist.

Ein kurzer Blick auf transformers hat Applicative Instanzen für die meisten Transformatoren.

Applicative f => Applicative (Backwards f) 
Applicative f => Applicative (Lift f) 
Applicative (ContT r m) 
Applicative m => Applicative (IdentityT m) 
Applicative m => Applicative (ReaderT r m) 
(Monoid w, Applicative m) => Applicative (WriterT w m) 
(Applicative f, Applicative g) => Applicative (Compose f g) 
(Applicative f, Applicative g) => Applicative (Product f g) 

Nur Transformatoren für Zustand und Wechsel (ExceptT und MaybeT) erfordern einen darunter liegenden monadisch für die Applicative Instanz.

(Functor m, Monad m) => Applicative (ExceptT e m) 
(Functor m, Monad m) => Applicative (MaybeT m) 
(Monoid w, Functor m, Monad m) => Applicative (RWST r w s m) 
(Functor m, Monad m) => Applicative (StateT s m) 

Es gibt eine Klasse für Monad Transformatoren. Ich kann sehen, wie etwas diese Monad Constraint erfordern könnte, da es anderswo nicht eingeführt werden kann.

class MonadTrans t where 
    lift :: (Monad m) => m a -> t m a 

Wo ist die Klasse für Applicative Transformatoren?

class ApTrans t where 
    liftAp :: (Applicative f) => f a -> t f a 

Oder einfach nur alte Transformatoren (obwohl ich mir keine Gesetze dafür vorstellen kann)?

class Trans t where 
    liftAny :: f a -> t f a 

Aufgrund des Unterschied nur in polymorphen Zwängen hat dieser typeclasses ein seltsames Varianz Muster. Abgesehen von ihren Gesetzen, die unvorhergesehene Einschränkungen berücksichtigen müssen, sollte alles, was eine Instanz von Trans ist, automatisch eine Instanz von ApTrans und MonadTrans sein, und alles, was eine Instanz von ApTrans ist, sollte automatisch eine Instanz von MonadTrans sein.

Wenn wir weiter zur mtl-Bibliothek gehen, sind die Klassen dort inkompatibel mit einem Applicative Transformatorstapel. Alle mir bekannten mtl-Klassen haben eine Monad Einschränkung. Zum Beispiel ist hier MonadReader

class Monad m => MonadReader r m | m -> r where 
    -- | Retrieves the monad environment. 
    ask :: m r 
    ask = reader id 

    -- | Executes a computation in a modified environment. 
    local :: (r -> r) --^The function to modify the environment. 
      -> m a  --^@[email protected] to run in the modified environment. 
      -> m a 

    -- | Retrieves a function of the current environment. 
    reader :: (r -> a) --^The selector function to apply to the environment. 
      -> m a 
    reader f = do 
     r <- ask 
     return (f r) 

Was ist der Zweck der Monad Einschränkung ist? Es macht MonadReader und die MonadReader Instanzen für viele der oben genannten Transformatoren inkompatibel mit Applicative Transformator-Stacks.

Ich würde naiv so etwas wie

class Reader r m | m -> r where 
    ask :: m r 
    local :: (r -> r) -> m a -> m a 

oder sogar Split local in eine eigene Klasse schreiben.

class Reader r m | m -> r where 
    ask :: m r 

class (Reader r m) => Local r m | m -> r where 
    local :: (r -> r) -> m a -> m a 

local könnte sehr schwierig sein, ohne Monad Instanz zu verwenden.Eine weitere nützliche Schnittstelle ohne die Monad Einschränkung würde irgendwo so etwas wie

class (Reader r m) => Local r m | m -> r where 
    local :: m (r -> r) -> m a -> m a 

Gibt es vorhandene Transformator Klassen, die die Monad Einschränkung nicht haben, oder gibt es einen tatsächlichen Bedarf für einen weiteren Transformator Klassenbibliothek?

+1

[Dies] (http://comonad.com/ Leser/2012/Abstracting-with-Applicatives /) ist es wert, über die Kombination von Applicates auf verschiedene Arten gelesen zu werden. – AndrewC

+0

@AndrewC Danke, ich wünschte, ich hätte das gelesen, bevor ich versuchte, die verknüpfte Frage zu beantworten. – Cirdec

+0

Ich denke, es gibt Anwendungstransformatoren, die nicht aus zusammensetzenden Anwendungen stammen. Definieren Sie zum Beispiel die Daten der monadischen Streams MStream m a = MStream (a, MStream m a). Dann ist "MStream Identity" ein "Applicative", und für jedes "Applicative m" ist "MStream m" ein "Applicative", und es gibt einen offensichtlichen "lift :: m a -> MStream m a" durch unendliche Wiederholung. Dennoch ist "MStream m" nicht die Zusammensetzung von "MStream" und "m"! (Übung, was ist das?) – Turion

Antwort

5

Wie J. Abrahamson sagte, sind Applicatives unter Produkten und Zusammensetzung geschlossen, so dass keine dedizierten Transformatorversionen benötigt werden. Allerdings gibt es auch keine Notwendigkeit, Ihre eigenen Applicative Produkt/Zusammensetzungstypen zu rollen, weil die Plattform bereits diese hat:

Ich habe festgestellt, dass das einfacher ist Art und Weise zu verwenden, diese mit der GeneralizedNewtypeDeriving Erweiterung, denn dann können Sie nur Typen definieren, wie diese:

newtype MyType m a = MyType (Compose (Const m) (Reader m) a) 
    deriving (Functor, Applicative) 

-- Plus a bunch of utility definitions to hide the use of Compose and generally 
-- keep you sane... 

Ein weiteres anderes nützliches Werkzeug in der Applicative Toolset sind das kostenlose applicative Funktors. Ich benutze normalerweise Edward Kmett's free library's version, aber es ist einfach, eigene zu rollen, wenn Sie weniger Abhängigkeiten wünschen.

können diese Definitionen auch nützlich sein (obwohl ich Vorschläge auf dem Namensschema begrüßen würde, vor allem die "I/O" Bit):

{-# LANGUAGE Rank2Types, TypeOperators #-} 

import Control.Applicative 
import Data.Functor.Compose 

-- | A handy infix type synonym for 'Compose', which allows us to 
-- stack 'Applicative's with less syntactic noise: 
-- 
-- > type CalculationT s p f = Reader (Frame s p) :. Reader (Cell s p) :. f 
-- > type Calculation s p = Calculation s p Identity 
-- 
-- Note that 'Identity' and ':.' form something a type-level monoid 
-- modulo @[email protected] equivalence. The following isomorphisms hold: 
-- 
-- > f :. Identity ~= Identity :. f ~= f 
-- > f :. g :. h ~= (f :. g) :. h 
-- 
type f :. g = Compose f g 
infixr :. 

-- | Lift an action from the outer functor into the composite. 
-- Alternative reading: append an 'Applicative' to the right of @[email protected] 
liftO :: (Functor f, Applicative g) => f a -> (f :. g) a 
liftO = Compose . fmap pure 

-- | Lift an action from the inner functor into the composite. 
-- Alternative reading: prepend an 'Applicative' to the left of @[email protected] 
liftI :: Applicative f => g a -> (f :. g) a 
liftI = Compose . pure 

-- | Lift a natural transformation from @[email protected] to @[email protected] into a morphism 
-- from @f :. [email protected] to @h :. [email protected] 
hoistO :: (forall x. f x -> h x) -> (f :. g) a -> (h :. g) a 
hoistO eta = Compose . eta . getCompose 

-- | Lift a natural transformation from @[email protected] to @[email protected] into a morphism 
-- from @f :. [email protected] to @f :. [email protected] 
hoistI :: Functor f => (forall x. g x -> h x) -> (f :. g) a -> (f :. h) a 
hoistI eta = Compose . fmap eta . getCompose 
+0

"Der Haufen von Utility-Funktionen, um den Gebrauch von Compose zu verbergen und gesund zu bleiben "Was bieten die mtl-Klassen für Monaden und was ich für Anwendungen suche? – Cirdec

+0

Es gibt auch 'mapI :: Functor f => (g a -> h b) -> (f:. G) a -> (f:. H) b 'wobei' mapI f = Compose. fmap f. getCompose'. mit dem Sie pfeilartige Funktionen heben können. 'mapI2 :: Applikativ f => (ga -> hb -> ic) -> (f:. g) a -> (f:. h) b -> (f:. i) c' wobei' mapI2 f (Verfassen fga) (Verfassen fhb) = Verfassen (fmap f fga <*> fhb) 'können Sie binäre Operatoren und Funktionen im Allgemeinen heben. – Cirdec

+0

Wenn ich richtig verstehe, werden Sie 'Data.Fuctor.Compose',' Data.Functor.Product', 'Data.Functor.Constant',' Data.Functor.Identity' und 'Control.Applicative.Lift', verallgemeinert Mittel zum Schreiben eines Monad-Transformers (wie Zustandsleser und -schreiber), der es erlaubt, 'GeneralisierteNewtypeDerivierung' zu verwenden? – akst

9

Applicative, im Gegensatz zu Monads, sind unter Produkten und Zusammensetzung geschlossen und benötigen daher keine spezielle Klasse von Dingen wie "Transformatoren". Hier ist eine kleine Bibliothek:

data (*) f g x = P (f x) (g x)  deriving Functor 
data C f g x = C (f (g x))  deriving Functor 

instance (Applicative f, Applicative g) => Applicative (f * g) where 
    pure a = P (pure a) (pure a) 
    P ff gf <*> P fx gx = P (ff <*> fx) (gf <*> gx) 

instance (Applicative f, Applicative g) => Applicative (C f g) where 
    pure = C . pure . pure 
    C fgf <*> C fgx = C (liftA2 (<*>) fgf fgx) 

Darüber hinaus ist alle Monaden sind applicatives so sollten wir in der Lage sein, diesen Code wiederverwenden. Leider zwingt das Fehlen von Subtyping von Applicative-Monad dazu, dass monadischer Code mehr Ausgrenzung erfordert als nötig und damit einen solchen Code verbietet. Es hätte korrigiert werden können, wenn alle diese Bibliotheken nach einer (Applicative m, Monad m) Einschränkung fragen würden, aber das tun sie nicht. Darüber hinaus, wie oft Sie sonst schreiben müssten

(MonadReader m, Monad m) => ... 

die Monad Superklasse Einschränkung ist bequem. Ich bin mir nicht sicher, dass es aber absolut notwendig ist.

+1

Ich habe nicht erkannt, dass die Schließung von 'Applicative' unter Komposition bedeutet, dass für die meisten Transformatoren' T', 'T f a' äquivalent zu' T Identity (f a) 'ist. Ich bin mir nicht sicher, ob das die Produkte abdeckt, da die 'Funktor'-Daten (*) f x1 = P (f x1) (x1)' wenn 'x1 ~ gx' nicht dasselbe ist wie dein' (*) fgx'. Dies birgt immer noch das Problem, den Kompositionsstapel geschickt zu durchdringen, was einfacher sein kann als das, was für Monad's getan wurde. Das erste, was ich versuchen würde, ist eine 'Generic's Stilklasse, die Dinge der Art' * -> * 'fragt, um ihre Struktur zu beschreiben und zu sehen, was damit erreicht werden kann. – Cirdec

+0

@Cirdec Siehe auch die [transformers] (http://hackage.haskell.org/package/transformers) -Bibliothek, die die [Zusammensetzung] definiert (http://hackage.haskell.org/package/transformers-0.4.1.0 /docs/Data-Functor-Compose.html), [Produkt] (http://hackage.haskell.org/package/transformers-0.4.1.0/docs/Data-Functor-Product.html) und [Summe] (http : //hackage.haskell.org/package/transformers-0.4.1.0/docs/Data-Functor-Sum.html) von Funktoren sowie ihre jeweiligen 'Applicative'-Instanzen. –

+2

@ PetrPudlák Bemerkenswerterweise schließen Anwendungen nicht über Summen, es sei denn, wir haben eine umgebende natürliche Transformation von "einer Seite zur anderen". Sie sind auch nicht-kanonisch, da sie eine Voreingenommenheit benötigen. –