packages feed

co-feldspar-0.1.0.1: src/Feldspar/Verify/FirstOrder.hs

{-# language DataKinds #-}
{-# language GADTs #-}
{-# language TypeFamilies #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language DefaultSignatures #-}
{-# language FlexibleInstances #-}
{-# language TypeOperators #-}
{-# language RankNTypes #-}
{-# language FlexibleContexts #-}
{-# language ConstraintKinds #-}

module Feldspar.Verify.FirstOrder where

import Control.Monad.Identity
import Control.Monad.State
import Control.Monad.Trans
import Control.Monad.Operational.Higher

import Data.Constraint
import Data.Maybe (fromMaybe)
import Data.Typeable

import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map

--------------------------------------------------------------------------------
-- * First-order representation of programs.
--------------------------------------------------------------------------------

-- | First-Order representation of programs, as a sequence of instructions.
data Sequence instr fs a
  where
    Val :: a -> Sequence instr fs a
    Seq :: b                                -- Binder.
        -> instr '(Sequence instr fs, fs) b -- Value.
        -> Sequence instr fs a
        -> Sequence instr fs a
  deriving Typeable

--------------------------------------------------------------------------------

data HO instr fs a
  where
    Skip    :: HO instr fs ()
    Unnamed :: instr fs a -> HO instr fs a
    Named   :: (Ex a, Typeable a) => instr fs a -> HO instr fs a

class Defunctionalise inv instr
  where
    type FO inv instr :: (* -> *, (* -> *, (* -> Constraint, *))) -> * -> *
    type FO inv instr = instr

    defunc :: Fresh m => inv -> instr fs a -> m (FO inv instr fs a)

    default defunc :: (FO inv instr ~ instr, Fresh m) =>
      inv -> instr fs a -> m (FO inv instr fs a)
    defunc _ = return

    refunc :: (Defunctionalise inv jnstr, Sub exp pred, pred Bool) => inv -> Subst ->
      FO inv instr (Param3 (Sequence (FO inv jnstr) (Param2 exp pred)) exp pred) a ->
      HO     instr (Param3 (Program jnstr (Param2 exp pred)) exp pred) a

instance (Defunctionalise inv instr, Defunctionalise inv jnstr) =>
    Defunctionalise inv (instr :+: jnstr)
  where
    type FO inv (instr :+: jnstr) = FO inv instr :+: FO inv jnstr

    defunc inv (Inl instr) = fmap Inl $ defunc inv instr
    defunc inv (Inr jnstr) = fmap Inr $ defunc inv jnstr

    refunc inv sub (Inl instr) = case refunc inv sub instr of
      Named i   -> Named $ Inl i
      Unnamed i -> Unnamed $ Inl i
      Skip      -> Skip

    refunc inv sub (Inr jnstr) = case refunc inv sub jnstr of
      Named j   -> Named $ Inr j
      Unnamed j -> Unnamed $ Inr j
      Skip      -> Skip

--------------------------------------------------------------------------------

defunctionalise ::
  ( HFunctor instr
  , Symbol instr
  , Defunctionalise inv instr
  , HTraversable (FO inv instr)
  ) =>
  inv -> Program instr fs a -> Sequence (FO inv instr) fs a
defunctionalise inv prg = evalState (defunctionaliseM inv prg) (0 :: Integer)

defunctionaliseM ::
  ( Monad m
  , Fresh m
  , HFunctor instr
  , Symbol instr
  , Defunctionalise inv instr
  , HTraversable (FO inv instr)
  ) =>
  inv -> Program instr fs a -> m (Sequence (FO inv instr) fs a)
defunctionaliseM inv prg = case view prg of
  Return val     -> return (Val val)
  (:>>=) instr f -> do
    binder <- dry instr
    instr' <- defunc inv instr
    fonstr <- htraverse (defunctionaliseM inv) instr'
    tail   <- defunctionaliseM inv (f binder)
    return (Seq binder fonstr tail)

--------------------------------------------------------------------------------

refunctionalise :: (Defunctionalise inv instr, Sub exp pred, pred Bool) =>
  inv ->
  Sequence (FO inv instr) (Param2 exp pred) a ->
  Program instr (Param2 exp pred) a
refunctionalise inv = refunctionaliseM inv Map.empty

refunctionaliseM :: (Defunctionalise inv instr, Sub exp pred, pred Bool) =>
  inv ->
  Subst ->
  Sequence (FO inv instr) (Param2 exp pred) a ->
  Program instr (Param2 exp pred) a
refunctionaliseM _   _   (Val val)       = return val
refunctionaliseM inv sub (Seq n fo tail) =
  case refunc inv sub fo of
    Unnamed instr -> do
      singleton instr
      refunctionaliseM inv sub tail
    Named instr -> do
      new <- singleton instr
      refunctionaliseM inv (extendSubst n new sub) tail
    Skip -> do 
      refunctionaliseM inv sub tail

--------------------------------------------------------------------------------

class HFunctor instr => HTraversable instr
  where
    htraverse :: Applicative f
      => (forall b . prog1 b -> f (prog2 b))
      ->    instr '(prog1, fs) a
      -> f (instr '(prog2, fs) a)
    htraverse _ i = pure (hfmap (\_ -> error "traverse compound instruction") i)

instance (HTraversable f, HTraversable g) => HTraversable (f :+: g)
  where
    htraverse f (Inl x) = fmap Inl (htraverse f x)
    htraverse f (Inr x) = fmap Inr (htraverse f x)
      
--------------------------------------------------------------------------------

class Monad m => Fresh m
  where
    fresh :: m Integer

    default fresh :: (m ~ t n, MonadTrans t, Fresh n) => m Integer
    fresh = lift fresh

instance Monad m => Fresh (StateT Integer m)
  where
    fresh = do i <- get; put (i + 1); return i

freshStr :: Fresh m => String -> m String
freshStr base = do i <- fresh; return (base ++ show i)

--------------------------------------------------------------------------------

class Symbol instr
  where
    dry :: Fresh m => instr fs a -> m a

instance (Symbol instr, Symbol jnstr) => Symbol (instr :+: jnstr)
  where
    dry (Inl instr) = dry instr
    dry (Inr jnstr) = dry jnstr

--------------------------------------------------------------------------------

class Name a
  where
    name :: a -> String

data E
  where
    E :: (Typeable a, Name a) => a -> E

instance Eq E
  where
    x == y = compare x y == EQ

instance Ord E
  where
    compare (E a) (E b) = compare (name a) (name b) `mappend`
      compare (typeOf a) (typeOf b)

class Ex a
  where
    hide :: Typeable a => a -> E

--------------------------------------------------------------------------------

type Subst = Map E E

class Substitute exp
  where
    type SubstPred exp :: * -> Constraint
    subst :: SubstPred exp a => Subst -> exp a -> exp a

class TypeablePred pred
  where
    witnessTypeable :: Dict (pred a) -> Dict (Typeable a)

type Sub exp pred = (Substitute exp, SubstPred exp ~ pred, TypeablePred pred)

extendSubst :: (Ex a, Typeable a) => a -> a -> Subst -> Subst
extendSubst x y = Map.insert (hide x) (hide y)

lookupSubst :: (Ex a, Typeable a) => Subst -> a -> a
lookupSubst sub x = fromMaybe x $ case Map.lookup (hide x) sub of
  Just (E y) -> cast y
  Nothing    -> Nothing

--------------------------------------------------------------------------------