content: {-# LANGUAGE AllowAmbiguousTypes #-}
  {-# LANGUAGE ConstraintKinds #-}
  {-# LANGUAGE DerivingVia #-}
  {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE FunctionalDependencies #-}
  {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  {-# LANGUAGE KindSignatures #-}
  {-# LANGUAGE MagicHash #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE QuantifiedConstraints #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE StandaloneDeriving #-}
  {-# LANGUAGE TypeApplications #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE TypeInType #-}
  {-# LANGUAGE TypeOperators #-}
  {-# LANGUAGE UnboxedTuples #-}
  {-# LANGUAGE UndecidableInstances #-}
  
  {-# OPTIONS_HADDOCK hide #-}
  
  module Capability.Sink.Internal.Class where
  
  import Capability.Reflection
  import Data.Coerce (coerce)
  import Data.Kind (Type)
  import GHC.Exts (Proxy#, proxy#)
  
  -- | Sinking capability.
  --
  -- An instance does not need to fulfill any additional laws
  -- besides the monad laws.
  class Monad m
    => HasSink (tag :: k) (a :: Type) (m :: Type -> Type) | tag m -> a
    where
      -- | For technical reasons, this method needs an extra proxy argument.
      -- You only need it if you are defining new instances of 'HasSink'.
      -- Otherwise, you will want to use 'yield'.
      -- See 'yield' for more documentation.
      yield_ :: Proxy# tag -> a -> m ()
  
  -- | @yield \@tag a@
  -- emits @a@ in the sink capability @tag@.
  yield :: forall tag a m. HasSink tag a m => a -> m ()
  yield = yield_ (proxy# @tag)
  {-# INLINE yield #-}
  
  --------------------------------------------------------------------------------
  
  data instance Reified tag (HasSink tag a) m = ReifiedSink {_yield :: a -> m ()}
  
  instance
    ( Monad m,
      Reifies s (Reified tag (HasSink tag a) m)
    ) =>
    HasSink tag a (Reflected s (HasSink tag a) m)
    where
    yield_ _ = coerce $ _yield (reified @s)
    {-# INLINE yield_ #-}
  