Planche : 1




Le langage Scheme

Revision: 1.11





  Scheme en Scheme





Christian Queinnec   UPMC--LIP6


Planche : 2


Tout ce que vous voulez savoir sur Scheme

Scheme est un langage universel, on peut donc écrire Scheme en Scheme.

C'est la fameuse fonction eval.

La définition de cette fonction est à la fois un manuel de référence, un outil de génie logiciel (traceur, metteur au point ...), un outil linguistique pour développer de nouveaux langages et la base de la programmation réflexive.


Planche : 3


La fonction eval


(define (evaluate e env)
  (if (atom? e) 
      (cond ((symbol? e) (lookup e env))
            ((or (number? e) (string? e) (char? e) (boolean? e))
             e )
            (else (wrong "Cannot evaluate" e)) )
      (case (car e)
        ((quote) (cadr e))
        ((if) (if (evaluate (cadr e) env)
                  (evaluate (caddr e) env)
                  (evaluate (cadddr e) env) ))
        ((begin) (eprogn (cdr e) env))
        ((set!) (update! (cadr e) env (evaluate (caddr e) env)))
        ((lambda) (make-function (cadr e) (cddr e) env))
        (else (invoke (evaluate (car e) env)
                      (evlis (cdr e) env) )) ) ) ) 


Planche : 4


Itérateurs autour d'eval


(define (evlis exps env)
  (if (pair? exps)
      (cons (evaluate (car exps) env)
            (evlis (cdr exps) env) )
      '() ) )

(define (eprogn exps env)
  (if (pair? exps)
      (if (pair? (cdr exps))
          (begin (evaluate (car exps) env)
                 (eprogn (cdr exps) env) )
          (evaluate (car exps) env) ) ) ) 

En DrScheme: (begin) induit le message malformed begin.


Planche : 5


Environnement


(define (lookup id env)
  (if (pair? env)
      (if (eq? (caar env) id)
          (cdar env)
          (lookup id (cdr env)) )
      (wrong "No such binding" id) ) )

(define (update! id env value)
  (if (pair? env)
      (if (eq? (caar env) id)
          (set-cdr! (car env) value)
          (update! id (cdr env) value) )
      (wrong "No such binding" id) ) ) 

En DrScheme: a (define a 3) est légal mais induit le message reference to undefined identifier: a avec undefined au sens de « non initialisé ».


Planche : 6


Fonctions


(define (make-function variables body env)
  (lambda (values)
     (eprogn body (extend env variables values)) ) )

(define (invoke fn args)
  (if (function? fn) 
      (fn args)
      (wrong "Not a function" fn) ) ) 

(define function? procedure?) 


Planche : 7


Extension d'environnement


(define (extend env names values)
  (cond ((pair? names)
         (if (pair? values)
             (cons (cons (car names) (car values))
                   (extend env (cdr names) (cdr values)) )
             (wrong "Too less values") ) )
        ((null? names)
             (if (null? values)
                 env 
                 (wrong "Too much values") ) )
        ((symbol? names) (cons (cons names values) env)) ) ) 


Planche : 8


Boucle fondamentale


(define (toplevel)
  (display (evaluate (read) global-env))
  (toplevel) ) 


Planche : 9


Environnement initial


(define (wrap-primitive name behavior comparator arity)
  (lambda (values)
    (if (comparator (length values) arity)
        (apply behavior values)
        (wrong "Incorrect arity" name) ) ) )

(define global-env
  (list (cons 'car  (wrap-primitive 'car  car = 1))
        (cons 'cons (wrap-primitive 'cons cons = 2))
        (cons 'list (wrap-primitive 'list list >= 0))
        ... ) ) 

Il y a quelques fonctions un peu spéciales comme apply et call/cc.


Planche : 10


Les différentes couches

eval est paramétré par deux types abstraits: Mais eval est aussi paramétré par; On peut exprimer ces dépendances en Scheme directement:


(define (build-evaluator env-repr fn-repr evaluate-repr global-env-repr)
  (call-with-values (env-repr)
    (lambda (lookup update! extend)
      (call-with-values (fn-repr lookup update! extend)
        (lambda (make-function function? invoke)
          (call-with-values 
              (evaluate-repr lookup update! extend 
                             make-function function? invoke )
            (lambda (evaluate)
              (define (evlis exps env)
                (if (pair? exps)
                    (cons (evaluate (car exps) env)
                          (evlis (cdr exps) env) )
                    '() ) )
              (define (eprogn exps env)
                (if (pair? exps)
                    (if (pair? (cdr exps))
                        (begin (evaluate (car exps) env)
                               (eprogn (cdr exps) env) )
                        (evaluate (car exps) env) ) ) )
              (call-with-values 
                  (global-env-repr lookup update! extend
                                   make-function function? invoke 
                                   evaluate evlis eprogn )
                (lambda (global-env)
                  (define (toplevel)
                    (display (evaluate (read) global-env))
                    (toplevel) )
                  toplevel ) ) ) ) ) ) ) ) ) 

(define myScheme
  (build-evaluator
   (lambda () 
     (define LOOKUP ...)
     (define UPDATE! ...)
     (define EXTEND ...)
     (values LOOKUP UPDATE! EXTEND) )
   (lambda (lookup update! extend)
     (define MAKE-FUNCTION ...)
     (define INVOKE ...)
     (values MAKE-FUNCTION INVOKE) )
   (lambda (lookup update! extend make-function function? invoke)
     (define EVAL ...)
     (values EVAL) )
   (lambda (lookup update! extend make-function function? invoke 
            evaluate evlis eprogn )
     (define GLOBALENV ...)
     (values GLOBALENV) ) ) )

(myScheme)  

J'oublais! call-with-values et values sont des traits nouveaux du R5RS qui peuvent s'écrire comme:
(define values list)
(define (call-with-values producer consumer)
  (apply consumer (producer)) ) 

((call-with-values (lambda () (values null? '() cons cdr))
   (lambda (zero? one * pred)
      (define (fact n)
        (if (zero? n)
            one
            (* n (fact (pred n))) ) )
      fact ) )
 '(a b c) )    ; ® ((a b c)(b c)(c))


Ce document a été traduit de LATEX par HEVEA.