learning-sicp

My embarrassing half assed SICP run.
git clone https://kaka.farm/~git/learning-sicp
Log | Files | Refs

original-repl.scm (16638B)


      1 (define-library (sicp solutions chapter-4 original-repl)
      2   (export
      3    eval
      4    main
      5 
      6    run-internal-tests
      7 
      8    make-environment
      9    setup-environment
     10    extend-environment
     11    )
     12   (import
     13    (rename (scheme base) (apply scheme:base:apply))
     14    (scheme cxr)
     15    (scheme read)
     16    (scheme write)
     17 
     18    (srfi srfi-1)
     19    (srfi srfi-9)
     20    (srfi srfi-64)
     21 
     22    (system vm trace)
     23 
     24    (ice-9 match)
     25    (ice-9 pretty-print)
     26 
     27    (sicp utils)
     28    )
     29 
     30   (begin
     31     ;; XXX: In 4.1.3 we see that the procedure `true?` is defined
     32     ;; using the host-variable `false` whose value is 'false, but we
     33     ;; don't see it defined anywhere in the good book, so we need to
     34     ;; define the host-variable false ourselves.
     35     ;;
     36     ;; In MIT Scheme, the one the SICP people seem to love to use for
     37     ;; SICP, the values of `false` and `true` are #f and #t, #f being
     38     ;; the only value that is falsituous and #t there for having a
     39     ;; truthious value that has no other meaning other than being
     40     ;; truthitiousic.
     41     ;;
     42     ;; ```
     43     ;; # `scheme` is the MIT Scheme executable:
     44     ;; $ guix shell mit-scheme -- scheme
     45     ;; MIT/GNU Scheme running under GNU/Linux
     46     ;; Type `^C' (control-C) followed by `H' to obtain information about interrupts.
     47     ;;
     48     ;; Copyright (C) 2020 Massachusetts Institute of Technology
     49     ;; This is free software; see the source for copying conditions. There is NO warranty; not even for
     50     ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     51     ;;
     52     ;; Image saved on Sunday March 7, 2021 at 3:24:56 PM
     53     ;;   Release 11.2 || SF || LIAR/x86-64
     54     ;;
     55     ;; 1 ]=> false
     56     ;;
     57     ;; ;Value: #f
     58     ;;
     59     ;; 1 ]=> true
     60     ;;
     61     ;; ;Value: #t
     62     ;;
     63     ;; 1 ]=>
     64     ;; ```
     65     (define false #f)
     66     (define true #t)
     67 
     68     (define (make-environment vars vals)
     69       "A comfort procedure because calling extend on the-empty-environment is a PITA."
     70       (extend-environment vars
     71                           vals
     72                           the-empty-environment))
     73 
     74     ;; XXX: As described in chapter 4.1.4 note 2, we need to name the
     75     ;; host `apply`, and the guest `apply` differently.  I define the
     76     ;; host's `apply` to be the name used in the book.
     77     (define apply-in-underlying-scheme scheme:base:apply)
     78 
     79 ;;; Start:
     80 
     81 ;;; 4.1.1
     82 
     83     (define (eval exp env)
     84       (cond ((self-evaluating? exp)
     85              exp)
     86             ((variable? exp)
     87              (lookup-variable-value exp env))
     88             ((quoted? exp)
     89              (text-of-quotation exp))
     90             ((assignment? exp)
     91              (eval-assignment exp env))
     92             ((definition? exp)
     93              (eval-definition exp env))
     94             ((if? exp)
     95              (eval-if exp env))
     96             ((lambda? exp)
     97              (make-procedure
     98               (lambda-parameters exp)
     99               (lambda-body exp)
    100               env))
    101             ((begin? exp)
    102              (eval-sequence
    103               (begin-actions exp)
    104               env))
    105             ((cond? exp)
    106              (eval (cond->if exp) env))
    107             ((application? exp)
    108              (apply (eval (operator exp) env)
    109                     (list-of-values
    110                      (operands exp)
    111                      env)))
    112             (else
    113              (error "Unknown expression type -- EVAL" exp))))
    114 
    115     (define (apply procedure arguments)
    116       (cond ((primitive-procedure? procedure)
    117              (apply-primitive-procedure
    118               procedure
    119               arguments))
    120             ((compound-procedure? procedure)
    121              (eval-sequence
    122               (procedure-body procedure)
    123               (extend-environment
    124                (procedure-parameters
    125                 procedure)
    126                arguments
    127                (procedure-environment
    128                 procedure))))
    129             (else
    130              (error "Unknown procedure type -- APPLY"
    131                     procedure))))
    132 
    133 ;;; Procedure arguments:
    134 
    135     (define (list-of-values exps env)
    136       (if (no-operands? exps)
    137           '()
    138           (cons (eval (first-operand exps) env)
    139                 (list-of-values (rest-operands exps) env))))
    140 
    141 ;;; Conditionals:
    142 
    143     (define (eval-if exp env)
    144       (if (true? (eval (if-predicate exp) env))
    145           (eval (if-consequent exp) env)
    146           (eval (if-alternative exp) env)))
    147 
    148 ;;; Sequences:
    149 
    150     (define (eval-sequence exps env)
    151       (cond ((last-exp? exps)
    152              (eval (first-exp exps) env))
    153             (else
    154              (eval (first-exp exps) env)
    155              (eval-sequence (rest-exps exps)
    156                             env))))
    157 
    158 ;;; Assignments and definitions
    159 
    160     (define (eval-assignment exp env)
    161       (set-variable-value!
    162        (assignment-variable exp)
    163        (eval (assignment-value exp) env)
    164        env)
    165       'ok)
    166 
    167     (define (eval-definition exp env)
    168       (define-variable!
    169         (definition-variable exp)
    170         (eval (definition-value exp) env)
    171         env)
    172       'ok)
    173 
    174 ;;; end 4.1.1
    175 
    176 ;;; 4.1.2
    177 
    178 
    179     (define (self-evaluating? exp)
    180       (cond ((number? exp) true)
    181             ((string? exp) true)
    182             (else false)))
    183 
    184     (define (variable? exp) (symbol? exp))
    185 
    186     (define (quoted? exp)
    187       (tagged-list? exp 'quote))
    188 
    189     (define (text-of-quotation exp)
    190       (cadr exp))
    191 
    192     (define (tagged-list? exp tag)
    193       (if (pair? exp)
    194           (eq? (car exp) tag)
    195           false))
    196 
    197     (define (assignment? exp)
    198       (tagged-list? exp 'set!))
    199 
    200     (define (assignment-variable exp)
    201       (cadr exp))
    202 
    203     (define (assignment-value exp) (caddr exp))
    204 
    205     (define (definition? exp)
    206       (tagged-list? exp 'define))
    207 
    208     (define (definition-variable exp)
    209       (if (symbol? (cadr exp))
    210           (cadr exp)
    211           (caadr exp)))
    212 
    213     (define (definition-value exp)
    214       (if (symbol? (cadr exp))
    215           (caddr exp)
    216           (make-lambda
    217            (cdadr exp)   ; formal-parameters
    218            (cddr exp)))) ; body
    219 
    220     (define (lambda? exp)
    221       (tagged-list? exp 'lambda))
    222     (define (lambda-parameters exp) (cadr exp))
    223     (define (lambda-body exp) (cddr exp))
    224 
    225     (define (make-lambda parameters body)
    226       (cons 'lambda (cons parameters body)))
    227 
    228     (define (if? exp) (tagged-list? exp 'if))
    229     (define (if-predicate exp) (cadr exp))
    230     (define (if-consequent exp) (caddr exp))
    231     (define (if-alternative exp)
    232       (if (not (null? (cdddr exp)))
    233           (cadddr exp)
    234           'false))
    235 
    236     (define (make-if predicate
    237                      consequent
    238                      alternative)
    239       (list 'if
    240             predicate
    241             consequent
    242             alternative))
    243 
    244     (define (begin? exp)
    245       (tagged-list? exp 'begin))
    246     (define (begin-actions exp) (cdr exp))
    247     (define (last-exp? seq) (null? (cdr seq)))
    248     (define (first-exp seq) (car seq))
    249     (define (rest-exps seq) (cdr seq))
    250 
    251     (define (sequence->exp seq)
    252       (cond ((null? seq) seq)
    253             ((last-exp? seq) (first-exp seq))
    254             (else (make-begin seq))))
    255 
    256     (define (make-begin seq) (cons 'begin seq))
    257 
    258     (define (application? exp) (pair? exp))
    259     (define (operator exp) (car exp))
    260     (define (operands exp) (cdr exp))
    261     (define (no-operands? ops) (null? ops))
    262     (define (first-operand ops) (car ops))
    263     (define (rest-operands ops) (cdr ops))
    264 
    265     (define (cond? exp)
    266       (tagged-list? exp 'cond))
    267     (define (cond-clauses exp) (cdr exp))
    268     (define (cond-else-clause? clause)
    269       (eq? (cond-predicate clause) 'else))
    270     (define (cond-predicate clause)
    271       (car clause))
    272     (define (cond-actions clause)
    273       (cdr clause))
    274     (define (cond->if exp)
    275       (expand-clauses (cond-clauses exp)))
    276     (define (expand-clauses clauses)
    277       (if (null? clauses)
    278           'false
    279           (let ((first (car clauses))
    280                 (rest (cdr clauses)))
    281             (if (cond-else-clause? first)
    282                 (if (null? rest)
    283                     (sequence->exp
    284                      (cond-actions first))
    285                     (error "ELSE clause isn't last: COND->IF"
    286                            clauses))
    287                 (make-if (cond-predicate first)
    288                          (sequence->exp
    289                           (cond-actions first))
    290                          (expand-clauses
    291                           rest))))))
    292 
    293 ;;; end 4.1.2
    294 
    295 ;;; 4.1.3
    296 
    297     ;; XXX: By default, false's value is #f, which is the only true
    298     ;; false.  All others are fake and would have the truthitousness
    299     ;; property.  Consult `info r7rs-small` or `C-h
    300     (define (true? x)
    301       (not (eq? x false)))
    302 
    303     (define (false? x)
    304       (eq? x false))
    305 
    306     (define (make-procedure parameters body env)
    307       (list 'procedure parameters body env))
    308     (define (compound-procedure? p)
    309       (tagged-list? p 'procedure))
    310     (define (procedure-parameters p) (cadr p))
    311     (define (procedure-body p) (caddr p))
    312     (define (procedure-environment p) (cadddr p))
    313 
    314     (define (enclosing-environment env) (cdr env))
    315     (define (first-frame env) (car env))
    316     (define the-empty-environment '())
    317 
    318     (define (make-frame variables values)
    319       (cons variables values))
    320     (define (frame-variables frame) (car frame))
    321     (define (frame-values frame) (cdr frame))
    322     (define (add-binding-to-frame! var val frame)
    323       (set-car! frame (cons var (car frame)))
    324       (set-cdr! frame (cons val (cdr frame))))
    325 
    326     (define (extend-environment vars vals base-env)
    327       (if (= (length vars) (length vals))
    328           (cons (make-frame vars vals) base-env)
    329           (if (< (length vars) (length vals))
    330               (error "Too many arguments supplied"
    331                      vars
    332                      vals)
    333               (error "Too few arguments supplied"
    334                      vars
    335                      vals))))
    336 
    337     (define (lookup-variable-value var env)
    338       (define (env-loop env)
    339         (define (scan vars vals)
    340           (cond ((null? vars)
    341                  (env-loop
    342                   (enclosing-environment env)))
    343                 ((eq? var (car vars))
    344                  (car vals))
    345                 (else (scan (cdr vars)
    346                             (cdr vals)))))
    347         (if (eq? env the-empty-environment)
    348             (error "Unbound variable" var)
    349             (let ((frame (first-frame env)))
    350               (scan (frame-variables frame)
    351                     (frame-values frame)))))
    352       (env-loop env))
    353 
    354     (define (set-variable-value! var val env)
    355       ;; XXX: Just trying to see what it would look like if I just used named lets.
    356       ;;
    357       ;; (let loop-over-environments ((env env))
    358       ;;   (cond
    359       ;;    ((eq? env the-empty-enviornment)
    360       ;;     (error "Unbound variable: SET!" var))
    361       ;;    (else
    362       ;;     (let ((frame (first-frame env)))
    363       ;;       (let loop-over-frame ((vars (frame-variables frame))
    364       ;;                             (vals (frame-values frame)))
    365       ;;         (cond
    366       ;;          ((null? vars)
    367       ;;           (loop-over-environments (enclosing-environment env)))
    368       ;;          ((eq? var (car vars))
    369       ;;           (set-car! vals val))
    370       ;;          (else
    371       ;;           (loop-over-frame ((cdr vars)
    372       ;;                             (cdr vals))))))))))
    373 
    374       (define (env-loop env)
    375         (define (scan vars vals)
    376           (cond ((null? vars)
    377                  (env-loop
    378                   (enclosing-environment env)))
    379                 ((eq? var (car vars))
    380                  (set-car! vals val))
    381                 (else (scan (cdr vars)
    382                             (cdr vals)))))
    383         (if (eq? env the-empty-environment)
    384             (error "Unbound variable: SET!" var)
    385             (let ((frame (first-frame env)))
    386               (scan (frame-variables frame)
    387                     (frame-values frame)))))
    388       (env-loop env))
    389 
    390     (define (define-variable! var val env)
    391       "Add a new variable VAR with value VAL in the top frame of environment ENV.
    392 
    393 If variable VAR already exists in the top frame, set VAR in the top
    394 frame to VAL."
    395 
    396       ;; We will CHANGE (why did I all uppercase "CHANGE" here?  I
    397       ;; don't remember.  Maybe it is some sort of a joke?) only
    398       ;; the first frame.
    399       (let ((frame (first-frame env)))
    400         (define (scan vars vals)
    401           (cond ((null? vars)
    402                  (add-binding-to-frame!
    403                   var val frame))
    404                 ((eq? var (car vars))
    405                  (set-car! vals val))
    406                 (else (scan (cdr vars)
    407                             (cdr vals)))))
    408         (scan (frame-variables frame)
    409               (frame-values frame))))
    410 
    411 ;;; 4.1.4
    412 
    413     (define (setup-environment)
    414       (dpp 'moo)
    415       (let* (;; XXX: See bug below on the quoted (define
    416              ;; primitive-procedures ...)
    417              (primitive-procedures
    418               (list (list 'car car)
    419                     (list 'cdr cdr)
    420                     (list 'cons cons)
    421                     (list 'null? null?)
    422                     (list '+ +)
    423                     (list '- -)
    424                     (list '* *)
    425                     (list '/ /)
    426                     (list '= =)
    427                     (list 'display display)
    428                     (list 'write write)
    429                     (list 'read read)))
    430              (initial-env
    431               (extend-environment
    432                (primitive-procedure-names primitive-procedures)
    433                (primitive-procedure-objects primitive-procedures)
    434                the-empty-environment)))
    435         (define-variable! 'true true initial-env)
    436         (define-variable! 'false false initial-env)
    437         initial-env))
    438 
    439     (define the-global-environment
    440       (begin
    441         (dpp 'moo)
    442         (setup-environment)))
    443 
    444     (define (primitive-procedure? proc)
    445       (tagged-list? proc 'primitive))
    446 
    447     (define (primitive-implementation proc)
    448       (cadr proc))
    449 
    450     ;; XXX: There is a bug here in SICP or GNU Guile or both.
    451     ;; primitive-procedures is `#<unspecified>` when this library is
    452     ;; being loaded.
    453     '(define primitive-procedures
    454        (list (list 'car car)
    455              (list 'cdr cdr)
    456              (list 'cons cons)
    457              (list 'null? null?)
    458              (list '+ +)
    459              (list '- -)
    460              (list '* *)
    461              (list '/ /)
    462              (list 'display display)
    463              (list 'write write)
    464              (list 'read read)))
    465 
    466     (define (primitive-procedure-names primitive-procedures)
    467       (map car
    468            primitive-procedures))
    469 
    470     (define (primitive-procedure-objects primitive-procedures)
    471       (map (lambda (proc)
    472              (list 'primitive (cadr proc)))
    473            primitive-procedures))
    474 
    475     (define (apply-primitive-procedure proc args)
    476       (apply-in-underlying-scheme
    477        (primitive-implementation proc) args))
    478 
    479     (define input-prompt  ";;; M-Eval input:")
    480     (define output-prompt ";;; M-Eval value:")
    481 
    482     (define (driver-loop)
    483       (prompt-for-input input-prompt)
    484       (let ((input (read)))
    485         (let ((output
    486                (eval input
    487                      the-global-environment)))
    488           (announce-output output-prompt)
    489           (user-print output)))
    490       (driver-loop))
    491 
    492     (define (prompt-for-input string)
    493       (newline) (newline)
    494       (display string) (newline))
    495 
    496     (define (announce-output string)
    497       (newline) (display string) (newline))
    498 
    499     (define (user-print object)
    500       (if (compound-procedure? object)
    501           (display
    502            (list 'compound-procedure
    503                  (procedure-parameters object)
    504                  (procedure-body object)
    505                  '<procedure-env>))
    506           (display object)))
    507 
    508     ;; XXX: Already defined above:
    509     ;; (define the-global-environment
    510     ;;   (setup-environment))
    511 
    512     (define (main args)
    513       (match args
    514         ('()
    515          (error "Arguments should have something rather than nothing."))
    516         (`(,arg0)
    517          (display arg0)
    518          (newline))
    519         (`(,arg0 "--debug")
    520          (display "DEBUG TURN ON!\n")
    521          (set! *debug* #t))
    522         (args (error "Unknown arguments:" args)))
    523 
    524       (driver-loop))
    525 
    526     (define (run-internal-tests)
    527       (test-eq 1
    528         (lookup-variable-value 'a
    529                                (extend-environment '(a +)
    530                                                    `(1 ,+)
    531                                                    the-empty-environment)))
    532       (test-eq 1
    533         (lookup-variable-value 'a
    534                                (extend-environment '(+ a)
    535                                                    `(,+ 1)
    536                                                    the-empty-environment)))
    537       (test-eq +
    538         (lookup-variable-value '+
    539                                (extend-environment '(a +)
    540                                                    `(1 ,+)
    541                                                    the-empty-environment))))
    542 
    543     ))