{-# LANGUAGE TypeApplications #-} {- | The type @RDualBool@ is like 'Bool', but allows recursive definitions: >>> :{ let x = RDB.true y = x RDB.&& z z = y RDB.|| RDB.false in RDB.get x :} True This finds the greatest solution, i.e. prefers 'True' over 'False': >>> :{ let x = x RDB.&& y y = y RDB.&& x in (RDB.get x, RDB.get y) :} (True,True) Use @RBool@ from "Data.Recursive.Bool" if you want the least solution. -} module Data.Recursive.DualBool (RDualBool, module Data.Recursive.DualBool) where import Data.Recursive.Internal import qualified Data.Propagator.Purify as Purify import Data.Propagator.P2 -- $setup -- >>> :load Data.Recursive.Bool Data.Recursive.DualBool -- >>> :module - Data.Recursive.Bool Data.Recursive.DualBool -- -- >>> :set -XFlexibleInstances -- >>> import Test.QuickCheck -- >>> import qualified Data.Recursive.Bool as RB -- >>> instance Arbitrary RB.RBool where arbitrary = RB.mk <$> arbitrary -- >>> instance Show RB.RBool where show = show . RB.get -- -- >>> import qualified Data.Recursive.DualBool as RDB -- >>> instance Arbitrary RDB.RDualBool where arbitrary = RDB.mk <$> arbitrary -- >>> instance Show RDB.RDualBool where show = show . RDB.get -- | Extracts the value of a 'RDualBool' get :: RDualBool -> Bool get (RDualBool p) = Prelude.not (Purify.get p) -- | prop> RDB.get (RDB.mk b) === b mk :: Bool -> RDualBool mk b = openR $ Purify.mk (Prelude.not b) -- | prop> RDB.get RDB.true == True true :: RDualBool true = openR $ Purify.mk False -- | prop> RDB.get RDB.false == False false :: RDualBool false = openR $ Purify.mk True -- | prop> RDB.get (r1 RDB.&& r2) === (RDB.get r1 && RDB.get r2) (&&) :: RDualBool -> RDualBool -> RDualBool (&&) = openR $ Purify.def2 $ \p1 p2 p -> do whenTop p1 (setTop p) whenTop p2 (setTop p) -- | prop> RDB.get (r1 RDB.|| r2) === (RDB.get r1 || RDB.get r2) (||) :: RDualBool -> RDualBool -> RDualBool (||) = openR $ Purify.def2 $ \p1 p2 p -> whenTop p1 (whenTop p2 (setTop p)) -- | prop> RDB.get (RDB.and rs) === and (map RDB.get rs) and :: [RDualBool] -> RDualBool and = openR $ Purify.defList $ \ps p -> mapM_ @[] (`implies` p) ps -- | prop> RDB.get (RDB.or rs) === or (map RDB.get rs) or :: [RDualBool] -> RDualBool or = openR $ Purify.defList go where go [] p = setTop p go (p':ps) p = whenTop p' (go ps p) -- | prop> RDB.get (RDB.not r1) === not (RB.get r1) not :: RBool -> RDualBool not = openR $ Purify.def1 $ \p1 p -> do implies p1 p -- | The identity function. This is useful when tying the knot, to avoid a loop that bottoms out: -- -- > let x = x in RDB.get x -- -- will not work, but -- -- >>> let x = RDB.id x in RDB.get x -- True -- -- does. -- -- | prop> RDB.get (RDB.id r) === RDB.get r id :: RDualBool -> RDualBool id = openR $ Purify.def1 $ \p1 p -> implies p1 p