Reputation: 2821
On stackage.org, the following cyclic declaration exists for liftA2
and <*>
for the Applicative
typeclass.
(<*>) = liftA2 id
liftA2 f x y = f <$> x <*> y
Is a non-cyclic declaration available for liftA2
or <*>
on the site. Are such completely cyclic references an oversight?
UPDATE:
The following (necessary) clarifying declarations seems to be missing from hoogle
docs:
<*> :: Functor F => F (a -> b) -> F a -> F b
and by implication (due to cyclic declaration)
liftA2 :: Functor F => (a -> b -> c) -> F a -> F b -> F c
Upvotes: 1
Views: 235
Reputation: 55049
[…] the following cyclic definition exists for
liftA2
and<*>
for theApplicative
type class.(<*>) = liftA2 id liftA2 f x y = f <$> x <*> y
Is a non-cyclic definition available for
liftA2
or<*>
on the site.
These aren’t definitions of the methods, exactly; they’re default definitions. A typeclass with one parameter is just a set of types, and an instance
definition is the price of admission for membership in the set. The Minimal
pragma for Applicative
tells you that you must implement one of these two methods, and that information is displayed in Haddock documentation.
The actual definitions of liftA2
, <*>
, and pure
are specific to the instances of Applicative
. Generally speaking, if a typeclass contains methods that can be implemented using only the other methods of the typeclass, then that method doesn’t strictly need to be part of the class, since it could be a top-level definition with a constraint.
However, such a method may be included anyway. This may be just for convenience, when it’s easier to define an instance in terms of one function even though it’s not the “most fundamental” operation. It’s also often for performance reasons: redundant methods tend to be included when it’s possible to implement a method more efficiently than the default for a particular type. In this case, for example, liftA2
may be able to traverse two structures together more efficiently than traversing one with <$>
and then the other separately with <*>
.
GHC also offers DefaultSignatures
as a way to add more specific defaults, typically defined in terms of Generic
, but this only lets you add typeclass constraints, largely for convenience with deriving
.
Are such completely cyclic references an oversight?
Not at all, they’re intentional. Cyclic definitions in default implementations of typeclass methods are quite common. For example, Eq
is defined in the Haskell Report something like this:
class Eq a where
(==) :: a -> a -> Bool
x == y = not (x /= y)
(/=) :: a -> a -> Bool
x /= y = not (x == y)
It is possible to forget to implement one of these, so that they both use the default, and thus represent an infinite loop, however:
This generates a warning by default (-Wmissing-methods
is enabled by -Wdefault
).
If a Minimal
pragma is not specified, all of the methods in the class are assumed to be required.
So really the only other options in this case are to remove one or the other from the class, or omit providing a default for one of them. If you want to know about how these methods are implemented for Applicative
, the thing to look at is the instance
implementations for concrete type constructors like []
, ZipList
, Maybe
, StateT
, and so on.
Upvotes: 6
Reputation: 477210
Is a non-cyclic definition available for liftA2?
The implementations of (<*>)
and liftA2
are specific to the instances of the Applicative
typeclass. Indeed, for each Applicative
instance you need to implement pure
, and (<*>)
or liftA2
, or as specified by the MINIMAL
pragma:
class Functor f => Applicative f where {-# MINIMAL pure, ((<*>) | liftA2) #-} -- …
For example for the Maybe
instance of Applicative
, this is implemented as:
instance Applicative Maybe where pure = Just Just f <*> m = fmap f m Nothing <*> _m = Nothing liftA2 f (Just x) (Just y) = Just (f x y) liftA2 _ _ _ = Nothing Just _m1 *> m2 = m2 Nothing *> _m2 = Nothing
Simply implementing pure
and (<*>)
would here be sufficient, since then liftA2
is implemented in terms of (<*>)
. It is however often more efficient to implement the other methods as well.
Upvotes: 3