tweag/capability

`zoom`, `magnify`, and `wrapError` forget other capabilities

aherrmann opened this issue · 0 comments

The functions zoom, magnify, and wrapError as introduced by #31 allow to modify a HasState, HasReader, or HasCatch/Throw capability according to a deriving strategy. However, they forget any other capabilities that were in scope before. E.g. in the following code the writer capability is forgotten:

verboseStates
  :: (HasState "both" (Int, Int) m, HasWriter "log" (Sum Int) m)
  => m ()
verboseStates = do
  let incAndTell
        :: (HasState "count" Int m, HasWriter "log" (Sum Int) m)
        => m ()
      incAndTell = do
        msg <- state @"count" (\n -> (n, succ n))
        tell @"log" (Sum msg)
  zoom @"both" @"count" @(Rename 1 :.: Pos 1 "both") $
    -- XXX: Cannot use incAndTell here, because the HasWriter has been forgotten.
    -- incAndTell
    pure ()

A more general combinator, call it using, would allow the above. E.g.

using
  @'[ HasState "count" Int `Via` Rename 1 :.: Pos 1 "both"
    , HasWriter "log" (Sum Int) `Via` Self ] $
  -- Can use incAndTell here
  incAndTell

It could be implemented as follows:

using :: forall vias m a. AllCapabilities vias (Combine vias m)
  => (forall m'. AllCapabilities vias m' => m' a)
  -> m a
using m = coerce @(Combine vias m a) m

newtype Combine (vias :: [*]) m (a :: *) = Combine (m a)
  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)

-- Add appropriate capability instances for `Combine`. E.g.
deriving via ((t :: (* -> *) -> * -> *) m)
  instance
    ( NoDuplicateStrategy (HasState tag s) vias
    , forall x. Coercible (m x) (t m x)
    , HasState tag s (t m)
    , Monad m )
    => HasState tag s (Combine (HasState tag s `Via` t ': vias) m)
deriving via (Combine vias m)
  instance {-# OVERLAPPABLE #-}
    ( HasState tag s (Combine vias m), Monad m )
    => HasState tag s (Combine (via ': vias) m)


data Via
  (capability :: (* -> *) -> Constraint)
  (strategy :: (* -> *) -> * -> *)
infix 8 `Via`

newtype Self m (a :: *) = Self (m a)
  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)