sbv-14.4: Data/SBV/Either.hs
-----------------------------------------------------------------------------
-- |
-- Module : Data.SBV.Either
-- Copyright : (c) Joel Burget
-- Levent Erkok
-- License : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Symbolic coproduct, symbolic version of Haskell's 'Either' type.
-----------------------------------------------------------------------------
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wall -Werror -Wno-orphans #-}
module Data.SBV.Either (
-- * Constructing sums
sLeft, sRight, liftEither, SEither, sEither, sEither_, sEithers
-- * Destructing sums
, either
-- * Mapping functions
, bimap, first, second
-- * Scrutinizing branches of a sum
, isLeft, isRight, fromLeft, fromRight
-- * Case analysis (for sCase quasi-quoter)
, sCaseEither, getLeft_1, getRight_1
) where
import Prelude hiding (either)
import qualified Prelude
import Data.SBV.Client
import Data.SBV.Core.Data
import Data.SBV.Core.Model (OrdSymbolic(..))
import Data.SBV.SCase (sCase)
#ifdef DOCTEST
-- $setup
-- >>> import Prelude hiding(either)
-- >>> import Data.SBV
#endif
-- | Make 'Either' symbolic.
--
-- >>> sLeft 3 :: SEither Integer Bool
-- Left 3 :: Either Integer Bool
-- >>> isLeft (sLeft 3 :: SEither Integer Bool)
-- True
-- >>> isLeft (sRight sTrue :: SEither Integer Bool)
-- False
-- >>> sRight sFalse :: SEither Integer Bool
-- Right False :: Either Integer Bool
-- >>> isRight (sLeft 3 :: SEither Integer Bool)
-- False
-- >>> isRight (sRight sTrue :: SEither Integer Bool)
-- True
mkSymbolic [''Either]
-- | Declare a symbolic either.
sEither :: (SymVal a, SymVal b) => String -> Symbolic (SEither a b)
sEither = free
-- | Declare a symbolic either, unnamed.
sEither_ :: (SymVal a, SymVal b) => Symbolic (SEither a b)
sEither_ = free_
-- | Declare a list of symbolic eithers.
sEithers :: (SymVal a, SymVal b) => [String] -> Symbolic [SEither a b]
sEithers = symbolics
-- | Construct an @SEither a b@ from an @Either (SBV a) (SBV b)@
--
-- >>> liftEither (Left 3 :: Either SInteger SBool)
-- Left 3 :: Either Integer Bool
-- >>> liftEither (Right sTrue :: Either SInteger SBool)
-- Right True :: Either Integer Bool
liftEither :: (SymVal a, SymVal b) => Either (SBV a) (SBV b) -> SEither a b
liftEither = Prelude.either sLeft sRight
-- | Case analysis for symbolic 'Either's. If the value 'isLeft', apply the
-- first function; if it 'isRight', apply the second function.
--
-- >>> either (*2) (*3) (sLeft (3 :: SInteger))
-- 6 :: SInteger
-- >>> either (*2) (*3) (sRight (3 :: SInteger))
-- 9 :: SInteger
-- >>> let f = uninterpret "f" :: SInteger -> SInteger
-- >>> let g = uninterpret "g" :: SInteger -> SInteger
-- >>> prove $ \x -> either f g (sLeft x) .== f x
-- Q.E.D.
-- >>> prove $ \x -> either f g (sRight x) .== g x
-- Q.E.D.
either :: forall a b c. (SymVal a, SymVal b, SymVal c)
=> (SBV a -> SBV c)
-> (SBV b -> SBV c)
-> SEither a b
-> SBV c
either brA brB sab = [sCase| sab of
Left x -> brA x
Right x -> brB x
|]
-- | Map over both sides of a symbolic 'Either' at the same time
--
-- >>> let f = uninterpret "f" :: SInteger -> SInteger
-- >>> let g = uninterpret "g" :: SInteger -> SInteger
-- >>> prove $ \x -> fromLeft (bimap f g (sLeft x)) .== f x
-- Q.E.D.
-- >>> prove $ \x -> fromRight (bimap f g (sRight x)) .== g x
-- Q.E.D.
bimap :: forall a b c d. (SymVal a, SymVal b, SymVal c, SymVal d)
=> (SBV a -> SBV b)
-> (SBV c -> SBV d)
-> SEither a c
-> SEither b d
bimap brA brC = either (sLeft . brA) (sRight . brC)
-- | Map over the left side of an 'Either'
--
-- >>> let f = uninterpret "f" :: SInteger -> SInteger
-- >>> prove $ \x -> first f (sLeft x :: SEither Integer Integer) .== sLeft (f x)
-- Q.E.D.
-- >>> prove $ \x -> first f (sRight x :: SEither Integer Integer) .== sRight x
-- Q.E.D.
first :: (SymVal a, SymVal b, SymVal c) => (SBV a -> SBV b) -> SEither a c -> SEither b c
first f = bimap f id
-- | Map over the right side of an 'Either'
--
-- >>> let f = uninterpret "f" :: SInteger -> SInteger
-- >>> prove $ \x -> second f (sRight x :: SEither Integer Integer) .== sRight (f x)
-- Q.E.D.
-- >>> prove $ \x -> second f (sLeft x :: SEither Integer Integer) .== sLeft x
-- Q.E.D.
second :: (SymVal a, SymVal b, SymVal c) => (SBV b -> SBV c) -> SEither a b -> SEither a c
second = bimap id
-- | Return the value from the left component. The behavior is undefined if
-- passed a right value, i.e., it can return any value.
--
-- >>> fromLeft (sLeft (literal 'a') :: SEither Char Integer)
-- 'a' :: SChar
-- >>> prove $ \x -> fromLeft (sLeft x :: SEither Char Integer) .== (x :: SChar)
-- Q.E.D.
-- >>> sat $ \x -> x .== (fromLeft (sRight 4 :: SEither Char Integer))
-- Satisfiable. Model:
-- s0 = 'A' :: Char
--
-- Note how we get a satisfying assignment in the last case: The behavior
-- is unspecified, thus the SMT solver picks whatever satisfies the
-- constraints, if there is one.
fromLeft :: forall a b. (SymVal a, SymVal b) => SEither a b -> SBV a
fromLeft = getLeft_1
-- | Return the value from the right component. The behavior is undefined if
-- passed a left value, i.e., it can return any value.
--
-- >>> fromRight (sRight (literal 'a') :: SEither Integer Char)
-- 'a' :: SChar
-- >>> prove $ \x -> fromRight (sRight x :: SEither Char Integer) .== (x :: SInteger)
-- Q.E.D.
-- >>> sat $ \x -> x .== (fromRight (sLeft (literal 2) :: SEither Integer Char))
-- Satisfiable. Model:
-- s0 = 'A' :: Char
--
-- Note how we get a satisfying assignment in the last case: The behavior
-- is unspecified, thus the SMT solver picks whatever satisfies the
-- constraints, if there is one.
fromRight :: forall a b. (SymVal a, SymVal b) => SEither a b -> SBV b
fromRight = getRight_1
-- | Custom 'OrdSymbolic' instance over 'SEither'.
instance (OrdSymbolic (SBV a), OrdSymbolic (SBV b), SymVal a, SymVal b) => OrdSymbolic (SBV (Either a b)) where
eab .< ecd = either (\a -> either (a .<) (const sTrue) ecd)
(\b -> either (const sFalse) (b .<) ecd)
eab
{- HLint ignore module "Reduce duplication" -}