TweetFollow Us on Twitter

Lambda
Volume Number:9
Issue Number:9
Column Tag:Lisp Listener

“The Lambda Lambada: Y Dance?”

Mutual Recursion

By André van Meulebrouck, Chatsworth, California

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

“Mathematics is thought moving in the sphere of complete abstraction from any particular instance of what it is talking about.” - Alfred North Whitehead

Welcome once again to Mutual of Omo Oz Y Old Kingdom (with apologies to the similar named TV series of yesteryears).

In this installment, Lambda, the forbidden (in conventional languages) function, does the lambada-the forbidden (in l-calculus) dance. Film at 11.

In [vanMeule Jun 91] the question was raised as to whether everything needed to create a metacircular interpreter (using combinators) has been given to the reader.

One of the last (if not the last) remaining items not yet presented is mutual recursion, which allows an interpreter’s eval and apply functions to do their curious tango (the “lambda lambada”?!?).

In this article, the derivation of a Y2 function will be shown. Y2 herein will be the sister combinator of Y, to be used for handling mutual recursion (of two functions) in the applicative order. The derivation of Y2 will be done in a similar manner as was done for deriving Y from pass-fact in [vanMeule May 92].

This exercise will hopefully give novel insights into Computer Science and the art of programming. (This is the stuff of Überprogrammers!) This exercise should also give the reader a much deeper understanding of Scheme while developing programming muscles in ways that conventional programming won’t.

Backdrop and motivation

[vanMeule Jun 91] described the minimalist game. The minimalist game is an attempt to program in Scheme using only those features of Scheme that have more or less direct counterparts in l-calculus. The aim of the minimalist game is (among other things):

1) To understand l-calculus and what it has to say about Computer Science.

2) To develop expressive skills. Part of the theory behind the minimalist game is that one’s expressive ability is not so much posited in how many programming constructs one knows, but in how cleverly one wields them. Hence, by deliberately limiting oneself to a restricted set of constructs, one is forced to exercise one’s expressive muscles in ways they would not normally get exercised when one has a large repertoire of constructs to choose from. The maxim here is: “learn few constructs, but learn them well”.

In l-calculus (and hence the minimalist game) there is no recursion. It turns out that recursion is a rather impure contortion in many ways! However, recursion can be simulated by making use of the higher order nature of l-calculus. A higher order function is a function which is either passed as an argument (to another function) or returned as a value. As thrifty as l-calculus is, it does have higher order functions, which is no small thing as very few conventional languages have such a capability, and those that do have it have only a very weak version of it. (This is one of the programming lessons to be learned from playing the minimalist game: The enormous power of higher order functions and the losses conventional languages suffer from not having them.)

Different kinds of recursion

As soon as a language has global functions or procedures and parameter passing provided via a stack discipline, you’ve got recursion! In fact, there is essentially no difference between a procedure calling itself or calling a different function-the same stack machinery that handles the one case will automatically handle the other. (There’s no need for the stack machinery to know nor care whether the user is calling other procedures or the same procedure.)

However, as soon as a language has local procedures, it makes a very big difference if a procedure calls itself! The problem is that when a local procedure sees a call to itself from within itself, by the rules of lexical scoping, it must look for its own definition outside of its own scope! This is because the symbol naming the recursive function is a free variable with respect to the context it occurs in.

; 1
>>> (let ((local-fact 
           (lambda (n)
             (if (zero? n)
                 1
                 (* n (local-fact (1- n)))))))
      (local-fact 5))
ERROR:  Undefined global variable
local-fact

Entering debugger.  Enter ? for help.
debug:> 

This is where letrec comes in.

; 2

>>> (letrec ((local-fact 
              (lambda (n)
                (if (zero? n)
                    1
                    (* n (local-fact (1- n)))))))
      (local-fact 5))
120

To understand what letrec is doing let’s translate it to its semantic equivalent. letrec can be simulated using let and set! [CR 91].

; 3
>>> (let ((local-fact ‘undefined))
      (begin
       (set! local-fact 
             (lambda (n)
               (if (zero? n)
                   1
                   (* n (local-fact (1- n))))))
       (local-fact 5)))
120

Mutual recursion is slightly different from “regular” recursion: instead of a function calling itself, it calls a different function that then calls the original function. For instance, “foo” and “fido” would be mutually recursive if foo called fido, and fido called foo. The letrec trick will work fine for mutual recursion.

; 4 

>>> (let ((my-even? ‘undefined)
          (my-odd? ‘undefined))
      (begin
       (set! my-even? 
             (lambda (n)
               (if (zero? n)
                   #t
                   (my-odd? (1- n)))))
       (set! my-odd? 
             (lambda (n)
               (if (zero? n)
                   #f
                   (my-even? (1- n)))))
       (my-even? 80)))
#t

The reason this works is because both functions that had to have mutual knowledge of each other were defined as symbols in a lexical context outside of the context in which the definitions were evaluated.

However, all the above letrec examples rely on being able to modify state. l-calculus doesn’t allow state to be modified. (An aside: since parallel machines have similar problems and restrictions in dealing with state, there is ample motivation for finding non-state oriented solutions to such problems in l-calculus.)

The recursion in local-fact can be ridded by using the Y combinator. However, in the my-even? and my-odd? example the Y trick doesn’t work because in trying to eliminate recursion using Y, the mutual nature of the functions causes us to get into a chicken-before-the-egg dilemma.

It’s clear we need a special kind of Y for this situation. Let’s call it Y2.

The pass-fact trick

[vanMeule May 92] derived the Y combinator in the style of [Gabriel 88] by starting with pass-fact (a version of the factorial function which avoids recursion by passing its own definition as an argument) and massaging it into two parts: a recursionless recursion mechanism and an abstracted version of the factorial function.

Let’s try the same trick for Y2, using my-even? and my-odd? as our starting point.

First, we want to massage my-even? and my-odd? into something that looks like pass-fact. Here’s what our “template” looks like:

; 5 

>>> (define pass-fact 
      (lambda (f n)
        (if (zero? n)
            1 
            (* n (f f (1- n))))))
pass-fact
>>> (pass-fact pass-fact 5)
120

Here’s a version of my-even? and my-odd? modeled after the pass-fact “template”.

; 6 
>>> (define even-odd
      (cons 
       (lambda (function-list)
         (lambda (n)
           (if (zero? n)
               #t
               (((cdr function-list) function-list)
                (1- n)))))
       (lambda (function-list)
         (lambda (n)
           (if (zero? n)
               #f
               (((car function-list) function-list) 
                (1- n)))))))
even-odd
>>> (define pass-even?
      ((car even-odd) even-odd))
pass-even?
>>> (define pass-odd?
      ((cdr even-odd) even-odd))
pass-odd?
>>> (pass-even? 8)
#t

This could derive one crazy!

Now that we know we can use higher order functions to get rid of the mutual recursion in my-even? and my-odd? the next step is to massage out the recursionless mutual recursion mechanism from the definitional parts that came from my-even? and my-odd?. The following is the code of such a derivation, including test cases and comments.

; 7
(define my-even?
  (lambda (n)
    (if (zero? n)
        #t
        (my-odd? (1- n)))))
;
(define my-odd?
  (lambda (n)
    (if (zero? n)
        #f
        (my-even? (1- n)))))
;
(my-even? 5)
;
; Get out of global environment-use local environment.
;
(define mutual-even?
  (letrec 
    ((my-even? (lambda (n)
                 (if (zero? n)
                     #t
                     (my-odd? (1- n)))))
     (my-odd? (lambda (n)
                (if (zero? n)
                    #f
                    (my-even? (1- n))))))
    my-even?))
;
(mutual-even? 5)
;
; Get rid of destructive letrec.  Use let instead.
; Make a list of the mutually recursive functions.
;
(define mutual-even?
  (lambda (n)
    (let 
      ((function-list 
        (cons (lambda (functions n) ; even?
                (if (zero? n)
                    #t
                    ((cdr functions) functions 
                                     (1- n))))
              (lambda (functions n) ; odd?
                (if (zero? n)
                    #f
                    ((car functions) functions 
                                     (1- n)))))))
      ((car function-list) function-list n))))
;
(mutual-even? 5)
;
; Curry, and get rid of initial (lambda (n) ...) .
;
(define mutual-even?
  (let 
    ((function-list 
      (cons (lambda (functions) ; even?
              (lambda (n) 
                (if (zero? n)
                    #t
                    (((cdr functions) functions) 
                     (1- n)))))
            (lambda (functions) ; odd?
              (lambda (n) 
                (if (zero? n)
                    #f
                    (((car functions) functions) 
                     (1- n))))))))
    ((car function-list) function-list)))
;
(mutual-even? 5)
;
; Abstract ((cdr functions) functions) out of if, etc..
;
(define mutual-even?
  (let 
    ((function-list 
      (cons (lambda (functions) 
              (lambda (n) 
                ((lambda (f)
                   (if (zero? n)
                       #t
                       (f (1- n))))
                 ((cdr functions) functions))))
            (lambda (functions) 
              (lambda (n) 
                ((lambda (f)
                   (if (zero? n)
                       #f
                       (f (1- n))))
                 ((car functions) functions)))))))
    ((car function-list) function-list)))
;
(mutual-even? 5)
;
; Massage functions into abstracted versions of 
; originals.
;
(define mutual-even?
  (let 
    ((function-list 
      (cons (lambda (functions) 
              (lambda (n) 
                (((lambda (f)
                    (lambda (n)
                      (if (zero? n)
                          #t
                          (f (1- n)))))
                  ((cdr functions) functions))
                 n)))
            (lambda (functions) 
              (lambda (n) 
                (((lambda (f)
                    (lambda (n)
                      (if (zero? n)
                          #f
                          (f (1- n)))))
                  ((car functions) functions))
                 n))))))
    ((car function-list) function-list)))
;
(mutual-even? 5)
;
; Separate abstracted functions out from recursive 
; mechanism.
;
(define mutual-even?
  (let 
    ((abstracted-functions
      (cons (lambda (f)
              (lambda (n)
                (if (zero? n)
                    #t
                    (f (1- n)))))
            (lambda (f)
              (lambda (n)
                (if (zero? n)
                    #f
                    (f (1- n))))))))
    (let 
      ((function-list 
        (cons (lambda (functions) 
                (lambda (n) 
                  (((car abstracted-functions)
                    ((cdr functions) functions))
                   n)))
              (lambda (functions) 
                (lambda (n) 
                  (((cdr abstracted-functions)
                    ((car functions) functions))
                   n))))))
      ((car function-list) function-list))))
;
(mutual-even? 5)
;
; Abstract out variable abstracted-functions in 2nd let.
;
(define mutual-even?
  (let 
    ((abstracted-functions
      (cons (lambda (f)
              (lambda (n)
                (if (zero? n)
                    #t
                    (f (1- n)))))
            (lambda (f)
              (lambda (n)
                (if (zero? n)
                    #f
                    (f (1- n))))))))
    ((lambda (abstracted-functions)
       (let 
         ((function-list 
           (cons (lambda (functions) 
                   (lambda (n) 
                     (((car abstracted-functions)
                       ((cdr functions) functions))
                      n)))
                 (lambda (functions) 
                   (lambda (n) 
                     (((cdr abstracted-functions)
                       ((car functions) functions))
                      n))))))
         ((car function-list) function-list)))
     abstracted-functions)))
;
(mutual-even? 5)
;
; Separate recursion mechanism into separate function.
;
(define y2
  (lambda (abstracted-functions)
    (let 
      ((function-list 
        (cons (lambda (functions) 
                (lambda (n) 
                  (((car abstracted-functions)
                    ((cdr functions) functions))
                   n)))
              (lambda (functions)
                (lambda (n) 
                  (((cdr abstracted-functions)
                    ((car functions) functions))
                   n))))))
      ((car function-list) function-list))))
;
(define mutual-even? 
  (y2
   (cons (lambda (f)
           (lambda (n)
             (if (zero? n)
                 #t
                 (f (1- n)))))
         (lambda (f)
           (lambda (n)
             (if (zero? n)
                 #f
                 (f (1- n))))))))
;
(mutual-even? 5)
;
; y2 has selector built into it-generalize it!
;
(define y2-choose
  (lambda (abstracted-functions)
    (lambda (selector)
      (let 
        ((function-list 
          (cons (lambda (functions) 
                  (lambda (n) 
                    (((car abstracted-functions)
                      ((cdr functions) functions))
                     n)))
                (lambda (functions)
                  (lambda (n) 
                    (((cdr abstracted-functions)
                      ((car functions) functions))
                     n))))))
        ((selector function-list) function-list)))))
;
; Now we can achieve the desired result-defining 
; both mutual-even? and mutual-odd? without recursion.
;
(define mutual-even-odd?
  (y2-choose
   (cons (lambda (f)
           (lambda (n)
             (if (zero? n)
                 #t
                 (f (1- n)))))
         (lambda (f)
           (lambda (n)
             (if (zero? n)
                 #f
                 (f (1- n))))))))
;
(define mutual-even? 
  (mutual-even-odd? car))
;
(define mutual-odd?
  (mutual-even-odd? cdr))  
;
(mutual-even? 5)
(mutual-odd? 5)
(mutual-even? 4)
(mutual-odd? 4)

Deriving Mutual Satisfaction

Notice that mutual-even? and mutual-odd? could have been defined using y2 instead of y2-choose, however, the definitional bodies of my-even? and my-odd? would have been repeated in defining mutual-even? and mutual-odd?.

Exercises for the Reader

• Herein Y2 was derived from mutual-even?. Try deriving it instead from pass-even?.

• Question for the Überprogrammer: if evaluation were normal order rather than applicative order, could we use the same version of Y for mutually recursive functions that we used for “regular” recursive functions (thus making a Y2 function unnecessary)?

• Another question: Let’s say we have 3 or more functions which are mutually recursive. What do we need to handle this situation when evaluation is applicative order? What about in normal order? (Note: evaluation in l-calculus is normal order.)

Looking Ahead

Creating a “minimalist” (i.e., combinator based) metacircular interpreter might now be possible if we can tackle the problem of manipulating state!

Thanks to:

The local great horned owls that watch over everything from on high; regularly letting fellow “night owls” know that all is well by bellowing their calming, reassuring “Who-w-h-o-o” sounds.

Bugs/infelicities due to: burning too much midnite oil!

Bibliography and References

[CR 91] William Clinger and Jonathan Rees (editors). “Revised4 Report on the Algorithmic Language Scheme”, LISP Pointers, SIGPLAN Special Interest Publication on LISP, Volume IV, Number 3, July-September, 1991. ACM Press.

[Gabriel 88] Richard P. Gabriel. “The Why of Y”, LISP Pointers, Vol. II, Number 2, October-November-December, 1988.

[vanMeule May 91] André van Meulebrouck. “A Calculus for the Algebraic-like Manipulation of Computer Code” (Lambda Calculus), MacTutor, Anaheim, CA, May 1991.

[vanMeule Jun 91] André van Meulebrouck. “Going Back to Church” (Church numerals.), MacTutor, Anaheim, CA, June 1991.

[vanMeule May 92] André van Meulebrouck. “Deriving Miss Daze Y”, (Deriving Y), MacTutor, Los Angeles, CA, April/May 1992.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Posterino 3.3.5 - Create posters, collag...
Posterino offers enhanced customization and flexibility including a variety of new, stylish templates featuring grids of identical or odd-sized image boxes. You can customize the size and shape of... Read more
Skim 1.4.28 - PDF reader and note-taker...
Skim is a PDF reader and note-taker for OS X. It is designed to help you read and annotate scientific papers in PDF, but is also great for viewing any PDF file. Skim includes many features and has a... Read more
Apple macOS Sierra 10.12.4 - The latest...
With Apple macOS Sierra, Siri makes its debut on Mac, with new features designed just for the desktop. Your Mac works with iCloud and your Apple devices in smart new ways, and intelligent... Read more
Apple Numbers 4.1 - Apple's spreads...
With Apple Numbers, sophisticated spreadsheets are just the start. The whole sheet is your canvas. Just add dramatic interactive charts, tables, and images that paint a revealing picture of your data... Read more
Xcode 8.3 - Integrated development envir...
Xcode includes everything developers need to create great applications for Mac, iPhone, iPad, and Apple Watch. Xcode provides developers a unified workflow for user interface design, coding, testing... Read more
Dropbox 22.4.24 - Cloud backup and synch...
Dropbox is an application that creates a special Finder folder that automatically syncs online and between your computers. It allows you to both backup files and keep them up-to-date between systems... Read more
Merlin Project 4.2.0 - Project managemen...
Merlin Project is the leading professional project management software for OS X. If you plan complex projects on your Mac, you won’t get far with a simple list of tasks. Good planning raises... Read more
Postbox 5.0.12 - Powerful and flexible e...
Postbox is a new email application that helps you organize your work life and get stuff done. It has all the elegance and simplicity of Apple Mail, but with more power and flexibility to manage even... Read more
Apple Pages 6.1 - Apple's word proc...
Apple Pages is a powerful word processor that gives you everything you need to create documents that look beautiful. And read beautifully. It lets you work seamlessly between Mac and iOS devices, and... Read more
RapidWeaver 7.3.2 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more

Hearthstone celebrates the upcoming Jour...
Hearthstone gets a new expansion, Journey to Un'Goro, in a little over a week, and they'll be welcoming the Year of the Mammoth, the next season, at the same time. There's a lot to be excited about, so Blizzard is celebrating in kind. Players will... | Read more »
4 smart and stylish puzzle games like Ty...
TypeShift launched a little over a week ago, offering some puzzling new challenges for word nerds equipped with an iOS device. Created by Zach Gage, the mind behind Spelltower, TypeShift boasts, like its predecessor, a sleak design and some very... | Read more »
The best deals on the App Store this wee...
Deals, deals, deals. We're all about a good bargain here on 148Apps, and luckily this was another fine week in App Store discounts. There's a big board game sale happening right now, and a few fine indies are still discounted through the weekend.... | Read more »
The best new games we played this week
It's been quite the week, but now that all of that business is out of the way, it's time to hunker down with some of the excellent games that were released over the past few days. There's a fair few to help you relax in your down time or if you're... | Read more »
Orphan Black: The Game (Games)
Orphan Black: The Game 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: Dive into a dark and twisted puzzle-adventure that retells the pivotal events of Orphan Black. | Read more »
The Elder Scrolls: Legends is now availa...
| Read more »
Ticket to Earth beginner's guide: H...
Robot Circus launched Ticket to Earth as part of the App Store's indie games event last week. If you're not quite digging the space operatics Mass Effect: Andromeda is serving up, you'll be pleased to know that there's a surprising alternative on... | Read more »
Leap to victory in Nexx Studios new plat...
You’re always a hop, skip, and a jump away from a fiery death in Temple Jump, a new platformer-cum-endless runner from Nexx Studio. It’s out now on both iOS and Android if you’re an adventurer seeking treasure in a crumbling, pixel-laden temple. | Read more »
Failbetter Games details changes coming...
Sunless Sea, Failbetter Games' dark and gloomy sea explorer, sets sail for the iPad tomorrow. Ahead of the game's launch, Failbetter took to Twitter to discuss what will be different in the mobile version of the game. Many of the changes make... | Read more »
Splish, splash! The Pokémon GO Water Fes...
Niantic is back with a new festival for dedicated Pokémon GO collectors. The Water Festival officially kicks off today at 1 P.M. PDT and runs through March 29. Magikarp, Squirtle, Totodile, and their assorted evolved forms will be appearing at... | Read more »

Price Scanner via MacPrices.net

13-inch MacBook Airs, Apple refurbished, in s...
Apple has Certified Refurbished 2016 13″ MacBook Airs available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: - 13″ 1.6GHz/8GB/128GB MacBook Air: $... Read more
12-inch Retina MacBooks on sale for $1199, sa...
B&H has 12″ 1.1GHz Retina MacBooks on sale for $100 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 12″ 1.1GHz Space Gray Retina MacBook: $1199 $100 off MSRP - 12″ 1.1GHz... Read more
Save up to $260 with Apple refurbished 12-inc...
Apple has Certified Refurbished 2016 12″ Retina MacBooks available for $200-$260 off MSRP. Apple will include a standard one-year warranty with each MacBook, and shipping is free. The following... Read more
13-inch 2.7GHz Retina MacBook Pro on sale for...
B&H Photo has the 2015 13″ 2.7GHz/128GB Retina Apple MacBook Pro on sale for $170 off MSRP. Shipping is free, and B&H charges NY tax only: - 13″ 2.7GHz/128GB Retina MacBook Pro (MF839LL/A): $... Read more
15-inch 2.2GHz Retina MacBook Pro on sale for...
B&H Photo has the 2015 15″ 2.2GHz Retina MacBook Pro (MJLQ2LL/A) on sale for $1799.99 including free shipping plus NY sales tax only. Their price is $200 off MSRP. Read more
Save up to $160 with Apple refurbished 9-inch...
Apple has Certified Refurbished 9″ and 12″ Apple iPad Pros available for up to $160 off the cost of new iPads. An Apple one-year warranty is included with each model, and shipping is free: - 32GB 9″... Read more
Apple Chip Foundry TSMC To Begin A11 System-o...
Digitimes’ Steve Shen is reporting today that according to the Chinese-language Economic Daily News (EDN), chipmaker and major Apple supplier foundery Taiwan Semiconductor Manufacturing Company (TSMC... Read more
MacX MediaTrans 3.5 iOS Data Transfer Spring...
MacXDVD Software has announced general availability of the latest MacX MedTrans 3.5, featuring a new user interface (UI). MacX MediaTrans is ann iPhone manager that enables free data transfer between... Read more
Regular Price $19.95 DupeZap 4 Finder For OS...
Hyperbolic Software has announced the release of DupeZap 4.0.2, their modern duplicate finder developed exclusively for macOS. DupeZap 4 is an utility for Mac owners seeking to reclaim disk space... Read more
B-Eng Releases SSD Health Check for MVNe for...
Fehraltorf, Switzerland based B-Eng has announced the release and immediate availability of SSD Health Check for MVNe for MacBook Pro, their app that delivers important data and insights for MVNe... Read more

Jobs Board

Fulltime aan de slag als shopmanager in een h...
Ben jij helemaal gek van Apple -producten en vind je het helemaal super om fulltime shopmanager te zijn in een jonge en hippe elektronicazaak? Wil jij werken in Read more
*Apple* Mobile Master - Best Buy (United Sta...
**492889BR** **Job Title:** Apple Mobile Master **Location Number:** 000886-Norwalk-Store **Job Description:** **What does a Best Buy Apple Mobile Master do?** Read more
*Apple* Mobile Master - Best Buy (United Sta...
**492472BR** **Job Title:** Apple Mobile Master **Location Number:** 000470-Seattle-Store **Job Description:** **What does a Best Buy Apple Mobile Master do?** Read more
*Apple* Mobile Master - Best Buy (United Sta...
**492562BR** **Job Title:** Apple Mobile Master **Location Number:** 000853-Jackson-Store **Job Description:** **What does a Best Buy Apple Mobile Master do?** Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.