module Data.So import Data.Bool %default total ||| Ensure that some run-time Boolean test has been performed. ||| ||| This lifts a Boolean predicate to the type level. See the function `choose` ||| if you need to perform a Boolean test and convince the type checker of this ||| fact. ||| ||| If you find yourself using `So` for something other than primitive types, ||| it may be appropriate to define a type of evidence for the property that you ||| care about instead. public export data So : Bool -> Type where Oh : So True export Uninhabited (So False) where uninhabited Oh impossible ||| Perform a case analysis on a Boolean, providing clients with a `So` proof export choose : (b : Bool) -> Either (So b) (So (not b)) choose True = Left Oh choose False = Right Oh export eqToSo : b = True -> So b eqToSo Refl = Oh export soToEq : So b -> b = True soToEq Oh = Refl ||| If `b` is True, `not b` can't be True export soToNotSoNot : So b -> Not (So (not b)) soToNotSoNot Oh = uninhabited ||| If `not b` is True, `b` can't be True export soNotToNotSo : So (not b) -> Not (So b) soNotToNotSo = flip soToNotSoNot export soAnd : {a : Bool} -> So (a && b) -> (So a, So b) soAnd soab with (choose a) soAnd {a=True} soab | Left Oh = (Oh, soab) soAnd {a=True} soab | Right prf = absurd prf soAnd {a=False} soab | Right prf = absurd soab export andSo : (So a, So b) -> So (a && b) andSo (Oh, Oh) = Oh export soOr : {a : Bool} -> So (a || b) -> Either (So a) (So b) soOr soab with (choose a) soOr {a=True} _ | Left Oh = Left Oh soOr {a=False} _ | Left Oh impossible soOr {a=False} soab | Right Oh = Right soab soOr {a=True} _ | Right Oh impossible export orSo : Either (So a) (So b) -> So (a || b) orSo (Left Oh) = Oh orSo (Right Oh) = rewrite orTrueTrue a in Oh