-- | The auxiliary effect 'Opaque' used by interpreters of 'Polysemy.Scoped.Scoped'
module Polysemy.Opaque (
  -- * Effect
  Opaque(..),

  -- * Interpreters
  toOpaque,
  fromOpaque,
  ) where

import Polysemy

-- | An effect newtype meant to be used to wrap polymorphic effect variables to
-- prevent them from jamming up resolution of 'Polysemy.Member'.
-- For example, consider:
--
-- @
-- badPut :: 'Sem' (e ': 'Polysemy.State.State' () ': r) ()
-- badPut = 'Polysemy.State.put' () -- error
-- @
--
-- This fails to compile. This is because @e@ /could/ be
-- @'Polysemy.State.State' ()@' -- in which case the 'Polysemy.State.put'
-- should target it instead of the concretely provided
-- @'Polysemy.State.State' ()@; as the compiler can't know for sure which effect
-- should be targeted, the program is rejected.
-- There are various ways to resolve this, including using 'raise' or
-- 'Polysemy.Membership.subsumeUsing'. 'Opaque' provides another way:
--
-- @
-- okPut :: 'Sem' (e ': 'Polysemy.State.State' () ': r) ()
-- okPut = 'fromOpaque' ('Polysemy.State.put' ()) -- OK
-- @
--
-- 'Opaque' is most useful as a tool for library writers, in the case where some
-- function of the library requires the user to work with an effect stack
-- containing some polymorphic effect variables. By wrapping the polymorphic
-- effect variables using 'Opaque', users of the function can use effects as
-- normal, without having to use 'raise' or 'Polysemy.Membership.subsumeUsing'
-- in order to have 'Polysemy.Member' resolve. The various interpreters of
-- 'Polysemy.Scoped.Scoped' are examples of such usage of 'Opaque'.
--
-- @since 1.9.0.0
newtype Opaque (e :: Effect) m a = Opaque (e m a)

-- | Wrap 'Opaque' around the top effect of the effect stack
toOpaque :: Sem (e ': r) a -> Sem (Opaque e ': r) a
toOpaque = rewrite Opaque
{-# INLINE toOpaque #-}

-- | Unwrap 'Opaque' around the top effect of the effect stack
fromOpaque :: Sem (Opaque e ': r) a -> Sem (e ': r) a
fromOpaque = rewrite (\(Opaque e) -> e)
{-# INLINE fromOpaque #-}
