Skip to main content
added 74 characters in body
Source Link
Eric Lippert
  • 46.4k
  • 22
  • 91
  • 128

If you do not already know what a monad is, today would be a great day to learn. I have a gentle introduction for OO programmers here:

https://ericlippert.com/2013/02/21/monads-part-one/

Your scenario is a small extension to the "maybe monad", also known as Nullable<T> in C# and Optional<T> in other languages.

Let's suppose you have an abstract type to represent the monad:

abstract class Measurement<T> { ... }

and then three subclasses:

final class Unknown<T> : Measurement<T> { ... a singleton ...}
final class Empty<T> : Measurement<T> { ... a singleton ... }
final class Actual<T> : Measurement<T> { ... a wrapper around a T ...}

We need an implementation of Bind:

abstract class Measurement<T>
{ 
    public Measurement<R> Bind(Func<T, Measurement<R>> f)
  {
    if (this is Unknown<T>) return Unknown<R>.Singleton;
    if (this is Empty<T>) return Empty<R>.Singleton;
    if (this is Actual<T>) return f(((Actual<T>)this).Value);
    throw ...
  }

From this you can write this simplified version of Bind:

public Measurement<R> Bind(Func<A, R> f) 
{
  return this.Bind(a => new Actual<R>(f(a));
}

And now you're done. You have a Measurement<int> in hand. You want to double it:

Measurement<int> m = whatever;
Measurement<int> doubled = m.Bind(a => a * 2);
Measurement<string> asString = m.Bind(a => a.ToString());

And follow the logic; if m is Empty<int> then asString is Empty<String>, excellent.

Similarly, if we have

Measurement<int> First()

and

Measurement<double> Second(int i);

then we can combine two measurements:

Measurement<double> d = First().Bind(Second);

and again, if First() is Empty<int> then d is Empty<double> and so on.

The key step is to get the bind operation correct. Think hard about it.

If you do not already know what a monad is, today would be a great day to learn. I have a gentle introduction for OO programmers here:

https://ericlippert.com/2013/02/21/monads-part-one/

Your scenario is a small extension to the "maybe monad", also known as Nullable<T> in C# and Optional<T> in other languages.

Let's suppose you have an abstract type to represent the monad:

abstract class Measurement<T> { ... }

and then three subclasses:

final class Unknown<T> : Measurement<T> { ... a singleton ...}
final class Empty<T> : Measurement<T> { ... a singleton ... }
final class Actual<T> : Measurement<T> { ... a wrapper around a T ...}

We need an implementation of Bind:

abstract class Measurement<T>
{ 
    public Measurement<R> Bind(Func<T, Measurement<R>> f)
  {
    if (this is Unknown<T>) return Unknown<R>.Singleton;
    if (this is Empty<T>) return Empty<R>.Singleton;
    if (this is Actual<T>) return f(((Actual<T>)this).Value);
    throw ...
  }

From this you can write this simplified version of Bind:

public Measurement<R> Bind(Func<A, R> f) 
{
  return this.Bind(a => new Actual<R>(f(a));
}

And now you're done. You have a Measurement<int> in hand. You want to double it:

Measurement<int> m = whatever;
Measurement<int> doubled = m.Bind(a => a * 2);
Measurement<string> asString = m.Bind(a => a.ToString());

And follow the logic; if m is Empty<int> then asString is Empty<String>, excellent.

Similarly, if we have

Measurement<int> First()

and

Measurement<double> Second(int i);

then we can combine two measurements:

Measurement<double> d = First().Bind(Second);

and again, if First() is Empty<int> then d is Empty<double> and so on.

If you do not already know what a monad is, today would be a great day to learn. I have a gentle introduction for OO programmers here:

https://ericlippert.com/2013/02/21/monads-part-one/

Your scenario is a small extension to the "maybe monad", also known as Nullable<T> in C# and Optional<T> in other languages.

Let's suppose you have an abstract type to represent the monad:

abstract class Measurement<T> { ... }

and then three subclasses:

final class Unknown<T> : Measurement<T> { ... a singleton ...}
final class Empty<T> : Measurement<T> { ... a singleton ... }
final class Actual<T> : Measurement<T> { ... a wrapper around a T ...}

We need an implementation of Bind:

abstract class Measurement<T>
{ 
    public Measurement<R> Bind(Func<T, Measurement<R>> f)
  {
    if (this is Unknown<T>) return Unknown<R>.Singleton;
    if (this is Empty<T>) return Empty<R>.Singleton;
    if (this is Actual<T>) return f(((Actual<T>)this).Value);
    throw ...
  }

From this you can write this simplified version of Bind:

public Measurement<R> Bind(Func<A, R> f) 
{
  return this.Bind(a => new Actual<R>(f(a));
}

And now you're done. You have a Measurement<int> in hand. You want to double it:

Measurement<int> m = whatever;
Measurement<int> doubled = m.Bind(a => a * 2);
Measurement<string> asString = m.Bind(a => a.ToString());

And follow the logic; if m is Empty<int> then asString is Empty<String>, excellent.

Similarly, if we have

Measurement<int> First()

and

Measurement<double> Second(int i);

then we can combine two measurements:

Measurement<double> d = First().Bind(Second);

and again, if First() is Empty<int> then d is Empty<double> and so on.

The key step is to get the bind operation correct. Think hard about it.

Source Link
Eric Lippert
  • 46.4k
  • 22
  • 91
  • 128

If you do not already know what a monad is, today would be a great day to learn. I have a gentle introduction for OO programmers here:

https://ericlippert.com/2013/02/21/monads-part-one/

Your scenario is a small extension to the "maybe monad", also known as Nullable<T> in C# and Optional<T> in other languages.

Let's suppose you have an abstract type to represent the monad:

abstract class Measurement<T> { ... }

and then three subclasses:

final class Unknown<T> : Measurement<T> { ... a singleton ...}
final class Empty<T> : Measurement<T> { ... a singleton ... }
final class Actual<T> : Measurement<T> { ... a wrapper around a T ...}

We need an implementation of Bind:

abstract class Measurement<T>
{ 
    public Measurement<R> Bind(Func<T, Measurement<R>> f)
  {
    if (this is Unknown<T>) return Unknown<R>.Singleton;
    if (this is Empty<T>) return Empty<R>.Singleton;
    if (this is Actual<T>) return f(((Actual<T>)this).Value);
    throw ...
  }

From this you can write this simplified version of Bind:

public Measurement<R> Bind(Func<A, R> f) 
{
  return this.Bind(a => new Actual<R>(f(a));
}

And now you're done. You have a Measurement<int> in hand. You want to double it:

Measurement<int> m = whatever;
Measurement<int> doubled = m.Bind(a => a * 2);
Measurement<string> asString = m.Bind(a => a.ToString());

And follow the logic; if m is Empty<int> then asString is Empty<String>, excellent.

Similarly, if we have

Measurement<int> First()

and

Measurement<double> Second(int i);

then we can combine two measurements:

Measurement<double> d = First().Bind(Second);

and again, if First() is Empty<int> then d is Empty<double> and so on.