Type families can't return a RankN type -- workarounds or alternatives?
haskell, haskell-lens, type-families, types
Solution
A lens is already a traversal, only its Rank2 quantifier doesn't make use of the full constraint (it merely requires `Functor`, not `Applicative`).
type Lens s t a b = ∀ f . Functor f => (a -> f b) -> s -> f t
type Traversal s t a b = ∀ f . Applicative f => (a -> f b) -> s -> f t
It is at the level of this constraint that you should introduce your type family:
import GHC.Exts (Constraint)
type family FieldOpticConstraint key keys :: (* -> *) -> Constraint where
FieldOpticConstraint key '[] = Applicative
FieldOpticConstraint key (key =: val ': xs) = Functor
FieldOpticConstraint key (_ ': xs) = FieldOpticConstraint key xs
Then `field` should not yield a `LensOrTraversal`, but always a custom Rank2-signature with the constraint determined by the type family.
Problem
I'm playing with an extensible record library, and I'm wanting to write a function `field` that can operate as either a `Lens` or a `Traversal` based on whether or not the `Symbol` key is in the list of keys. The type family is given: ``` type family LensOrTraversal key keys s t a b where LensOrTraversal key '[] s t a b = Traversal s t a b LensOrTraversal key (key =: val ': xs) s t a b = Lens s t a b LensOrTraversal key (foo =: bar ': xs) s t a b = LensOrTraversal key xs s t a b ``` This code gives me an error: ``` /home/matt/Projects/hash-rekt/src/Data/HashRecord/Internal.hs:433:5: error: • Illegal polymorphic type: Traversal s t a b • In the equations for closed type family ‘LensOrTraversal’ In the type family declaration for ‘LensOrTraversal’ ``` Ideally, I'd like to be able to reuse the `field` name for both lenses and traversals, as it would allow you to write ``` >>> let testMap = empty & field @"foo" .~ 'a' >>> :t testMap HashRecord '["foo" =: Char] >>> testMap ^. field @"foo" 'a' >>> testMap ^. field @"bar" Type error >>> testMap ^? field @"bar" Nothing ``` which follows common `lens` idioms. I can provide an `fieldTraversal` function that does what I want, but I'd prefer to overload the name `field` if possible. How would you work around this limitation of type families?