Clase 5: Semántica de lenguajes de programación¶
Cómo vamos¶
-
Abstracción de datos
- Representación inductiva y recursiva.
- Proceso de desarrollar funciones para datos recursivos.
-
TADs: Tipos abstractos de datos
- Tienen dos partes: implementación e interfaz. La implementación puede cambiar de tipo de representación, pero el programador interactúa con la interfaz, por lo que esto es transparente.
- Para implementar un TAD:
- Constructores: Permiten crear un elemento perteneciente al TAD.
- Observadores: Predicados para validar qué variante es y los extractores para extraer la información dentro de los TAD.
- Alcance y ligadura de variables:
let: Alcance en bloque (funcional).let*: Alcance secuencial (imperativo).letrec: Alcance recursivo.
- Diseño de TADs: Representación basada en listas, basada en procedimientos y los datatypes.
- Los datatypes son independientes del tipo y son una representación basada en la gramática.
; Definición de un datatype para árboles binarios
(define-datatype tree tree?
(empty-node) ; Constructor para nodo vacío
(non-empty-node ; Constructor para nodo con contenido
(key symbol?) ; La clave es un símbolo
(left tree?) ; Subárbol izquierdo
(right tree?) ; Subárbol derecho
)
)
; Función para convertir un árbol en una lista en recorrido preorden
(define tree->list
(lambda (arb)
(cases tree arb ; Abre el datatype 'tree'
(empty-node () '()) ; Caso base: nodo vacío devuelve lista vacía
(non-empty-node (k l r) ; Caso recursivo: nodo con clave y subárboles
(append
(list k) ; Agrega la clave actual
(tree->list l) ; Recorre recursivamente el subárbol izquierdo
(tree->list r) ; Recorre recursivamente el subárbol derecho
)
)
)
)
)
-
Recordar: cuando se trabaja con datos recursivos, en el caso recursivo se debe llamar a la misma función.
-
Representación concreta vs. abstracta:
- Representación concreta: El código fuente.
- Representación abstracta: AST (Árbol de sintaxis abstracta).
- Parser (concreta a abstracta): Se usan los constructores que provee el datatype.
- Unparser (abstracta a concreta): Se usa
casespara abrir el TAD y generar la representación concreta.
Conceptos teóricos adicionales¶
- Semántica de lenguajes: Estudio del significado de los programas, contrastando con la sintaxis (estructura). Incluye semántica operacional, denotacional y axiomática.
- AST (Árbol de sintaxis abstracta): Representación jerárquica de la estructura del programa, eliminando detalles sintácticos superficiales (como paréntesis, puntos y coma). Es fundamental para análisis estático, optimización e interpretación.
- TAD (Tipo abstracto de datos): Encapsula una estructura de datos ocultando su implementación. Solo se interactúa a través de operaciones definidas en su interfaz, promoviendo modularidad y mantenibilidad.
- Alcance (scope):
- Estático (léxico): Las variables se resuelven según la estructura del código (como en
let). - Dinámico: Las variables se resuelven en tiempo de ejecución según el flujo de llamadas.
- Estático (léxico): Las variables se resuelven según la estructura del código (como en
- Ligadura (binding): Asociación entre un identificador y una entidad (valor, procedimiento, etc.). Puede ser temprana (en compilación) o tardía (en ejecución).
Tabla de resumen¶
| Concepto | Descripción | Ejemplo/Nota |
|---|---|---|
| Abstracción de datos | Ocultar detalles de implementación, mostrando solo operaciones esenciales. | Representaciones inductivas/recursivas. |
| TAD (Tipo abstracto de datos) | Estructura que separa interfaz (uso) de implementación (representación interna). | Constructores y observadores. |
| Constructores | Funciones que crean instancias del TAD. | empty-node, non-empty-node. |
| Observadores | Funciones que inspeccionan o extraen información del TAD. | tree?, cases. |
Alcance en bloque (let) |
Las variables son visibles solo dentro del bloque, sin dependencia secuencial. | Alcance funcional. |
Alcance secuencial (let*) |
Las variables se definen en secuencia, pudiendo referenciar las anteriores. | Alcance imperativo. |
Alcance recursivo (letrec) |
Permite definiciones mutuamente recursivas dentro del mismo bloque. | Usado para procedimientos recursivos. |
| Datatypes | Definición de tipos inductivos basados en gramática, independientes de representación concreta. | define-datatype en Scheme. |
| Representación concreta | Código fuente textual con sintaxis específica. | Programa escrito por el desarrollador. |
| Representación abstracta (AST) | Árbol que captura la estructura lógica del programa, sin detalles sintácticos. | Resultado del parsing. |
| Parser | Convierte representación concreta en abstracta (construye AST). | Usa constructores del datatype. |
| Unparser | Convierte representación abstracta en concreta (genera código). | Usa cases para extraer y formatear. |
| Recursión en datos | Estructuras que se definen en términos de sí mismas, requiriendo casos base y recursivos. | Árboles, listas. |
Comentarios adicionales¶
- La separación entre representación concreta y abstracta es clave en el diseño de lenguajes: el AST facilita la manipulación y transformación de programas (por ejemplo, en optimizaciones de compiladores).
- Los TADs no solo encapsulan datos, sino que también pueden garantizar invariantes (por ejemplo, un árbol binario de búsqueda siempre ordenado).
- En semántica operacional, el AST es el estado inicial sobre el cual se definen reglas de evaluación paso a paso.
- El uso de
casesen Scheme (EOPL style) es un patrón de "pattern matching" primitivo que permite descomponer datatypes de manera segura y legible. - La recursión estructural (como en
tree->list) sigue naturalmente la definición inductiva del datatype, asegurando terminación si hay casos base bien definidos. - La elección entre
let,let*yletrecafecta no solo el alcance, sino también la posibilidad de referencias circulares y el orden de evaluación.