Classes for accessing and mutating nested data types with corresponding adapter
classes for MonadState
, MonadReader
and MonadError
. Inspired by the
next level mtl with classy optics talk.
Lenses and Prisms from lens are autogenerated with TH by splicing with
deepPrisms
and deepLenses
.
The generator recurses into single-field constructors and record fields if
there are instances of DeepPrisms
or DeepLenses
for their parameter types.
For MonadError
:
{-# LANGUAGE TemplateHaskell #-}
import Cornea (MonadDeepError(throwHoist))
import Control.Monad.Trans.Except (runExceptT)
import Data.DeepPrisms (deepPrisms)
newtype Error = Error String
newtype Inner = Inner Error
deepPrisms ''Inner
data Mid = Mid Inner
deepPrisms ''Mid
newtype Outer = Outer Mid
deepPrisms ''Outer
throwDeep :: MonadDeepError e Inner m => m ()
throwDeep = throwHoist (Inner (Error "boom"))
main :: IO (Either Outer ())
main = runExceptT throwDeep
In main
, MonadError Outer IO
and DeepPrisms Outer Inner
are summoned.
Analogously for MonadState
:
{-# LANGUAGE TemplateHaskell #-}
import Cornea (MonadDeepState(get, gets, put))
import Control.Monad.Trans.State (execStateT)
import Data.DeepLenses (deepLenses)
newtype S = S Int
newtype Inner = Inner { _innerS :: S }
deepLenses ''Inner
data Mid = Mid { _midInner :: Inner }
deepLenses ''Mid
newtype Outer = Outer { _outerMid :: Mid }
deepLenses ''Outer
stateDeep :: MonadDeepState s Inner m => m ()
stateDeep = do
(Inner (S a)) <- get
b <- gets $ \(Inner (S b)) -> b
put (Inner (S (a + b + 3)))
main :: IO Outer
main = do
execStateT stateDeep (Outer (Mid (Inner (S 5))))
MonadReader
works basically the same as MonadState
.