diff --git a/ZenLib.Tests/OptionTests.cs b/ZenLib.Tests/OptionTests.cs index cda2744..41c08fe 100644 --- a/ZenLib.Tests/OptionTests.cs +++ b/ZenLib.Tests/OptionTests.cs @@ -4,285 +4,370 @@ namespace ZenLib.Tests { - using System.Diagnostics.CodeAnalysis; - using System.Numerics; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using ZenLib; - using ZenLib.Tests.Network; - using static ZenLib.Tests.TestHelper; - using static ZenLib.Zen; + using System.Diagnostics.CodeAnalysis; + using System.Numerics; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using ZenLib; + using ZenLib.Tests.Network; + using static ZenLib.Tests.TestHelper; + using static ZenLib.Zen; + + /// + /// Tests for the Zen option type. + /// + [TestClass] + [ExcludeFromCodeCoverage] + public class OptionTests + { + /// + /// Test that option equality works. + /// + [TestMethod] + public void TestOptionEquality() + { + Assert.AreNotEqual(Option.None(), null); + Assert.AreNotEqual(Option.None(), 4); + Assert.AreEqual(Option.None(), Option.None()); + Assert.AreEqual(new Option(false, 1), new Option(false, 2)); + Assert.AreNotEqual(Option.None(), Option.Some(0)); + Assert.AreNotEqual(Option.Some(0), Option.None()); + Assert.AreEqual(Option.Some(0), Option.Some(0)); + Assert.AreNotEqual(Option.Some(0), Option.Some(1)); + Assert.AreNotEqual(Option.None().GetHashCode(), Option.Some(1).GetHashCode()); + } /// - /// Tests for the Zen option type. + /// Test that finding an option works. /// - [TestClass] - [ExcludeFromCodeCoverage] - public class OptionTests + [TestMethod] + public void TestOptionFind() { - /// - /// Test that option equality works. - /// - [TestMethod] - public void TestOptionEquality() - { - Assert.AreNotEqual(Option.None(), null); - Assert.AreNotEqual(Option.None(), 4); - Assert.AreEqual(Option.None(), Option.None()); - Assert.AreEqual(new Option(false, 1), new Option(false, 2)); - Assert.AreNotEqual(Option.None(), Option.Some(0)); - Assert.AreNotEqual(Option.Some(0), Option.None()); - Assert.AreEqual(Option.Some(0), Option.Some(0)); - Assert.AreNotEqual(Option.Some(0), Option.Some(1)); - Assert.AreNotEqual(Option.None().GetHashCode(), Option.Some(1).GetHashCode()); - } - - /// - /// Test that finding an option works. - /// - [TestMethod] - public void TestOptionFind() - { - var zf = new ZenFunction, bool>(o => o.IsSome()); - var example = zf.Find((i, o) => o); - Assert.IsTrue(example.HasValue); - Assert.AreEqual(Option.Some(0), example.Value); - } - - /// - /// Test none has no value. - /// - [TestMethod] - public void TestOptionNone() - { - CheckAgreement>(o => o.IsSome()); - } - - /// - /// Test some returns the correct value. - /// - [TestMethod] - public void TestOptionSomeInt() - { - RandomBytes(x => CheckAgreement>(o => And(o.IsSome(), o.Value() == Constant(x)))); - } - - /// - /// Test option with tuple underneath. - /// - [TestMethod] - public void TestOptionSomeTuple() - { - RandomBytes(x => - { - CheckAgreement>>(o => - { - var item1 = o.Value().Item1() == x; - var item2 = o.Value().Item2() == x; - return And(o.IsSome(), Or(item1, item2)); - }); - }); - } - - /// - /// Test option match. - /// - [TestMethod] - public void TestOptionMatch() - { - CheckValid>(o => - Implies(And(o.IsSome(), o.Value() <= Constant(4)), - o.Case(none: () => False(), some: v => v <= Constant(4)))); - } - - /// - /// Test option value or. - /// - [TestMethod] - public void TestOptionNullValueType() - { - CheckAgreement>>(o => o.Value().Item1() == o.Value().Item2()); - } - - /// - /// Test option value or. - /// - [TestMethod] - public void TestOptionValueOrDefaultValid() - { - CheckValid>(o => o.Select(v => Constant(2)).ValueOrDefault(Constant(2)) == Constant(2)); - } - - /// - /// Test option value or default. - /// - [TestMethod] - public void TestOptionValueOrDefault() + var zf = new ZenFunction, bool>(o => o.IsSome()); + var example = zf.Find((i, o) => o); + Assert.IsTrue(example.HasValue); + Assert.AreEqual(Option.Some(0), example.Value); + } + + /// + /// Test none has no value. + /// + [TestMethod] + public void TestOptionNone() + { + CheckAgreement>(o => o.IsSome()); + } + + /// + /// Test some returns the correct value. + /// + [TestMethod] + public void TestOptionSomeInt() + { + RandomBytes(x => CheckAgreement>(o => And(o.IsSome(), o.Value() == Constant(x)))); + } + + /// + /// Test option with tuple underneath. + /// + [TestMethod] + public void TestOptionSomeTuple() + { + RandomBytes(x => + { + CheckAgreement>>(o => { - var zf = new ZenFunction, int, int>((o, i) => o.ValueOrDefault(i)); + var item1 = o.Value().Item1() == x; + var item2 = o.Value().Item2() == x; + return And(o.IsSome(), Or(item1, item2)); + }); + }); + } - Assert.AreEqual(1, zf.Evaluate(Option.Some(1), 3)); - Assert.AreEqual(3, zf.Evaluate(Option.None(), 3)); + /// + /// Test option match. + /// + [TestMethod] + public void TestOptionMatch() + { + CheckValid>(o => + Implies(And(o.IsSome(), o.Value() <= Constant(4)), + o.Case(none: () => False(), some: v => v <= Constant(4)))); + } - zf.Compile(); - Assert.AreEqual(1, zf.Evaluate(Option.Some(1), 3)); - Assert.AreEqual(3, zf.Evaluate(Option.None(), 3)); + /// + /// Test option value or. + /// + [TestMethod] + public void TestOptionNullValueType() + { + CheckAgreement>>(o => o.Value().Item1() == o.Value().Item2()); + } - Assert.AreEqual(1, Option.Some(1).ValueOrDefault(3)); - Assert.AreEqual(3, Option.None().ValueOrDefault(3)); - } + /// + /// Test option value or. + /// + [TestMethod] + public void TestOptionValueOrDefaultValid() + { + CheckValid>(o => o.Select(v => Constant(2)).ValueOrDefault(Constant(2)) == Constant(2)); + } - /// - /// Test option IsSome. - /// - [TestMethod] - public void TestOptionIsSome() - { - var zf = new ZenFunction, bool>(o => o.IsSome()); + /// + /// Test option value or default. + /// + [TestMethod] + public void TestOptionValueOrDefault() + { + var zf = new ZenFunction, int, int>((o, i) => o.ValueOrDefault(i)); - Assert.AreEqual(true, zf.Evaluate(Option.Some(1))); - Assert.AreEqual(false, zf.Evaluate(Option.None())); + Assert.AreEqual(1, zf.Evaluate(Option.Some(1), 3)); + Assert.AreEqual(3, zf.Evaluate(Option.None(), 3)); - zf.Compile(); - Assert.AreEqual(true, zf.Evaluate(Option.Some(1))); - Assert.AreEqual(false, zf.Evaluate(Option.None())); + zf.Compile(); + Assert.AreEqual(1, zf.Evaluate(Option.Some(1), 3)); + Assert.AreEqual(3, zf.Evaluate(Option.None(), 3)); - Assert.AreEqual(true, Option.Some(1).IsSome()); - Assert.AreEqual(false, Option.None().IsSome()); - } + Assert.AreEqual(1, Option.Some(1).ValueOrDefault(3)); + Assert.AreEqual(3, Option.None().ValueOrDefault(3)); + } - /// - /// Test option IsNone. - /// - [TestMethod] - public void TestOptionIsNone() - { - var zf = new ZenFunction, bool>(o => o.IsNone()); + /// + /// Test option OrElse. + /// + [TestMethod] + public void TestOptionOrElse() + { + var zf = new ZenFunction, Option, Option>((o1, o2) => o1.OrElse(() => o2)); - Assert.AreEqual(false, zf.Evaluate(Option.Some(1))); - Assert.AreEqual(true, zf.Evaluate(Option.None())); + Assert.AreEqual(Option.Some(1), zf.Evaluate(Option.Some(1), Option.Some(3))); + Assert.AreEqual(Option.Some(3), zf.Evaluate(Option.None(), Option.Some(3))); - zf.Compile(); - Assert.AreEqual(false, zf.Evaluate(Option.Some(1))); - Assert.AreEqual(true, zf.Evaluate(Option.None())); + zf.Compile(); + Assert.AreEqual(Option.Some(1), zf.Evaluate(Option.Some(1), Option.Some(3))); + Assert.AreEqual(Option.Some(3), zf.Evaluate(Option.None(), Option.Some(3))); - Assert.AreEqual(false, Option.Some(1).IsNone()); - Assert.AreEqual(true, Option.None().IsNone()); - } + Assert.AreEqual(Option.Some(1), Option.Some(1).OrElse(() => Option.Some(3))); + Assert.AreEqual(Option.Some(3), Option.None().OrElse(() => Option.Some(3))); + } - /// - /// Test option where. - /// - [TestMethod] - public void TestOptionWhereValid() - { - CheckValid>(o => - Implies(And(o.IsSome(), o.Value() <= Constant(4)), Not(o.Where(v => v > Constant(4)).IsSome()))); - } - - /// - /// Test option Where. - /// - [TestMethod] - public void TestOptionWhere() - { - var zf = new ZenFunction, Option>(o => o.Where(i => i > 10)); - - Assert.AreEqual(Option.None(), zf.Evaluate(Option.Some(1))); - Assert.AreEqual(Option.None(), zf.Evaluate(Option.None())); - Assert.AreEqual(Option.Some(11), zf.Evaluate(Option.Some(11))); - - zf.Compile(); - Assert.AreEqual(Option.None(), zf.Evaluate(Option.Some(1))); - Assert.AreEqual(Option.None(), zf.Evaluate(Option.None())); - Assert.AreEqual(Option.Some(11), zf.Evaluate(Option.Some(11))); - - Assert.AreEqual(Option.None(), Option.Some(1).Where(i => i > 10)); - Assert.AreEqual(Option.None(), Option.None().Where(i => i > 10)); - Assert.AreEqual(Option.Some(11), Option.Some(11).Where(i => i > 10)); - } - - /// - /// Test option to sequence. - /// - [TestMethod] - public void TestOptionToSequenceValid1() - { - CheckAgreement>(o => o.ToFSeq().IsEmpty(), runBdds: false); - } - - /// - /// Test option to sequence. - /// - [TestMethod] - public void TestOptionToSequenceValid2() - { - CheckValid>(o => Implies(o.IsSome(), o.ToFSeq().Length() == (BigInteger)1), runBdds: false); - } - - /// - /// Test option ToSequence. - /// - [TestMethod] - public void TestOptionToSequence() - { - var zf = new ZenFunction, FSeq>(o => o.ToFSeq()); + /// + /// Test option IsSome. + /// + [TestMethod] + public void TestOptionIsSome() + { + var zf = new ZenFunction, bool>(o => o.IsSome()); - Assert.AreEqual(1, zf.Evaluate(Option.Some(1)).ToList().Count); - Assert.AreEqual(0, zf.Evaluate(Option.None()).ToList().Count); + Assert.AreEqual(true, zf.Evaluate(Option.Some(1))); + Assert.AreEqual(false, zf.Evaluate(Option.None())); - zf.Compile(); - Assert.AreEqual(1, zf.Evaluate(Option.Some(1)).ToList().Count); - Assert.AreEqual(0, zf.Evaluate(Option.None()).ToList().Count); + zf.Compile(); + Assert.AreEqual(true, zf.Evaluate(Option.Some(1))); + Assert.AreEqual(false, zf.Evaluate(Option.None())); - Assert.AreEqual(1, Option.Some(1).ToSequence().ToList().Count); - Assert.AreEqual(0, Option.None().ToSequence().ToList().Count); - } + Assert.AreEqual(true, Option.Some(1).IsSome()); + Assert.AreEqual(false, Option.None().IsSome()); + } - /// - /// Test option select. - /// - [TestMethod] - public void TestOptionSelectValid() - { - CheckValid>(o => - Implies(o.IsSome(), o.Select(v => v + Constant(1)).Value() == o.Value() + Constant(1))); - } - - /// - /// Test option Where. - /// - [TestMethod] - public void TestOptionSelect() - { - var zf = new ZenFunction, Option>(o => o.Select(i => i + 1)); - - Assert.AreEqual(Option.None(), zf.Evaluate(Option.None())); - Assert.AreEqual(Option.Some(2), zf.Evaluate(Option.Some(1))); - Assert.AreEqual(Option.Some(11), zf.Evaluate(Option.Some(10))); - - zf.Compile(); - Assert.AreEqual(Option.None(), zf.Evaluate(Option.None())); - Assert.AreEqual(Option.Some(2), zf.Evaluate(Option.Some(1))); - Assert.AreEqual(Option.Some(11), zf.Evaluate(Option.Some(10))); - - Assert.AreEqual(Option.None(), Option.None().Select(i => i + 1)); - Assert.AreEqual(Option.Some(2), Option.Some(1).Select(i => i + 1)); - Assert.AreEqual(Option.Some(11), Option.Some(10).Select(i => i + 1)); - } - - /// - /// Test non-null default. - /// - [TestMethod] - public void TestOptionDefault() - { - var f = new ZenFunction(() => - { - var x = Option.Null(); - return x.Value(); - }); - - Assert.AreEqual(0U, f.Evaluate().DstIp.Value); - } + /// + /// Test option IsNone. + /// + [TestMethod] + public void TestOptionIsNone() + { + var zf = new ZenFunction, bool>(o => o.IsNone()); + + Assert.AreEqual(false, zf.Evaluate(Option.Some(1))); + Assert.AreEqual(true, zf.Evaluate(Option.None())); + + zf.Compile(); + Assert.AreEqual(false, zf.Evaluate(Option.Some(1))); + Assert.AreEqual(true, zf.Evaluate(Option.None())); + + Assert.AreEqual(false, Option.Some(1).IsNone()); + Assert.AreEqual(true, Option.None().IsNone()); + } + + /// + /// Test option where. + /// + [TestMethod] + public void TestOptionWhereValid() + { + CheckValid>(o => + Implies(And(o.IsSome(), o.Value() <= Constant(4)), Not(o.Where(v => v > Constant(4)).IsSome()))); + } + + /// + /// Test option Where. + /// + [TestMethod] + public void TestOptionWhere() + { + var zf = new ZenFunction, Option>(o => o.Where(i => i > 10)); + + Assert.AreEqual(Option.None(), zf.Evaluate(Option.Some(1))); + Assert.AreEqual(Option.None(), zf.Evaluate(Option.None())); + Assert.AreEqual(Option.Some(11), zf.Evaluate(Option.Some(11))); + + zf.Compile(); + Assert.AreEqual(Option.None(), zf.Evaluate(Option.Some(1))); + Assert.AreEqual(Option.None(), zf.Evaluate(Option.None())); + Assert.AreEqual(Option.Some(11), zf.Evaluate(Option.Some(11))); + + Assert.AreEqual(Option.None(), Option.Some(1).Where(i => i > 10)); + Assert.AreEqual(Option.None(), Option.None().Where(i => i > 10)); + Assert.AreEqual(Option.Some(11), Option.Some(11).Where(i => i > 10)); + } + + /// + /// Test option to sequence. + /// + [TestMethod] + public void TestOptionToSequenceValid1() + { + CheckAgreement>(o => o.ToFSeq().IsEmpty(), runBdds: false); + } + + /// + /// Test option to sequence. + /// + [TestMethod] + public void TestOptionToSequenceValid2() + { + CheckValid>(o => Implies(o.IsSome(), o.ToFSeq().Length() == (BigInteger)1), runBdds: false); + } + + /// + /// Test option ToSequence. + /// + [TestMethod] + public void TestOptionToSequence() + { + var zf = new ZenFunction, FSeq>(o => o.ToFSeq()); + + Assert.AreEqual(1, zf.Evaluate(Option.Some(1)).ToList().Count); + Assert.AreEqual(0, zf.Evaluate(Option.None()).ToList().Count); + + zf.Compile(); + Assert.AreEqual(1, zf.Evaluate(Option.Some(1)).ToList().Count); + Assert.AreEqual(0, zf.Evaluate(Option.None()).ToList().Count); + + Assert.AreEqual(1, Option.Some(1).ToSequence().ToList().Count); + Assert.AreEqual(0, Option.None().ToSequence().ToList().Count); + } + + /// + /// Test option select. + /// + [TestMethod] + public void TestOptionSelectValid() + { + CheckValid>(o => + Implies(o.IsSome(), o.Select(v => v + Constant(1)).Value() == o.Value() + Constant(1))); + } + + /// + /// Test option Select. + /// + [TestMethod] + public void TestOptionSelect() + { + var zf = new ZenFunction, Option>(o => o.Select(i => i + 1)); + + Assert.AreEqual(Option.None(), zf.Evaluate(Option.None())); + Assert.AreEqual(Option.Some(2), zf.Evaluate(Option.Some(1))); + Assert.AreEqual(Option.Some(11), zf.Evaluate(Option.Some(10))); + + zf.Compile(); + Assert.AreEqual(Option.None(), zf.Evaluate(Option.None())); + Assert.AreEqual(Option.Some(2), zf.Evaluate(Option.Some(1))); + Assert.AreEqual(Option.Some(11), zf.Evaluate(Option.Some(10))); + + Assert.AreEqual(Option.None(), Option.None().Select(i => i + 1)); + Assert.AreEqual(Option.Some(2), Option.Some(1).Select(i => i + 1)); + Assert.AreEqual(Option.Some(11), Option.Some(10).Select(i => i + 1)); + } + + /// + /// Test option AndThen. + /// + [TestMethod] + public void TestOptionAndThen() + { + var zf = new ZenFunction, Option>(o => o.AndThen(i => Option.Create(i + 1))); + + Assert.AreEqual(Option.None(), zf.Evaluate(Option.None())); + Assert.AreEqual(Option.Some(2), zf.Evaluate(Option.Some(1))); + Assert.AreEqual(Option.Some(11), zf.Evaluate(Option.Some(10))); + + zf.Compile(); + Assert.AreEqual(Option.None(), zf.Evaluate(Option.None())); + Assert.AreEqual(Option.Some(2), zf.Evaluate(Option.Some(1))); + Assert.AreEqual(Option.Some(11), zf.Evaluate(Option.Some(10))); + + Assert.AreEqual(Option.None(), Option.None().AndThen(i => Option.Some(i + 1))); + Assert.AreEqual(Option.Some(2), Option.Some(1).AndThen(i => Option.Some(i + 1))); + Assert.AreEqual(Option.Some(11), Option.Some(10).AndThen(i => Option.Some(i + 1))); + } + + /// + /// Test non-null default. + /// + [TestMethod] + public void TestOptionDefault() + { + var f = new ZenFunction(() => + { + var x = Option.Null(); + return x.Value(); + }); + + Assert.AreEqual(0U, f.Evaluate().DstIp.Value); + } + + /// + /// Test option and. + /// + [TestMethod] + public void TestOptionAnd() + { + var zf = new ZenFunction, Option, Option>((o1, o2) => o1.And(o2)); + + Assert.AreEqual(Option.None(), zf.Evaluate(Option.None(), Option.Some(1))); + Assert.AreEqual(Option.Some(2), zf.Evaluate(Option.Some(1), Option.Some(2))); + Assert.AreEqual(Option.None(), zf.Evaluate(Option.Some(1), Option.None())); + + zf.Compile(); + Assert.AreEqual(Option.None(), zf.Evaluate(Option.None(), Option.Some(1))); + Assert.AreEqual(Option.Some(2), zf.Evaluate(Option.Some(1), Option.Some(2))); + Assert.AreEqual(Option.None(), zf.Evaluate(Option.Some(1), Option.None())); + + Assert.AreEqual(Option.None(), Option.None().And(Option.Some(1))); + Assert.AreEqual(Option.Some(2), Option.Some(1).And(Option.Some(2))); + Assert.AreEqual(Option.None(), Option.Some(1).And(Option.None())); + } + + /// + /// Test option or. + /// + [TestMethod] + public void TestOptionOr() + { + var zf = new ZenFunction, Option, Option>((o1, o2) => o1.Or(o2)); + + Assert.AreEqual(Option.None(), zf.Evaluate(Option.None(), Option.None())); + Assert.AreEqual(Option.Some(1), zf.Evaluate(Option.None(), Option.Some(1))); + Assert.AreEqual(Option.Some(1), zf.Evaluate(Option.Some(1), Option.None())); + + zf.Compile(); + Assert.AreEqual(Option.None(), zf.Evaluate(Option.None(), Option.None())); + Assert.AreEqual(Option.Some(1), zf.Evaluate(Option.None(), Option.Some(1))); + Assert.AreEqual(Option.Some(1), zf.Evaluate(Option.Some(1), Option.None())); + + Assert.AreEqual(Option.None(), Option.None().Or(Option.None())); + Assert.AreEqual(Option.Some(1), Option.None().Or(Option.Some(1))); + Assert.AreEqual(Option.Some(1), Option.Some(1).Or(Option.None())); } -} + } +} \ No newline at end of file diff --git a/ZenLib/DataTypes/Option.cs b/ZenLib/DataTypes/Option.cs index 0c63435..e5c4697 100644 --- a/ZenLib/DataTypes/Option.cs +++ b/ZenLib/DataTypes/Option.cs @@ -47,6 +47,22 @@ public T ValueOrDefault(T other) return other; } + /// + /// Return the option if it has a value, or the result of a function if not. + /// Lazy equivalent (without unwrapping) of . + /// + /// The default-generating function. + /// An option of the underlying type. + public Option OrElse(Func> other) + { + if (this.HasValue) + { + return this; + } + + return other(); + } + /// /// Map a function over an option. /// @@ -58,6 +74,18 @@ public Option Select(Func function) return this.HasValue ? Option.Some(function(this.Value)) : Option.None(); } + /// + /// Map a function that returns an option over an option and "flatten" the result. + /// Also known as "flat map" or "bind". + /// + /// The function. + /// A new option with the function mapped over the value. + public Option AndThen(Func> function) + { + Contract.AssertNotNull(function); + return this.HasValue ? function(this.Value) : Option.None(); + } + /// /// Filter an option with a predicate. /// @@ -102,6 +130,30 @@ public FSeq ToSequence() return this.HasValue ? new FSeq().AddFront(this.Value) : new FSeq(); } + /// + /// Return the "conjunction" of the option with another: + /// an option with no value if this option has no value, + /// otherwise the other option. + /// + /// The other option. + /// An option. + public Option And(Option other) + { + return !this.HasValue ? this : other; + } + + /// + /// Return the "disjunction" of the option with another: + /// this option if it has a value, + /// otherwise the other option. + /// + /// The other option. + /// An option. + public Option Or(Option other) + { + return this.HasValue ? this : other; + } + /// /// Convert the option to a string. /// @@ -220,6 +272,24 @@ public static Zen> Select(this Zen> expr, Func()); } + /// + /// The Zen expression for mapping (and projecting) over an option. + /// Also known as "flat map" or "bind". + /// + /// The expression. + /// The function. + /// The expression type. + /// The function return type. + /// Zen value. + public static Zen> AndThen(this Zen> expr, + Func, Zen>> function) + { + Contract.AssertNotNull(expr); + Contract.AssertNotNull(function); + + return If(expr.IsSome(), function(expr.Value()), Option.Null()); + } + /// /// The Zen expression for filtering over an option. /// @@ -231,7 +301,7 @@ public static Zen> Where(this Zen> expr, Func, Zen Contract.AssertNotNull(expr); Contract.AssertNotNull(function); - return If(And(expr.IsSome(), function(expr.Value())), expr, Option.Null()); + return If(Zen.And(expr.IsSome(), function(expr.Value())), expr, Option.Null()); } /// @@ -248,6 +318,20 @@ public static Zen ValueOrDefault(this Zen> expr, Zen deflt) return If(expr.IsSome(), expr.Value(), deflt); } + /// + /// The Zen expression for an option if it has a value, or the result of a function otherwise. + /// + /// The expression. + /// The function. + /// Zen value. + public static Zen> OrElse(this Zen> expr, Func>> function) + { + Contract.AssertNotNull(expr); + Contract.AssertNotNull(function); + + return If(expr.IsSome(), expr, function()); + } + /// /// The Zen expression for whether an option has a value. /// @@ -261,7 +345,7 @@ public static Zen IsSome(this Zen> expr) } /// - /// The Zen expression for whether an option has a value. + /// The Zen expression for whether an option has no value. /// /// The expression. /// Zen value. @@ -273,7 +357,7 @@ public static Zen IsNone(this Zen> expr) } /// - /// The Zen expression for whether an option has a value. + /// The Zen expression for representing the option as a finite sequence. /// /// The expression. /// Zen value. @@ -284,5 +368,37 @@ public static Zen> ToFSeq(this Zen> expr) var l = FSeq.Empty(); return If(expr.IsSome(), l.AddFront(expr.Value()), l); } + + /// + /// The Zen expression for a "conjunction" of two options. + /// None if the first option is None, otherwise the second value. + /// + /// + /// + /// + /// + public static Zen> And(this Zen> expr1, Zen> expr2) + { + Contract.AssertNotNull(expr1); + Contract.AssertNotNull(expr2); + + return If(expr1.IsNone(), expr1, expr2); + } + + /// + /// The Zen expression for a "disjunction" of two options. + /// The first option if it is Some, otherwise the second value. + /// + /// + /// + /// + /// + public static Zen> Or(this Zen> expr1, Zen> expr2) + { + Contract.AssertNotNull(expr1); + Contract.AssertNotNull(expr2); + + return If(expr1.IsSome(), expr1, expr2); + } } }