`Index` TypeFamily for `Ixed`/`At` inconsistent with FunDeps for `*WithIndex`
tysonzero opened this issue · 3 comments
Is there a particular reason for this inconsistency?
It seems like just using a single type family Index
for both would be ideal. I realize such a type family would have to be floated up to indexed-traversable
there is a kind issue:
class Functor f => FunctorWithIndex i f | f -> i where -- f :: Type -> Type
-- but
type family Index (s :: Type) :: Type -- s :: Type
so these cannot be easily the same.
Why one is TF and onther is FD, I don't know exactly. Index
is used by bothIx
and Contains
classes, but that can be done with (anonymous) FD as well. (I like FDs more, but I guess i'm in minority in that: EDIT: IMHO non-associated open type family is the worst option).
It's a little awkward with the kind mismatch as you have to use a somewhat dummy a
in Index (f a)
, but I can't seem to find any major downsides?
class FunctorWithIndex f where
imap :: (Index (f a) -> a -> b) -> f a -> f b
type instance Index [a] = Int
instance FunctorWithIndex [] where
imap f = zipWith f [0 ..]
I'm biased towards TypeFamilies, partly because they just feel more composable and functional, but also largely just because I think instance heads should be very simple and clearly decidable.
Various things I want to do that really aren't all that complicated require UndecidableInstances
(and the currently incoherent FlexibleInstances
) when done with FD's that don't require anything beyond TypeFamilies
when done with TFs. Even outside the extensions, when glancing at them I can't quickly tell as a developer that the instances are reasonable without looking through the FD's at the class definition and mentally ignoring all the variables to the right of an arrow.
I also like being able to immediately tell that Index s
is a function of s
in a type signature, instead of having to look up the class definition or guess if s
and i
are related or what have you.
but I can't seem to find any major downsides?
Index (f a)
can depend on element type a
. E.g. if you try to state imap f . imap g ≡ imap (\i -> f i . g i)
law, you'd need forall a b. Index a ~ Index b
like requirement.
This is matter of taste. I prefer FDs for this use case (and e.g. Each
, where TFs would create a complete mess).
This is also be a major breaking change, affecting a lot of people for a what I would say a stylistic benefit. Not worth considering.
EDIT: indexed-traversable
is also used by optics
, and I used it in projects not using lens
or optics
(e.g. witherable
).