You can mix classes with lenses to simulate overloaded recording fields, down to the point. See, for example, makeFields in Control.Lens.TH . I'm trying to figure out if there is a good way to reuse the same name as the lens for some types, and a workaround for others. It is noteworthy that, taking into account the amount of products, each product may have lenses that deteriorate before going around the amount. The simplest thing I could think of: **:
First try
class Boo booey where type Con booey :: (* -> *) -> Constraint boo :: forall f . Con booey f => (Int -> f Int) -> booey -> f booey
This works great for simple things like
data Boop = Boop Int Char instance Boo Boop where type Con Boop = Functor boo f (Boop ic) = (\i' -> Boop i' c) <$> fi
But he falls on his face as soon as you need something more complex, for example
instance Boo boopy => Boo (Maybe boopy) where
which should be able to create Traversal , regardless of the choice of base Boo .
Second attempt
The next thing I tried, what types of work is to restrict the Con family. It gets rude. First change the class:
class LTEApplicative c where lteApplicative :: Applicative a :- ca class LTEApplicative (Con booey) => Boo booey where type Con booey :: (* -> *) -> Constraint boo :: forall f . Con booey f => (Int -> f Int) -> booey -> f booey
This causes the Boo instances to contain clear evidence that their Boo creates a Traversal' booey Int . A few more things:
instance LTEApplicative Applicative where lteApplicative = Sub Dict instance LTEApplicative Functor where lteApplicative = Sub Dict -- flub :: Boo booey => Traversal booey booey Int Int flub :: forall booey f . (Boo booey, Applicative f) => (Int -> f Int) -> booey -> f booey flub = case lteApplicative of Sub (Dict :: Dict (Con booey f)) -> boo instance Boo boopy => Boo (Maybe boopy) where type Con (Maybe boopy) = Applicative boo _ Nothing = pure Nothing boo f (Just x) = Just <$> hum fx where hum :: Traversal' boopy Int hum = flub
And the basic Boop example works unchanged.
Why is it still sucking
Now we have Boo , creating Lens or Traversal under the appropriate circumstances, and we can always use it as Traversal , but every time we want to do this, we must first drag that this is really one. This, of course, is too inconvenient to implement overloaded record fields! Is there a better way?
** This code compiles with the following (may not be minimal):
{-# LANGUAGE PolyKinds, TypeFamilies, TypeOperators, FlexibleContexts, ScopedTypeVariables, RankNTypes, KindSignatures #-} import Control.Lens import Data.Constraint