2014-10-31 10 views
8

Dire che ho un tipo di dati A che è applicabile. (Per l'esempio, possiamo supporre che A sia Identity).Istanza applicativa per funzioni dallo stesso dominio a Applicativo

ora ho un nuovo tipo di dati che corrisponde alla "trasformazione" da un A all'altro:

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

voglio definire l'istanza applicativo banale per (B a) che produce una nuova trasformazione che si applica entrambi gli argomenti di <*> al suo ingresso e poi utilizza la definizione di < *> dall'istanza applicativo di A.

formulazione di questo è abbastanza semplice:

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 

Tuttavia, ho la sensazione che ci dovrebbe essere un modo semplice di scrivere in pointfree scrivendo il fatto che (-> a) è un Functional Applicativo.

Come un esempio di quello che ho in mente, prendere in considerazione la mia definizione del corrispondente istanza Functor:

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

Esiste un modo altrettanto semplice per definire l'istanza applicativo?

risposta

12

Uno dei fatti ordinate circa Applicative è che questa classe è chiusa in composizione. È possibile ottenere la seguente da Data.Functor.Compose:

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 

L'istanza Applicative per (->) a, che si mettono su, è questa:

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

Ora, cerchiamo di espandere Compose ff <*> Compose fa :: Compose ((->) (A a)) A b (alcuni passaggi saltati):

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

Quindi quello che stai facendo è effettivamente la composizione di (->) (A a) e A .

+1

Sarebbe corretto dire che 'B a b' nella domanda è equivalente a' tipo B a = Componi ((->) (A a)) A'? –

+0

@ChristianConkle: Sì. –

7

Questo, probabilmente?

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

Per la risposta di Luis Casillas: Se B è letteralmente il tipo di dati con cui stai lavorando, puoi semplicemente utilizzare Compose ((->) (A a)) A. Il costruttore di dati avrebbe tipo Compose :: (A a -> A b) -> Compose ((->) (A a)) A b.

È inoltre possibile utilizzare un sinonimo di tipo: type B a = Compose ((->) (A a)) A.

Puoi avere un sacco di simpatici smooshing functors insieme a Compose, Product, Sum e amici.

Problemi correlati