-----------------------------------------------------------------------------
-- |
-- Module:      Data.Pattern.Pattern
-- License:     BSD3
-- Maintainer:  Brent Yorgey <byorgey@cis.upenn.edu>
-- Stability:   experimental
-- Portability: non-portable (see .cabal)
--
-- The main types used in the implementation of first-class patterns:
-- 'Pattern' and 'Clause'.
-----------------------------------------------------------------------------

{-# LANGUAGE PolyKinds, DataKinds #-}
module Data.Pattern.Base (
  -- * Patterns
  Pattern(..),

  -- * Clauses
  Clause, runClause,
  (->>), (<|>),

  -- * Internals
  module Data.Pattern.Base.TypeList,
  module Data.Pattern.Base.Tuple,
 ) where

import Data.Pattern.Base.TypeList
import Data.Pattern.Base.Tuple

import Data.Maybe

import Control.Applicative
import Control.Monad
import Control.Monad.Trans.Reader

-- | The pattern type. A value of type @Pattern vars a@ is a pattern
--   which matches values of type @a@ and binds variables with types
--   given by the type-list @vars@.  For example, something of type
--
-- > Pattern (a :*: c :*: Nil) (a,b,c)
--
--   is a pattern which matches against a triple and binds values of
--   types @a@ and @c@. (A pattern of this type can be constructed as
--   @tup3 var __ var@.)
--
--   Many \"normal\" patterns can be conveniently defined using 'mk0',
--   'mk1', 'mk2', and so on.
newtype Pattern vars a = Pattern { runPattern :: a -> Maybe (Tuple vars) }

-- | Pattern-match clauses. Typically something of the form
--
--   @pattern '->>' function@
--
--   where the function takes one argument for each variable bound by
--   the pattern.
--
--   Clauses can be constructed with @('->>')@, run with 'tryMatch',
--   and manipulated by the 'Monad' and 'MonadPlus' instances. In
--   particular, the @('<|>')@ operator from the 'Alternative' class
--   is the way to list multiple cases in a pattern.
newtype Clause a r = Clause { runClause :: ReaderT a Maybe r
                              -- ^ Extract the underlying computation
                              -- constituting a 'Clause'. This
                              -- function is not intended to be used
                              -- directly; instead, see 'match',
                              -- 'tryMatch', 'mmatch', and 'elim' from
                              -- "Data.Pattern.Common".
                            }
    deriving newtype (Functor,Applicative,Monad,Alternative,MonadPlus)

-- (<|>) is infix 3, so we make (->>) infix 4.
infix 4 ->>

-- | Construct a 'Clause' from a pattern and a function which takes
--   one argument for each variable bound by the pattern. For example,
--
-- > pair __ nothing     ->> 3
-- > pair var nothing    ->> \x -> x + 3
-- > pair var (just var) ->> \x y -> x + y + 3
(->>) :: Pattern vars a -> Fun vars r -> Clause a r
(Pattern p) ->> k = Clause (ReaderT $ fmap (flip runTuple k) . p)