ekmett/lens

`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).