Closed Shimuuar closed 4 years ago
Thanks for bringing this up! I too saw ProductB
as a stronger "applicative", but was somehow convinced that we were in need of the the uniqueness properties that follow from the laws most of the time, so adding an intermediate ApplicativeB
class was not worth it.
So I went to see those places where I thought we were using this, and ended up with the exact opposite conclusion: ProductB
just captures those types whose unique "applicative" instance can be generated automatically, but I don't think we are actually using the extra "uniqueness" guarantees anywhere else. This is great, I wished I had noticed it before :slightly_smiling_face:
So, how should an ApplicativeB
class look like? I don't think we can have a direct equivalent of <*>
(apply
) without something like impredicative-types. So without apply
, this would be probably better named MonoidalB
.
class FunctorB b => MonoidalB b where
bunit :: b Unit
bprod :: b f -> b g -> b (f `Product` g)
With laws (following Applicative programming with effects)
Naturality of bprod
:
bmap (hf ** hg) (bf `bprod` bg) = bmap hf bf `bprod` bmap hg bg
where
(**) :: (forall a. f a -> f' a)
-> (forall a. g a -> g' a)
-> (f `Product` g) a
-> (f' `Product` g') a
hf ** hg (Pair fa ha) = Pair (hf fa) (hg ga)
Left/right identity:
bmap (\(Pair _ ga) -> ga) (bunit `bprod` bg) = bg
bmap (\(Pair fa _) -> fa) (bf `bprod` bunit) = bf
Associativity:
bmap (\(Pair fa (Pair ga ha)) -> Pair (Pair fa ga) ha) (bf `bprod` (bg `bprod` bh)) = (bf `bprod` bg) `bprod` bh
I think ProductB
is a different kinded applicative (provided that we relax its laws). We can't define analog of <*>
so have to use bprod
. And buniq
and bunit
are equivalent:
bunit = buniq Unit
buniq f = bmap (\_ -> f) bunit
Should we use buinq
we'll have to change identity laws. This should work I think:
bmap (\(Pair _ ga) -> ga) (buniq Unit `bprod` bg) = bg
bmap (\(Pair fa _) -> fa) (bf `bprod` buniq Unit) = bf
So we already have an applicative. Its name obscures in connection to Applicative type class and maybe it should be renamed but I have no opinion on that. All in all only thing needed to add Applicative is to change documentation.
P.S. It always amazed me how much one can deduce just by looking at documentation!
This is now fixed on master
. ProductB
was renamed to ApplicativeB
(also ProductBC
got removed, wasn't really needed these days). I think I managed to do this without completely breaking backwards compatibility: Data.Barbie
is now deprecated but should be exposing the previous API.
Release 2.0.0.0
includes these changes
This issue didn't arise from any practical problem. I just studied library and noticed that laws for ProductB are overly strict. Let me explain why
Let start from
Applicative
:But we can rewrite it in slightly different but equivalent form:
And
ProductB
look exactly like second variant of Applicative except for different kind. If we look at ProductB as different-kinded applicative thenBad
from documentation admits writer-like instance:In fact we can turn any Applicative instance into ProductB instance (admittedly quite useless):
I didn't checked how applicative laws translate for ProductB but I don't expect any problems