简体   繁体   中英

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?

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 .

这可能呢?

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM