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
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. TheMinimal
pragma forApplicative
tells you that you must implement one of these two methods, and that information is displayed in Haddock documentation.The actual definitions of
liftA2
,<*>
, andpure
are specific to the instances ofApplicative
. 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 ofGeneric
, but this only lets you add typeclass constraints, largely for convenience withderiving
.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: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 theinstance
implementations for concrete type constructors like[]
,ZipList
,Maybe
,StateT
, and so on.