packages feed

alms-0.6.0: src/Type/Recursive.hs

-- | Facilities for proper handling of equirecursive types
module Type.Recursive (
  -- * Equirecursive type standardization
  standardizeMus,

  -- * Non-equirecursive comparison
  NoRec(..),
) where

import Util
import Util.MonadRef
import Type.Internal

import Prelude ()
import qualified Data.Map as M
import qualified Data.Set as S
import Control.Monad.ST (runST)

-- | Put all recursion in standard form.
--   PRECONDITION: The type is in 'standardize' standard form and all
--   type variables are substituted
standardizeMus ∷ Ord tv ⇒ Type tv → Type tv
standardizeMus σ00 = runST $ do
  counter ← newRef (0 ∷ Int)
  let loop g0 σ0 = do
        case M.lookup σ0 g0 of
          Just (i, used') → do
            writeRef used' True
            return (fvTy i)
          Nothing → do
            i    ← gensym
            used ← newRef False
            let g = M.insert σ0 (i, used) g0
            σ0'  ← case σ0 of
              TyQu qu qls σ → do
                is ← mapM (const gensym) qls
                σ' ← loop g (openTy 0 (map fvTy is) σ)
                return (TyQu qu qls (closeTy 0 is σ'))
              TyApp tc σs   → TyApp tc `liftM` mapM (loop g) σs
              TyVar _       → return σ0
              TyRow n σ1 σ2 → TyRow n `liftM` loop g σ1 `ap` loop g σ2
              TyMu _ σ1     → loop g0 (openTy 0 [σ0] σ1)
            wasUsed ← readRef used
            return $ if wasUsed
              then TyMu Nope (closeTy 0 [i] σ0')
              else σ0'
      gensym  = do
        i ← readRef counter
        writeRef counter (i + 1)
        return (Right i)
      clean = either id (error "BUG! (standardizeMus)")
  σ00' ← loop M.empty (Left <$> σ00)
  return (clean <$> σ00')


-- | Newtype for defining 'Eq' and 'Ord' on types treating 'TyMu' as a
-- normal type constructor without unfolding.  We build the correct
-- equirecursive operations on top of this.
newtype NoRec tv = NoRec (Type tv)

instance Ord tv ⇒ Eq (Type tv) where
  σ1 == σ2 = compare σ1 σ2 == EQ

instance Ord tv ⇒ Ord (Type tv) where
  compare σ10 σ20 = evalState (loop σ10 σ20) S.empty where
    compareM a b = return (compare a b)
    loop σ1 σ2 = do
      seen ← get
      if (S.member (NoRec σ1, NoRec σ2) seen ||
          S.member (NoRec σ2, NoRec σ1) seen)
        then return EQ
        else do
          put (S.insert (NoRec σ1, NoRec σ2) seen)
          case (σ1, σ2) of
            (TyMu _ σ1', _)
              → loop (openTy 0 [σ1] σ1') σ2
            (_, TyMu _ σ2')
              → loop σ1 (openTy 0 [σ2] σ2')
            (TyVar v1, TyVar v2)
              → compareM v1 v2
            (TyQu qu1 qls1 σ1', TyQu qu2 qls2 σ2')
              → compareM qu1 qu2        `thenCmpM`
                compareM qls1 qls2      `thenCmpM`
                loop σ1' σ2'
            (TyRow n1 σ1f σ1r, TyRow n2 σ2f σ2r)
              → compareM n1 n2          `thenCmpM`
                loop σ1f σ2f            `thenCmpM`
                loop σ1r σ2r
            (TyApp n1 σs1, TyApp n2 σs2)
              → compareM n1 n2          `thenCmpM`
                compareM (length σs1) (length σs2)
                                        `thenCmpM`
                foldl' thenCmpM (return EQ) (zipWith loop σs1 σs2)
            (TyVar _, _)
              → return LT
            (_, TyVar _)
              → return GT
            (TyQu _ _ _, _)
              → return LT
            (_, TyQu _ _ _)
              → return GT
            (TyApp _ _, _)
              → return LT
            (_, TyApp _ _)
              → return GT

instance Ord tv ⇒ Eq (NoRec tv) where
  σ1 == σ2 = compare σ1 σ2 == EQ

instance Ord tv ⇒ Ord (NoRec tv) where
  NoRec σ10 `compare` NoRec σ20 = loop σ10 σ20 where
    loop (TyVar r1)         (TyVar r2)
      = compare r1 r2
    loop (TyQu qu1 qls1 σ1) (TyQu qu2 qls2 σ2)
      = compare qu1 qu2   `mappend`
        compare qls1 qls2 `mappend`
        loop σ1 σ2
    loop (TyMu _ σ1)        (TyMu _ σ2)
      = loop σ1 σ2
    loop (TyRow l1 t1 t1')  (TyRow l2 t2 t2')
      = compare l1 l2     `mappend`
        loop t1 t2        `mappend`
        loop t1' t2'
    loop (TyApp tc1 σs1)    (TyApp tc2 σs2)
      = compare tc1 tc2   `mappend`
        mconcat (zipWith loop σs1 σs2)
    loop (TyVar _)          _                  = LT
    loop _                  (TyVar _)          = GT
    loop (TyQu _ _ _)       _                  = LT
    loop _                  (TyQu _ _ _)       = GT
    loop (TyMu _ _)         _                  = LT
    loop _                  (TyMu _ _)         = GT
    loop (TyRow _ _ _)      _                  = LT
    loop _                  (TyRow _ _ _)      = GT