diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..6e68ebe8 Binary files /dev/null and b/.DS_Store differ diff --git a/examples/Wrappers.dfy b/examples/Wrappers.dfy index f9f5b111..f40a5716 100644 --- a/examples/Wrappers.dfy +++ b/examples/Wrappers.dfy @@ -10,6 +10,9 @@ include "../src/Wrappers.dfy" module Demo { import opened Wrappers + import opened Wrappers.Option + import opened Wrappers.Result + import opened Wrappers.Outcome // ------ Demo for Option ---------------------------- // We use Option when we don't need to pass around a reason for the failure, @@ -60,7 +63,7 @@ module Demo { // Sometimes we want to go from Option to Result: method FindName(m: MyMap) returns (res: Result) { // Will return a default error message in case of None: - res := m.Get("name").ToResult(); + res := ToResult(m.Get("name")); // We can also match on the option to write a custom error: match m.Get("name") case Some(n) => res := Success(n); diff --git a/src/Collections/Arrays/BinarySearch.dfy b/src/Collections/Arrays/BinarySearch.dfy index d8bfeb4f..03f6d450 100644 --- a/src/Collections/Arrays/BinarySearch.dfy +++ b/src/Collections/Arrays/BinarySearch.dfy @@ -9,7 +9,7 @@ include "../../Wrappers.dfy" include "../../Relations.dfy" module BinarySearch { - import opened Wrappers + import opened Wrappers.Option import opened Relations method BinarySearch(a: array, key: T, less: (T, T) -> bool) returns (r: Option) diff --git a/src/Collections/Maps/Imaps.dfy b/src/Collections/Maps/Imaps.dfy index b5da6de7..e1c4829d 100644 --- a/src/Collections/Maps/Imaps.dfy +++ b/src/Collections/Maps/Imaps.dfy @@ -12,7 +12,7 @@ include "../../Wrappers.dfy" module {:options "-functionSyntax:4"} Imaps { - import opened Wrappers + import opened Wrappers.Option function Get(m: imap, x: X): Option { diff --git a/src/Collections/Maps/Maps.dfy b/src/Collections/Maps/Maps.dfy index f666d8c4..8eb2d803 100644 --- a/src/Collections/Maps/Maps.dfy +++ b/src/Collections/Maps/Maps.dfy @@ -12,7 +12,7 @@ include "../../Wrappers.dfy" module {:options "-functionSyntax:4"} Maps { - import opened Wrappers + import opened Wrappers.Option function Get(m: map, x: X): Option { diff --git a/src/Collections/Sequences/Seq.dfy b/src/Collections/Sequences/Seq.dfy index 60c9612d..09b17bee 100644 --- a/src/Collections/Sequences/Seq.dfy +++ b/src/Collections/Sequences/Seq.dfy @@ -20,7 +20,8 @@ include "../../Relations.dfy" module {:options "-functionSyntax:4"} Seq { - import opened Wrappers + import opened Wrappers.Option + import opened Wrappers.Result import opened MergeSort import opened Relations import Math @@ -613,7 +614,7 @@ module {:options "-functionSyntax:4"} Seq { /* Applies a function to every element of a sequence, returning a Result value (which is a failure-compatible type). Returns either a failure, or, if successful at every element, the transformed sequence. */ - function {:opaque} MapWithResult(f: (T ~> Result), xs: seq): (result: Result, E>) + function {:opaque} MapWithResult(f: (T ~> Result), xs: seq): (result: Result, E>) requires forall i :: 0 <= i < |xs| ==> f.requires(xs[i]) ensures result.Success? ==> && |result.value| == |xs| diff --git a/src/Comonad.dfy b/src/Comonad.dfy new file mode 100644 index 00000000..93bb4ac7 --- /dev/null +++ b/src/Comonad.dfy @@ -0,0 +1,172 @@ +// RUN: %dafny /compile:0 "%s" + +/******************************************************************************* +* Copyright by the contributors to the Dafny Project +* SPDX-License-Identifier: MIT +*******************************************************************************/ + +include "Functor.dfy" +include "Cowrappers.dfy" + +abstract module {:options "-functionSyntax:4"} Comonad refines Functor { + /* Structure */ + type F(!new) + + function Extract(w: F): T + + function Duplicate(w: F): F> + + /* Naturality */ + lemma LemmaExtractNaturality(eq: (T, T) -> bool, f: S -> T, w: F) + requires EquivalenceRelation(eq) + ensures eq(f(Extract(w)), Extract(Map(f)(w))) + + lemma LemmaDuplicateNaturality(eq: (T, T) -> bool, f: S -> T, w: F) + requires EquivalenceRelation(eq) + ensures EquivalenceRelation(Equal(eq)) + ensures Equal(Equal(eq))(Duplicate(Map(f)(w)), Map(Map(f))(Duplicate(w))) + + /* Counitality and Coassociativity */ + lemma LemmaCoUnitalityExtract(eq: (T, T) -> bool, w: F) + requires EquivalenceRelation(eq) + ensures && Equal(eq)(Map(Extract)(Duplicate(w)), w) + && Equal(eq)(w, Extract(Duplicate(w))) + + lemma LemmaCoAssociativityDuplicate(eq: (T, T) -> bool, w: F) + requires && EquivalenceRelation(eq) + && EquivalenceRelation(Equal(eq)) + && EquivalenceRelation(Equal(Equal(eq))) + ensures Equal(Equal(Equal(eq)))(Map(Duplicate)(Duplicate(w)), Duplicate(Duplicate(w))) +} + +abstract module {:options "-functionSyntax:4"} CoreaderRightComonad refines Comonad { + import Cowrappers.Coreader + + type X(!new) + + function eql(x: X, y: X): bool + + lemma LemmaEquiv() + ensures EquivalenceRelation(eql) + + /* Functor structure */ + type F(!new) = Coreader.Coreader + + function Map(f: S -> T): F -> F { + Coreader.MapRight(f) + } + + ghost function Equal(eq: (T, T) -> bool): (F, F) -> bool { + Coreader.Equal(eql, eq) + } + + /* Properties of Equal */ + lemma LemmaEquivRelLift(eq: (T, T) -> bool) { + LemmaEquiv(); + } + + /* Properties of Map */ + lemma LemmaMapFunction(eq: (T, T) -> bool, f: S -> T, g: S -> T) { + LemmaEquiv(); + } + + lemma LemmaMapFunctorial(eq: (U, U) -> bool, f: S -> T, g: T -> U, w: F) { + LemmaEquiv(); + } + + lemma LemmaMapIdentity(eq: (T, T) -> bool, id: T -> T) { + LemmaEquiv(); + } + + /* Comonad structure */ + function Extract(w: F): T { + Coreader.ExtractRight(w) + } + + function Duplicate(w: F): F> { + Coreader.DuplicateLeft(w) + } + + /* Naturality */ + lemma LemmaExtractNaturality(eq: (T, T) -> bool, f: S -> T, w: F) { + } + + lemma LemmaDuplicateNaturality(eq: (T, T) -> bool, f: S -> T, w: F) { + LemmaEquiv(); + } + + /* Counitality and Coassociativity */ + lemma LemmaCoUnitalityExtract(eq: (T, T) -> bool, w: F) { + LemmaEquiv(); + } + + lemma LemmaCoAssociativityDuplicate(eq: (T, T) -> bool, w: F) { + LemmaEquiv(); + } +} + +abstract module {:options "-functionSyntax:4"} CoreaderLeftComonad refines Comonad { + import Cowrappers.Coreader + + type X(!new) + + function eqr(x: X, y: X): bool + + lemma LemmaEquiv() + ensures EquivalenceRelation(eqr) + + /* Functor structure */ + type F(!new) = Coreader.Coreader + + function Map(f: S -> T): F -> F { + Coreader.MapLeft(f) + } + + ghost function Equal(eq: (T, T) -> bool): (F, F) -> bool { + Coreader.Equal(eq, eqr) + } + + /* Properties of Equal */ + lemma LemmaEquivRelLift(eq: (T, T) -> bool) { + LemmaEquiv(); + } + + /* Properties of Map */ + lemma LemmaMapFunction(eq: (T, T) -> bool, f: S -> T, g: S -> T) { + LemmaEquiv(); + } + + lemma LemmaMapFunctorial(eq: (U, U) -> bool, f: S -> T, g: T -> U, w: F) { + LemmaEquiv(); + } + + lemma LemmaMapIdentity(eq: (T, T) -> bool, id: T -> T) { + LemmaEquiv(); + } + + /* Comonad structure */ + function Extract(w: F): T { + Coreader.ExtractLeft(w) + } + + function Duplicate(w: F): F> { + Coreader.DuplicateRight(w) + } + + /* Naturality */ + lemma LemmaExtractNaturality(eq: (T, T) -> bool, f: S -> T, w: F) { + } + + lemma LemmaDuplicateNaturality(eq: (T, T) -> bool, f: S -> T, w: F) { + LemmaEquiv(); + } + + /* Counitality and Coassociativity */ + lemma LemmaCoUnitalityExtract(eq: (T, T) -> bool, w: F) { + LemmaEquiv(); + } + + lemma LemmaCoAssociativityDuplicate(eq: (T, T) -> bool, w: F) { + LemmaEquiv(); + } +} \ No newline at end of file diff --git a/src/Cowrappers.dfy b/src/Cowrappers.dfy new file mode 100644 index 00000000..f95c3302 --- /dev/null +++ b/src/Cowrappers.dfy @@ -0,0 +1,61 @@ +// RUN: %dafny /compile:0 "%s" + +/******************************************************************************* +* Copyright by the contributors to the Dafny Project +* SPDX-License-Identifier: MIT +*******************************************************************************/ + +module {:options "-functionSyntax:4"} Cowrappers { + module {:options "-functionSyntax:4"} Coreader { + + datatype Coreader<+S,+T> = Coreader(left: S, right: T) + + function ExtractRight(r: Coreader): T { + r.right + } + + function ExtractLeft(r: Coreader): S { + r.left + } + + function DuplicateLeft(r: Coreader): Coreader> { + Coreader(r.left, r) + } + + function DuplicateRight(r: Coreader): Coreader,T> { + Coreader(r, r.right) + } + + function ExtendRight(f: Coreader -> T2, r: Coreader): Coreader { + Coreader(r.left, f(r)) + } + + function ExtendLeft(f: Coreader -> S2, r: Coreader): Coreader { + Coreader(f(r), r.right) + } + + function MapRight(f: T1 -> T2): Coreader -> Coreader { + (r: Coreader) => + Coreader(r.left, f(r.right)) + } + + function MapLeft(f: S1 -> S2): Coreader -> Coreader { + (r: Coreader) => + Coreader(f(r.left), r.right) + } + + function KleisliCompositionRight(f: Coreader -> T2, g: Coreader -> T3): Coreader -> T3 { + r => g(ExtendRight(f, r)) + } + + function KleisliCompositionLeft(f: Coreader -> S2, g: Coreader -> S3): Coreader -> S3 { + r => g(ExtendLeft(f, r)) + } + + ghost function Equal(eql: (S, S) -> bool, eqr: (T, T) -> bool): (Coreader, Coreader) -> bool { + (r1: Coreader, r2: Coreader) => + eql(r1.left, r2.left) && eqr(r1.right, r2.right) + } + + } +} \ No newline at end of file diff --git a/src/FileIO/FileIO.dfy b/src/FileIO/FileIO.dfy index 48ea0950..928502fd 100644 --- a/src/FileIO/FileIO.dfy +++ b/src/FileIO/FileIO.dfy @@ -18,9 +18,9 @@ include "../Wrappers.dfy" * File path symbols including . and .. are allowed. */ module {:options "-functionSyntax:4"} FileIO { - import opened Wrappers + import opened Wrappers.Result - export provides ReadBytesFromFile, WriteBytesToFile, Wrappers + export provides ReadBytesFromFile, WriteBytesToFile, Result /* * Public API diff --git a/src/Functions.dfy b/src/Functions.dfy index 85757547..17eb2aa5 100644 --- a/src/Functions.dfy +++ b/src/Functions.dfy @@ -21,4 +21,8 @@ module {:options "-functionSyntax:4"} Functions { forall x1, x2 :: f(x1) == f(x2) ==> x1 == x2 } + function Composition(f: X -> Y, g: Y -> Z): X -> Z { + x => g(f(x)) + } + } diff --git a/src/Functor.dfy b/src/Functor.dfy new file mode 100644 index 00000000..16244a74 --- /dev/null +++ b/src/Functor.dfy @@ -0,0 +1,42 @@ +// RUN: %dafny /compile:0 "%s" + +/******************************************************************************* +* Copyright by the contributors to the Dafny Project +* SPDX-License-Identifier: MIT +*******************************************************************************/ + +include "Functions.dfy" +include "Relations.dfy" + +abstract module {:options "-functionSyntax:4"} Functor { + import opened Functions + import opened Relations + + /* Structure */ + type F(!new) + + function Map(f: S -> T): F -> F + + ghost function Equal(eq: (T, T) -> bool): (F, F) -> bool + requires EquivalenceRelation(eq) + + /* Properties of Equal */ + lemma LemmaEquivRelLift(eq: (T, T) -> bool) + requires EquivalenceRelation(eq) + ensures EquivalenceRelation(Equal(eq)) + + /* Properties of Map */ + lemma LemmaMapFunction(eq: (T, T) -> bool, f: S -> T, g: S -> T) + requires EquivalenceRelation(eq) + requires forall x: S :: eq(f(x), g(x)) + ensures forall w: F :: Equal(eq)(Map(f)(w), Map(g)(w)) + + lemma LemmaMapFunctorial(eq: (U, U) -> bool, f: S -> T, g: T -> U, w: F) + requires EquivalenceRelation(eq) + ensures Equal(eq)(Map(Composition(f, g))(w), Composition(Map(f), Map(g))(w)) + + lemma LemmaMapIdentity(eq: (T, T) -> bool, id: T -> T) + requires EquivalenceRelation(eq) + requires forall x: T :: eq(id(x), x) + ensures forall w: F :: Equal(eq)(Map(id)(w), w) +} \ No newline at end of file diff --git a/src/Monad.dfy b/src/Monad.dfy new file mode 100644 index 00000000..723dc999 --- /dev/null +++ b/src/Monad.dfy @@ -0,0 +1,435 @@ +// RUN: %dafny /compile:0 "%s" + +/******************************************************************************* +* Copyright by the contributors to the Dafny Project +* SPDX-License-Identifier: MIT +*******************************************************************************/ + +include "Functor.dfy" +include "Wrappers.dfy" + +abstract module {:options "-functionSyntax:4"} Monad refines Functor { + /* Structure */ + type F(!new) + + function Return(x: T): F + + function Join(ww: F>): F + + /* Naturality */ + lemma LemmaReturnNaturality(eq: (T, T) -> bool, f: S -> T, x: S) + requires EquivalenceRelation(eq) + ensures Equal(eq)(Map(f)(Return(x)), Return(f(x))) + + lemma LemmaJoinNaturality(eq: (T, T) -> bool, f: S -> T, ww: F>) + requires EquivalenceRelation(eq) + ensures Equal(eq)(Join(Map(Map(f))(ww)), Map(f)(Join(ww))) + + /* Unitality and Associativity */ + lemma LemmaUnitalityJoin(eq: (T, T) -> bool, w: F) + requires EquivalenceRelation(eq) + ensures Equal(eq)(Join(Map(Return)(w)), w) && Equal(eq)(w, Join(Return(w))) + + lemma LemmaAssociativityJoin(eq: (T, T) -> bool, www: F>>) + requires EquivalenceRelation(eq) + ensures Equal(eq)(Join(Map(Join)(www)), Join(Join(www))) +} + + +abstract module {:options "-functionSyntax:4"} ResultMonad refines Monad { + import Wrappers.Result + + type E(!new,==) + + function eqe(x: E, y: E): bool + + lemma LemmaEquiv() + ensures EquivalenceRelation(eqe) + + /* Functor structure */ + type F(!new) = Result.Result + + function Map(f: S -> T): F -> F { + Result.Map(f) + } + + ghost function Equal(eq: (T, T) -> bool): (F, F) -> bool { + Result.Equal(eq, eqe) + } + + /* Properties of Equal */ + lemma LemmaEquivRelLift(eq: (T, T) -> bool) { + LemmaEquiv(); + } + + /* Properties of Map */ + lemma LemmaMapFunction(eq: (T, T) -> bool, f: S -> T, g: S -> T) { + LemmaEquiv(); + } + + lemma LemmaMapFunctorial(eq: (U, U) -> bool, f: S -> T, g: T -> U, w: F) { + LemmaEquiv(); + } + + lemma LemmaMapIdentity(eq: (T, T) -> bool, id: T -> T) { + LemmaEquiv(); + } + + /* Monad structure */ + function Return(x: T): F { + Result.Return(x) + } + + function Join(ww: F>): F { + Result.Join(ww) + } + + /* Naturality */ + lemma LemmaReturnNaturality(eq: (T, T) -> bool, f: S -> T, x: S) { + } + + lemma LemmaJoinNaturality(eq: (T, T) -> bool, f: S -> T, ww: F>) { + LemmaEquiv(); + } + + /* Unitality and Associativity */ + lemma LemmaUnitalityJoin(eq: (T, T) -> bool, w: F) { + LemmaEquiv(); + } + + lemma LemmaAssociativityJoin(eq: (T, T) -> bool, www: F>>) { + LemmaEquiv(); + } +} + + +module {:options "-functionSyntax:4"} WriterMonad refines Monad { + import Wrappers.Writer + + /* Functor structure */ + type F(!new) = Writer.Writer + + function Map(f: S -> T): F -> F { + Writer.Map(f) + } + + ghost function Equal(eq: (T, T) -> bool): (F, F) -> bool { + Writer.Equal(eq) + } + + /* Properties of Equal */ + lemma LemmaEquivRelLift(eq: (T, T) -> bool) { + } + + /* Properties of Map */ + lemma LemmaMapFunction(eq: (T, T) -> bool, f: S -> T, g: S -> T) { + } + + lemma LemmaMapFunctorial(eq: (U, U) -> bool, f: S -> T, g: T -> U, w: F) { + } + + lemma LemmaMapIdentity(eq: (T, T) -> bool, id: T -> T) { + } + + /* Monad structure */ + function Return(x: T): F { + Writer.Return(x) + } + + function Join(ww: F>): F { + Writer.Join(ww) + } + + /* Naturality */ + lemma LemmaReturnNaturality(eq: (T, T) -> bool, f: S -> T, x: S) { + } + + lemma LemmaJoinNaturality(eq: (T, T) -> bool, f: S -> T, ww: F>) { + } + + /* Unitality and Associativity */ + lemma LemmaUnitalityJoin(eq: (T, T) -> bool, w: F) { + } + + lemma LemmaAssociativityJoin(eq: (T, T) -> bool, www: F>>) { + } +} + + +module {:options "-functionSyntax:4"} OptionMonad refines Monad { + import Wrappers.Option + + /* Functor structure */ + type F(!new) = Option.Option + + function Map(f: S -> T): F -> F { + Option.Map(f) + } + + ghost function Equal(eq: (T, T) -> bool): (F, F) -> bool { + Option.Equal(eq) + } + + /* Properties of Equal */ + lemma LemmaEquivRelLift(eq: (T, T) -> bool) { + } + + /* Properties of Map */ + lemma LemmaMapFunction(eq: (T, T) -> bool, f: S -> T, g: S -> T) { + } + + lemma LemmaMapFunctorial(eq: (U, U) -> bool, f: S -> T, g: T -> U, w: F) { + } + + lemma LemmaMapIdentity(eq: (T, T) -> bool, id: T -> T) { + } + + /* Monad structure */ + function Return(x: T): F { + Option.Return(x) + } + + function Join(ww: F>): F { + Option.Join(ww) + } + + /* Naturality */ + lemma LemmaReturnNaturality(eq: (T, T) -> bool, f: S -> T, x: S) { + } + + lemma LemmaJoinNaturality(eq: (T, T) -> bool, f: S -> T, ww: F>) { + } + + /* Unitality and Associativity */ + lemma LemmaUnitalityJoin(eq: (T, T) -> bool, w: F) { + } + + lemma LemmaAssociativityJoin(eq: (T, T) -> bool, www: F>>) { + } +} + + +abstract module {:options "-functionSyntax:4"} EitherRightMonad refines Monad { + import Wrappers.Either + + type X(!new) + + function eql(x: X, y: X): bool + + lemma LemmaEquiv() + ensures EquivalenceRelation(eql) + + /* Functor structure */ + type F(!new) = Either.Either + + function Map(f: S -> T): F -> F { + Either.MapRight(f) + } + + ghost function Equal(eq: (T, T) -> bool): (F, F) -> bool { + Either.Equal(eql, eq) + } + + /* Properties of Equal */ + lemma LemmaEquivRelLift(eq: (T, T) -> bool) { + LemmaEquiv(); + } + + /* Properties of Map */ + lemma LemmaMapFunction(eq: (T, T) -> bool, f: S -> T, g: S -> T) { + LemmaEquiv(); + } + + lemma LemmaMapFunctorial(eq: (U, U) -> bool, f: S -> T, g: T -> U, w: F) { + LemmaEquiv(); + } + + lemma LemmaMapIdentity(eq: (T, T) -> bool, id: T -> T) { + LemmaEquiv(); + } + + /* Monad structure */ + function Return(x: T): F { + Either.ReturnRight(x) + } + + function Join(ww: F>): F { + Either.JoinRight(ww) + } + + /* Naturality */ + lemma LemmaReturnNaturality(eq: (T, T) -> bool, f: S -> T, x: S) { + } + + lemma LemmaJoinNaturality(eq: (T, T) -> bool, f: S -> T, ww: F>) { + LemmaEquiv(); + } + + /* Unitality and Associativity */ + lemma LemmaUnitalityJoin(eq: (T, T) -> bool, w: F) { + LemmaEquiv(); + } + + lemma LemmaAssociativityJoin(eq: (T, T) -> bool, www: F>>) { + LemmaEquiv(); + } +} + + +abstract module {:options "-functionSyntax:4"} EitherLeftMonad refines Monad { + import Wrappers.Either + + type X(!new,==) + + function eqr(x: X, y: X): bool + + lemma LemmaEquiv() + ensures EquivalenceRelation(eqr) + + /* Functor structure */ + type F(!new) = Either.Either + + function Map(f: S -> T): F -> F { + Either.MapLeft(f) + } + + ghost function Equal(eq: (T, T) -> bool): (F, F) -> bool { + Either.Equal(eq, eqr) + } + + /* Properties of Equal */ + lemma LemmaEquivRelLift(eq: (T, T) -> bool) { + LemmaEquiv(); + } + + /* Properties of Map */ + lemma LemmaMapFunction(eq: (T, T) -> bool, f: S -> T, g: S -> T) { + LemmaEquiv(); + } + + lemma LemmaMapFunctorial(eq: (U, U) -> bool, f: S -> T, g: T -> U, w: F) { + LemmaEquiv(); + } + + lemma LemmaMapIdentity(eq: (T, T) -> bool, id: T -> T) { + LemmaEquiv(); + } + + /* Monad structure */ + function Return(x: T): F { + Either.ReturnLeft(x) + } + + function Join(ww: F>): F { + Either.JoinLeft(ww) + } + + /* Naturality */ + lemma LemmaReturnNaturality(eq: (T, T) -> bool, f: S -> T, x: S) { + } + + lemma LemmaJoinNaturality(eq: (T, T) -> bool, f: S -> T, ww: F>) { + LemmaEquiv(); + } + + /* Unitality and Associativity */ + lemma LemmaUnitalityJoin(eq: (T, T) -> bool, w: F) { + LemmaEquiv(); + } + + lemma LemmaAssociativityJoin(eq: (T, T) -> bool, www: F>>) { + LemmaEquiv(); + } +} + + +abstract module {:options "-functionSyntax:4"} ReaderMonad refines Monad { + import Wrappers.Reader + + type X(!new) + + /* Functor structure */ + type F(!new) = Reader.Reader + + function Map(f: S -> T): F -> F { + Reader.Map(f) + } + + ghost function Equal(eq: (T, T) -> bool): (F, F) -> bool { + Reader.Equal(eq) + } + + /* Properties of Equal */ + lemma LemmaEquivRelLift(eq: (T, T) -> bool) { + forall w: F ensures Equal(eq)(w, w) { + forall x: X ensures eq(w.f(x), w.f(x)) { + assert eq(w.f(x), w.f(x)); + } + } + assert Reflexive(Equal(eq)); + + forall v, w: F ensures Equal(eq)(v, w) <==> Equal(eq)(w, v) { + calc { + Equal(eq)(v, w); + <==> + forall x: X :: eq(v.f(x), w.f(x)); + <==> { assert Symmetric(eq); } + forall x: X :: eq(v.f(x), w.f(x)); + <==> + Equal(eq)(w, v); + } + } + assert Symmetric(Equal(eq)); + + forall v, w, u: F ensures Equal(eq)(v, w) && Equal(eq)(w, u) ==> Equal(eq)(v, u) { + if Equal(eq)(v, w) && Equal(eq)(w, u) { + forall x: X ensures eq(v.f(x), u.f(x)) { + calc { + eq(v.f(x), w.f(x)) && eq(w.f(x), u.f(x)); + ==> { assert Transitive(eq); } + eq(v.f(x), u.f(x)); + } + } + } + } + assert Transitive(Equal(eq)); + } + + /* Properties of Map */ + lemma LemmaMapFunction(eq: (T, T) -> bool, f: S -> T, g: S -> T) { + } + + lemma LemmaMapFunctorial(eq: (U, U) -> bool, f: S -> T, g: T -> U, w: F) { + } + + lemma LemmaMapIdentity(eq: (T, T) -> bool, id: T -> T) { + } + + /* Monad structure */ + function Return(x: T): F { + Reader.Return(x) + } + + function Join(ww: F>): F { + Reader.Join(ww) + } + + /* Naturality */ + lemma LemmaReturnNaturality(eq: (T, T) -> bool, f: S -> T, x: S) { + } + + lemma LemmaJoinNaturality(eq: (T, T) -> bool, f: S -> T, ww: F>) { + } + + /* Unitality and Associativity */ + lemma LemmaUnitalityJoin(eq: (T, T) -> bool, w: F) { + } + + lemma LemmaAssociativityJoin(eq: (T, T) -> bool, www: F>>) { + } +} + + + + + diff --git a/src/Relations.dfy b/src/Relations.dfy index ac25532f..93029480 100644 --- a/src/Relations.dfy +++ b/src/Relations.dfy @@ -19,6 +19,10 @@ module {:options "-functionSyntax:4"} Relations { forall x, y :: R(x, y) && R(y, x) ==> x == y } + ghost predicate Symmetric(R: (T, T) -> bool) { + forall x, y :: R(x, y) <==> R(y, x) + } + ghost predicate Connected(R: (T, T) -> bool) { forall x, y :: x != y ==> R(x, y) || R(y, x) } @@ -45,6 +49,12 @@ module {:options "-functionSyntax:4"} Relations { && Connected(R) } + ghost predicate EquivalenceRelation(R: (T, T) -> bool) { + && Reflexive(R) + && Symmetric(R) + && Transitive(R) + } + ghost predicate SortedBy(a: seq, lessThan: (T, T) -> bool) { forall i, j | 0 <= i < j < |a| :: lessThan(a[i], a[j]) } diff --git a/src/Unicode/Unicode.dfy b/src/Unicode/Unicode.dfy index 316e61e0..56204168 100644 --- a/src/Unicode/Unicode.dfy +++ b/src/Unicode/Unicode.dfy @@ -10,8 +10,6 @@ include "../Collections/Sequences/Seq.dfy" // This module implements basic functionality of Unicode 14.0. module {:options "-functionSyntax:4"} Unicode { - import opened Wrappers - import Seq /** diff --git a/src/Unicode/UnicodeEncodingForm.dfy b/src/Unicode/UnicodeEncodingForm.dfy index 1cd3c366..70e77d88 100644 --- a/src/Unicode/UnicodeEncodingForm.dfy +++ b/src/Unicode/UnicodeEncodingForm.dfy @@ -26,7 +26,7 @@ include "Unicode.dfy" * code unit subsequences to scalar values. */ abstract module {:options "-functionSyntax:4"} UnicodeEncodingForm { - import opened Wrappers + import opened Wrappers.Option import Functions import Seq @@ -48,7 +48,7 @@ abstract module {:options "-functionSyntax:4"} UnicodeEncodingForm { * A code unit is the minimal bit combination that can represent a unit of encoded text for processing or * interchange. (Section 3.9 D77.) */ - type CodeUnit + type CodeUnit(==) /** * A well-formed Unicode code unit sequence that maps to a single Unicode scalar value. diff --git a/src/Unicode/Utf8EncodingScheme.dfy b/src/Unicode/Utf8EncodingScheme.dfy index 210e1082..f7a3d813 100644 --- a/src/Unicode/Utf8EncodingScheme.dfy +++ b/src/Unicode/Utf8EncodingScheme.dfy @@ -29,8 +29,6 @@ include "Utf8EncodingForm.dfy" * but that runs into . */ module {:options "-functionSyntax:4"} Utf8EncodingScheme { - import opened Wrappers - import BoundedInts import Seq import Unicode diff --git a/src/Wrappers.dfy b/src/Wrappers.dfy index 22fc4bf5..1b8a098e 100644 --- a/src/Wrappers.dfy +++ b/src/Wrappers.dfy @@ -4,96 +4,429 @@ * Copyright by the contributors to the Dafny Project * SPDX-License-Identifier: MIT *******************************************************************************/ - module {:options "-functionSyntax:4"} Wrappers { - - datatype Option<+T> = None | Some(value: T) { - function ToResult(): Result { - match this - case Some(v) => Success(v) - case None() => Failure("Option is None") + module {:options "-functionSyntax:4"} Result { + import opened Option + + datatype Result = | Success(value: T) | Failure(error: E) { + predicate IsSuccess() { + Success? + } + + predicate IsFailure() { + Failure? + } + + function GetValue(): T + requires Success? + { + value + } + + function GetValueDefault(default: T): T { + match this + case Success(v) => v + case Failure(_) => default + } + + function PropagateFailure(): Result + requires Failure? + { + Failure(error) + } + + function GetError(): E + requires Failure? + { + error + } + + function Extract(): T + requires Success? + { + value + } + + function ToOption(): Option { + match this + case Success(v) => Some(v) + case Failure(_) => None + } + + function ToSeq(): seq { + match this + case Success(v) => [v] + case Failure(_) => [] + } + + function ToSet(): set { + match this + case Success(v) => {v} + case Failure(_) => {} + } } - function UnwrapOr(default: T): T { - match this - case Some(v) => v - case None() => default + function Return(v: T): Result { + Result.Success(v) } - predicate IsFailure() { - None? + function Bind(r: Result, f: S -> Result): Result { + match r + case Success(v) => f(v) + case Failure(e) => Result.Failure(e) } - function PropagateFailure(): Option - requires None? - { - None + function Join(rr: Result,E>): Result { + match rr + case Success(v) => v + case Failure(e) => Result.Failure(e) } - function Extract(): T - requires Some? - { - value + function Map(f: S -> T): Result -> Result { + (r: Result) => + match r + case Success(v) => Result.Success(f(v)) + case Failure(e) => Result.Failure(e) + } + + function MapError(f: E1 -> E2): Result -> Result { + (r: Result) => + match r + case Success(v) => Result.Success(v) + case Failure(e) => Result.Failure(f(e)) + } + + function KleisliComposition(f: S -> Result, g: T -> Result): S -> Result { + s => Bind(f(s), g) + } + + function Success(v: T): Result { + Result.Success(v) + } + + function Failure(e: E): Result { + Result.Failure(e) + } + + function Fold(f: T -> U, g: E -> U): Result -> U { + (r: Result) => + match r + case Success(v) => f(v) + case Failure(e) => g(e) + } + + function Equal(eqt: (T, T) -> bool, eqe: (E, E) -> bool): (Result, Result) -> bool { + (r1: Result, r2: Result) => + match (r1, r2) + case (Success(v1), Success(v2)) => eqt(v1, v2) + case (Failure(e1), Failure(e2)) => eqe(e1, e2) + case _ => false + } + + function Compare(cmps: (T, T) -> int, cmpf: (E, E) -> int): (Result, Result) -> int { + (r1: Result, r2: Result) => + match (r1, r2) + case (Success(v1), Success(v2)) => cmps(v1, v2) + case (Failure(e1), Failure(e2)) => cmpf(e1, e2) + case (Success(_), Failure(_)) => -1 + case (Failure(_), Success(_)) => 1 } } - datatype Result<+T, +R> = | Success(value: T) | Failure(error: R) { - function ToOption(): Option - { - match this - case Success(s) => Some(s) - case Failure(e) => None() + module {:options "-functionSyntax:4"} Writer { + datatype Writer = Result(value: T, log: string) { + function GetValue(w: Writer): T { + w.value + } + + function GetLog(w: Writer): string { + w.log + } } - function UnwrapOr(default: T): T - { - match this - case Success(s) => s - case Failure(e) => default + function Return(v: T): Writer { + Result(v, "") } - predicate IsFailure() { - Failure? + function Bind(w: Writer, f: S -> Writer): Writer { + Writer.Result(f(w.value).value, w.log + f(w.value).log) } - function PropagateFailure(): Result - requires Failure? - { - Failure(this.error) + function Join(ww: Writer>): Writer { + Result((ww.value).value, (ww.value).log + ww.log) } - function MapFailure(reWrap: R -> NewR): Result - { - match this - case Success(s) => Success(s) - case Failure(e) => Failure(reWrap(e)) + function Map(f: S -> T): Writer -> Writer { + (w: Writer) => Writer.Result(f(w.value), w.log) + } + + function KleisliComposition(f: S -> Writer, g: T -> Writer): S -> Writer { + s => Bind(f(s), g) + } + + function Result(v: T, s: string): Writer { + Writer.Result(v, s) + } + + ghost function Equal(eq: (T, T) -> bool): (Writer, Writer) -> bool { + (w1: Writer, w2: Writer) => + && eq(w1.value, w2.value) + && w1.log == w2.log + } + } + + module {:options "-functionSyntax:4"} Option { + datatype Option<+T(==)> = None | Some(value: T) { + function GetValueDefault(default: T): T { + match this + case None => default + case Some(v) => v + } + + function GetValue(): T + requires Some? + { + value + } + + predicate IsFailure() { + None? + } + + function Extract(): T + requires Some? + { + value + } + + function PropagateFailure(): Option + requires None? + { + None + } + + function ToSeq(): seq { + match this + case None => [] + case Some(v) => [v] + } + + function ToSet(): set { + match this + case None => {} + case Some(v) => {v} + } + } + + function Return(v: T): Option { + Option.Some(v) } - function Extract(): T - requires Success? + function Bind(o: Option, f: S -> Option): Option { + match o + case None => Option.None + case Some(v) => f(v) + } + + function Join(oo: Option>): Option { + match oo + case None => Option.None + case Some(o) => o + } + + function Map(f: S -> T): Option -> Option { - value + (o: Option) => + match o + case None => Option.None + case Some(v) => Option.Some(f(v)) + } + + function KleisliComposition(f: S -> Option, g: T-> Option): S -> Option { + s => Bind(f(s), g) + } + + function Fold(u: U, f: T -> U): Option -> U { + (o: Option) => + match o + case None => u + case Some(v) => f(v) + } + + function Equal(eq: (T, T) -> bool): (Option, Option) -> bool { + (o1: Option, o2: Option) => + match (o1, o2) + case (Some(v1), Some(v2)) => eq(v1, v2) + case (None, None) => true + case _ => false + } + + function Compare(cmp: (T, T) -> int): (Option, Option) -> int { + (o1: Option, o2: Option) => + match (o1, o2) + case (Some(v1), Some(v2)) => cmp(v1, v2) + case (None, None) => 0 + case (None, Some(_)) => -1 + case (Some(_), None) => 1 } } - datatype Outcome = Pass | Fail(error: E) - { - predicate IsFailure() { - Fail? + module {:options "-functionSyntax:4"} Either { + import opened Option + + datatype Either<+S,+T> = Left(left: S) | Right(right: T) { + predicate IsLeft() { + Left? + } + + predicate IsRight() { + Right? + } + } + + function FindLeft(e: Either): Option { + match e + case Left(v) => Some(v) + case Right(v) => None + } + + function FindRight(e: Either): Option { + match e + case Left(v) => None + case Right(v) => Some(v) + } + + function ReturnLeft(v: S): Either { + Either.Left(v) } - // Note: PropagateFailure returns a Result, not an Outcome. - function PropagateFailure(): Result - requires Fail? + + function ReturnRight(v: T): Either { + Either.Right(v) + } + + + + function JoinRight(ee: Either>): Either { + match ee + case Left(v) => Either.Left(v) + case Right(v) => v + } + + function JoinLeft(ee: Either,T>): Either { + match ee + case Left(v) => v + case Right(v) => Either.Right(v) + } + + function MapLeft(f: S -> T): Either -> Either { + (e: Either) => + match e + case Left(v) => Either.Left(f(v)) + case Right(v) => Either.Right(v) + } + + function MapRight(f: T -> U): Either -> Either { + (e: Either) => + match e + case Left(v) => Either.Left(v) + case Right(v) => Either.Right(f(v)) + } + + function Map(f: S1 -> S2, g: T1 -> T2): Either -> Either { + (e: Either) => + match e + case Left(v) => Either.Left(f(v)) + case Right(v) => Either.Right(g(v)) + } + + function Fold(f: S -> U, g: T -> U): Either -> U { + (e: Either) => + match e + case Left(v) => f(v) + case Right(v) => g(v) + } + + function Equal(eql: (S, S) -> bool, eqr: (T, T) -> bool): (Either, Either) -> bool { + (e1: Either, e2: Either) => + match (e1, e2) + case (Left(v1), Left(v2)) => eql(v1, v2) + case (Right(v1), Right(v2)) => eqr(v1, v2) + case _ => false + } + + function Compare(cmpl: (S, S) -> int, cmpr: (T, T) -> int): (Either, Either) -> int { + (e1: Either, e2: Either) => + match (e1, e2) + case (Left(v1), Left(v2)) => cmpl(v1, v2) + case (Right(v1), Right(v2)) => cmpr(v1, v2) + case (Left(_), Right(_)) => -1 + case (Right(_), Left(_)) => 1 + } + } + + module {:options "-functionSyntax:4"} Reader { + datatype Reader<-X,+T> = Reader(f: X -> T) + + function Return(t: T): Reader { + Reader(x => t) + } + + function Bind(r: Reader, f: S -> Reader): Reader { + Reader(x => (f((r.f)(x)).f)(x)) + } + + function Join(rr: Reader>): Reader { + Reader(x => (rr.f(x)).f(x)) + } + + function Map(f: S -> T): Reader -> Reader { + (r: Reader) => + Reader(x => f((r.f)(x))) + } + + function MapContra(f: X -> Y): Reader -> Reader { + (r: Reader) => + Reader(x => r.f(f(x))) + } + + function KleisliComposition(f: S -> Reader, g: T -> Reader): S -> Reader { + s => Bind(f(s), g) + } + + ghost function Equal(eq: (T, T) -> bool): (Reader, Reader) -> bool { - Failure(this.error) + (r1: Reader, r2: Reader) => + forall x: X :: eq(r1.f(x), r2.f(x)) + } + } + + module {:options "-functionSyntax:4"} Outcome { + import Result + + datatype Outcome = Pass | Fail(error: E) { + predicate IsFailure() { + Fail? + } + // Note: PropagateFailure returns a Result, not an Outcome. + function PropagateFailure(): Result.Result + requires Fail? + { + Result.Failure(this.error) + } + // Note: no Extract method + } + + // A helper function to ensure a requirement is true at runtime + // :- Need(5 == |mySet|, "The set MUST have 5 elements.") + function Need(condition: bool, error: E): (result: Outcome) + { + if condition then Pass else Fail(error) } - // Note: no Extract method } - // A helper function to ensure a requirement is true at runtime - // :- Need(5 == |mySet|, "The set MUST have 5 elements.") - function Need(condition: bool, error: E): (result: Outcome) - { - if condition then Pass else Fail(error) + function ToResult(o: Option.Option): Result.Result { + match o + case Some(v) => Result.Success(v) + case None => Result.Failure("Option is None") } -} +} \ No newline at end of file diff --git a/src/Wrappers.dll b/src/Wrappers.dll new file mode 100644 index 00000000..d6f54e80 Binary files /dev/null and b/src/Wrappers.dll differ