Title: Clases y Mdulos en Haskell
1Clases y Módulos en Haskell
2Índice de contenidos
- Clases tipo y Sobrecarga
- Una perspectiva diferente
- Diferencias con otros lenguajes
- Clases estándar de Haskell
- 4.1-Ejemplo, la clase Show
- Instancias derivadas
- Módulos
-
31.Clases tipo y Sobrecarga
41.Clases tipo y Sobrecarga
- Supongamos que en Haskell no hubiese
sobrecarga, y que quisiésemos comprobar si un
elemento particular es miembro de una lista de
tipo Bool.
51.Clases tipo y Sobrecarga
- En ese caso la función quedaría así
- elemBool Bool -gt Bool -gt Bool
- elemBool x False
- elemBool x (yys)
- (x Bool y) elemBool x ys
61.Clases tipo y Sobrecarga
- Como vemos, tendríamos que escribir
- Bool
- para definir la función de igualdad sobre Bool.
- Si ahora quisiésemos definir una función
similar pero para el caso de los enteros - elemInt Int -gt Int -gt Bool
- sería igual que la anterior pero cambiando
- Int por Bool
71.Clases tipo y Sobrecarga
- Una solución a nuestro problema es hacer de
la función de igualdad un parámetro más que se le
pase a la función - elemGen (a -gt a -gt Bool) -gt a gt a -gt Bool
- Inconvenientes
- Demasiado general
- Poca legilibilidad
81.Clases tipo y Sobrecarga
- La alternativa consiste en definir una función
que use la igualdad sobrecargada - elem a -gt a -gt Bool
- Ventajas
- Reusabilidad
- Programas más legibles
91.Clases tipo y Sobrecarga
- . En Haskell las clases tipo proporcionan una
forma estructurada de controlar la sobrecarga. - Nos permiten declarar aquellos tipos que son
instancias de las clases, así como proporcionar
definiciones a las operaciones sobrecargadas que
se asocian a la clase. Para el ejemplo anterior - class Eq a where
- () a -gt a -gt Bool
- un tipo a es una instancia de la clase Eq si
existe una operación definida sobre el tipo
a.
101.Clases tipo y Sobrecarga
- Los miembros de una clase son llamados
instancias. Para el ejemplo anterior, serían
instancias de la clase Eq - Int, Float, Bool, Char
- Tuplas de los anteriores (Int, Bool), Char
111.Clases tipo y Sobrecarga
- Para expresar restricciones de tipo
utilizaremos lo que se conoce con el nombre de
contexto. Estos contextos se sitúan al comienzo
de la expresión de tipos. Por ejemplo si queremos
la función - Elem ( Eq a ) gt a -gt a -gt Bool
- se tiene que cumplir que el tipo de a tiene
que ser instancia del tipo Eq. Esta
característica debe ser usada en el ejemplo de la
declaración de elem, es decir incluir una
restricción de contexto
121.Clases tipo y Sobrecarga
- cómo podemos especificar que tipos son
instancias de la clase Eq ? - Instance Eq Bool where
- True True True
- False False True
- _ _ False
131.Clases tipo y Sobrecarga
- Para el caso de los enteros
- instance Eq Integer where
- x y x integerEq y
-
- La declaración de la función es conocida
con el nombre de método
141.Clases tipo y Sobrecarga
- Otro ejemplo, tipo recursivo Tree
- data Tree a Leaf a Branch (Tree a) (Tree a)
-
- instance (Eq a) gt Eq (Tree a) where
- Leaf a Leaf b a b
- (Branch l1 r1)(Branch l2 r2) (l1l2) (r1
r2) - _ _ False
- Además se utiliza el contexto (Eq a) porque
las hojas son igualadas mediante ese operador.
151.Clases tipo y Sobrecarga
- Otra declaración de la clase Eq podría ser
-
- class Eq a where
- ( ), (/) a -gt a -gt Bool
- x / y not (x y)
lt-Método por defecto
Cuando en una instancia particular de una clase
se omite el método de una determinada operación
se emplea el método por defecto definido en la
declaración de la clase.
161.Clases tipo y Sobrecarga
- Haskell también puede soportar extensión de
clases, por ejemplo podemos definir una clase Ord
que herede todas las operaciones de la clase Eq.
La clase Ord sería la siguiente -
- class (Eq a) gt Ord a where
- (lt), (lt), (gt), (gt) a -gt a -gt Bool
- max, min a -gt a -gt a
Eq es una superclase de Ord.
171.Clases tipo y Sobrecarga
- Ventajas de la extensión de clases
- Escribir contextos cortos
- (Ord a) en lugar de (Eq a, Ord a)
- Uso de los métodos de superclase para definir los
de subclase. - x lt y x lt y x / y
Ejemplo de uso para Quicksort
181.Clases tipo y Sobrecarga
- También Haskell nos permite utilizar herencia
múltiple, es decir una clase puede tener más de
una superclase, ejemplo -
- class (Eq a, Show a) gt C a where
192.Una perspectiva diferente
- Las clases capturan un conjunto de
operaciones. Un objeto particular puede ser una
instancia de una clase y tendrá los métodos
correspondiente a las operaciones de la clase.
Las clases pueden estar relacionadas por
herencia, formando así superclases y subclases y
permitiendo herencia de operaciones y métodos.
203.Diferencias con otros Lenguajes
- Haskell separa la definición de un tipo de la
definición de métodos asociados con ese tipo.
- Los métodos de clase se corresponden con
funciones virtuales en clases de C o Java.
- Haskell no soporta el estilo de sobrecarga de
C, en la cual funciones con diferentes tipos
comparten un nombre común.
213.Diferencias con otros Lenguajes
- C y Java agrega información para identificar
para la representación de un objeto en tiempo de
ejecución. En Haskell la información es añadida
lógicamente en lugar de tomar valores físicos a
través del sistema de tipos.
- En Haskell no hay una clase base como por ejemplo
la Object en Java
223.Diferencias con otros Lenguajes
- Nos ha de quedar claro que los tipos de
Haskell no son como los objetos de la
programación orientada a objetos, ya que las
instancias no contienen un grupo de variables
cuyo contenido determinará el estado de la
instancia como ocurre con los objetos de la POO.
234.Clases Estándar de Haskell
-
- En Haskell hay un conjunto de clases ya
definidas en el Prelude (modulo que carga el
Haskell por defecto) y los tipos que aparecen en
el Prelude son instancias de esas clases.
244.Clases Estándar de Haskell
254.1.Ejemplo Clase Show
- Las instancias de la clase Show son aquellos
tipos que pueden ser convertidos a String de
caracteres ( para I/O ). -
- show ( 123 ) gt "123"
264.1.Ejemplo Clase Show
- El tipo de esta función es
-
- show (Show a) gt a -gt String
274.1.Ejemplo Clase Show
-
- -Declaración por defecto de la clase Show dada
en el Prelude. Como se observa, el método a
modificar será showsPrec puesto que show se basa
en él. - class Show a where
- showsPrec Int -gt a -gt String -gt String
- show a -gt String
- showsPrec _ x s (show x) s
- show x showsPrec 0 x ""
284.1.Ejemplo Clase Show
- Ejemplo de uso
- Nuestro objetivo es pasar de un dato con tipo
Tree, a una representación fácilmente legible del
tipo.
data Tree a Leaf a Branch (Tree a) (Tree a)
294.1.Ejemplo Clase Show
En principio vamos a trabajar como si show no
estuviese implementado como método. Por lo que
tendremos que apoyarnos en showsPrec para
implementar la función auxiliar showsTree.
- instance (Show a) gt Show (Tree a) where
- showsPrec _ x s showsTree x s
entero
string
304.1.Ejemplo Clase Show
- showsTree (Show a) gt Tree a -gt String -gt
String - showsTree (Leaf x) s show x s
- showsTree (Branch l r) s lt (showsTree l s)
(showsTree r s) gt
314.1.Ejemplo Clase Show
- Como normalmente nos encontraremos la clase
show implementada como método no nos tendremos
que preocupar del manejo de los parámetros de
showsPrec. - Así pues, el ejemplo anterior queda más sencillo
y legible, incluso si no hacemos una función
auxiliar (otrora showsTree).
324.1.Ejemplo Clase Show
- instance (Show a) gt Show (Tree a) where
- show (Leaf x) show x
- show (Branch l r) lt (show l)
-
- (show r) gt
334.2.Ejemplo Clase Read
- La clase Read proporciona las operaciones para
realizar el paso contrario de Show, pasar de un
String de caracteres a valores del tipo que
estemos tratando.
Su tipo es read (Read a) gt String -gt a
Ejemplo de uso read ("123") gt 123
345. Instancias Derivadas
- Si queremos generar una instancia de la clase
Eq para el tipo Tree, tendríamos que utilizar una
declaración de instancia y tendríamos que definir
los métodos correspondientes para el tipo, el
resultado sería el siguiente
instance (Eq a) gt Eq (Tree a) where (Leaf x)
(Leaf y) x y (Branch l r)
(Branch l' r') l l' r r' _
_ False
355. Instancias Derivadas
- Esto se puede simplificar en la propia definición
del dato - data Tree a Leaf a
- Branch (Tree a) (Tree a)
deriving Eq
365. Instancias Derivadas
- Podemos generar instancias de las clases Ord,
Enum, Read, Show y en general de todas las
clases estándar de Haskell simplemente utilizando
la cláusula deriving. Podemos especificar más de
una clase, en ese caso todos los nombres deben
estar entre paréntesis y separados por coma.
376. Módulos
- Un módulo en Haskell cumple el doble objetivo de
controlar el espacio de nombres y de crear
estructuras de datos. - Un módulo puede contener todas las declaraciones
del lenguaje sentencias, declaraciones de tipo
y datos, declaraciones de clases e instancias,
definición de funciones, etc.. Las declaraciones
de importación deben aparecer al principio, el
resto pueden aparecer en cualquier orden. - Más de un módulo puede residir en un simple
fichero.
386. Módulos
- module Tree ( Tree(Leaf,Branch), fringe ) where
-
- data Tree a Leaf a Branch (Tree a) (Tree a)
-
- fringe Tree a -gt a
- fringe (Leaf x) x
- fringe (Branch left right) fringe left
fringe right
LISTA DE EXPORTACIONES
396. Módulos
- Si la lista de exportaciones es omitida, todas
las declaraciones del módulo serán exportadas. - Los nombres de los constructores y del tipo han
sido agrupados, esto se podría reducir si ponemos
Tree (...). - Cualquier nombre en el ámbito del módulo puede
ser exportado. Los elementos que están siendo
importados o exportados se denominan entidades.
El módulo Tree puede ser importado desde otro
módulo module Main (main) where import Tree
( Tree(Leaf,Branch), fringe ) main print
(fringe (Branch (Leaf 1) (Leaf 2)))
406.1. Nombres Cualificados
- los nombre cualificados son usados para resolver
el problema de que diferentes entidades compartan
el mismo nombre. - Por ejemplo si tenemos otra declaración para
fringe (visto anteriormente).
module Fringe(fringe) where import
Tree(Tree(..)) fringe Tree a -gt a Una
definición diferente para fringe fringe (Leaf x)
x fringe (Branch x y) fringe x
416.1. Nombres Cualificados
- module Main where
- import Tree ( Tree(Leaf,Branch), fringe )
- import qualified Fringe ( fringe )
- main do print (fringe (Branch (Leaf 1) (Leaf
2))) - print (Fringe.fringe (Branch (Leaf 1) (Leaf 2)))
Se usa qualified a la hora de importar Deberá ir
precedido por un punto y el nombre del módulo
del que viene.
426.2. Tipos Abstractos de Datos
-
- Los módulos proporcionan la única manera de
construir tipos abstractos de datos (TAD) en
Haskell. La característica principal de los TADs
es que la representación de los tipos se oculta
todas las operaciones de los TADs son hechas a un
nivel abstracto el cual no depende de la
representación.
436.2. Tipos Abstractos de Datos
- Por ejemplo, para el tipo tree una posible
representación mediante un TAD es la siguiente - data Tree a sólo el nombre del tipo
- leaf a -gt Tree a
- branch Tree a -gt Tree a -gt Tree a
- cell Tree a -gt a
- left, right Tree a -gt Tree a
- isLeaf Tree a -gt Bool
446.2. Tipos Abstractos de Datos
- Lo que el usuario no conocerá será la
representación del tipo Tree y la implementación
de las diferentes operaciones ya que todo esto
aparecería en un módulo similar al siguiente del
cual el usuario solo tiene que importar.
456.2. Tipos Abstractos de Datos
- module TreeADT (Tree, leaf, branch, cell,
- left, right, isLeaf) where
-
- data Tree a Leaf a Branch (Tree
a) (Tree a) - leaf Leaf
- branch Branch
- cell (Leaf a) a
- left (Branch l r) l
- right (Branch l r) r
- isLeaf (Leaf _) True
- isLeaf _ False
466.2. Tipos Abstractos de Datos
- Ventajas
- -Ocultación de información.
- -Posibilidad de cambiar la representación del
tipo sin afectar a los usuarios del tipo.
47FIN
- Presentación por Pedro González Cuadrado
- Curso 2002/2003