Standard operations on the <T> monad out Bind and Map option

I use the C # implementation from the monk F # option<`a`> from the book of Petricek (Real World Functional Programming):

 internal enum OptionType { Some, None } internal abstract class Option<T> { private readonly OptionType tag; protected Option(OptionType tag) { this.tag = tag; } public OptionType Tag { get { return this.tag; } } public bool MatchNone() { return this.Tag == OptionType.None; } public bool MatchSome(out T value) { if (this.Tag == OptionType.Some) { value = ((Some<T>)this).Value; } else { value = default(T); } return this.Tag == OptionType.Some; } } internal sealed class None<T> : Option<T> { public None() : base(OptionType.None) { } } internal sealed class Some<T> : Option<T> { private readonly T value; public Some(T value) : base(OptionType.Some) { this.value = value; } public T Value { get { return this.value; } } } internal static class Option { public static Option<T> None<T>() { return new None<T>(); } public static Some<T> Some<T>(T value) { return new Some<T>(value); } } internal static class OptionExtensions { public static Option<T2> Bind<T1, T2>(this Option<T1> option, Func<T1, Option<T2>> func) { T1 value1; if (option.MatchSome(out value1)) { return func(value1); } return Option.None<T2>(); } public static Option<T2> Map<T1, T2>(this Option<T1> option, Func<T1, T2> func) { T1 value1; if (option.MatchSome(out value1)) { return Option.Some(func(value1)); } return Option.None<T2>(); } } 

Now I need an operation to retrieve the value if it is not None or returns the default value.

I was wondering if this is possible using a combination of Map and Bind , but I think it is not.

So, I went back to the F # documentation , it gave me hints about some other useful extension methods that I need to add, but not quite what I need.

I designed this function to fulfill my need:

 public static T2 Return<T1, T2>(this Option<T1> option, Func<T1, T2> func, T2 noneValue) { T1 value1; if (option.MatchSome(out value1)) { return func(value1); } return noneValue; } 

In order not to reinvent the wheel, the question arises: are there any reference or general functional patterns for defining operations on Option<T> monad? Is it correct to add new operations if necessary?

+4
source share
1 answer

The monad, where you can always extract value, is usually comonad. You know that the monad M<T> has methods (in C # syntax)

 static M<T> Unit<T>(T t) { ... } static M<R> Bind<A, R>(M<A> ma, Func<A, M<R>> func) { ... } 

Or, alternatively, you can make a monad from

 static M<T> Unit<T>(T t) { ... } static M<R> FMap<A, R>(M<A> ma, Func<A, R> func) { ... } static M<T> Join<T>(M<M<T>> mmt) { ... } 

Two characteristics are equivalent; you can build one of the given implementations of the other.

Komonad has operations

 static T Extract<T>(M<T> mt) { ... } static M<R> Extend<A, R>(M<A> ma, Func<M<A>, R> func) { ... } 

Extraction is the "opposite" of the module, and Extend is the "opposite" of Bind.

Alternatively, you can also define comonad with these operations:

 static T Extract<T>(M<T> mt) { ... } static M<R> FMap<A, R>(M<A> ma, Func<A, R> func) { ... } static M<M<T>> Duplicate<T>(M<T> mt) { ... } 

Where Duplicate is the opposite of Join. Again, two characteristics are equivalent; given one, you can build another.

It is clear that you cannot implement Extract only with Bind, Unit, FMap and Join data, because none of them returns T, and you need its T.

In any version of comonads, the problem you are facing is that the optional monad is not really comonad, because there is no natural way to implement Extract if the monadic value is "missing."

Now you can do what Nullable<T> does if you want. Nullable<T>.GetValueOrDefault() returns the value if it is, and default(T) if it did not. This is probably the best thing you can do here if you want to do optional in comonad.

+15
source

All Articles