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 throwDeepIn 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.