moo-nad-0.1.0.1: lib/Moo/Prelude.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | Invocation helpers for functions with effects in a monad 'D' and which are
-- stored in the environment 'E' of a reader-like monad 'M'.
module Moo.Prelude (
self,
call,
-- * Re-exports from Moo
M,
E,
D
) where
import Moo
import Data.Kind
import GHC.TypeLits
import Control.Monad.Reader
import Control.Monad.Dep.Has
type Call :: Type -> Constraint
class Call curried where
type LiftedD curried :: Type
-- | Given a way of extracting from the environment 'E' a @curried@
-- function that ends in a 'D' action, lift the @curried@ 'D'-function into the main
-- monad 'M'.
self :: (E -> curried) -> LiftedD curried
instance Call (D r) where
type LiftedD (D r) = M r
self extractor = do
e <- ask
liftD $ extractor e
instance Call curried' => Call (a -> curried') where
type LiftedD (a -> curried') = a -> LiftedD curried'
self extractor a =
let extractor' = \e -> extractor e a
in self @curried' extractor'
-- | Provided that the environment 'E' 'Control.Monad.Dep.Has' a @component@, and
-- given a way of extracting from the @component@ a @curried@ function that
-- ends in a 'D' action, lift the @curried@ 'D'-function into the main monad
-- 'M'.
--
-- The extractor must be monomorphic on the @component@, so that the intended
-- instance of 'Control.Monad.Dep.Has' is picked.
--
-- The typical case is for the @component@ to be a parameterized record and for
-- the extractor to be a field accessor.
call :: forall component curried . (Has component D E, Call curried) => (component D -> curried) -> LiftedD curried
call extractor = self (extractor . dep @component)