content: 
  module Tintin.Capabilities
    (
      {-|
  'liftCapability' allows executing an 'IO' action stored in
  a Capability value in the 'Effectful' monad.
  
  It is intended to be used within the Capability definition
  module, not outside.
  
  @
  data MyCapability = MyCapability
    { _myAction :: IO ()
    }
  
  myAction :: Has MyCapability e
           => Effectful e ()
  myAction = liftCapability _myAction
  @
      -}
      liftCapability
    )
  where
  
  import Tintin.Core
  
  
  {-
  This class should NOT be instantiated outside of this module,
  as it serves to make the code more clean outside.
  
  It allows us to do a
  
  liftCapability f
  
  instead of
  
  liftCapability $ \capability -> f capability foo bar quux
  
  where foo, bar, and quux are the remaining arguments.
  
  These two instances allow the compiler to recurse on the arguments
  and typecheck that the application is correct.
  
  See: https://stackoverflow.com/q/50353294/3847023
  -}
  
  class LiftableWithCapability v where
  
    -- v must have an execution type like 'IO a'
    type Execution v :: *
  
    -- also, v must have a context available, to get the Capability from
    type Context v :: *
  
    -- We provide liftCapability, that ensures that it exists in the Context of v
    -- and, after passing a function that takes the capability and executes it in the
    -- Execution type of V
    liftCapability :: Has capability (Context v) => (capability -> Execution v) -> v
  
  
  
  -- We instantiate it for our Effectful type, which is an alias for ReaderT e IO a
  instance LiftableWithCapability (ReaderT e IO a) where
  
    -- The execution type of that is 'IO a'
    type Execution (ReaderT e IO a) = IO a
  
    -- And the context is e
    type Context (ReaderT e IO a) = e
  
    -- liftCapability just grabs the capability and calls the function on it
    liftCapability f = do
      capability <- asks getter
      lift ( f capability )
  
  
  -- a function that takes some parameter a and returns v also can be lifted
  instance LiftableWithCapability v => LiftableWithCapability (a -> v) where
  
    -- The execution type of the function, is just the Execution of the return type
    type Execution (a -> v) = a -> Execution v
  
    -- Same with the Context, the Context of the return type
    type Context (a -> v) = Context v
  
    -- We flip the arguments of f, so the capability record stays in the last place
    -- and we can recurse over it, until we reach the definition from before.
    --
    -- It is the same as doing 'liftCapability $ \x -> f x b'
    liftCapability f = liftCapability . flip f
  
  