Programación Funcional Avanzada - Práctico 1 26-08-2016 → 09-09-2016 1. Considere los módulos module A (S (), module B ) where import Prelude () import B (T (. .)) data S = C | D module B (T (D)) where data T = C | D (a) ¿Qué nombres exporta el módulo A? (b) ¿Qué nombres están en el alcance del cuerpo del módulo A y dónde están definidos? Si existen nombres ambiguos, lı́stelos. 2. En Haskell, una (sub-) expresión puede ser anotada explı́citamente con un tipo utilizando (::). Por ejemplo, si escribimos: x = [[ ]] el tipo de x es [[a]] y si escribimos: x = [[ ] :: [Int ]] el tipo es [[Int]]. Utilizando módulos, muestre un pequeño programa donde a una (sub-) expresión no se le puede anotar su tipo. Pruebe en GHC y muestre los mensajes de error obtenidos. 3. Considere las siguientes definiciones: data Tree a = Empty | Node (Tree a) a (Tree a) deriving Show size :: Tree a → Int size Empty =0 size (Node l r ) = size l + 1 + size r 1 length :: [a ] → Int length [ ] =0 length (x : xs) = 1 + length xs flatten :: Tree a → [a ] flatten Empty = [] flatten (Node l a r ) = flatten l ++ [a ] ++ flatten r (+ +) :: [a ] → [a ] → [a ] [] + + ys = ys (x : xs) + + ys = x : (xs ++ ys) Demuestre el siguiente Teorema utilizando razonamiento ecuacional: ∀ (t :: Tree a).length (flatten t) ≡ size t Previamente demuestre el siguiente Lema, que le será útil para demostrar el Teorema: ∀ (xs :: [a ]) (ys :: [a ]).length (xs ++ ys) ≡ length xs + length ys Recuerde que (+) es asociativo y 0 es su elemento neutro. 4. Escriba un módulo Haskell con todas las definiciones del ejercicio anterior. Importe QuickCheck y defina una instancia de Arbitrary para poder probar propiedades parametrizadas por Trees. instance Arbitrary a ⇒ Arbitrary (Tree a) ... Defina propiedades QuickCheck para el Teorema y Lema del ejercicio anterior. Defina una función mirror que invierta un árbol, y defina propiedades QuickCheck para verificar que: (a) invertir un árbol y luego aplicarle flatten es lo mismo que aplicar flatten a un árbol y luego invertir (reverse) la lista resultante (b) invertir dos veces un árbol resulta en el árbol original (c) el tamaño de un árbol invertido es el mismo que el tamaño del árbol original. Analice los datos de prueba generados utilizando collect. 5. Dado el siguiente término: (λx .λy.y x ) ((λx .(λy.x (y y)) (λy.x (y y))) (λx .x z )) (λx .λy.y) (a) Indique cuales ocurrencias de variables son ligaduras, ligadas y libres. 2 (b) Liste los β-redexes que tiene el término. (c) ¿Cuántas formas normales tiene el término? Escriba una secuencia de β-reducciones que llegue a una forma normal. 6. Considere las codificaciones de booleanos, pares y naturales en términos del cálculo lambda vistas en clase. Usando las mismas defina las siguientes funciones: (a) (b) (c) (d) SWAP , que intercambia las componentes de un par DUP , que duplica un natural EXP2 , que retorna el exponente en base 2 de un natural PRED, que retorna el predecesor de un natural (considere que el predecesor de cero es cero). (e) IFTE , que dado un booleano y dos términos implementa if-then-else. 7. En el cálculo lambda las funciones recursivas se codifican utilizando combinadores de “punto fijo”. Un ejemplo de estos combinadores es: fix = λf → (λx → f (x x )) (λx → f (x x )) Este combinador no es recursivo, pero puede ser usado para definir funciones recursivas debido a la siguiente propiedad (si quiere puede intentar demostrarla usando razonamiento ecuacional): fix f ≡ f (fix f ) (a) Explique brevemente por qué este combinador no se puede tipar correctamente en Haskell. (b) Si definimos fix (utilizando la recursión de Haskell) como: fix f = f (fix f ) ¿Cuál es su tipo? (c) Considere la siguiente definición de la función suma: suma n = if n ≡ 0 then 0 else n + suma (n − 1) Defina esta función utilizando fix . 8. (OPCIONAL) Considere nuevamente las codificaciones de booleanos, pares y naturales en términos del cálculo lambda. Usando dicha codificación defina la función suma del ejercicio anterior de la siguientes formas: (a) Usando la formulación presentada en el ejercicio anterior y el operador Y de punto fijo visto en clase. (b) Directamente usando la codificación de los naturales, sin usar if-thenelse. Para ello considere la siguiente formulación de suma: suma 0 =0 suma (n + 1) = n + 1 + suma n 3