I've just written a function (for Data.Sequence)
traverseWithIndex :: Applicative f => (Int -> a -> f b) -> Seq a -> f (Seq b)
which should obey
traverseWithIndex f = sequenceA . mapWithIndex f
Thankfully, this is a straightforward mechanical modification of the source of mapWithIndex, so I am quite confident it is correct. However, in more complex cases thorough testing would be required. I'm trying to write a QuickCheck property to test this simple one. Obviously, I can't try it out with every Applicative functor! When testing monoids, it makes good sense to test with the free monoid over (i.e., finite lists of) some type. So it seems sensible here to test with the free applicative functor over some functor. There are two difficulties:
How do I choose an appropriate base functor? I presumably want a nasty one that isn't applicative or traversable or anything, but such a thing seems likely hard to work with.
How do I compare the results? They'll have functions in them, so they have no Eq instance.
Here's a partial(?) solution. The main aspects we want to check are 1) obviously the same value is computed, and 2) the effects are performed in the same order. I think the following code is self-explanatory enough:
{-# LANGUAGE FlexibleInstances #-}
module Main where
import Control.Applicative
import Control.Applicative.Free
import Data.Foldable
import Data.Functor.Identity
import Test.QuickCheck
import Text.Show.Functions -- for Show instance for function types
data Fork a = F a | G a deriving (Eq, Show)
toIdentity :: Fork a -> Identity a
toIdentity (F a) = Identity a
toIdentity (G a) = Identity a
instance Functor Fork where
fmap f (F a) = F (f a)
fmap f (G a) = G (f a)
instance (Arbitrary a) => Arbitrary (Fork a) where
arbitrary = elements [F,G] <*> arbitrary
instance (Arbitrary a) => Arbitrary (Ap Fork a) where
arbitrary = oneof [Pure <$> arbitrary,
Ap <$> (arbitrary :: Gen (Fork Int)) <*> arbitrary]
effectOrder :: Ap Fork a -> [Fork ()]
effectOrder (Pure _) = []
effectOrder (Ap x f) = fmap (const ()) x : effectOrder f
value :: Ap Fork a -> a
value = runIdentity . runAp toIdentity
checkApplicative :: (Eq a) => Ap Fork a -> Ap Fork a -> Bool
checkApplicative x y = effectOrder x == effectOrder y && value x == value y
succeedingExample = quickCheck (\f x -> checkApplicative
(traverse (f :: Int -> Ap Fork Int) (x :: [Int]))
(sequenceA (fmap f x)))
-- note reverse
failingExample = quickCheck (\f x -> checkApplicative
(traverse (f :: Int -> Ap Fork Int) (reverse x :: [Int]))
(sequenceA (fmap f x)))
-- instance just for example, could make a more informative one
instance Show (Ap Fork Int) where show _ = "<Ap>"
-- values match ...
betterSucceedingExample = quickCheck (\x ->
value (sequenceA (x :: [Ap Fork Int]))
== value (fmap reverse (sequenceA (reverse x))))
-- but effects don't.
betterFailingExample = quickCheck (\x -> checkApplicative
(sequenceA (x :: [Ap Fork Int]))
(fmap reverse (sequenceA (reverse x))))
The output looks like:
*Main Text.Show.Functions> succeedingExample
+++ OK, passed 100 tests.
*Main Text.Show.Functions> failingExample
*** Failed! Falsifiable (after 3 tests and 2 shrinks):
<function>
[0,1]
*Main Text.Show.Functions> betterSucceedingExample
+++ OK, passed 100 tests.
*Main Text.Show.Functions> betterFailingExample
*** Failed! Falsifiable (after 10 tests and 1 shrink):
[<Ap>,<Ap>]
Obviously, I can't try it out with every Applicative functor!
I'm reminded of this blog post series, which I won't claim to fully understand:
http://comonad.com/reader/2012/abstracting-with-applicatives/
http://comonad.com/reader/2013/algebras-of-applicatives/
The lesson that I recall drawing from this is that nearly every applicative functor you see in the wild turns out to be the composition, product or (restricted) coproduct of simpler ones like these (not meant to be exhaustive):
Const
Identity
(->)
So while you can't try it out with every Applicative functor, there are inductive arguments that you might be able to exploit in QuickCheck properties to gain confidence that your function works for large inductively-defined families of functors. So for example you could test:
Your function works correctly for the "atomic" applicatives of your choice;
If your function works correctly for functors f and g, it works correctly for Compose f g, Product f g and Coproduct f g.
How do I compare the results? They'll have functions in them, so they have no Eq instance.
Well, I think you may have to look at QuickCheck testing of function equality. Last time I had to do something along those lines I went with Conal's checkers library, which has an EqProp class for "[t]ypes of values that can be tested for equality, perhaps through random sampling." This should give you an idea already—even if you don't have an Eq instance for functions, QuickCheck may be capable of proving that two functions are unequal. Critically, this instance exists:
instance (Show a, Arbitrary a, EqProp b) => EqProp (a -> b)
...and any type that has an Eq instance has a trivial EqProp instance where (=-=) = (==).
So that suggests, to my mind, using Coyoneda Something as the base functor, and figuring out how to plug together all the little functions.
Related
I asked a similar question before (how to implement mapAccumM?).
I need the one which folds from the right as well (mapAccumR):
mapAccumRM :: (Monad m, Traversable t)
=> (a -> b -> m (a, c)) -> a -> t b -> m (a, t c)
Is there a simple implementation for this?
One approach is to define new instances of Traversable that use the ordering you like. For example, for lists, one might simply define a new type:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Applicative
import Data.Traversable
newtype BackwardsList a = BackwardsList [a]
deriving (Eq, Ord, Read, Show, Functor, Foldable)
instance Traversable BackwardsList where
traverse f (BackwardsList xs) = BackwardsList <$> go xs where
go [] = pure []
go (x:xs) = liftA2 (flip (:)) (go xs) (f x)
In ghci, we can see the difference between this and the standard instance:
> runState (traverse (\_ -> modify (+1) >> get) "hello, world!") 0
([1,2,3,4,5,6,7,8,9,10,11,12,13],13)
> runState (traverse (\_ -> modify (+1) >> get) (BackwardsList "hello, world!")) 0
(BackwardsList [13,12,11,10,9,8,7,6,5,4,3,2,1],13)
This approach is fairly simple; however, it requires a new type (and the associated newtype wrapping/unwrapping garbage) for every new traversal order that you are interested in.
One could consider defining a new type class for ordered traversals. Let's see one way this might be done. We'll need a little prelude:
{-# LANGUAGE Rank2Types, TypeFamilies #-}
import Control.Applicative
import Data.Foldable
import Data.Traversable
import Data.Tree
Most Haskell data types can be viewed as fixed-points of polynomial functors; and the functors that they are fixed-points of are good descriptions of the "spine" of the data structure. We'll abuse this idea to give a concrete way to encode the ordering that should be used during traversal. The class itself looks like this:
type Order t = forall f a. Applicative f => Spine t (f a) (f (t a)) -> f (t a)
class OrderedTraversable t where
data Spine t :: * -> * -> *
otraverse :: Applicative f => Order t -> (a -> f b) -> t a -> f (t b)
Notice that the type of otraverse looks just like the type of traverse, except that it now takes an extra ordering argument. The ordering argument is, in a sense, variadic; since different data types have different numbers of values/children at various places in their structure, and the ordering may care about all of them. (Of special interest is here is the technique of using rank-2 types to prevent an ordering from observing "too much" about a data structure: it can't use special facts about a given instance of Applicative or given kind of element to decide how to traverse a spine, only decisions based on the shape of the spine are allowed.) Let's see a simple example, for lists:
instance OrderedTraversable [] where
-- Cute hack: the normal presentation for the spine of a list uses both
-- a `Cons` and a `Nil`; but parametricity says the only thing an
-- `Order []` can do with a `Nil` is `pure []` anyway. So let's just
-- bake that into `otraverse`.
data Spine [] a r = Cons a r
otraverse order f = go where
go [] = pure []
go (x:xs) = order (Cons (f x) (go xs))
Compare with the implementation of Traversable for lists in the standard library (I have taken the liberty of expanding the definition of foldr to make it more closely match the code above):
instance Traversable [] where
traverse f = go where
go [] = pure []
go (x:xs) = (:) <$> f x <*> go xs
As you can see, the primary difference is that we have abstracted which function to use to combine f x and go xs. We can recover the standard Traversable instance with a "head-first" order. There is also a "last-first" order; and these are basically the only two orders that make sense for lists.
headFirst, lastFirst :: Order []
headFirst (Cons fx fxs) = liftA2 (:) fx fxs
lastFirst (Cons fx fxs) = liftA2 (flip (:)) fxs fx
In ghci, we can now see how they differ:
> runState (traverse (\_ -> modify (+1) >> get) "hello, world!") 0
([1,2,3,4,5,6,7,8,9,10,11,12,13],13)
> runState (otraverse headFirst (\_ -> modify (+1) >> get) "hello, world!") 0
([1,2,3,4,5,6,7,8,9,10,11,12,13],13)
> runState (otraverse lastFirst (\_ -> modify (+1) >> get) "hello, world!") 0
([13,12,11,10,9,8,7,6,5,4,3,2,1],13)
To give another example, here is how you might use this class with rose trees:
instance OrderedTraversable Tree where
data Spine Tree a r = SNode a [r]
otraverse order f = go where
go (Node x ts) = order (SNode (f x) (map go ts))
-- two example orders for trees
prefix, postfix :: Order [] -> Order Tree
prefix list (SNode fx fts) = liftA2 Node fx (otraverse list id fts)
postfix list (SNode fx fts) = liftA2 (flip Node) (otraverse list id fts) fx
Note that there are actually infinitely many "good" ordering functions for rose trees; two that are particularly likely to be what you want are included above.
Foldable is a superclass of Traversable, similarly to how Functor is a superclass of Applicative and Monad.
Similar to the case of Monad, where it is possible to basically implement fmap as
liftM :: Monad m => (a->b) -> m a -> m b
liftM f q = return . f =<< q
we could also emulate foldMap as
foldLiftT :: (Traversable t, Monoid m) => (a -> m) -> t a -> m
foldLiftT f = fst . traverse (f >>> \x -> (x,x))
-- or: . sequenceA . fmap (f >>> \x -> (x, x))
using the Monoid m => (,) m monad. So the combination of superclass and methods bears in both cases a certain redundancy.
In case of monads, it can be argued that a “better” definition of the type class would be (I'll skip applicative / monoidal)
class (Functor m) => Monad m where
return :: a -> m a
join :: m (m a) -> m a
at least that's what's used in category theory. This definition does, without using the Functor superclass, not permit liftM, so it is without this redundancy.
Is a similar transformation possible for the Traversable class?
To clarify: what I'm after is a re-definition, let's call it,
class (Functor t, Foldable t) => Traversable t where
skim :: ???
such that we could make the actual Traverse methods top-level functions
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
but it would not be possible to make generically
instance (Traversable t) => Foldable t where
foldMap = ... skim ...
data T
instance Traversable T where
skim = ...
I'm not asking because I need this for something particular; it's a conceptual question so as to better understand the difference between Foldable and Traversable. Again much like Monad vs Functor: while >>= is much more convenient than join for everyday Haskell programming (because you usually need precisely this combination of fmap and join), the latter makes it simpler to grasp what a monad is about.
Foldable is to Functor as Traversable is to Monad, i.e. Foldable and Functor are superclasses of Monad and Traversable (modulo all the applicative/monad proposal noise).
Indeed, that's already in the code
instance Foldable f => Traversable f where
...
So, it's not clear what more there is to want. Foldable is characterized by toList :: Foldable f => f a -> [a] while Traversable depends ultimately on not only being able to abstract the content as a list like toList does, but also to be able to extract the shape
shape :: Functor f => f a -> f ()
shape = fmap (const ())
and then recombine them
combine :: Traversable f => f () -> [a] -> Maybe (f a)
combine f_ = evalStateT (traverse pop f_) where
pop :: StateT [a] Maybe a
pop = do x <- get
case x of
[] = empty
(a:as) = set as >> return a
which depends on traverse.
For more information on this property see this blog post by Russell O'Connor.
Super hand-wavy because it's late, but the extra power that Traversable has over Foldable is a way to reconstruct the original structure. For example, with lists:
module MyTraverse where
import Data.Foldable
import Data.Traversable
import Control.Applicative
import Data.Monoid
data ListRec f x = ListRec
{ el :: f (Endo [x])
}
instance Applicative f => Monoid (ListRec f x) where
mempty = ListRec (pure mempty)
mappend (ListRec l) (ListRec r) =
ListRec (mappend <$> l <*> r)
toM :: Functor f => f b -> ListRec f b
toM this = ListRec $ (Endo . (:)) <$> this
fromM :: Functor f => ListRec f b -> f [b]
fromM (ListRec l) = flip appEndo [] <$> l
myTraverse :: Applicative f => (a-> f b) -> [a] -> f [b]
myTraverse f xs = fromM $ foldMap (toM . f) xs
I think this myTraverse behaves the same as traverse, using only the classes Applicative, Foldable, and Monoid. You could re-write it to use foldr instead of foldMap if you wanted to get rid of Monoid.
lists are easy because they're a flat structure. However, I strongly suspect that you could use a Zipper to get the proper reconstruction function for any structure (since zippers are generically derivable, they should always exists).
But even with a zipper, you don't have any way of indicating that structure to the monoid/function. Notionally, it seems Traversable adds something like
class Traversed t where
type Path t :: *
annotate :: t a -> [(Path t, a)]
fromKeyed :: [(Path t, a)] -> t a
this seems to overlap heavily with Foldable, but I think that's inevitable when trying to associate the paths with their constituent values.
Given two monads, Monad m and Monad n, I would like to transform m (n a) into n (m a). But there seems to be no generic way because both (>>=) and return deals with only one monad type, and although (>>=) allows extracting content from a monad, you must pack them back to the same monad type so it can be a result value.
However, if we set m to a fixed type, the job becomes easy. Take Maybe as an example:
reorder :: (Monad n) => Maybe (n a) -> n (Maybe a)
reorder Nothing = return Nothing
reorder (Just x) = do
x' <- x
return $ Just x'
Or a list:
reorder :: (Monad n) => [n a] -> n [a]
reorder [] = return []
reorder (x:xs) = do
x' <- x
xs' <- reorder xs
return (x':xs')
Not hard to see, we've got a pattern here. To be more obvious, write it in a Applicative way, and it's no more than applying the data constructor to each element:
reorder (Just x) = Just <$> x
reorder (x:xs) = (:) <$> x <*> (reorder xs)
My question is: does a haskell typeclass already exist to describe such operations, or do I have to invent the wheel myself?
I had a brief search in the GHC documentation, and found nothing useful for this topic.
Data.Traversable provides what you are looking for:
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
GHC even provides support for automatically deriving instances:
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
import Data.Foldable
import Data.Traversable
data List a = Nil | Cons a (List a)
deriving(Functor, Foldable, Traversable)
A quick search on hoogle for (Monad m, Monad n) => m (n a) -> n (m a) showed me that there are several functions that (roughly) comply with the signature you're looking for:
Data.Traversable.sequence :: (Traversable t, Monad m) => t (m a) -> m (t a);
Data.Traversable.sequenceA :: Applicative f => t (f a) -> f (t a);
Contro.Monad.sequence :: Monad m => [m a] -> m [a] (also exported by Prelude).
Both [a] and Maybe a are instances of traversable, so your reorder functions are just applications of Data.Traversable.sequence. One could write, in example:
ghci> (Data.Traversable.sequence $ Just (return 1)) :: IO (Maybe Int)
Just 1
it :: Maybe Int
ghci> (Data.Traversable.sequence $ Just ([1])) :: [Maybe Int]
[Just 1]
it :: [Maybe Int]
ghci> (Data.Traversable.sequence $ [Just 1]) :: Maybe [Int]
Just [1]
it :: Maybe [Int]
Please note however that the specific class declaration is class (Functor t, Foldable t) => Traversable t, and if you look also at the types of the other two functions, it does not seems like what you're looking for could possibly be done in a generic way for all monads m and n without restrictions/preconditions.
It can't be done in general: a good example of a monad that can't do this is the reader (or function) monad. That would require the following function to be definable:
impossible :: (r -> IO a) -> IO (r -> a)
It's not straightforward to prove that a function cannot be implemented. But intuitively, the problem is that whatever IO has to be done in the value returned has to be done before we know what the r parameter is. So impossible readFile would have to yield a pure function FilePath -> String before it knew which files to open. Clearly, at least, impossible can't do what you'd want it to.
Not all Monads can commute in that way. Edward Kmett's distributive package provides a typeclass Distributive for type constructors that is similar to what you desire (simplified):
class Functor g => Distributive g where
distribute :: Functor f => f (g a) -> g (f a)
collect :: Functor f => (a -> g b) -> f a -> g (f b)
Default definitions are provided for distribute and collect, written in terms of each other. I found this package by searching hayoo for the desired type signature.
Consider the following type:
data SomeType m a = SomeType (m Integer) [a]
We can easily make that type an instance of Functor with the following code:
instance Functor (SomeType m) where
fmap f (SomeType m lst) = SomeType m (map f lst)
However, if instead the params of the SomeType type were swapped:
data SomeType2 a m = SomeType2 (m Integer) [a]
Then the above instance definition doesn't work.
Is there some way of making SomeType2 an instance of Functor? If not, are there any up and coming additions to haskell/ghc that would make it possible?
Biased am I, but I think this is a great opportunity to make use of Control.Newtype, a little piece of kit that's a mere "cabal install newtype" away.
Here's the deal. You want to flip around type constructors to get your hands on functoriality (for example) in a different parameter. Define a newtype
newtype Flip f x y = Flip (f y x)
and add it to the Newtype class thus
instance Newtype (Flip f x y) (f y x) where
pack = Flip
unpack (Flip z) = z
The Newtype class is just a directory mapping newtypes to their unvarnished equivalents, providing handy kit, e.g. op Flip is the inverse of Flip: you don't need to remember what you called it.
For the problem in question, we can now do stuff like this:
data Bif x y = BNil | BCons x y (Bif x y) deriving Show
That's a two parameter datatype which happens to be functorial in both parameters. (Probably, we should make it an instance of a Bifunctor class, but anyway...) We can make it a Functor twice over: once for the last parameter...
instance Functor (Bif x) where
fmap f BNil = BNil
fmap f (BCons x y b) = BCons x (f y) (fmap f b)
...and once for the first:
instance Functor (Flip Bif y) where
fmap f (Flip BNil) = Flip BNil
fmap f (Flip (BCons x y b)) = Flip (BCons (f x) y (under Flip (fmap f) b))
where under p f is a neat way to say op p . f . p.
I tell you no lies: let us try.
someBif :: Bif Int Char
someBif = BCons 1 'a' (BCons 2 'b' (BCons 3 'c' BNil))
and then we get
*Flip> fmap succ someBif
BCons 1 'b' (BCons 2 'c' (BCons 3 'd' BNil))
*Flip> under Flip (fmap succ) someBif
BCons 2 'a' (BCons 3 'b' (BCons 4 'c' BNil))
In these circumstances, there really are many ways the same thing can be seen as a Functor, so it's right that we have to make some noise to say which way we mean. But the noise isn't all that much if you're systematic about it.
This isn't possible, and I don't think it will be anytime soon.
Order of type parameters is thus important. The last value is the one that you're going to "contain" for use with Functor, etc.
I tried to get this working by defining a type synonym that flipped the type parameters around and then used the TypeSynonymInstances extension, but it failed to work.
You can use a newtype wrapper which swaps the type parameters. But then you get a new type, and have to make a distinction of the original and the wrapped type in your code.
The dumb answer you already knew is: flip your parameters!
For GHC to support this sort of thing without any extra wrapping, you would need something like Type-level lambdas, which are probably not going to be added anytime soon. (I'd love to be proven wrong about that)
instance Functor (\a -> SomeType2 a m) where
-- fmap :: (a -> b) -> SomeType2 a m -> SomeType2 b m
fmap f (SomeType2 lst m) = SomeType (map f lst) m
Since we already have TypeSynonymInstances, we might be able to hope for PartiallyAppliedTypeSynonymInstances sometime slightly sooner than never.
type SomeType3 m a = SomeType2 a m
instance Functor (SomeType m) where
-- fmap :: (a -> b) -> SomeType3 m a -> SomeType3 m b
-- or, synonymously:
-- fmap :: (a -> b) -> SomeType2 a m -> SomeType2 a m
fmap f (SomeType2 lst m) = SomeType (map f lst) m
I've been trying to "learn me a Haskell" through the online book LYAH.
The author describes the behaviour of Functors of the Applicative type as sort of having the ability to extract a function from one functor and mapping it over a second functor; this is through the <*> function declared for the Applicative type class:
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
As a simple example, the Maybe type is an instance of Applicative under the following implementation:
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
An example of the behaviour mentioned previously:
ghci> Just (*2) <*> Just 10 -- evaluates to Just 20
so the <*> operator "extracts" the (*2) function from the first operand and maps it over the second operand.
Now in Applicative types, both operands of <*> are of the same type, so I thought as an exercise why not try implementing a generalisation of this behaviour, where the two operands are Functors of different types, so I could evaluate something like this:
Just (2*) <*:*> [1,2,3,4] -- should evaluate to [2,4,6,8]
So this is what I came up with:
import Control.Applicative
class (Applicative f, Functor g) => DApplicative f g where
pure1 :: a -> f a
pure1 = pure
(<*:*>) :: f ( a -> b ) -> g a -> g b -- referred below as (1)
instance DApplicative Maybe [] where -- an "instance pair" of this class
(Just func) <*:*> g = fmap func g
main = do putStrLn(show x)
where x = Just (2*) <*:*> [1,2,3,4] -- it works, x equals [2,4,6,8]
Now, although the above works, I'm wondering if we can do better; is it possible to give a default implementation for <*:*> that can be applied to a variety of f & g pairs, in the declaration for DApplicative f g itself? And this leads me to the following question: Is there a way to pattern match on constructors across different data types?
I hope my questions make some sense and I'm not just spewing nonsense (if I am, please don't be too harsh; I'm just an FP beginner up way past his bedtime...)
This does make sense, but it ends up being not particularly useful in its current form. The problem is exactly what you've noticed: there is no way to provide a default which does sensible things with different types, or to generally convert from f to g. So you'd have a quadratic explosion in the number of instances you'd need to write.
You didn't finish the DApplicative instance. Here's a 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 behaviors of both Maybe and [], because with Just it does what you expect, but with Nothing it returns nothing, an empty list.
So instead of writing DApplicative which takes two different types, what if you had a way of combining two applicatives f and g into a single type? If you generalize this action, you could then use a standard Applicative with the new type.
This could be done with the standard formulation of Applicatives as
liftAp :: f (g (a -> b)) -> f (g a) -> f (g b)
liftAp l r = (<*>) <$> l <*> r
but instead let's change Maybe:
import Control.Applicative
newtype MaybeT f a = MaybeT { runMaybeT :: f (Maybe a) }
instance (Functor f) => Functor (MaybeT f) where
fmap f (MaybeT m) = MaybeT ((fmap . fmap) f m)
instance (Applicative f) => Applicative (MaybeT f) where
pure a = MaybeT (pure (pure a))
(MaybeT f) <*> (MaybeT m) = MaybeT ( (<*>) <$> f <*> m)
Now you just need a way to convert something in the inner applicative, f, into the combined applicative MaybeT f:
lift :: (Functor f) => f a -> MaybeT f a
lift = MaybeT . fmap Just
This looks like a lot of boilerplate, but ghc can automatically derive nearly all of it.
Now you can easily use the 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 at Nothing may be surprising, but if you think of a list as representing indeterminism you can probably see how it could be useful. If you wanted the dual behavior of returning either Just [a] or Nothing, you just need a transformed List ListT Maybe a.
This isn't quite the same as the instance I wrote for DApplicative. The reason is because of the types. DApplicative converts an f into a g. That's only possible when you know the specific f and g. To generalize it, the result needs to combine the behaviors of both f and g as this implementation does.
All of this works with Monads too. Transformed types such as MaybeT are provided by monad transformer libraries such as mtl.
Your DApplicative instance for Maybe is not complete: What should happen if the first argument of <*:*> is Nothing?
The choice what to do for every combination isn't clear. For two lists <*> would generate all combinations: [(+2),(+10)] <*> [3,4] gives [5,6,13,14]. For two ZipLists you have a zip-like behaviour: (ZipList [(+2),(+10)]) <*> (ZipList [3,4]) gives [5,14]. So you have to choose one of both possible behaviours for a DApplicative of a list and a ZipList, there is no "correct" version.