title: Combo lenses and prisms for sums of products?
body: <p>If I have a record type, I can do pretty much anything I want to it with lenses. If I have a sum type, I can do pretty much whatever I want to it with prisms. But if I have a sum that includes a record, <code>makeFields</code> doesn't give me lenses into the fields (of course), but only traversals for them. <code>declarePrisms</code> seems a bit more promising. According to the documentation,</p>
  
  <pre><code>declarePrisms [d|
    data Exp = Lit Int | Var String | Lambda{ bound::String, body::Exp }
    |]
  </code></pre>
  
  <p>will create</p>
  
  <pre><code>data Exp = Lit Int | Var String | Lambda { bound::String, body::Exp }
  _Lit :: Prism' Exp Int
  _Var :: Prism' Exp String
  _Lambda :: Prism' Exp (String, Exp)
  </code></pre>
  
  <p>This gets me almost there, but what I really want is more like this:</p>
  
  <pre><code>data Exp = Lit Int | Var String | Lambda String Exp
  data LambdaRec = { _bound::String, _body::Exp }
  ...
  _Lambda :: Prism' Exp LambdaRec
  -- bound and body lenses into LambdaRec,
  -- and ideally also traversals to look at them in Exp.
  class MightBeLambda t where
    type BoundOptic t
    type BodyOptic t
    bound :: BoundOptic t
    body :: BodyOptic t
  instance MightBeLambda Exp where
    type BoundOptic Exp = Traversal' Exp String
    ...
  instance MightBeLambda LambdaRec where
    type BoundOptic LambdaRec = Lens' LambdaRec String
  </code></pre>
  
  <p>Is there any way to do something like this automatically, or would I have to do it by hand?</p>
  
  <hr>
  
  <p>An even crazier way one might wish to do it:</p>
  
  <pre><code>data ExpTag = LitT | VarT | LambdaT
  
  data Exp' :: ExpTag -&gt; * where
    Lit' :: Int -&gt; Exp' LitT
    Var' :: String -&gt; Exp' VarT
    Lambda' :: { _bound::String, _body::Exp } -&gt; Exp' LambdaT
  </code></pre>
  
  <p>Then the prisms can be defined evilly, using <code>unsafeCoerce</code> to avoid any risk of copying records.</p>