

my $expr = ["Div",
            ["Con", 3],
            ["Div",
             ["Con", 2],
             ["Con", 0],
            ]
           ];
             
            

sub eval_expr {
  my $expr = shift;
  my ($op, $se1, $se2) = @$expr;

  return Monad->result($se1) if $op eq "Con";

  #Otherwise, "Div"
  eval_expr($se1) - 
    sub { my $v1 = shift;
          eval_expr($se2) - 
            sub { my $v2 = shift;
                  if ($v2 == 0) { Monad->raise("Division by zero") }
                  else { Monad->result($v1/$v2) }
                }
          }
  }

my $result = eval_expr($expr);
if ($result->is_exception) {
  die "Exception: " . $result->[1];
} else {
  print "Result: " . $result->value . "\n";
}

package Monad;
use overload "-" => 'and_then', '""' => \&overload::StrVal;

sub and_then {
  my ($first, $secondf) = @_;
  return $first if is_exception($first);
  return $secondf->($first->value);
}

sub raise { 
  my $class = shift;
  my $s = shift;
  bless ["EXCEPTION", $s] => $class;
}
sub is_exception { $_[0][0] eq "EXCEPTION" }

sub result {
  my $class = shift;
  my $val = shift;
  bless ["RESULT", $val] => $class;
}
sub value { 
  my $self = shift;
  die if $self->is_exception; # Should never happen
  $self->[1];
}
  

