        ################################################################
        #
        # Lambda Calculus demonstration in Perl
        # 4 April 1999 
        # M-J. Dominus (mjd-perl-lc@plover.com)
        # http://www.plover.com/~mjd/perl/lambda/
        #

        ############
        #
        # Truth and truth testing

        # IF = %btf.btf
        $IF = sub { my $b = shift;
                    sub { my $t = shift;
                          sub { my $f = shift;
                                ($b)->($t)->($f);
                              }
                        }
                  }
        ;
        
        # TRUE = %xy.x
        $TRUE = sub { my $x = shift;
                      sub { my $y = shift;
                            $x;
                          }
                    }
        ;
        
        # FALSE = %xy.y
        $FALSE = sub { my $x = shift;
                       sub { my $y = shift;
                             $y;
                           }
                     }
        ;

        ############
        #
        # Pairs (conses)
        
        # PAIR = %xyb.bxy
        $PAIR = sub { my $x = shift;
                      sub { my $y = shift;
                            sub { my $b = shift;
                                  ($b)->($x)->($y);
                                }
                          }
                    }
        ;
        
        # FIRST = %p.(p TRUE)
        $FIRST = sub { my $p = shift;
                       ($p)->($TRUE);
                     }
        ;
        
        # SECOND = %p.(p FALSE)
        $SECOND = sub { my $p = shift;
                        ($p)->($FALSE);
                      }
        ;
        
        ############
        #
        # Numerals
        #
        
        $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);
                    }
        ;
        
        
        ############
        #
        # We use this utility function only for checking to see if the
        # addition was done correctly
        #       
        
        sub convert_to_perl_number {
          my $n = shift;
          my $o = 0;
          until ($IF->($IS_ZERO->($n))->(1)->(undef)) {
            $o++; $n = $PRED->($n);
          }
          $o;
        }

        ############
        #
        # Fixed-point combinator
        # YM has the property that
        #    (YM f Q) = f (YM f)
        # for any Q
        #
        # YM = %f.(%x.(%y.f (x x)))(%x.(%y.f (x x)))
        
        $YM = 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));
                              };
                        })
        
                  }
        ;
        
        # Q is arbitrary, so we use something simple:  %x.x
        $FORCE = sub { my $x = shift; $x };
        
        ############
        #
        # Here's the basis for our addition function
        # R = %g.(%mn.(IF (IS_ZERO m) 
        #                 (%q.n)
        #                 (%q.(g FORCE) (PRED m) (SUCC n))
        #             ) FORCE
        #        )
        
        $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);
                             }
                       }
                 }
        ;
        
        # This constructs the actual addition function
        $ADD = $YM->($R)->($FORCE); 
        
        # Add 1+1 with recursive addition function
        $mystery_result = $ADD->($ONE)->($ONE);
        print "1+1 = ", convert_to_perl_number($mystery_result), "\n";
        
