This makes sense, but ultimately it is not particularly useful in its current form. The problem is that you noticed: there is no way to provide a default value that does reasonable things with different types, or even convert from f to g . Thus, you will have a quadratic explosion in the number of copies that you need to write.
You have not finished the DApplicative instance. Here's the full implementation for Maybe and [] :
instance DApplicative Maybe [] where -- an "instance pair" of this class (Just func) <*:*> g = fmap func g Nothing <*:*> g = []
This combines the behavior of Maybe as well as [] , because with Just it does what you expect, but with Nothing it returns nothing, an empty list.
So, instead of writing a DApplicative that accepts two different types, what if you have a way to combine the two applications f and g into one type? If you generalize this action, you can use the standard Applicative with a new type.
This can be done with the standard formulation of Applicability as
liftAp :: f (g (a -> b)) -> f (ga) -> f (gb) liftAp lr = (<*>) <$> l <*> r
but change Maybe instead:
import Control.Applicative newtype MaybeT fa = MaybeT { runMaybeT :: f (Maybe a) } instance (Functor f) => Functor (MaybeT f) where fmap f (MaybeT m) = MaybeT ((fmap . fmap) fm) instance (Applicative f) => Applicative (MaybeT f) where pure a = MaybeT (pure (pure a)) (MaybeT f) <*> (MaybeT m) = MaybeT ( (<*>) <$> f <*> m)
Now you just need to convert something into the internal application f into the combined applicative MaybeT f :
lift :: (Functor f) => fa -> MaybeT fa lift = MaybeT . fmap Just
It looks like a lot of templates, but ghc can automatically output almost everything.
Now you can easily use combined functions:
*Main Control.Applicative> runMaybeT $ pure (*2) <*> lift [1,2,3,4] [Just 2,Just 4,Just 6,Just 8] *Main Control.Applicative> runMaybeT $ MaybeT (pure Nothing) <*> lift [1,2,3,4] [Nothing,Nothing,Nothing,Nothing]
This behavior in Nothing can be surprising, but if you think of the list as representing indeterminism, you can probably see how this can be useful. If you want double behavior to return either Just [a] or Nothing , you just need a converted ListT Maybe a list.
This is not exactly the same as the instance I wrote for DApplicative . The reason is related to types. DApplicative converts a f to g . This is only possible when you know the specific f and g . To generalize it, the result must combine the behavior of both f and g , as the implementation does.
All this works with Monads. Converted types such as MaybeT are provided by monad transformer libraries such as mtl .