Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applicative instance for functions from same domain to Applicative

Say I have a data type A which is applicative. (For the sake of the example, we can assume A is Identity).

I now have a new data type that corresponds to the "transformation" from one A to another:

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

I want to define the trivial Applicative instance for (B a) that produces a new transformation which applies both arguments of <*> to its input and then uses the definition of <*> from the Applicative instance of A.

Formulating this is simple enough:

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

However, I have the feeling that there should be a straightforward pointfree way of writing this using the fact that (-> a) is an Applicative Functor.

As a sample of what I have in mind, consider my definition of the corresponding Functor instance:

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

Is there a similarly simple way to define the Applicative instance?

like image 630
DanielM Avatar asked Oct 31 '14 21:10

DanielM


3 Answers

One of the neat facts about Applicative is that this class is closed under composition. You can get the following from 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

The Applicative instance for (->) a, which you bring up, is this:

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

Now, let's expand Compose ff <*> Compose fa :: Compose ((->) (A a)) A b (some steps skipped):

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

So what you're doing is effectively the composition of (->) (A a) and A.

like image 169
Luis Casillas Avatar answered Sep 29 '22 09:09

Luis Casillas


This, probably?

(B ftrans) <*> (B xtrans) = B ((<*>) <$> ftrans <*> xtrans)
like image 21
user3237465 Avatar answered Sep 29 '22 10:09

user3237465


To piggy-back on Luis Casillas's answer: If B is literally the data type you're working with, you can simply use Compose ((->) (A a)) A instead. The data constructor would have type Compose :: (A a -> A b) -> Compose ((->) (A a)) A b.

You can also use a type synonym: type B a = Compose ((->) (A a)) A.

You can have a lot of fun smooshing functors together with Compose, Product, Sum, and friends.

like image 38
Christian Conkle Avatar answered Sep 29 '22 11:09

Christian Conkle