'How to use lens to access a record field behind a sum type
I am trying to access a nested record using lenses and prisms in Haskell:
import Data.Text (Text)
import Control.Lens.TH
data State = State
{ _stDone :: Bool
, _stStep :: StateStep
}
data StateStep
= StatePause
| StateRun
{ _stCounter :: Int
, _stMMistake :: Maybe Text
}
makeLenses ''State
makeLenses ''StateStep
makePrisms ''StateStep
main :: IO ()
main = do
let st = State False $ StateRun 0 Nothing
-- works, but the `_2` seems weird
mMistake = st ^? stStep . _StateStepRun . _2 . _Just
-- why not something like (the following does not compile)
mMistake = st ^. stStep . _StateStepRun . _Just . stMMistake
The line that works leaves some questions open. I am unsure whether or not the type match by coincidence. The field _stMMistake has type Maybe Text, but what about
let st = State False StatePause
? I am missing the explicit join.
And I am clueless about how prisms work. While it seems logical for the prism to give me a tuple, at the same time I expected something composable in the sense that I can go deeper into my nested structure, using lenses. Do I have to derive my instances manually for this, maybe?
Solution 1:[1]
Optics generally work out more cleanly when sum type constructors each have at most one field. In your case, you could write something like
data StateStep
= StatePause
| StateRun {-# UNPACK #-} !Runny
data Runny = Runny
{ _ryCounter :: Int
, _ryNoMistake :: Maybe Text
}
Using a strict field and (since that field is not "small" in the sense of -funpack-small-strict-fields) an {-# UNPACK #-} pragma, you can ensure that StateStep has the same runtime representation as in your code. But now you can get nice field lenses into Runny and everything will work out nicely—no magicked-up tuples.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | dfeuer |
