{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UnicodeSyntax #-}

module Abt.Class.Abt
( Abt(..)
) where

import Abt.Types.Nat
import Abt.Types.View
import Abt.Class.Monad
import Abt.Class.Show1

import Control.Applicative hiding (Const)
import Data.Vinyl
import Data.Vinyl.Functor
import qualified Data.List as L

-- | The 'Abt' signature represents mediation between an arbitrary (possibly
-- nameless) term representaion, and a simple one (the 'View'). Based on
-- the (effectful) ismorphism @'into' / 'out'@ between representations, many
-- operations can be defined generically for arbitrary operator sets, including
-- substitution and aggregation of free variables.
--
class (Show1 o, Show v)  Abt (v  *) (o  [Nat]  *) (t  Nat  *) | t  v o where
  -- | Convert a 'View' into a term.
  --
  into
     View v o n t
     t n

  -- | Convert a term into a simple 'View'.
  --
  out
     MonadVar v m
     t n
     m (View v o n t)

  -- | The injection from variables to terms.
  --
  var
     v
     t Z
  var = into . V

  -- | Construct an abstraction.
  --
  (\\)
     v
     t n
     t (S n)
  v \\ e = into $ v :\ e

  -- | Construct an operator term.
  --
  ($$)
     o ns
     Rec t ns
     t Z
  o $$ es = into $ o :$ es
  infixl 1 $$

  -- | Substitute a term for a variable.
  --
  subst
     MonadVar v m
     t Z
     v
     t n
     m (t n)
  subst e v e' = do
    oe'  out e'
    case oe' of
      V v'  return $ if v == v' then e else e'
      v' :\ e''  (v' \\) <$> subst e v e''
      o :$ es  (o $$) <$> subst e v `rtraverse` es

  -- | Instantiate the bound variable of an abstraction.
  --
  (//)
     MonadVar v m
     t (S n)
     t Z
     m (t n)
  xe // e' = do
    v :\ e  out xe
    subst e' v e

  -- | Compute the free variables of a term.
  --
  freeVars
     MonadVar v m
     t n
     m [v]
  freeVars e = do
    oe  out e
    case oe of
      V v  return [v]
      v :\ e' 
        L.delete v <$>
          freeVars e'
      _ :$ es 
        fmap concat . sequence . recordToList $
          Const . freeVars <<$>> es

  -- | Render a term into a human-readable string.
  --
  toString
     MonadVar v m
     t n
     m String
  toString e = do
    vu  out e
    case vu of
      V v  return $ show v
      v :\ e'  do
        estr  toString e'
        return $ show v ++ "." ++ estr
      o :$ RNil  return $ show1 o
      o :$ es  do
        es'  sequence . recordToList $ Const . toString <<$>> es
        return $ show1 o ++ "[" ++ L.intercalate ";" es' ++ "]"