
my $expr =         ["Div", ["Con", 0],  
                ["Div", ["Con", 4], ["Con", 5]]]
;

        sub Eval {
          my $expr = shift;
          my ($op, $e1, $e2) = @$expr;
          if ($op eq "Con") {
            Monad->result($e1);
          } else {
            my ($v1, $v2);
                     ($v1 = Eval($e1))
            ->andThen($v2 = Eval($e2))
            ->andThen(do {
                        if ($v2->value_of == 0) { 
                          Monad->raise("Division by zero");
                        } else {
                          Monad->result($v1->value_of / $v2->value_of);
                        }                        
                      });
          }
        }


my $res = Eval($expr);
if ($res->is_exception) {
  warn "Calculation failed: ", $res->message, "\n";
} else {
  print "Result: ", $res->value_of, "\n";
}


package Monad;

        sub is_exception {
          my $self = shift;
          $self->[0] eq "EXCEPTION";
        }

        sub result {
          my ($class, $val) = @_;
          bless [ "RESULT", $val ] => $class;
        }

        sub andThen {
          my ($a, $b) = @_;
          if ($a->is_exception) { return $a }
          else { return $b }
        }

        sub raise {
          my ($class, $message) = @_;   
          bless [ "EXCEPTION", $message ] => $class;
        }



        # Extract message from an exception
        sub message {
          my $self = shift;
          $self->[1];
        }

        # Extract the returned value from a normal result
        sub value_of {
          my $self = shift;
          return $self->[1];
        }
