As we've discussed, everything Distributive should be Representable, but maybe the librarian doesn't want to commit to a particular Rep. I just realize that there's actually a way to get a sort of middle ground, though it's a bit awkward.
class Functor f => Distributive f where
data family RepD f -- Must be a newtype if Representable
tabulateD :: (RepD f -> a) -> f a
indexD :: f a -> RepD f -> a
class (Distributive f, Coercible (Rep f) (RepD f) => Representable f where
type Rep f
tabulate :: forall f a. Representable f => (Rep f -> a) -> f a
tabulate = coerce (tabulateD :: (RepD f -> a) -> f a)
index :: forall f a. Representable f => f a -> Rep f -> a
index = coerce (indexD :: f a -> RepD f -> a)
If the module defining f chooses not to export the RepD f newtype constructor, then the choice of representation is hidden.
As we've discussed, everything
Distributive
should beRepresentable
, but maybe the librarian doesn't want to commit to a particularRep
. I just realize that there's actually a way to get a sort of middle ground, though it's a bit awkward.If the module defining
f
chooses not to export theRepD f
newtype constructor, then the choice of representation is hidden.