CS 5510 Homework 6

Due: Wednesday, October 4th, 2017 11:59pm

Implement an interpreter with lazy evaluation and the following grammar:

  <Expr> = <Num>
         | <Sym>
         | {+ <Expr> <Expr>}
         | {* <Expr> <Expr>}
         | {lambda {<id>} <Expr>}
         | {<Expr> <Expr>}
         | {let {[<id> <Expr>]} <Expr>}
         | {if0 <Expr> <Expr> <Expr>}
         | {pair <Expr> <Expr>}
         | {fst <Expr>}
         | {snd <Expr>}

That is, a language with single-argument functions and application, an if-zero conditional, and pair, fst, and snd operations. (The language does not include recursive bindings or records.) Unlike cons, the pair operation does not require its second argument to be a list (and we do not have an empty-list value, anyway).

Implement your interpreter with the plai-typed language, not a lazy language.

Evaluation of the interpreted langauge must be lazy. In particular, if a function never uses the value of an argument, then the argument expression should not be evaluated. Similarly, if the first or second part of a pair is never needed, then the first or second expression should not be evaluated.

Start with more-lazy.rkt. Expand the parse function to support the new forms: if0, pair, fst, and snd. Also, as in HW 4, provide an interp-expr function; the interp-expr wrapper for interp should take an expression and return either a number S-expression, `function for a function result, or `pair for a pair result. (Meanwhile, the interp function should never return the symbol `pair, just like the starting interp function never returns the symbol `function.)

  (test (interp-expr (parse '10))
        '10)
  (test (interp-expr (parse '{+ 10 17}))
        '27)
  (test (interp-expr (parse '{* 10 7}))
        '70)
  (test (interp-expr (parse '{{lambda {x} {+ x 12}}
                              {+ 1 17}}))
        '30)
  
  (test (interp-expr (parse '{let {[x 0]}
                               {let {[f {lambda {y} {+ x y}}]}
                                 {+ {f 1}
                                    {let {[x 3]}
                                      {f 2}}}}}))
        '3)
  
  (test (interp-expr (parse '{if0 0 1 2}))
        '1)
  (test (interp-expr (parse '{if0 1 1 2}))
        '2)
  
  (test (interp-expr (parse '{pair 1 2}))
        `pair)
  (test (interp-expr (parse '{fst {pair 1 2}}))
        '1)
  (test (interp-expr (parse '{snd {pair 1 2}}))
        '2)
  
  ;; Lazy evaluation:
  (test (interp-expr (parse '{{lambda {x} 0}
                              {+ 1 {lambda {y} y}}}))
        '0)
  (test (interp-expr (parse '{let {[x {+ 1 {lambda {y} y}}]}
                               0}))
        '0)
  (test (interp-expr (parse '{fst {pair 3
                                        {+ 1 {lambda {y} y}}}}))
        '3)
  (test (interp-expr (parse '{snd {pair {+ 1 {lambda {y} y}}
                                        4}}))
        '4)
  (test (interp-expr (parse '{fst {pair 5
                                        ;; Infinite loop:
                                        {{lambda {x} {x x}}
                                         {lambda {x} {x x}}}}}))
        '5)
  
  (test (interp-expr 
         (parse 
          '{let {[mkrec
                  ;; This is call-by-name mkrec
                  ;;  (simpler than call-by-value):
                  {lambda {body-proc}
                    {let {[fX {lambda {fX}
                                {body-proc {fX fX}}}]}
                      {fX fX}}}]}
              {let {[fib
                     {mkrec
                      {lambda {fib}
                        ;; Fib:
                        {lambda {n}
                          {if0 n
                               1
                               {if0 {+ n -1}
                                    1
                                    {+ {fib {+ n -1}}
                                       {fib {+ n -2}}}}}}}}]}
                ;; Call fib on 4:
                {fib 4}}}))
        '5)

  (test (interp-expr 
         (parse 
          '{let {[mkrec
                  ;; This is call-by-name mkrec
                  ;;  (simpler than call-by-value):
                  {lambda {body-proc}
                    {let {[fX {lambda {fX}
                                {body-proc {fX fX}}}]}
                      {fX fX}}}]}
             {let {[nats-from
                    {mkrec
                     {lambda {nats-from}
                       ;; nats-from:
                       {lambda {n}
                         {pair n {nats-from {+ n 1}}}}}}]}
               {let {[list-ref
                      {mkrec
                       {lambda {list-ref}
                         ;; list-ref:
                         {lambda {n}
                           {lambda {l}
                             {if0 n
                                  {fst l}
                                  {{list-ref {+ n -1}} {snd l}}}}}}}]}
                 ;; Call list-ref on infinite list:
                 {{list-ref 4} {nats-from 2}}}}}))
        '6)

Last update: Friday, September 29th, 2017
mflatt@cs.utah.edu