Tarea 3 (Entrega: 19 de julio de 2020)

Esta tarea se distribuye con un archivo zip (base-tarea3) que contiene 2 archivos: main.rkt y tests.rkt. Los archivos main.rkt y tests.rkt están incompletos, y en ellos tienen que implementar lo que se solicita en las preguntas siguientes.

Deben entregar via U-cursos un archivo .zip que contenga los archivos main.rkt y tests.rkt.

Consulte las normas de entrega de tareas en http://pleiad.cl/teaching/cc4101

¡Los tests, los contratos y las descripciones forman parte de su evaluación!

En esta tarea extenderemos un lenguaje base para soportar objetos y delegación. El lenguaje base tiene números, booleanos y operaciones sobre ellos. Además contiene expressiones if, seqn, local y define, pero no tiene soporte para funciones. Se emplea call-by-value como estrategia de evaluación. Tome un tiempo de experimentar con el lenguaje entregado antes de comenzar a implementar las extensiones pedidas.

Objetos como valores (3,0 ptos.)

En esta sección se extenderá el lenguaje MiniScheme+ con objetos.

A continuación se presenta la sintaxis del lenguaje extendido (se omiten las del lenguaje base):

<expr> ::= ... (expresiones del lenguage entregado) ...
        | (object <def>*)
        | (get <id>)
        | (set <id> <expr>)
        | (send <expr> <id> <expr>*)
        | this
 
<def>  ::= (field <id> <expr>)
         | (method <id> (<id>*) <expr>)

Donde:

  • object permite crear un nuevo objeto (anónimo), con definiciones de campos y métodos.
  • get y set permiten acceder a un campo del objeto corriente o modificarlo.
Para realizar modificaciones con set puede usar mutación. Es decir, no se requiere el uso de un store.
  • send permite invocar un método de un objeto dado, con 0 o más argumentos.
  • this permite acceder al objeto actual.
  • una definición de campo (field) incluye una expresión de inicialización, que se ejecuta nuevamente para cada instancia creada.
  • una definición de método (method) especifica 0 o más parámetros, y el cuerpo del método.

Los objetos son tratados como valores. Usted debe decidir los atributos que tendrán sus respectivos nodos en el AST: piensan bien en que se necesita para evaluar un objeto, en particular con respeto al ambiente. Modifique el parser y el intérprete para soportar el lenguaje extendido. Además, los errores deben manejarse de la siguiente forma:

  • El acceso a un campo inexistente de un objeto debe arrojar el error “field not found”.
  • La invocación de un método inexistente debe lanzar el error “method not found”.
  • Solamente es válido usar this, set y get dentro de un objeto. Al exterior debe lanzar el erro “… used outside of an object”.

Veamos algunos ejemplos de objetos en acción:

;; comportamiento esperado
> (run-val '(local
              [(define o (object
                          (field x 1)
                          (field y 2)
                          (method sum (z) (+ (get x) (+ (get y) z)))
                          (method set-x (val) (set x val))
                          (method get-y () (get y))))]
            (seqn
             (send o set-x (+ 1 3))
             (+ (send o sum 3) (send o get-y)))))
11
;; los objetos son valores
> (run-val
       '(local
            [(define a
               (object
                (method auto-apply (o)
                        (send o apply o))
                (method foo () 5)))
             (define o (send a auto-apply
                             (object
                              (method apply (other) (send other apply2 this))
                              (method apply2 (other) this)
                              (method foo () 42))))]
          (send o foo)))
 
42
Orden de evaluación:
  • Dentro de una expresión (object [: e] members) se evalua e en primer lugar y después los campos en el orden en que aparecen.
  • Dentro de una expresión (send e method-id args) se evalua e en primer lugar y después los argumentos args de izquierda a derecha.

Objetos con delegación (2,0 puntos)

En esta sección deberá implementar un sistema de delegación para objetos, similar al que poseen lenguajes como Self o Javascript. Antes de hacer este ejercicio, lea cuidadamente la sección 4 de OOPLAI.

La sintaxis se debe extender de la siguiente manera:

<expr> ::= ... (expresiones previas) ...
        | (object [: <expr>] <def>*)
 

Donde los paréntesis cuadrados implican opcionalidad. En caso de estar presente la expresión opcional en primera posición, esta debe evaluar a un objeto, al cuál se deben delegar los mensajes que el objeto definido no comprende, siguiendo la semántica de delegación.

Note que debe mantener el soporte para expresiones de la forma (object <def>*), para crear objetos cuyos mensajes incomprendidos no son delegados.

> (run-val '(local
              [(define smart-computer (object
                                       (method secret? (something) 42)))
               (define everything (object))
               (define oracle (object : smart-computer))]
               (send oracle secret? everything)))
 
42  

Su implementación debe asegurar la semántica de delegación.

> (run-val '(local
              [(define seller (object
                               (method multiplier () 1)
                               (method price (item-number)
                                       (* item-number (send this multiplier)))))
               (define broker (object : seller
                                      (method multiplier () 2)))]
               (send broker price 3)))
 
6  
 
> (run-val '(local
               ([define x (object
                           (field z 3)
                           (method get () (get z)))]
                [define y (object : x)])
             (send y get)))
 
3
 
> (run-val '(local
               ([define x (object
                           (field z 3)
                           (method get () (get z)))]
                [define y (object : x
                               (method get () (get z)))])
             (send y get)))
"field not found"

Copias de objetos con delegación (1,0 punto)

Agregue dos operaciones shallow-copy y deep-copy que permiten replicar un objeto.

<expr> ::= ... (expresiones previas) ...
        | (shallow-copy <expr>)
        | (deep-copy <expr>)
 

La expresión shallow-copy crea un objeto nuevo duplicando los campos del objeto dado, y que tiene los mismos métodos (no intentaremos duplicar los ambientes de los métodos). La expresión deep-copy funciona como shallow-copy pero además copia la cadena de delegación de manera recursiva.

;; a simple monotone counter
> (define counter '(object
                  (field count 0)
                  (method incr () (set count (+ 1 (get count))))
                  (method get () (get count))))
 
;; sequence of operations over 2 counters
> (define (incrs-multiply x y)
  `(seqn
    (send ,y incr)
    (seqn
     (send ,x incr)
     (seqn
      (send ,x incr)
      (* (send ,x get) (send ,y get))
      ))))
 
;; no delegation, shallow-copy -> no sharing      
> (run-val
       `(local ([define c ,counter])
          (seqn (send c incr)
                (local ([define c2 (shallow-copy c)])
                  ,(incrs-multiply 'c 'c2)))))
 
6
 
;; delegation, shallow-copy -> sharing
> (run-val
       `(local ([define c (object : ,counter)])
          (seqn (send c incr)
                (local ([define c2 (shallow-copy c)])
                  ,(incrs-multiply 'c 'c2)))))
 
16
 
;; delegation, deep-copy -> no sharing
> (run-val
       `(local ([define c (object : ,counter)])
          (seqn (send c incr)
                (local ([define c2 (deep-copy c)])
                  ,(incrs-multiply 'c 'c2)))))
 
6
 

[Bonus] Codificando lambdas con objetos (0,5 ptos.)

Ahora incorporamos lambdas a nuestro lenguaje. A diferencia de lo visto durante el curso, en esta ocasión no daremos una interpretación directa de las funciones, usted debe ingeniar una manera de codificar una lambda como un objeto. Esto significa que no puede modificar el interprete para soportar funciones, ni aplicaciones de funciones. Las modificaciones que debe hacer son en el parser.

<expr> ::= ...
         | (fun (<id>*) <expr>)
         | (<expr> <expr>*)

Ejemplo de uso:

> (run-val '(local
              [(define f (fun (x)
                              (+ x x)))]
              (f 5)))
10