packages feed

alms-0.6.0: src/Type/Reduce.hs

{-# LANGUAGE TypeFamilies #-}
module Type.Reduce (
  matchReduce,
  headNormalizeTypeK, headNormalizeType,
  headReduceType, ReductionState(..),
  majorReductionSequence, reductionSequence, reductionSequence'
) where

import Util
import Error
import Type.Internal
import Type.TyVar (Tv)
import Type.Ppr ()

import Prelude ()
import Data.Generics (Typeable, Data)
import qualified Data.List as List

instance Tv tv ⇒ Viewable (Type tv) where
  type View (Type tv) = Type tv
  view = headNormalizeTypeK 1000

-- | Reduce a type to head normal form
headNormalizeType ∷ Ord tv ⇒ Type tv → Type tv
headNormalizeType = last . reductionSequence

-- | Allow @k0@ steps to reduce a type to head normal form, or call
--  'error'
headNormalizeTypeK ∷ Tv tv ⇒ Int → Type tv → Type tv
headNormalizeTypeK k0 σ0 = loop k0 (reductionSequence σ0) where
  loop _ []     = throw $
    almsBug StaticsPhase "headNormalizeTypeK"
            "got empty reduction sequence"
  loop _ [σ]    = σ
  loop 0 (σ:_)  = throw $
    AlmsError StaticsPhase bogus
      [msg|
        Reduction of type $q:σ0 has not converged after $k0
        steps; stopped at $q:σ.
      |]
  loop k (_:σs) = loop (k - 1) σs

-- | Given two types, try to reduce them to a pair with a common
--   head constructor.  We assume that the two types given don't
--   match in the head already.
matchReduce ∷ Ord tv ⇒ Type tv → Type tv → Maybe (Type tv, Type tv)
matchReduce σ1 σ2 =
  List.find isCandidate
            (safeTail (allPairsBFS (majorReductionSequence σ1)
                                   (majorReductionSequence σ2)))
  where
    isCandidate (TyApp tc _, TyApp tc' _) = tc == tc'
    isCandidate _                         = True
    safeTail []     = []
    safeTail (_:σs) = σs

-- | Returns all pairs of a pair of lists, breadth first
allPairsBFS ∷ [a] → [b] → [(a, b)]
allPairsBFS xs0 ys0 = loop [(xs0, ys0)] where
  loop []   = []
  loop xsys = [ (x, y) | (x:_, y:_) ← xsys ]
           ++ loop (take 1 [ (xs, ys) | (xs, _:ys) ← xsys ]
                        ++ [ (xs, ys) | (_:xs, ys) ← xsys ])

-- | A major reduction sequence is a reduction sequence filtered
--   to show only changes in the head constructor.
majorReductionSequence ∷ Ord tv ⇒ Type tv → [Type tv]
majorReductionSequence = clean . reductionSequence where
  clean []        = []
  clean (σ:σs)    = σ : cleanWith σ σs
  cleanWith σ@(TyApp tc _) ((TyApp tc' _) : σs)
    | tc == tc'  = cleanWith σ σs
  cleanWith _ σs = clean σs

-- | The reduction sequence of a type
reductionSequence ∷ Ord tv ⇒ Type tv → [Type tv]
reductionSequence σ = (σ:) $ case headReduceType σ of
  Next σ' → reductionSequence σ'
  _       → []

-- | The reduction sequence of a type along with a final status
--   indicator
reductionSequence' ∷ Ord tv ⇒ Type tv → ([Type tv], ReductionState ())
reductionSequence' σ = first (σ:) $ case headReduceType σ of
  Next σ' → reductionSequence' σ'
  rs      → ([], () <$ rs)

-- | The state of a type reduction
data ReductionState t
  -- | The type is head-normal -- that is, its head constructor is
  --   not a type synonym/operator
  = Done
  -- | The type has a next head-reduction step
  | Next t
  -- | The type may reduce further in the future, but right now it
  --   has a pattern match that depends on the value of a type variable
  | Blocked
  -- | The type's head constructor is a synonym/operator, but it
  --   can never take a step, due to a failed pattern match
  | Stuck
  deriving (Eq, Ord, Show, Functor, Typeable, Data)

-- | Perform one head reduction step.
headReduceType ∷ Ord tv ⇒ Type tv → ReductionState (Type tv)
headReduceType σ0 = case σ0 of
  TyQu _ _ _  → Done
  TyVar _     → Done
  TyRow _ _ _ → Done
  TyMu _ σ    → Next $ openTy 0 [σ0] σ
  TyApp tc [σ1, σ2] | tc == tcRowMap
              → applyRowMap σ1 σ2
  TyApp tc σs → maybe Done (clauses tc σs) (tcNext tc)
  where
  --
  clauses _  _  []                = Stuck
  clauses tc σs ((tps, rhs):rest) = case patts tps σs of
    Right σs'  → Next $ openTy 0 σs' (elimEmptyF rhs)
    Left Stuck → clauses tc σs rest
    Left rs    → TyApp tc <$> rs
  --
  patts []       []               = Right []
  patts (tp:tps) (σ:σs)           = case patt tp σ of
    Right σs'     → case patts tps σs of
      Right σss'      → Right (σs' ++ σss')
      Left rs         → Left ((σ:) <$> rs)
    Left Blocked  → Left $ either ((σ:) <$>) (const Blocked) (patts tps σs)
    Left rs       → Left $ (:σs) <$> rs
  patts _        _                = Left Stuck
  --
  patt (TpVar _)       σ          = Right [σ]
  patt (TpApp tc tps)  σ          = case σ of
    TyApp tc' σs
      | tc == tc' → ((TyApp tc' <$>) +++ id) (patts tps σs)
    TyVar _       → Left Blocked
    _             → case headReduceType σ of
      Done            → Left Stuck
      rs              → Left rs
  patt (TpRow _)      σ           = Right [σ]

applyRowMap ∷ Ord tv ⇒ Type tv → Type tv → ReductionState (Type tv)
applyRowMap σcxt σarg = case σarg of
  TyRow lab σ1 σ2
    → Next $ TyRow lab (plugHole σcxt σ1) (tyRowMap σcxt σ2)
  TyApp tc [] | tc == tcRowEnd
    → Next tyRowEnd
  _ → tyRowMap σcxt <$> headReduceType σarg

plugHole         ∷ Ord tv ⇒ Type tv → Type tv → Type tv
plugHole σcxt σ' = foldType (mkQuF TyQu) (mkBvF bvTy) fvTy fcon
                            TyRow (mkMuF TyMu) σcxt
  where
    fcon tc σs =
      if tc == tcRowHole
        then σ'
        else TyApp tc σs