2014-10-31 6 views
8

Angenommen, ich habe einen Datentyp A, die Anwendung ist. (Für das Beispiel können wir annehmen, dass AIdentity ist).Anwendbare Instanz für Funktionen von der gleichen Domäne zu Applicative

Ich habe nun einen neuen Datentyp, der von einem A zum anderes zu der „Transformation“ entspricht:

data B a b = B (A a -> A b) 

I die triviale Applicative Instanz für (B a) definiert werden soll, der eine neue Transformation erzeugt, die beiden Argumente gelten von <*> an seinem Eingang und verwendet dann die Definition von < *> von der Applicative Instanz von A.

formulieren dies einfach genug ist:

instance Applicative (B a) where 
    pure x = B $ const $ pure x 

    (B ftrans) <*> (B xtrans) = B fxtrans 
     where fxtrans inp = let fout = ftrans inp 
           xout = xtrans inp 
          in fout <*> xout 

Allerdings habe ich das Gefühl, dass es eine einfache, pointfree Möglichkeit geben sollte, dies zu schreiben, indem man die Tatsache verwendet, dass (-> a) ein Applicative Functor ist.

Als eine Probe von dem, was ich im Sinne habe, meine Definition der entsprechenden Functor Instanz betrachten:

instance Functor (B a) where 
    fmap f (B xtrans) = B $ (fmap f) <$> xtrans 

Gibt es eine ähnlich einfache Weise die Applicative Instanz zu definieren?

Antwort

12

Einer der ordentlichen Fakten über Applicative ist, dass diese Klasse unter Komposition abgeschlossen ist. Sie können die folgenden von Data.Functor.Compose erhalten:

newtype Compose f g a = Compose { getCompose :: f (g a) } 

instance (Functor f, Functor g) => Functor (Compose f g) where 
    fmap f (Compose fga) = Compose (fmap (fmap f) fga) 

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

Die Applicative Instanz für (->) a, die Sie bringen, ist dies:

instance Applicative ((->) r) where 
    pure = const 
    ff <*> fa = \r -> let f = ff r 
          a = fa r 
         in f a 

Jetzt wollen wir Compose ff <*> Compose fa :: Compose ((->) (A a)) A b erweitern (einige Schritte übersprungen):

Compose ff <*> Compose fa 
    == Compose $ (<*>) <$> ff <*> fa 
    == Compose $ \r -> let f = ff r 
          a = fa r 
         in f <*> a 

Also was Sie tun, ist effektiv die Zusammensetzung (->) (A a) und A .

+1

Wäre es richtig zu sagen, dass "B a b" in der Frage äquivalent zu "Typ B a = Compose ((->) (A a)) A" ist? –

+0

@ChristianConkle: Ja. –

7

Dies, wahrscheinlich?

(B ftrans) <*> (B xtrans) = B ((<*>) <$> ftrans <*> xtrans) 
2

Um auf Luis Casillas 'Antwort zu huckepack zu gehen: Wenn B buchstäblich der Datentyp ist, mit dem Sie arbeiten, können Sie einfach stattdessen verwenden. Der Datenkonstruktor würde den Typ Compose :: (A a -> A b) -> Compose ((->) (A a)) A b haben.

Sie können auch ein Typen-Synonym verwenden: type B a = Compose ((->) (A a)) A.

Sie können eine Menge Spaß smooshing Funktoren zusammen mit Compose, Product, Sum und Freunde haben.

Verwandte Themen