cornea: classy optical monadic state

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

Please see the README on GitHub at

[Skip to Readme]


Change log None available
Dependencies base (==4.*), either, lens, lifted-base, monad-control, mtl, relude, template-haskell, th-abstraction, transformers [details]
License BSD-2-Clause-Patent
Copyright 2021 Torsten Schmits
Author Torsten Schmits
Category Lens
Home page
Bug tracker
Source repo head: git clone
Uploaded by tek at 2021-05-07T11:08:06Z




Maintainer's Corner

For package maintainers and hackage trustees

Readme for cornea-

[back to package description]


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.