アヤチノオト

覚書のこと

D言語でモナドインタフェースとMaybeモナド

maybe.d

module maybe;

import std.conv;
import std.stdio;

void main()
{
  // モナド則を満たしているかのチェック

  Maybe!int m = Just(10);

  // Left identity
  // return a >>= f == f a
  write("Monad laws 1: ");
  writeln(Maybe!int.Return(10).Bind(x => Just (x^2)) ==
	  (x => Just (x^2))(10));

  // Rithg identity
  // m >>= return == m
  write("Monad laws 2: ");
  writeln(m.Bind(&Maybe!int.Return) ==
	  m);

  // Associativity
  // (m >>= f) >>= g == m >>= (\x -> f x >>= g)
  // f = \y -> Just (y^2)
  // g = \z -> Just (z/3.0)
  write("Monad laws 3: ");
  writeln(m.Bind(y => Just (y^2)).Bind(z => Just(z/3.0)) ==
	  m.Bind(x => (y => Just (y^2))(x).Bind(z => Just (z/3.0))));
}

// Just :: a -> Maybe a
Maybe!T Just(T)(T v)
{
  return Maybe!T.Just(v);
}

// Nothing :: Maybe a
Maybe!T Nothing(T)()
{
  return Maybe!T.Nothing();
}

interface Monad(M) {
  // Bind :: m a -> (a -> m b) -> m b
  M!U Bind(T, U)(T);

  // Return :: a -> m a
  static M!T Return(T)(T);
}

class Maybe(T): Monad!(Maybe!T)
{
  const T value;
  const bool has_value;

  // Type constructor Just T
  this(T v) {
    value = v;
    has_value = true;
  }

  // Type constructor Nothing
  this() {
    value = cast(T)null;
    has_value = false;
  }

  override string toString()
  {
    if (this.isJust()) {
      return "Just " ~ to!string(value) ~
	     " :: Maybe " ~ typeid(T).toString();
    } else {
      return "Nothing" ~ " :: Maybe" ~ typeid(T).toString();
    }
  }

  override bool opEquals(Object a)
  {
    auto other = to!(Maybe!T)(a);
    if (this.isJust() && other.isJust()) {
      return this.value == other.value;
    } else if (this.isNothing() && other.isNothing()) {
      return true;
    } else {
      return false;
    }
  }

  bool isNothing()
  {
    return !has_value;
  }

  bool isJust()
  {
    return has_value;
  }

  static Maybe!T Just(T v)
  {
    return new Maybe!T(v);
  }

  static Maybe!T Nothing()
  {
    return new Maybe!T();
  }

  public Maybe!U Bind(U)(Maybe!U function(T) f)
  {
    if (this.isJust()) {
      // (Just x) >>= f = Just (f x)
      return f(value);
    } else {
      // Nothing >>= f = Nothing
      return Maybe!U.Nothing();
    } 
  }

  static public Maybe!T Return(T v) {
    return Maybe!T.Just(v);
  }
}

こんなかんじで、モナド則も満たしています。たぶん

$ dmd -run maybe.d
Monad laws 1: true
Monad laws 2: true
Monad laws 3: true