packages feed

refinery (empty) → 0.1.0.0

raw patch · 9 files changed

+492/−0 lines, 9 filesdep +basedep +containersdep +exceptionssetup-changed

Dependencies added: base, containers, exceptions, mmorph, mtl, pipes, refinery, semigroupoids

Files

+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for refinery++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,29 @@+BSD 3-Clause License++Copyright (c) 2018, Reed Mullanix+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++* Redistributions of source code must retain the above copyright notice, this+  list of conditions and the following disclaimer.++* Redistributions in binary form must reproduce the above copyright notice,+  this list of conditions and the following disclaimer in the documentation+  and/or other materials provided with the distribution.++* Neither the name of the copyright holder nor the names of its+  contributors may be used to endorse or promote products derived from+  this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,8 @@+# refinery++`refinery` is a toolkit for building proof refinement/proof automation systems, based roughly on the [+Algebraic Foundations of Proof Refinement](https://arxiv.org/abs/1703.05215).++## Overview+The main datatype of the library is `TacticT goal extract m a`, which is a monad transformer that behaves as a tactic.+When creating your domain-specific tactics, you should use `RuleT` and `rule` to implement them.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ refinery.cabal view
@@ -0,0 +1,66 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.31.1.+--+-- see: https://github.com/sol/hpack+--+-- hash: f61ff60d97eac2723f257e2cb25ede1205a5cb260049a1708c66c7ab0d4669f8++name:           refinery+version:        0.1.0.0+synopsis:       Toolkit for building proof automation systems+description:    Please see the README on GitHub at <https://github.com/githubuser/refinery#readme>+category:       Language+homepage:       https://github.com/totbwf/refinery#readme+bug-reports:    https://github.com/totbwf/refinery/issues+author:         Reed Mullanix+maintainer:     reedmullanix@gmail.com+copyright:      2019 Reed Mullanix+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+  type: git+  location: https://github.com/totbwf/refinery++library+  exposed-modules:+      Refinery.ProofState+      Refinery.Tactic+      Refinery.Tactic.Internal+  other-modules:+      Paths_refinery+  hs-source-dirs:+      src+  build-depends:+      base >=4.7 && <5+    , containers >=0.5+    , exceptions >=0.10+    , mmorph >=1+    , mtl >=2+    , pipes >=4+    , semigroupoids >=5+  default-language: Haskell2010++test-suite refinery-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_refinery+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , containers >=0.5+    , exceptions >=0.10+    , mmorph >=1+    , mtl >=2+    , pipes >=4+    , refinery+    , semigroupoids >=5+  default-language: Haskell2010
+ src/Refinery/ProofState.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE UndecidableInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Refinery.ProofState+-- Copyright   :  (c) Reed Mullanix 2019+-- License     :  BSD-style+-- Maintainer  :  reedmullanix@gmail.com+--+--+module Refinery.ProofState+  ( ProofStateT(..)+  , axiom+  , mapExtract+  )+where++import Control.Applicative+import Control.Monad+import Control.Monad.Trans+import Control.Monad.Catch+import Control.Monad.Except+import Control.Monad.Reader.Class+import Control.Monad.State.Class+import Control.Monad.IO.Class++import Pipes.Core++newtype ProofStateT ext m jdg = ProofStateT { unProofStateT :: Client jdg ext m ext }++instance (Monad m) => Functor (ProofStateT ext m) where+  fmap f (ProofStateT p) = ProofStateT $ (request . f) >\\ p++instance (Monad m) => Applicative (ProofStateT ext m) where+  pure a = ProofStateT $ request a+  (ProofStateT pf) <*> (ProofStateT pa) = ProofStateT $ (\f -> (request . f) >\\ pa) >\\ pf++instance (Monad m) => Monad (ProofStateT ext m) where+  return = pure+  (ProofStateT p) >>= k = ProofStateT $ (unProofStateT . k) >\\ p++instance MonadTrans (ProofStateT ext) where+  lift m = ProofStateT $ request =<< (lift m)++instance (MonadIO m) => MonadIO (ProofStateT ext m) where+  liftIO m = ProofStateT $ request =<< (liftIO m)++instance (MonadError err m) => MonadError err (ProofStateT ext m) where+  throwError e = ProofStateT $ lift $ throwError e+  catchError (ProofStateT m) h = ProofStateT $ catchError m (unProofStateT . h)++instance (MonadThrow m) => MonadThrow (ProofStateT ext m) where+  throwM e = ProofStateT $ lift $ throwM e++instance (MonadCatch m) => MonadCatch (ProofStateT ext m) where+  catch (ProofStateT m) h = ProofStateT $ catch m (unProofStateT . h)++instance (MonadReader env m) => MonadReader env (ProofStateT ext m) where+  ask = lift ask+  local f (ProofStateT m) = ProofStateT $ local f m++instance (MonadState s m) => MonadState s (ProofStateT ext m) where+  get = lift get+  put = lift . put++axiom :: (Monad m) => ext -> ProofStateT ext m jdg+axiom e = ProofStateT $ return e++mapExtract :: (Monad m) => (ext -> ext') -> (ext' -> ext) -> ProofStateT ext m jdg -> ProofStateT ext' m jdg+mapExtract into out p = ProofStateT $ fmap into ((\j ->  fmap out $ request j) >\\ (unProofStateT p))
+ src/Refinery/Tactic.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE UndecidableInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Refinery.Tactic+-- Copyright   :  (c) Reed Mullanix 2019+-- License     :  BSD-style+-- Maintainer  :  reedmullanix@gmail.com+module Refinery.Tactic+  ( TacticT+  , runTacticT+  -- * Tactic Combinators+  , (<@>)+  , try+  , many_+  , choice+  , progress+  -- * Subgoal Manipulation+  , goal+  , focus+  , forSubgoals+  -- * Tactic Creation+  , MonadExtract(..)+  , MonadRule(..)+  , RuleT+  , rule+  , MonadProvable(..)+  , ProvableT(..)+  , Provable+  , runProvable+  -- * Re-Exports+  , Alt(..)+  ) where++import Data.Functor.Alt+import Control.Applicative+import Control.Monad.Except+import Control.Monad.Reader+import Control.Monad.State.Strict+import Control.Monad.Trans+import Control.Monad.IO.Class+import Control.Monad.Morph++import Data.Bifunctor++import Pipes.Core+import Pipes.Lift (runStateP)++import Refinery.ProofState+import Refinery.Tactic.Internal++-- | Create a tactic that applies each of the tactics in the list to one subgoal.+--+-- When the number of subgoals is greater than the number of provided tactics,+-- the identity tactic is applied to the remainder. When the number of subgoals is+-- less than the number of provided tactics, the remaining tactics are ignored.+(<@>) :: (MonadProvable jdg m) => TacticT jdg ext m () -> [TacticT jdg ext m ()] -> TacticT jdg ext m ()+t <@> ts = stateful t applyTac (ts ++ repeat (pure ()))+  where+    applyTac j = do+      tac <- gets head+      modify tail+      hoist lift $ asRule j tac++-- | Tries to run a tactic, backtracking on failure+try :: (MonadProvable jdg m, MonadError err m) => TacticT jdg ext m () -> TacticT jdg ext m ()+try t = t <!> pure ()++-- | Runs a tactic repeatedly until it fails+many_ :: (MonadProvable jdg m, MonadError err m) => TacticT jdg ext m () -> TacticT jdg ext m ()+many_ t = try (t >> many_ t)++-- | Get the current goal+goal :: (Monad m) => TacticT jdg ext m jdg+goal = TacticT $ get+++-- | @choice err ts@ tries to apply a series of tactics @ts@, and commits to the+-- 1st tactic that succeeds. If they all fail, then @err@ is thrown+choice :: (MonadProvable jdg m, MonadError err m) => err -> [TacticT jdg ext m a] -> TacticT jdg ext m a+choice err [] = throwError err+choice err (t:ts) = t <!> choice err ts++-- | @progress eq err t@ applies the tactic @t@, and checks to see if the+-- resulting subgoals are all equal to the initial goal by using @eq@. If they+-- are, it throws @err@.+progress :: (MonadProvable jdg m, MonadError err m) => (jdg -> jdg -> Bool) -> err ->  TacticT jdg ext m a -> TacticT jdg ext m a+progress eq err t = do+  j <- goal+  a <- t+  j' <- goal+  if j `eq` j' then pure a else throwError err++-- | Apply the first tactic, and then apply the second tactic focused on the @n@th subgoal.+focus :: (MonadProvable jdg m, Monad m) => TacticT jdg ext m () -> Int -> TacticT jdg ext m () -> TacticT jdg ext m ()+focus t ix t' = stateful t applyTac 0+  where+    applyTac j = do+      n <- get+      put (n + 1)+      hoist lift $ asRule j (if n == ix then t' else pure ())++-- | Applies @f@ to every subgoals resulting from the tactic @t@.+forSubgoals :: (Monad m) => TacticT jdg ext m a -> (jdg -> m b) -> TacticT jdg ext m a+forSubgoals t f = TacticT $ StateT $ \j -> ProofStateT $+   action >\\ (unProofStateT $ runStateT (unTacticT t) j)+  where+    action (a, j) = do+      lift $ f j+      request (a, j)++-- | Runs a tactic, producing the extract, along with a list of unsolved subgoals.+runTacticT :: (MonadExtract ext m) => TacticT jdg ext m () -> jdg -> m (ext, [jdg])+runTacticT (TacticT t) j =+  fmap (second reverse) $ flip runStateT [] $ runEffect $ server +>> (hoist lift $ unProofStateT $ execStateT t j)+  where+    server :: (MonadExtract ext m) => jdg -> Server jdg ext (StateT [jdg] m) ext+    server j = do+      modify (j:)+      h <- hole+      respond h >>= server++class (Monad m) => MonadExtract ext m | m -> ext where+  -- | Generates a "hole" of type @ext@, which should represent+  -- an incomplete extract.+  hole :: m ext+  default hole :: (MonadTrans t, MonadExtract ext m1, m ~ t m1) => m ext+  hole = lift hole++instance (MonadExtract ext m) => MonadExtract ext (Proxy a' a b' b m)+instance (MonadExtract ext m) => MonadExtract ext (StateT s m)+instance (MonadExtract ext m) => MonadExtract ext (ReaderT env m)+instance (MonadExtract ext m) => MonadExtract ext (ExceptT err m)+instance (MonadExtract ext m) => MonadExtract ext (RuleT jdg ext m)+++-- | Turn an inference rule into a tactic.+rule :: (Monad m) => (jdg -> RuleT jdg ext m ext) -> TacticT jdg ext m ()+rule r = TacticT $ StateT $ \j -> ProofStateT $ (\j' -> request ((), j')) >\\ unRuleT (r j)
+ src/Refinery/Tactic/Internal.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Refinery.Tactic.Internal+-- Copyright   :  (c) Reed Mullanix 2019+-- License     :  BSD-style+-- Maintainer  :  reedmullanix@gmail.com+--+--+-- = WARNING+-- This module is considered __internal__, and can+-- change at any given time.+module Refinery.Tactic.Internal+  ( TacticT(..)+  , mapTacticT+  , stateful+  , asRule+  , MonadRule(..)+  , RuleT(..)+  , mapRuleT+  , MonadProvable(..)+  , ProvableT(..)+  , Provable+  , runProvable+  )+where++import Data.Functor.Alt+import Control.Applicative+import Control.Monad.Identity+import Control.Monad.Except+import Control.Monad.Catch+import Control.Monad.Reader+import Control.Monad.State.Strict+import Control.Monad.Trans+import Control.Monad.IO.Class+import Control.Monad.Morph++import Data.Bifunctor++import Pipes.Core+import Pipes.Lift (evalStateP, runStateP)++import Refinery.ProofState++-- | A @'TacticT'@ is a monad transformer that performs proof refinement.+-- The type variables signifiy:+--+-- * @jdg@ - The goal type. This is the thing we are trying to construct a proof of.+-- * @ext@ - The extract type. This is what we will recieve after running the tactic.+-- * @m@ - The base monad.+-- * @a@ - The return value. This to make @'TacticT'@ a monad, and will always be @'()'@+newtype TacticT jdg ext m a = TacticT { unTacticT :: StateT jdg (ProofStateT ext m) a }+  deriving ( Functor+           , MonadReader env+           , MonadError err+           , MonadIO+           , MonadThrow+           , MonadCatch+           )++instance (MonadProvable jdg m) => Applicative (TacticT jdg ext m) where+  pure a = TacticT $ StateT $ proving >=> \j -> pure (a, j)+  (<*>) = ap++instance (MonadProvable jdg m) => Monad (TacticT jdg ext m) where+  return = pure+  t >>= k = TacticT $ StateT $ proving >=> \j -> do+    (a, j') <- runStateT (unTacticT t) j+    runStateT (unTacticT $ k a) =<< proving j'++-- | Map the unwrapped computation using the given function+mapTacticT :: (Monad m) => (m a -> m b) -> TacticT jdg ext m a -> TacticT jdg ext m b+mapTacticT f (TacticT m) = TacticT $ m >>= (lift . lift . f . return)++instance (MonadError err m) => Alt (TacticT jdg ext m) where+  (TacticT t1) <!> (TacticT t2) = TacticT $ t1 `catchError` (const t2)++instance MonadTrans (TacticT jdg ext) where+  lift m = TacticT $ lift $ lift m++instance (MonadProvable jdg m, MonadState s m) => MonadState s (TacticT jdg ext m) where+  get = lift get+  put = lift . put++-- | Helper function for making "stateful" tactics like "<@>"+stateful :: (Monad m) => TacticT jdg ext m a -> (jdg -> RuleT jdg ext (StateT s m) ext) -> s -> TacticT jdg ext m a+stateful (TacticT t) f s = TacticT $ StateT $ \j -> ProofStateT $+  evalStateP s $ action >\\ (hoist lift $ unProofStateT $ runStateT t j)+  where+    action (a, j) = (\j' -> request (a, j')) >\\ (unRuleT $ f j)++-- | Transforms a tactic into a rule. Useful for doing things with @'stateful'@.+asRule :: (Monad m) => jdg -> TacticT jdg ext m a -> RuleT jdg ext m ext+asRule j t = RuleT $ unProofStateT $ execStateT (unTacticT t) j++-- | A @'RuleT'@ is a monad transformer for creating inference rules.+newtype RuleT jdg ext m a = RuleT { unRuleT :: Client jdg ext m a }+  deriving ( Functor+           , Applicative+           , Monad+           , MonadReader env+           , MonadState s+           , MonadError err+           , MonadIO+           , MonadThrow+           , MonadCatch+           , MonadTrans+           , MFunctor+           )+++-- | Map the unwrapped computation using the given function+mapRuleT :: (Monad m) => (m a -> m b) -> RuleT jdg ext m a -> RuleT jdg ext m b+mapRuleT f (RuleT m) = RuleT $ m >>= (lift . f . return)++class (Monad m) => MonadRule jdg ext m | m -> jdg, m -> ext where+  -- | Create a subgoal, and return the resulting extract.+  subgoal :: jdg -> m ext+  default subgoal :: (MonadTrans t, MonadRule jdg ext m1, m ~ t m1) => jdg -> m ext+  subgoal = lift . subgoal++instance (Monad m) => MonadRule jdg ext (RuleT jdg ext m) where+  subgoal j = RuleT $ request j++instance (MonadRule jdg ext m) => MonadRule jdg ext (ReaderT env m)+instance (MonadRule jdg ext m) => MonadRule jdg ext (StateT env m)+instance (MonadRule jdg ext m) => MonadRule jdg ext (ExceptT env m)+instance (MonadRule jdg ext m) => MonadRule jdg ext (ProvableT env m)++class (Monad m) => MonadProvable jdg m | m -> jdg where+  -- | Applies a transformation of goals at every step of the tactic.+  proving :: jdg -> m jdg+  default proving :: (MonadTrans t, MonadProvable jdg m1, m ~ t m1) => jdg -> m jdg+  proving = lift . proving++instance (MonadProvable jdg m) => MonadProvable jdg (ProofStateT ext m)+instance (MonadProvable jdg m) => MonadProvable jdg (ReaderT r m)+instance (MonadProvable jdg m) => MonadProvable jdg (StateT s m)+instance (MonadProvable jdg m) => MonadProvable jdg (ExceptT err m)+instance (Monad m) => MonadProvable jdg (ProvableT jdg m) where+  proving = pure++-- | Helper newtype for when you don't have any need for the mechanisms of MonadProvable.+newtype ProvableT jdg m a = ProvableT { runProvableT :: m a }+  deriving (Functor, Applicative, Monad, MonadIO, MonadState s, MonadError err)++type Provable jdg a = ProvableT jdg Identity a++instance MonadTrans (ProvableT jdg) where+  lift = ProvableT++runProvable :: Provable jdg a -> a+runProvable = runIdentity . runProvableT
+ test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"