Description
Before #229 was merged, ap
depended on bind
and pure
. Now that #229 has been merged, ap
depends on bind
and map
.
This means that defining apply = ap
as well as map = liftA1
will cause loops. When you try to use map
with such a type, the following happens:
map
- is implemented via
liftA1
, - which calls
apply
, - which is implemented via
ap
, - ... which calls
map
, and we loop.
Effect is one such type; this was causing failures in purescript/purescript-refs#29 (comment).
I think the law changes in #229 were correct, but perhaps changing ap
to have a Bind
constraint rather than a Monad
constraint should be reverted? I think changing the rules for when it's safe to use these default implementation functions is probably too dangerous a breaking change to be justifiable. Maybe default implementation functions should only be allowed to use type class members from classes which are below the class which they are providing a default implementation for in the hierarchy? That is, it's fine for liftA1
to depend on both apply
and pure
, because they are members from Apply
and Applicative
respectively, which are both below Functor
. Also, it's fine for ap
to depend on both bind
and pure
, because they are from Bind
and Applicative
, which are both below Apply
. However, it's not fine for ap
to depend on map
, because that's from Functor
, which is above Apply
.