Open masaeedu opened 2 years ago
I considered something like lenses
in the past (maybe for any functor, not just Identity
?) but I'm not sure I see usages that are not covered well by generic-lens
already. Do you have scenarios where you'd like to operate on all lenses at once?
For btraverse1
, would one need a new class? E.g., can't one derive it from btraverse
and an auxiliary newtype wrapper that pretends an Apply
is actually an Applicative
, or something like that?
For
btraverse1
, would one need a new class? E.g., can't one derive it frombtraverse
and an auxiliary newtype wrapper that pretends anApply
is actually anApplicative
, or something like that?
@jcpetruzza That makes sense at a high level, but I haven't been able to make it work. I think the relevant newtype is MaybeApply f a = Either (f a) a
(where Apply f => Applicative (MaybeApply f)
). But in order to convert a traversal over MaybeApply f
into a traversal over f
itself, you end up needing an operation Apply f => Either (f (b g)) (b g) -> f (b g)
, and I'm honestly not sure how to supply that in general for a "TraversableB
HKD with at least one position".
You can instead make it work with something like this as the primitive operation:
bhead :: b f -> Some b
or even:
brefute :: b VoidF -> Void
But this is a bit more mysterious for an implementor.
I think I wasn't really very clear, sorry! What I meant is that since "Apply
is Applicative
without pure
", and btraverse
requires an Applicative
but doesn't use pure
, we can use a newtype wrapper to pretend an Apply
is an Applicative
whose pure
will never be called. Something like this:
newtype CantUsePure f a = CantUsePure (f a)
deriving newtype Functor
instance Apply f => Applicative (CantUsePure f) where
pure = error "Can't use pure!"
CantUsePure f <*> CantUsePure x = CantUsePure (f <.> x)
btraverse1
:: (TraversableB b, Apply e)
=> (forall a . f a -> e (g a))
-> b f
-> e (b g)
btraverse1 h
= coerce . btraverse (CantUsePure . h)
Of course, one shouldn't export CantUsePure
.
I think I rather not introduce a dependency just for this type of functions; and one could put them in a separate package, anyway
@jcpetruzza The main issue as I understand it is that the quality of "not using pure
" isn't attached to f
, but to b
. In a context where you're abstract in b
, and want to demand that b
has at least one "position" where f
occurs, there doesn't seem to be any safe way to get around requiring a subclass of TraversableB
(although the best way to encode its operations is debatable).
Of course we can just use the error
technique to bypass the type system and pretend that any TraversableB
is Traversable1B
, and then blow up at runtime if we've made a mistake. This doesn't seem ideal though, and I'd rather not lie to the consumer of my API in this way.
You are correct, if you have something that is an Apply
and not an Applicative
, this method would work as a backdoor only if you control the instances on which you use it. I thought it would always work with the generic instances we provide, but this is not true, since it will currently blow on types that have values not under f
.
I still think that there is no difference between TraversableB
and Traversable1B
, in that, afaics, both have exactly the same instances. If Apply
were a super-class of Applicative
, we'd just require Apply
for the type of the effect instead (the generic instances would just be a bit more complex to write, I think)
On further thought, Traversable1B
has fewer instances than TraversableB
, I think. One can't get an instance for:
data Unit (f :: k -> Type) = Unit
or, more in general, for any type with constructors where f
does not occur.
@jcpetruzza Yes, exactly. This is why you would want Traversable1B
to be a subclass of TraversableB
.
If
Apply
were a super-class ofApplicative
, we'd just requireApply
for the type of the effect instead (the generic instances would just be a bit more complex to write, I think)
Things are a bit confusing here because the relationship between Traversable
/Traversable1B
and Applicative
/Apply
is contravariant, so the variance is flipped around. Traversable
is a superclass of Traversable1
as a consequence of Applicative
being a subclass of Apply
.
FYI barbies-th provides an equivalent AccessorsB
class: https://hackage.haskell.org/package/barbies-th-0.1.9/docs/Barbies-TH.html#t:AccessorsB
It would be great to have this in barbies so that barbies-th focus on the Template Haskell part
@fumieval FWIW i've revised my position on the "bag of lenses" class. I now believe that what we want is the class of HKDs that are products. A naive encoding of this is pretty straightforward:
type HKD k = (k -> Type) -> Type
type HList :: [k] -> HKD k
data HList xs f
where
HNil :: HList '[] f
HCons :: f x -> HList xs f -> HList (x ': xs) f
type ProductB :: HKD k -> Constraint
class ProductB (b :: HKD k)
where
type Components b :: [k]
to :: b f -> HList (Components b) f
from :: HList (Components b) f
-- Laws:
-- to . from = id
-- from . to = id
I believe this would subsume ConstraintsB
, and would also supply the bag of lenses we're looking for in certain circumstances. Additionally, it provides a good basis for discussing the duality of products and coproducts and their interactions.
Hello there. I was wondering if you'd be amenable to adding the following classes (and ways of deriving them):
and:
There might be some way to turn
ProductLike
in a sensible way (perhaps involvingCo
) but based on my rushed thinking so far I haven't been able to think of anything.