úÎ!jv_üš      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„ … † ‡ ˆ ‰ Š ‹ Œ  Ž   ‘ ’ “ ” • – — ˜ ™ š › œ  ž Ÿ   Ą ˘ Ł ¤ Ľ Ś § ¨ Š Ş Ť Ź ­ Ž Ż ° ą ˛ ł ´ ľ ś ˇ ¸ (c) Matt Noonan 2018 BSD-stylematt.noonan@gmail.comportableSafe -.=?@AHVígdp"A specialized proxy for arguments.gdp(Position: the right-hand side of a type.gdp'Position: the left-hand side of a type.gdp•Get or modify a type within a larger type. This is entirely a type-level operation, there is nothing corresponding to a value access or update.gdp!Inhabitant of the argument proxy.(c) Matt Noonan 2018 BSD-stylematt.noonan@gmail.comportableNone%8@ACg gdp2A class for extracing "the" underlying value.  , should ideally be a coercion from some newtype wrap of a back to a.2For this common use case, in the module where newtype New a = New a is defined, an instance of The) can be created with an empty definition: -newtype New a = New a instance The (New a) a gdp=A view pattern for discarding the wrapper around a value. f (The x) = expression x is equivalent to &f x = let x' = the x in expression x'   (c) Matt Noonan 2018 BSD-stylematt.noonan@gmail.comportableSafe-H6  gdpThe Proof] type is used as a domain-specific language for constructing proofs. A value of type Proof p' represents a proof of the proposition p.A Proof› as an argument to a function represents an assumption. For example, this function constructions a proof of "P or Q" from the assumption "P and Q": Nand2or :: Proof (p && q) -> Proof (p || q) and2or pq = introOrL $ elimAndL pq If the body of the proof does not match the proposition you claim to be proving, the compiler will raise a type error. Here, we accidentally try to use  or_introR instead of  or_introL: Nand2or :: Proof (p && q) -> Proof (p || q) and2or pq = introOrR $ elimAndL pqresulting in the error ˙‹ " Couldn't match type p  with q  p  is a rigid type variable bound by the type signature for: and2or :: forall p q. Proof (p && q) -> Proof (p || q) q  is a rigid type variable bound by the type signature for: and2or :: forall p q. Proof (p && q) -> Proof (p || q) Expected type: Proof (p || q) Actual type: Proof (p || p) gdpsorryÍ can be used to provide a "proof" of any proposition, by simply assering it as true. This is useful for stubbing out portions of a proof as you work on it, but subverts the entire proof system.#_Completed proofs should never use sorry!_gdpaxiom, like sorry4, provides a "proof" of any proposition. Unlike sorryC, which is used to indicate that a proof is still in progress, axioml is meant to be used by library authors to assert axioms about how their library works. For example: ‘data Reverse xs = Reverse Defn data Length xs = Length Defn revLengthLemma :: Proof (Length (Reverse xs) == Length xs) revLengthLemma = axiom   (c) Matt Noonan 2018 BSD-stylematt.noonan@gmail.comportableNone ,-=>?@AHISUVXM?gdpThe  Defining P& constraint holds in any module where P has been defined as a newtype wrapper of Defn. It holds only' in that module, if the constructor of P is not exported.gdpLLibrary authors can introduce new names in a controlled way by creating newtype wrappers of Defn. The constructor of the newtype should *not* be exported, so that the library can retain control of how the name is introduced. If a newtype wrapper of DefnG contains phantom parameters, these parameters should be given the nominal type role; otherwise, library users may be able to use coercions to manipulate library-specific names in a manner not blessed by the library author. čnewtype Bob = Bob Defn bob :: Int ~~ Bob bob = defn 42 newtype FooOf name = FooOf Defn type role FooOf nominal -- disallow coerce :: FooOf name1 -> FooOf name2 fooOf :: (Int ~~ name) -> (Int ~~ FooOf name) fooOf x = defn (the x) gdpAn infix alias for .gdpA value of type  a ~~ name; has the same runtime representation as a value of type a$, with a phantom "name" attached.gdpZIntroduce a name for the argument, and pass the named argument into the given function.gdpSame as , but names two values at once.gdpSame as !, but names three values at once.gdpIn the module where the name f is defined, attach the name f to a value.(c) Matt Noonan 2018 BSD-stylematt.noonan@gmail.comportableNone-.=>?@AHISUVXbŁ gdpAn infix alias for .gdpThe Equalsą relation is used to express equality between two entities. Given an equality, you are then able to substitute one side of the equality for the other, anywhere you please.gdp&Chain equalities, a la Liquid Haskell.gdp.Apply a function to both sides of an equality.gdp‚Given a formula and an equality over one of its arguments, replace the left-hand side of the equality with the right-hand side.gdp Substitute x' for x under the function f*, on the left-hand side of an equality.gdp Substitute x' for x under the function f+, on the right-hand side of an equality. gdpcTest if the two named arguments are equal and, if so, produce a proof of equality for the names.!gdpReflect an equality between x and y/ into a propositional equality between the types x and y. ˙+newtype Bob = Bob Defn bob :: Int ~~ Bob bob = defn 42 needsBob :: (Int ~~ Bob) -> Int needsBob x = the x + the x isBob :: (Int ~~ name) -> Maybe (Proof (name == Bob)) isBob = same x bob f :: (Int ~~ name) -> Int f x = case reflectEq <$> isBob x of Nothing -> 17 Just Refl -> needsBob x x "gdp3Convert a propositional equality between the types x and y into a proof of x == y.  !"  !"4(c) Matt Noonan 2018 BSD-stylematt.noonan@gmail.comportableNone-@AHUV˘A 'gdp A function f is  injective if  f x == f y implies x == y . The  Injective f) typeclass provides a single method, *elim_inj :: (f x == f y) -> Proof (x == y).Within the module where F is defined, you can declare F, to be injective with an empty instance: L-- {x} == {y} implies x == y. data Singleton x instance Injective Singleton )gdpA binary operation # distributes over % on the right if (x % y)  z == (x  z) % (y # z) for all x, y, and z . The DistributiveR c c') typeclass provides a single method, ;distributiveR :: Proof (c (c' x y) z == c' (c x z) (c y z)).Within the module where F and G are defined, you can declare F to distribute over G$ on the left with an empty instance: -- (x  Intersect y) Union z == (x Union z)  Intersect (y UnionQ z) data Union x y data Intersect x y instance DistributiveR Union Intersect +gdpA binary operation # distributes over % on the left if x  (y % z) == (x  y) % (x # z) for all x, y, and z . The DistributiveL c c') typeclass provides a single method, ;distributiveL :: Proof (c x (c' y z) == c' (c x y) (c x z)).Within the module where F and G are defined, you can declare F to distribute over G$ on the left with an empty instance: -- x Union (y  Intersect z) == (x Union y)  Intersect (x UnionQ z) data Union x y data Intersect x y instance DistributiveL Union Intersect -gdpA binary operation # is associative if x  (y  z) == (x  y)  z for all x, y, and z . The  Associative c) typeclass provides a single method, 1associative :: Proof (c x (c y z) == c (c x y) z).Within the module where F is defined, you can declare F. to be associative with an empty instance: T-- Define an associative binary operation data Union x y instance Associative Union /gdpA binary operation # is commutative if x  y == y  x for all x and y . The  Commutative c) typeclass provides a single method, %commutative :: Proof (c x y == c y x).Within the module where F is defined, you can declare F. to be commutative with an empty instance: S-- Define a commutative binary operation data Union x y instance Commutative Union 1gdpA binary operation # is idempotent if  x # x == x for all x . The  Idempotent c) typeclass provides a single method,  idempotent :: Proof (c p p == p).Within the module where F is defined, you can declare F- to be idempotent with an empty instance: R-- Define an idempotent binary operation data Union x y instance Idempotent Union 3gdpA binary relation R is  transitivea if, for all x, y, z, if R(x, y) is true and R(y, z) is true, then R(x, z) is true. The  Transitive r) typeclass provides a single method, -transitive :: r x y -> r y z -> Proof (r x z)/, proving R(x, z) from R(x, y) and R(y, z).Within the module where R! is defined, you can declare R) to be transitive with an empty instance: V-- Define a transitive binary relation data CanReach p q instance Transitive CanReach 5gdpA binary relation R is  symmetricR if, for all x and y, R(x, y) is true if and only if R(y, x) is true. The  Symmetric) typeclass provides a single method, #symmetric :: r x y -> Proof (r y x)8, proving the implication "R(x, y) implies R(y, x)".Within the module where R! is defined, you can declare R( to be symmetric with an empty instance: P-- Define a symmetric binary relation data NextTo p q instance Symmetric NextTo 7gdpA binary relation R is  reflexive) if, for all x, R(x, x) is true. The  Reflexive r) typeclass provides a single method, refl :: Proof (r x x)), proving R(x, x) for an arbitrary x.%Within the module where the relation R! is defined, you can declare R( to be reflexive with an empty instance: V-- Define a reflexive binary relation data SameColor p q instance Reflexive SameColor 9gdp transitive, with the arguments flipped.'()*+,-./0123456789785634912/0-.+,)*'((c) Matt Noonan 2018 BSD-stylematt.noonan@gmail.comportableNone-@AHSUVXű8=gdpšThe inference rules so far have all been valid in constructive logic. Proofs in classical logic are also allowed, but will be constrained by the = typeclass.>gdpAn infix alias for Implies.?gdpAn infix alias for And.@gdpAn infix alias for And.AgdpAn infix alias for And.BgdpAn infix alias for Or.CgdpAn infix alias for Or.DgdpAn infix alias for Or.EgdpUniversal quantification.FgdpExistential quantifiation.GgdpThe implication "p implies q".HgdpThe negation of p.IgdpThe disjunction of p and q.JgdpThe conjunction of p and q.KgdpThe constant "false".LgdpThe constant "true".Mgdp5Apply a derivation to the left side of a conjunction.Ngdp6Apply a derivation to the right side of a conjunction.Ogdp?Apply derivations to the left and right sides of a conjunction.Pgdp5Apply a derivation to the left side of a disjunction.Qgdp6Apply a derivation to the right side of a disjunction.Rgdp?Apply derivations to the left and right sides of a disjunction.Sgdp6Apply a derivation to the left side of an implication.Tgdp7Apply a derivation to the right side of an implication.Ugdp@Apply derivations to the left and right sides of an implication.Vgdp(Apply a derivation inside of a negation.WgdpTRUE@ is always true, and can be introduced into a proof at any time.XgdpKThe Law of Noncontradiction: for any proposition P, "P and not-P" is false.YgdpProve "P and Q" from P and Q.ZgdpProve "P and Q" from Q and P.[gdpProve "P or Q" from P.\gdpProve "P or Q" from Q.]gdpUProve "P implies Q" by demonstrating that, from the assumption P, you can prove Q.^gdpaProve "not P" by demonstrating that, from the assumption P, you can derive a false conclusion._gdp_ is an alias for ^|, with a somewhat more suggestive name. Not-introduction corresponds to the proof technique "proof by contrapositive".`gdpProve a contradiction from P and "not P".agdpa is `Q with the arguments flipped. It is useful when you want to partially apply ` to a negation.bgdpIProve "For all x, P(x)" from a proof of P(*c*) with *c* indeterminate.cgdpMProve "There exists an x such that P(x)" from a specific instance of P(c).dgdp4From the assumption "P and Q", produce a proof of P.egdp4From the assumption "P and Q", produce a proof of Q.šgdpNFrom the assumption "P and Q", produce both a proof of P, and a proof of Q.fgdpeIf you have a proof of R from P, and a proof of R from Q, then convert "P or Q" into a proof of R.ggdp1Eliminate the first alternative in a conjunction.hgdp2Eliminate the second alternative in a conjunction.igdpAGiven "P imples Q" and P, produce a proof of Q. (modus ponens)jgdp modusPonens is just id with the arguments flipped. In this form, it is useful for partially applying an implication.kgdpHModus tollens lets you prove "Not P" from "P implies Q" and "Not Q".JModus tollens is not fundamental, and can be derived from other rules: ˙ ----- (assumption) p --> q, p --------------------- (modus ponens) Not q, q -------------------------- (contradicts') FALSE ------------------------------------- (not-intro) Not p #We can encode this proof tree as a Proof to implement  modus_tollens: ŽmodusTollens :: Proof (p --> q) -> Proof (Not q) -> Proof (Not p) modusTollens impl q' = introNot $ p -> contradicts' q' (modusPonens impl p) lgdpFrom a falsity, prove anything.mgdpCGiven "For all x, P(x)" and any c, prove the proposition "P(c)".ngdp~Given a proof of Q from P(c) with c generic, and the statement "There exists an x such that P(x)", produce a proof of Q.ogdp Discharge a  ClassicalJ constraint, by saying "I am going to allow a classical argument here."{NOTE: The existence of this function means that a proof should only be considered constructive if it does not have a  Classical) constraint, *and* it does not invoke  classically.pgdpiThe Law of the Excluded Middle: for any proposition P, assert that either P is true, or Not P is true.qgdp†Proof by contradiction: this proof technique allows you to prove P by showing that, from "Not P", you can prove a falsehood.WProof by contradiction is not a theorem of constructive logic, so it requires the  Classicali constraint. But note that the similar technique of proof by contrapositive (not-introduction) isE valid in constructive logic! For comparison, the two types are: ˜contradiction :: Classical => (Proof (Not p) -> Proof FALSE) -> Proof p introNot :: (Proof p -> Proof FALSE) -> Proof (Not p) The derivation of proof by contradiction from the law of the excluded middle goes like this: first, use the law of the excluded middle to prove  p || Not p#. Then use or-elimination to prove pž for each alternative. The first alternative is immediate; for the second alternative, use the provided implication to get a proof of falsehood, from which the desired conclusion is derived. 3contradiction impl = elimOr id (absurd . impl) lem rgdp Alias for q, to complete the patterns.sgdpfDouble-negation elimination. This is another non-constructive proof technique, so it requires the  Classical constraint.€The derivation of double-negation elimination follows from proof by contradiction, since "Not (Not p)" contradicts "Not p". 3 elimNotNot p'' = contradiction (contradicts' p'') 7=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrs7LKJA@?IDCBG>HEFWXYZ[\]^_`abcdefghijklmn=opqrsMNOPQRSTUV >1?3@3A3B2C2D2G1I2J3(c) Matt Noonan 2018 BSD-stylematt.noonan@gmail.comportableNone-@AHĽ€gdpA binary relation R is  antisymmetricO if, for all x and y, when R(x, y) is true, then R(y, x) is false. The  Antisymmetric) typeclass provides a single method, -antisymmetric :: r x y -> Proof (Not (r y x))H, proving the implication "R(x, y) implies the negation of R(y, x)".Within the module where R! is defined, you can declare R, to be antisymmetric with an empty instance: Ÿ-- Define an antisymmetric binary relation newtype AncestorOf p q = AncestorOf Defn type role DifferentColor nominal nominal instance Antisymmetric AncestorOf ‚gdpA binary relation R is  irreflexive* if, for all x, R(x, x) is false. The  Irreflexive r) typeclass provides a single method, irrefl :: Proof (Not (r x x))9, proving the negation of R(x, x) for an arbitrary x.%Within the module where the relation R! is defined, you can declare R* to be irreflexive with an empty instance: §-- Define an irreflexive binary relation newtype DifferentColor p q = DifferentColor Defn type role DifferentColor nominal nominal instance Irreflexive DifferentColor €‚ƒ‚ƒ€ NoneISUVX ¸„gdpA Fact p is an implicit proof of p0, propagated by Haskell's constraint solver. Fact|s can be used to implicitly introduce or propagate proofs. However, there is a small run-time cost associated with using Fact!s: typeclass dictionaries for Factds are still passed around. In many cases they will be optimized away, but this is not guaranteed.…gdpRecall a known Fact from the implicit context.†gdp$Add a proof to the implicit context.‡gdpAApply an implication to a predicate in the implicit context. The (a ~~ n)k parameter is not actually used; it's type is used to help select a specific fact from the context. @ -- A safe ş€ function, using an implicitly-passed safety proof. head :: Fact (IsCons xs) => ([a] ~~ xs) -> a head xs = Prelude.head (the xs)†- Reverse, and a lemma. reverse :: ([a] ~~ xs) -> ([a] ~~ Reverse xs) revConsLemma :: Proof (IsCons xs) -> Proof (IsCons (Reverse xs))- Implement a safe ťU function. last :: Fact (IsCons xs) => ([a] ~~ xs) -> a last xs = note (revConsLemma ‡ xs) $ head (reverse xs)„…†‡„…†‡ None %&'>IUVX_g5öˆgdp'A name for referring to the empty list.‰gdp(A name for referring to the result of a cons operation.ŠgdpA variation on ListCaseQ that passes the shape facts implicitly. Pattern-matching on a constructor of  ListCase'4 will bring a shape proof into the implicit context.gdpTPossible shapes of a list, along with evidence that the list has the given shape.‘gdpNames for the parts of a list.”gdp.Predicates about the possible shapes of lists.™gdpcClassify a named list by shape, producing evidence that the list matches the corresponding case.šgdp*Classify a named list by shape, producing implicit: evidence that the list matches the corresponding case.›gdp0Extract the first element from a non-empty list.œgdp8Extract all but the first element from a non-empty list.gdp2Construct a list from an element and another list.žgdpThe empty list, named Nil'.Ÿgdp Axiom: The head of cons x y is x. gdp Axiom: The tail of cons x y is y.ĄgdpAxiom: The result of cons x y satisfies IsCons.źgdp Axiom: The empty list satisfies IsNil.˘gdpAxiom: a list xs satisfies exactly one of  IsCons xs or IsNil xs.Łgdp$Induction principle: If a predicate P$ is true for the empty list, and PJ is true for a list whenever it is true for the tail of the list, then P is true.ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ Ą˘Ł¤ĽŚ›œž‘‰ˆ”“’¤ĽŚ˘ĄŸ Ł™Ž˜—šŠ‹Œ–• (c) Matt Noonan 2018 BSD-stylematt.noonan@gmail.comportableNone =>?@AHISUVX\(§gdpAn infix alias for ¨.¨gdp Given a type a and a predicate p, the type a ?p represents a refinement type for a. Values of type a ?p should be values of type a that satisfy the predicate p.Values of type a ?p= have the same run-time representation as values of type a; the proposition p2 does not carry a run-time space or time cost.Šgdp Given a type a and a proposition p, the type  (a ::: p) represents a value of type a(, with an attached "ghost proof" of p.Values of the type  (a ::: p)L have the same run-time representation as values of the normal type a; the proposition p2 does not carry a run-time space or time cost.ŞgdpMGiven a value and a proof, attach the proof as a ghost proof on the value.ŤgdpZGiven a value and a proof, apply a function to the value but leave the proof unchanged.Źgdp\Apply an implication to the ghost proof attached to a value, leaving the value unchanged.­gdp+Forget the ghost proof attached to a value.Žgdp%Extract the ghost proof from a value.Żgdp2For library authors: assert that a property holds.°gdpCExistential introduction for names: given a named value of type a that satisfies a predicate p, coerce to a value of type a ?p.ągdp<Existential elimination for names: given a value of type a ?p8, re-introduce a new name to produce a value of type a ~~ name ::: p name.˛gdpŮTake a simple function with one named argument and a named return, plus an implication relating a precondition to a postcondition of the function, and produce a function between refined input and output types. ˙‹newtype NonEmpty xs = NonEmpty Defn type role Nonempty nominal -- disallows coercion of Nonempty's argument. newtype Reverse xs = Reverse Defn type role Reverse nominal rev :: ([a] ~~ xs) -> ([a] ~~ Reverse xs) rev xs = defn (reverse (the xs)) rev_nonempty_lemma :: NonEmpty xs -> Proof (NonEmpty (Reverse xs)) rev' :: ([a] ?NonEmpty) -> ([a] ?NonEmpty) rev' = rev ...? rev_nonempty_lemma łgdp[Traverse a collection of refined values, re-introducing names in the body of the action.´gdpSame as ł(, but ignores the action's return value.ľgdpSame as ˝", with the argument order flipped.śgdpSame as ž", with the argument order flipped.§¨ŠŞŤŹ­ŽŻ°ą˛ł´ľśŠŞŹŤ­Ž¨§Ż°ą˛ł´ľś§1Š1 (c) Matt Noonan 2018 BSD-stylematt.noonan@gmail.comportableNone]đ‚  !"'()*+,-./0123456789=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrs€‚ƒ„…†‡§¨ŠŞŤŹ­ŽŻ°ą˛ł´ľśż   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ   ‘ ’ “ ” • – — ˜ ™ š › œ  ž Ÿ   Ą  ž ˘ Ł ¤ Ľ Ś § ¨ Š Ş Ť Ź ­ Ž Ż ° ą ˛ ł ´ ľ ś ˇ ¸ š ş ť ź ˝ ž ż Ŕ ÁÂĂĤĂÄĹ ĆĂÇČĂÉĘË"gdp-0.0.3.0-5O0GJYKNp10L7mAjmy8H8vData.ArgumentsData.The Logic.Proof Theory.NamedTheory.Equality Logic.ClassesLogic.PropositionalLogic.NegClassesLogic.Implicit Theory.Lists Data.RefinedGDPArgRHSLHSArgumentGetArgSetArgarg$fArgumentTYPETYPEEitherLHS$fArgumentTYPETYPEEitherRHSThetheProofsorryaxiomDefiningDefn~~Namednamename2name3defn $fTheNameda==Equals==.apply substitute substituteL substituteRsame reflectEqpropEq$fArgumentTYPETYPEEqualsRHS$fArgumentTYPETYPEEqualsLHS$fArgumentTYPENatEquals1$fArgumentTYPENatEquals0 Injectiveelim_inj DistributiveR distributiveR DistributiveL distributiveL Associative associative Commutative commutative Idempotent idempotent Transitive transitive Symmetric symmetric Reflexiverefl transitive'$fReflexivekTYPEEquals$fSymmetrickTYPEEquals$fTransitivekTYPEEquals Classical-->/\∧&&\/∨||ForAllExistsImpliesNotOrAndFALSETRUEfirstAnd secondAndbimapAndfirstOrsecondOrbimapOrlmapImplrmapImpl dimapImplmapNottrue noncontraintroAnd introAnd'introOrLintroOrR introImplintroNotcontrapositive contradicts contradicts' introUnivintroExelimAndLelimAndRelimOrelimOrLelimOrRelimImpl modusPonens modusTollensabsurdelimUnivelimEx classicallylem contradictionelimNot elimNotNot$fDistributiveLkTYPEAndAnd$fDistributiveRkTYPEAndAnd$fAssociativeTYPEAnd$fSymmetrickTYPEAnd$fDistributiveLkTYPEOrOr$fDistributiveLkTYPEOrAnd$fDistributiveLkTYPEAndOr$fDistributiveRkTYPEOrOr$fDistributiveRkTYPEOrAnd$fDistributiveRkTYPEAndOr$fAssociativeTYPEOr$fSymmetrickTYPEOr Antisymmetric antisymmetric IrreflexiveirreflFactknownnoteonNil'Cons' ListCase'Cons_Nil_ListCaseIsCons_IsNil_TailHeadIsNilIsConsIsListNilConsclassify classify'headtailconsnil headOfCons tailOfCons consIsCons listShapes listInduction consIsList nilIsList listIsList? Satisfies:::...$:...>exorciseconjureassertunnamerename...? traverseP traverseP_forPforP_ $fThe:::a$fTheSatisfiesaelimAndbaseGHC.ListlastnilIsNilData.Traversabletraverse Data.Foldable traverse_