$IF = sub { my $b = shift;
	    sub { my $t = shift;
		  sub { my $f = shift;
			($b)->($t)->($f);
		      }
		}
	  }
;

$TRUE = sub { my $x = shift;
	      sub { my $y = shift;
		    $x;
		  }
	    }
;

$FALSE = sub { my $x = shift;
	       sub { my $y = shift;
		     $y;
		   }
	     }
;

$PAIR = sub { my $x = shift;
	      sub { my $y = shift;
		    sub { my $b = shift;
			  ($b)->($x)->($y);
			}
		  }
	    }
;
$FIRST = sub { my $p = shift;
	       ($p)->($TRUE);
	     }
;
$SECOND = sub { my $p = shift;
		($p)->($FALSE);
	      }
;
$ZERO = ($PAIR)->($TRUE)->($TRUE);
$SUCC = sub { my $n = shift;
	      ($PAIR)->($FALSE)->($n);
	    }
;
$ONE = ($SUCC)->($ZERO);
$TWO = ($SUCC)->($ONE);
$IS_ZERO = sub { my $n = shift;
	      ($FIRST)->($n);
	    }
;
$PRED = sub { my $n = shift;
	      ($SECOND)->($n);
	    }
;

sub convert_to_perl_number {
  my $n = shift;
  return "OOPS($n)" unless ref $n eq 'CODE';
  my $o = 0;
  until ($IF->($IS_ZERO->($n))->(1)->(undef)) {
    $o++; $n = $PRED->($n);
  }
  $o;
}

sub print_number { print 
		     "If this is a number, its value is ", 
		     convert_to_perl_number(shift()), 
		     "\n" ;
		 }

# This doesn't work because of Perl's call-by-value semantics
$Y = sub { my $f = shift;
	   (sub { my $x = shift;
		  ($f)->(($x)->($x));
		})->(sub { my $x = shift;
			   ($f)->(($x)->($x));
			 });
};


# Use this one instead:
$YV = sub { my $f = shift;
	    (sub { my $x = shift; 
		  sub { my $y = shift; 
			$f->($x->($x));
		      };
		})->
	      (sub { my $x = shift; 
		  sub { my $y = shift; 
			$f->($x->($x));
		      };
		})

	  }
;

$FORCE = sub { my $x = shift; $x };

$R = sub { my $g = shift;
	   sub { my $m = shift;
		 sub { my $n = shift; 
		       $IF->($IS_ZERO->($m))
			 ->(sub { my $q = shift; $n} )
			   ->(sub { my $q = shift;
				    ($g->($FORCE))->($PRED->($m))->($SUCC->($n));
				  })->($FORCE);
		     }
	       }
	 }
;

$S = sub { my $g = shift;
	   sub { my $m = shift;
		 sub { my $n = shift; 
		       $IF->($IS_ZERO->($m))
			 ->(sub { my $q = shift; $ZERO} )
			   ->(sub { my $q = shift;
				    $ADD->($n)->($g->($PRED->($m))->($n))
				  })->($FORCE);
		     }
	       }
	 }
;

# Yes, this actually constructs the addition function
$ADD = $YV->($R)->($FORCE); 
$MUL = $YV->($S)->($FORCE); 

1;

$mystery_result = $MUL->($TWO)->($TWO);

print_number $mystery_result;

