learning-sicp

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

commit 7d6e880fb77a5c9d2151a38aef1f4322ecbfdfce
parent 6e824bcec28a8f7297d540bc37626b2d3393610a
Author: Yuval Langer <yuval.langer@gmail.com>
Date:   Mon, 22 Jul 2024 11:32:12 +0300

Finish copying the SICP REPL almost verbatim from the book, for use as template for the exercises.

Diffstat:
Msicp/solutions/chapter-4/original-repl.scm | 597++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msicp/tests/chapter-4/original-repl-tests.scm | 22++++++++++++++++++++--
Msicp/utils.scm | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
3 files changed, 411 insertions(+), 286 deletions(-)

diff --git a/sicp/solutions/chapter-4/original-repl.scm b/sicp/solutions/chapter-4/original-repl.scm @@ -11,65 +11,106 @@ ) (import (rename (scheme base) (apply scheme:base:apply)) - (prefix (scheme cxr) scheme:cxr:) - (prefix (scheme read) scheme:read:) - (prefix (scheme write) scheme:write:) + (scheme cxr) + (scheme read) + (scheme write) + (srfi srfi-1) (srfi srfi-9) (srfi srfi-64) + (system vm trace) + (ice-9 match) + (ice-9 pretty-print) (sicp utils) ) (begin + ;; XXX: In 4.1.3 we see that the procedure `true?` is defined + ;; using the host-variable `false` whose value is 'false, but we + ;; don't see it defined anywhere in the good book, so we need to + ;; define the host-variable false ourselves. + ;; + ;; In MIT Scheme, the one the SICP people seem to love to use for + ;; SICP, the values of `false` and `true` are #f and #t, #f being + ;; the only value that is falsituous and #t there for having a + ;; truthious value that has no other meaning other than being + ;; truthitiousic. + ;; + ;; ``` + ;; # `scheme` is the MIT Scheme executable: + ;; $ guix shell mit-scheme -- scheme + ;; MIT/GNU Scheme running under GNU/Linux + ;; Type `^C' (control-C) followed by `H' to obtain information about interrupts. + ;; + ;; Copyright (C) 2020 Massachusetts Institute of Technology + ;; This is free software; see the source for copying conditions. There is NO warranty; not even for + ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + ;; + ;; Image saved on Sunday March 7, 2021 at 3:24:56 PM + ;; Release 11.2 || SF || LIAR/x86-64 + ;; + ;; 1 ]=> false + ;; + ;; ;Value: #f + ;; + ;; 1 ]=> true + ;; + ;; ;Value: #t + ;; + ;; 1 ]=> + ;; ``` + (define false #f) + (define true #t) + (define (make-environment vars vals) + "A comfort procedure because calling extend on the-empty-environment is a PITA." (extend-environment vars vals the-empty-environment)) + ;; XXX: As described in chapter 4.1.4 note 2, we need to name the + ;; host `apply`, and the guest `apply` differently. I define the + ;; host's `apply` to be the name used in the book. + (define apply-in-underlying-scheme scheme:base:apply) + ;;; Start: ;;; 4.1.1 (define (eval exp env) - (pp (list 'eval exp)) - (dp) - (define eval-result - (cond ((self-evaluating? exp) - exp) - ((variable? exp) - (lookup-variable-value exp env)) - ((quoted? exp) - (text-of-quotation exp)) - ((assignment? exp) - (eval-assignment exp env)) - ((definition? exp) - (eval-definition exp env)) - ((if? exp) - (eval-if exp env)) - ((lambda? exp) - (make-procedure - (lambda-parameters exp) - (lambda-body exp) - env)) - ((begin? exp) - (eval-sequence - (begin-actions exp) - env)) - ((cond? exp) - (eval (cond->if exp) env)) - ((application? exp) - (apply (eval (operator exp) env) - (list-of-values - (operands exp) - env))) - (else - (error "Unknown expression type -- EVAL" exp)))) - (dpp `(eval-result ,eval-result)) - (dp) - eval-result) + (cond ((self-evaluating? exp) + exp) + ((variable? exp) + (lookup-variable-value exp env)) + ((quoted? exp) + (text-of-quotation exp)) + ((assignment? exp) + (eval-assignment exp env)) + ((definition? exp) + (eval-definition exp env)) + ((if? exp) + (eval-if exp env)) + ((lambda? exp) + (make-procedure + (lambda-parameters exp) + (lambda-body exp) + env)) + ((begin? exp) + (eval-sequence + (begin-actions exp) + env)) + ((cond? exp) + (eval (cond->if exp) env)) + ((application? exp) + (apply (eval (operator exp) env) + (list-of-values + (operands exp) + env))) + (else + (error "Unknown expression type -- EVAL" exp)))) (define (apply procedure arguments) (cond ((primitive-procedure? procedure) @@ -89,6 +130,14 @@ (error "Unknown procedure type -- APPLY" procedure)))) +;;; Procedure arguments: + + (define (list-of-values exps env) + (if (no-operands? exps) + '() + (cons (eval (first-operand exps) env) + (list-of-values (rest-operands exps) env)))) + ;;; Conditionals: (define (eval-if exp env) @@ -98,7 +147,6 @@ ;;; Sequences: - (define (eval-sequence exps env) (cond ((last-exp? exps) (eval (first-exp exps) env)) @@ -109,8 +157,6 @@ ;;; Assignments and definitions - - (define (eval-assignment exp env) (set-variable-value! (assignment-variable exp) @@ -119,17 +165,10 @@ 'ok) (define (eval-definition exp env) - (dp `(eval-definition ,exp)) - (dp) - (let ((def-var (definition-variable exp)) - (def-val (definition-value exp))) - (dp `(definition-variable ,def-var)) - (dp `(definition-value ,def-val)) - (dp) - (define-variable! - (definition-variable exp) - (eval (definition-value exp) env) - env)) + (define-variable! + (definition-variable exp) + (eval (definition-value exp) env) + env) 'ok) ;;; end 4.1.1 @@ -138,78 +177,82 @@ (define (self-evaluating? exp) - (or (number? exp) - (string? exp))) + (cond ((number? exp) true) + ((string? exp) true) + (else false))) + (define (variable? exp) (symbol? exp)) - (define (quoted? exp) (tagged-list? exp 'quote)) - (define (text-of-quotation exp) (cadr exp)) + (define (quoted? exp) + (tagged-list? exp 'quote)) + + (define (text-of-quotation exp) + (cadr exp)) (define (tagged-list? exp tag) - (and (pair? exp) - (eq? (car exp) tag))) + (if (pair? exp) + (eq? (car exp) tag) + false)) - (define (assignment? exp) (tagged-list? exp 'set!)) - (define (assignment-variable exp) (cadr exp)) - (define (assignment-value exp) (scheme:cxr:caddr exp)) + (define (assignment? exp) + (tagged-list? exp 'set!)) + + (define (assignment-variable exp) + (cadr exp)) + + (define (assignment-value exp) (caddr exp)) (define (definition? exp) (tagged-list? exp 'define)) + (define (definition-variable exp) - (cond - ((symbol? (cadr exp)) - (cadr exp)) - (else - (scheme:cxr:caadr exp)))) + (if (symbol? (cadr exp)) + (cadr exp) + (caadr exp))) + (define (definition-value exp) - (cond - ((symbol? (cadr exp)) - (scheme:cxr:caddr exp)) - (else - (let ((formal-parameters (scheme:cxr:cdadr exp)) - (body (cddr exp))) - (dp `(formal-parameters ,formal-parameters)) - (dp `(body ,body)) - (dp) - (make-lambda formal-parameters - body))))) - - (define (lambda? exp) (tagged-list? exp 'lambda)) + (if (symbol? (cadr exp)) + (caddr exp) + (make-lambda + (cdadr exp) ; formal-parameters + (cddr exp)))) ; body + + (define (lambda? exp) + (tagged-list? exp 'lambda)) (define (lambda-parameters exp) (cadr exp)) (define (lambda-body exp) (cddr exp)) + (define (make-lambda parameters body) - (let ((our-lambda `(lambda ,parameters ,@body))) - (dp `(our-lambda ,our-lambda)) - our-lambda)) + (cons 'lambda (cons parameters body))) (define (if? exp) (tagged-list? exp 'if)) (define (if-predicate exp) (cadr exp)) - (define (if-consequent exp) (scheme:cxr:caddr exp)) + (define (if-consequent exp) (caddr exp)) (define (if-alternative exp) - (match exp - (`(if ,p ,c ,a) - a) - (`(if ,p ,c) - 'false) - (otherwise (error "Malformed if:" otherwise))) - ;; (cond - ;; ((not (null? (scheme:cxr:cdddr exp))) - ;; (scheme:cxr:cdddr exp) - ;; 'false)) - ) - (define (make-if predicate consequent alternative) - (list 'if predicate consequent alternative)) - - (define (begin? exp) (tagged-list? exp 'begin)) + (if (not (null? (cdddr exp))) + (cadddr exp) + 'false)) + + (define (make-if predicate + consequent + alternative) + (list 'if + predicate + consequent + alternative)) + + (define (begin? exp) + (tagged-list? exp 'begin)) (define (begin-actions exp) (cdr exp)) (define (last-exp? seq) (null? (cdr seq))) (define (first-exp seq) (car seq)) (define (rest-exps seq) (cdr seq)) + (define (sequence->exp seq) - (cond - ((null? seq) seq) - ((last-exp? seq) (first-exp seq)) - (else (make-begin seq)))) + (cond ((null? seq) seq) + ((last-exp? seq) (first-exp seq)) + (else (make-begin seq)))) + (define (make-begin seq) (cons 'begin seq)) (define (application? exp) (pair? exp)) @@ -219,240 +262,264 @@ (define (first-operand ops) (car ops)) (define (rest-operands ops) (cdr ops)) - (define (cond? exp) (tagged-list? exp 'cond)) + (define (cond? exp) + (tagged-list? exp 'cond)) (define (cond-clauses exp) (cdr exp)) - (define (cond-else-clause? clause) (eq? (cond-predicate clause) 'else)) - (define (cond-predicate clause) (car clause)) - (define (cond-actions clause) (cdr clause)) - (define (cond->if exp) (expand-clauses (cond-clauses exp))) + (define (cond-else-clause? clause) + (eq? (cond-predicate clause) 'else)) + (define (cond-predicate clause) + (car clause)) + (define (cond-actions clause) + (cdr clause)) + (define (cond->if exp) + (expand-clauses (cond-clauses exp))) (define (expand-clauses clauses) - (cond - ((null? clauses) - 'false - (let ((first-clause (car clauses)) - (rest-of-clauses (cdr clauses))) - (cond - ((cond-else-clause? first-clause) - (cond - ((null? rest-of-clauses) - (sequence->exp - (cond-actions first-clause))) - (else - (error "ELSE clause isn't last: COND->IF")))) - (else - (make-if (cond-predicate first-clause) - (sequence->exp (cond-actions first-clause)) - (expand-clauses rest-of-clauses)))))))) + (if (null? clauses) + 'false + (let ((first (car clauses)) + (rest (cdr clauses))) + (if (cond-else-clause? first) + (if (null? rest) + (sequence->exp + (cond-actions first)) + (error "ELSE clause isn't last: COND->IF" + clauses)) + (make-if (cond-predicate first) + (sequence->exp + (cond-actions first)) + (expand-clauses + rest)))))) ;;; end 4.1.2 ;;; 4.1.3 - ;; The symbol false is the only false in the language. - + ;; XXX: By default, false's value is #f, which is the only true + ;; false. All others are fake and would have the truthitousness + ;; property. Consult `info r7rs-small` or `C-h + (define (true? x) + (not (eq? x false))) - (define (true? x) (not (false? x))) - (define (false? x) (eq? x 'false)) - - (define (make-frame variables values) (cons variables values)) - (define (frame-variables frame) (car frame)) - (define (frame-values frame) (cdr frame)) - (define (add-binding-to-frame! var val frame) - (set-car! frame (cons var (frame-variables frame))) - (set-cdr! frame (cons val (frame-values frame)))) - - (define (define-variable! var val env) - "Add variable VAR with value VAL to environment ENV." - - ;; We will CHANGE only the first frame - (let ((frame (first-frame env))) - (define (scan vars vals) - (cond ((null? vars) - (add-binding-to-frame! var - val - frame)) - ((eq? var (car vars)) - (set-car! vals val)) - (else (scan (cdr vars) - (cdr vals))))) - (dpp `(frame-values ,(frame-values frame))) - (dp) - (scan (frame-variables frame) - (frame-values frame)))) - - (define (enclosing-environment env) (cdr env)) - (define (first-frame env) (car env)) - (define the-empty-environment '()) + (define (false? x) + (eq? x false)) (define (make-procedure parameters body env) (list 'procedure parameters body env)) (define (compound-procedure? p) (tagged-list? p 'procedure)) (define (procedure-parameters p) (cadr p)) - (define (procedure-body p) (scheme:cxr:caddr p)) - (define (procedure-environment p) (scheme:cxr:cadddr p)) + (define (procedure-body p) (caddr p)) + (define (procedure-environment p) (cadddr p)) + + (define (enclosing-environment env) (cdr env)) + (define (first-frame env) (car env)) + (define the-empty-environment '()) + + (define (make-frame variables values) + (cons variables values)) + (define (frame-variables frame) (car frame)) + (define (frame-values frame) (cdr frame)) + (define (add-binding-to-frame! var val frame) + (set-car! frame (cons var (car frame))) + (set-cdr! frame (cons val (cdr frame)))) (define (extend-environment vars vals base-env) - (let ((vars-length (length vars)) - (vals-length (length vals))) - (cond - ((= vars-length vals-length) - (cons (make-frame vars vals) base-env)) - ((< vars-length vals-length) - (error "Too many arguments supplied:" vars vals)) - (else - (error "Too few arguments supplied:" vars vals))))) + (if (= (length vars) (length vals)) + (cons (make-frame vars vals) base-env) + (if (< (length vars) (length vals)) + (error "Too many arguments supplied" + vars + vals) + (error "Too few arguments supplied" + vars + vals)))) (define (lookup-variable-value var env) (define (env-loop env) - (dpp `(env ,env)) - (dp) (define (scan vars vals) - (dpp `(vars ,vars vals ,vals)) - (dp) (cond ((null? vars) (env-loop (enclosing-environment env))) ((eq? var (car vars)) - (dpp `(car-of-vals ,(car vals))) - (dp) (car vals)) (else (scan (cdr vars) (cdr vals))))) - (cond - ((eq? env the-empty-environment) - (error "Unbound variable" var)) - (else - (let ((frame (first-frame env))) - (scan (frame-variables frame) - (frame-values frame)))))) + (if (eq? env the-empty-environment) + (error "Unbound variable" var) + (let ((frame (first-frame env))) + (scan (frame-variables frame) + (frame-values frame))))) (env-loop env)) - (define (set-variable-value! var val env) - (define (env-lookup env) + ;; XXX: Just trying to see what it would look like if I just used named lets. + ;; + ;; (let loop-over-environments ((env env)) + ;; (cond + ;; ((eq? env the-empty-enviornment) + ;; (error "Unbound variable: SET!" var)) + ;; (else + ;; (let ((frame (first-frame env))) + ;; (let loop-over-frame ((vars (frame-variables frame)) + ;; (vals (frame-values frame))) + ;; (cond + ;; ((null? vars) + ;; (loop-over-environments (enclosing-environment env))) + ;; ((eq? var (car vars)) + ;; (set-car! vals val)) + ;; (else + ;; (loop-over-frame ((cdr vars) + ;; (cdr vals)))))))))) + + (define (env-loop env) (define (scan vars vals) - (cond - ((null? vars) - (env-lookup - (enclosing-environment env))) - ((eq? var (car vars)) - (set-car! vals val)) - (else - (scan (cdr vars) - (cdr vals))))) + (cond ((null? vars) + (env-loop + (enclosing-environment env))) + ((eq? var (car vars)) + (set-car! vals val)) + (else (scan (cdr vars) + (cdr vals))))) (if (eq? env the-empty-environment) (error "Unbound variable: SET!" var) (let ((frame (first-frame env))) (scan (frame-variables frame) (frame-values frame))))) - (env-lookup env)) + (env-loop env)) - (define (list-of-values exps env) - (if (no-operands? exps) - '() - (cons (eval (first-operand exps) env) - (list-of-values (rest-operands exps) env)))) + (define (define-variable! var val env) + "Add a new variable VAR with value VAL in the top frame of environment ENV. + +If variable VAR already exists in the top frame, set VAR in the top +frame to VAL." + + ;; We will CHANGE (why did I all uppercase "CHANGE" here? I + ;; don't remember. Maybe it is some sort of a joke?) only + ;; the first frame. + (let ((frame (first-frame env))) + (define (scan vars vals) + (cond ((null? vars) + (add-binding-to-frame! + var val frame)) + ((eq? var (car vars)) + (set-car! vals val)) + (else (scan (cdr vars) + (cdr vals))))) + (scan (frame-variables frame) + (frame-values frame)))) ;;; 4.1.4 (define (setup-environment) - (let* ((primitive-procedures - `((car ,car) - (cdr ,cdr) - (cons ,cons) - (null? ,null?))) - (primitive-procedure-names (map car primitive-procedures)) - (primitive-procedure-objects (map (lambda (proc) - `(primitive ,(cadr proc))) - primitive-procedures)) + (dpp 'moo) + (let* (;; XXX: See bug below on the quoted (define + ;; primitive-procedures ...) + (primitive-procedures + (list (list 'car car) + (list 'cdr cdr) + (list 'cons cons) + (list 'null? null?) + (list '+ +) + (list '- -) + (list '* *) + (list '/ /) + (list 'display display) + (list 'write write) + (list 'read read))) (initial-env (extend-environment - primitive-procedure-names - primitive-procedure-objects + (primitive-procedure-names primitive-procedures) + (primitive-procedure-objects primitive-procedures) the-empty-environment))) - (define-variable! 'true 'true initial-env) - (define-variable! 'false 'false initial-env) + (define-variable! 'true true initial-env) + (define-variable! 'false false initial-env) initial-env)) (define the-global-environment - (setup-environment)) - - (define (primitive-procedure? proc) (tagged-list? proc 'primitive)) - (define (primitive-implementation proc) (cadr proc)) + (begin + (dpp 'moo) + (setup-environment))) + + (define (primitive-procedure? proc) + (tagged-list? proc 'primitive)) + + (define (primitive-implementation proc) + (cadr proc)) + + ;; XXX: There is a bug here in SICP or GNU Guile or both. + ;; primitive-procedures is `#<unspecified>` when this library is + ;; being loaded. + '(define primitive-procedures + (list (list 'car car) + (list 'cdr cdr) + (list 'cons cons) + (list 'null? null?) + (list '+ +) + (list '- -) + (list '* *) + (list '/ /) + (list 'display display) + (list 'write write) + (list 'read read))) + + (define (primitive-procedure-names primitive-procedures) + (map car + primitive-procedures)) + + (define (primitive-procedure-objects primitive-procedures) + (map (lambda (proc) + (list 'primitive (cadr proc))) + primitive-procedures)) (define (apply-primitive-procedure proc args) - (scheme:base:apply + (apply-in-underlying-scheme (primitive-implementation proc) args)) - (define input-prompt ";;; M-Eval input:") + (define input-prompt ";;; M-Eval input:") (define output-prompt ";;; M-Eval value:") (define (driver-loop) (prompt-for-input input-prompt) - (let loop ((input (scheme:read:read))) - - (when *debug* (pp (list 'input input))) - (cond - ((eof-object? input) - (when *debug* (pp (list 'the-global-environment - the-global-environment)))) - (else - (when *debug* (pp (list 'the-global-environment - the-global-environment))) - (let* ((output (eval input the-global-environment))) - (announce-output output-prompt) - (user-print output)) - (prompt-for-input input-prompt) - (loop (scheme:read:read)))))) + (let ((input (read))) + (let ((output + (eval input + the-global-environment))) + (announce-output output-prompt) + (user-print output))) + (driver-loop)) (define (prompt-for-input string) - (newline) - (newline) - (scheme:write:display string) - (newline)) + (newline) (newline) + (display string) (newline)) (define (announce-output string) - (newline) - (scheme:write:display string) - (newline)) + (newline) (display string) (newline)) (define (user-print object) - (cond - ((procedure? object) - (let ((proc (list 'compound-procedure - (procedure-parameters object) - (procedure-body object) - '<procedure-env>))) - (when *debug* (pp proc)) - proc)) - (else - (pretty-print object)))) + (if (compound-procedure? object) + (display + (list 'compound-procedure + (procedure-parameters object) + (procedure-body object) + '<procedure-env>)) + (display object))) + + ;; XXX: Already defined above: + ;; (define the-global-environment + ;; (setup-environment)) (define (main args) (match args ('() (error "Arguments should have something rather than nothing.")) (`(,arg0) - (scheme:write:display arg0) + (display arg0) (newline)) (`(,arg0 "--debug") - (scheme:write:display "DEBUG TURN ON!\n") + (display "DEBUG TURN ON!\n") (set! *debug* #t)) (args (error "Unknown arguments:" args))) - (eval '(define (x y) - (cond - ((null? x) - y) - (else - (cons (car x) - (append (cdr x) - y))))) - the-global-environment) - - (dp (list 'starting-with-the-global-environment: the-global-environment)) - (dp) (driver-loop)) (define (run-internal-tests) diff --git a/sicp/tests/chapter-4/original-repl-tests.scm b/sicp/tests/chapter-4/original-repl-tests.scm @@ -1,6 +1,8 @@ (define-library (sicp tests chapter-4 original-repl-tests) (import (scheme base) + (srfi srfi-64) + (prefix (sicp solutions chapter-4 original-repl) repl:) (sicp utils)) @@ -8,7 +10,6 @@ (set! *debug* #t) (test-begin "original-repl-tests") - (test-group "internal-tests" (repl:run-internal-tests)) @@ -16,7 +17,7 @@ (repl:eval 1 '())) (test-equal '() - (repl:eval '(quote ()) '())) + (repl:eval '(quote ()) '())) (test-equal 1 (repl:eval '(+ -1 2) @@ -50,4 +51,21 @@ `((primitive ,+)) (repl:setup-environment)))) + (test-equal 1 + (repl:eval '((lambda (a b) + (+ a b)) + -1 2) + (repl:extend-environment '(+) + `((primitive ,+)) + (repl:setup-environment)))) + + (test-equal 1 + (repl:eval '(begin + (define (plus a b) + (+ a b)) + (plus -1 2)) + (repl:extend-environment '(+) + `((primitive ,+)) + (repl:setup-environment)))) + (test-end "original-repl-tests"))) diff --git a/sicp/utils.scm b/sicp/utils.scm @@ -1,13 +1,9 @@ (define-library (sicp utils) - (import (scheme base) - (scheme write) - - (ice-9 pretty-print) - - (srfi srfi-19)) (export *debug* accumulate + debug! + debug? dp dpp enumerate-interval @@ -15,26 +11,70 @@ flatmap pp ) + (import (scheme base) + (scheme write) + + (ice-9 pretty-print) + + (srfi srfi-1) + (srfi srfi-19) + (srfi srfi-28) + + ;;(prefix (guile) guile:) + (only (guile) + define* + define-syntax-rule + current-source-location)) (begin (define *debug* #f) + (define* (debug! #:optional x) + (if x + (set! *debug* (not *debug*)) + (set! *debug* x))) + (define (debug?) *debug*) (define nil '()) - (define (dp . x) - "Debug Print." - (when *debug* - (if (null? x) - (newline) - (begin (write (car x)) - (newline) - (car x))))) + (define-syntax-rule (dp x) + ;; Debug Print. + (when (debug?) + (let* ((loc (current-source-location)) + (line-number (cdr (assoc 'line loc eq?))) + (column-number (cdr (assoc 'column loc eq?))) + (x-val x)) + (display line-number) + (display ":") + (display column-number) + (display ":") + (newline) + (write (quote x)) + (newline) + (display " = " ) + (newline) + (write x-val) + (display "\n\n") + x-val))) - (define (dpp x) - "Debug Pretty Print." - (when *debug* - (pretty-print x) - x)) + (define-syntax dpp + ;; Debug Pretty Print. + (syntax-rules () + ((dpp x) + (when (debug?) + (let* ((loc (current-source-location)) + (line-number (cdr (assoc 'line loc eq?))) + (column-number (cdr (assoc 'column loc eq?))) + (x-val x)) + (display line-number) + (display ":") + (display column-number) + (display ":") + (newline) + (pretty-print (quote x)) + (display "=> ") + (pretty-print x-val) + (newline) + x-val))))) (define (pp x) (pretty-print x))