{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

-- |
-- Module:       Data.OpenUnion
-- Description:  Open unions (type-indexed co-products) for extensible effects.
-- Copyright:    (c) 2016 Allele Dev; 2017 Ixperta Solutions s.r.o.; 2017 Alexis King
-- License:      BSD3
-- Maintainer:   Alexis King <lexi.lambda@gmail.com>
-- Stability:    experimental
-- Portability:  GHC specific language extensions.
--
-- Open unions (type-indexed co-products, i.e. type-indexed sums) for
-- extensible effects All operations are constant-time.
module Data.OpenUnion
  ( -- * Open Union
    Union

    -- * Open Union Operations
  , Weakens(..)
  , (:++:)
  , decomp
  , weaken
  , extract

    -- * Open Union Membership Constraints
  , Member(..)
  , Members
  , LastMember
  ) where

import Data.Kind (Constraint)

import Data.OpenUnion.Internal
  ( Member(inj, prj)
  , Union
  , Weakens(weakens)
  , (:++:)
  , decomp
  , extract
  , weaken
  )

-- | A shorthand constraint that represents a combination of multiple 'Member'
-- constraints. That is, the following 'Members' constraint:
--
-- @
-- 'Members' '[Foo, Bar, Baz] effs
-- @
--
-- …is equivalent to the following set of 'Member' constraints:
--
-- @
-- ('Member' Foo effs, 'Member' Bar effs, 'Member' baz effs)
-- @
--
-- Note that, since each effect is translated into a separate 'Member'
-- constraint, the order of the effects does /not/ matter.
type family Members effs effs' :: Constraint where
  Members (eff ': effs) effs' = (Member eff effs', Members effs effs')
  Members '[] effs' = ()

-- | Like 'Member', @'LastMember' eff effs@ is a constraint that requires that
-- @eff@ is in the type-level list @effs@. However, /unlike/ 'Member',
-- 'LastMember' requires @m@ be the __final__ effect in @effs@.
--
-- Generally, this is not especially useful, since it is preferable for
-- computations to be agnostic to the order of effects, but it is quite useful
-- in combination with 'Control.Monad.Freer.sendM' or
-- 'Control.Monad.Base.liftBase' to embed ordinary monadic effects within an
-- 'Control.Monad.Freer.Eff' computation.
class Member m effs => LastMember m effs | effs -> m
instance {-# OVERLAPPABLE #-} LastMember m effs => LastMember m (eff ': effs)
instance LastMember m (m ': '[])