packages feed

quickspec-2.2.1: src/QuickSpec/Internal/Pruning/PartialApplication.hs

-- Pruning support for partial application and the like.
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances, RecordWildCards, MultiParamTypeClasses, FlexibleContexts, GeneralizedNewtypeDeriving, UndecidableInstances, DeriveFunctor, DeriveGeneric #-}
module QuickSpec.Internal.Pruning.PartialApplication where

import QuickSpec.Internal.Term as Term
import QuickSpec.Internal.Type
import QuickSpec.Internal.Pruning.Background hiding (Pruner)
import QuickSpec.Internal.Pruning
import QuickSpec.Internal.Prop as Prop
import QuickSpec.Internal.Terminal
import QuickSpec.Internal.Testing
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import GHC.Generics
import Data.Hashable
import Data.Hashable.Generic

data PartiallyApplied f =
    -- A partially-applied function symbol.
    -- The Int describes how many arguments the function expects.
    Partial f Int
    -- The ($) operator, for oversaturated applications.
    -- The type argument is the type of the first argument to ($).
  | Apply Type
  deriving (Eq, Ord, Functor, Generic)

instance Hashable f => Hashable (PartiallyApplied f) where
  hashWithSalt = genericHashWithSalt

instance Sized f => Sized (PartiallyApplied f) where
  size (Partial f _) = size f
  size (Apply _) = 1

instance Arity (PartiallyApplied f) where
  arity (Partial _ n) = n
  arity (Apply _) = 2

instance Pretty f => Pretty (PartiallyApplied f) where
  pPrint (Partial f n) = pPrint f <#> text "@" <#> pPrint n
  pPrint (Apply _) = text "$"

instance PrettyTerm f => PrettyTerm (PartiallyApplied f) where
  termStyle (Partial f _) = termStyle f
  termStyle (Apply _) = infixStyle 2

instance Typed f => Typed (PartiallyApplied f) where
  typ (Apply ty) = arrowType [ty] ty
  typ (Partial f _) = typ f
  otherTypesDL (Apply _) = mempty
  otherTypesDL (Partial f _) = otherTypesDL f
  typeSubst_ sub (Apply ty) = Apply (typeSubst_ sub ty)
  typeSubst_ sub (Partial f n) = Partial (typeSubst_ sub f) n

partial :: f -> Term (PartiallyApplied f)
partial f = Fun (Partial f 0)

total :: Arity f => f -> PartiallyApplied f
total f = Partial f (arity f)

smartApply ::
  Typed f => Term (PartiallyApplied f) -> Term (PartiallyApplied f) -> Term (PartiallyApplied f)
smartApply (Fun (Partial f n) :@: ts) u =
  Fun (Partial f (n+1)) :@: (ts ++ [u])
smartApply t u = simpleApply t u

simpleApply ::
  Typed f =>
  Term (PartiallyApplied f) -> Term (PartiallyApplied f) -> Term (PartiallyApplied f)
simpleApply t u =
  Fun (Apply (typ t)) :@: [t, u]

instance (Typed f, Background f) => Background (PartiallyApplied f) where
  background (Partial f _) =
    map (Prop.mapFun (\f -> Partial f arity)) (background f) ++
    [ simpleApply (partial n) (vs !! n) === partial (n+1)
    | n <- [0..arity-1] ]
    where
      arity = typeArity (typ f)
      partial i =
        Fun (Partial f i) :@: take i vs
      vs = map Var (zipWith V (typeArgs (typ f)) [0..])
  background _ = []

newtype Pruner fun pruner a =
  Pruner { run :: pruner a }
  deriving (Functor, Applicative, Monad, MonadIO, MonadTester testcase term, MonadTerminal)

instance MonadTrans (Pruner fun) where
  lift = Pruner

instance (PrettyTerm fun, Typed fun, MonadPruner (Term (PartiallyApplied fun)) norm pruner) => MonadPruner (Term fun) norm (Pruner fun pruner) where
  normaliser =
    Pruner $ do
      norm <- normaliser
      return $ \t ->
        norm . encode $ t

  add prop =
    Pruner $ do
      add (encode <$> canonicalise prop)

  decodeNormalForm hole t =
    Pruner $ do
      t <- decodeNormalForm (fmap (fmap (flip Partial 0)) . hole) t
      let f (Partial x _) = NotId x
          f (Apply _) = Id
      return $ t >>= eliminateId . Term.mapFun f

encode :: Typed fun => Term fun -> Term (PartiallyApplied fun)
encode (Var x) = Var x
encode (Fun f) = partial f
encode (t :$: u) = smartApply (encode t) (encode u)