José A. Alonso
Departamento de Ciencias de la Computación e I.A.
Universidad de Sevilla
Sevilla, 26 de agosto de 2019
Nota: La versión interactiva de este tema se encuentra en Binder.
En el preludio está definida la clase de los semigrupos por
Los semigrupos deben de cumplir la ley asociativa:
Se puede ver la información de los semigrupos con
:info Semigroup
Ejemplos de semigrupos:
En los semigrupos están definidas las funciones
tales que
sconcat xs
aplica la operación del semigrupo a todos los elementos de la lista no vacía xs
.stimes n x
aplica n veces la operación del semigrupo con el elemento x
.Por ejemplo,
import GHC.Base
import Data.List.NonEmpty
sconcat (fromList [[2,5],[3],[4,7,6]])
[2,5,3,4,7,6]
stimes 3 [1,6]
[1,6,1,6,1,6]
En Data.Semigroup se encuentran las definiciones de distintos semigrupos. Por ejemplo,
import Data.Semigroup
:info Semigroup
El semigrupo de las listas está definido por
Por ejemplo,
[3,5] <> [2,4,6]
[3,5,2,4,6]
3 `stimes` [1,6,2]
[1,6,2,1,6,2,1,6,2]
Los semigrupos numéricos están definidos en Data.Semigroup por
Por ejemplo,
3 <> 5 :: Sum Int
Sum {getSum = 8}
3 <> 5 :: Product Int
Product {getProduct = 15}
Otros ejemplos de semigrupos definidos (con la correspondiente operación) son
Por ejemplo,
Max 3 <> Max 10 <> Max 2
Max {getMax = 10}
Min 3 <> Min 10 <> Min 2
Min {getMin = 2}
Any True <> Any False <> Any True
Any {getAny = True}
All True <> All False <> All True
All {getAll = False}
También está definida la clase de los monoides por
Se puede ver con
:info Monoid
Los monoides deben de cumplir las leyes asociativa y neutro:
Ejemplo de monoide:
En Data.Monoids se encuentran las definiciones de monides. Por ejemplo,
import Data.Monoid
:info Monoid
La clase de los monoides está definida por
Cumpliendo las siguientes propiedades
Está definido por
Por ejemplo,
[1,2] `mappend` [5,6,4]
[1,2,5,6,4]
[1,2] `mappend` mempty
[1,2]
mconcat [[3,2],[5],[9,0,7]]
[3,2,5,9,0,7]
Maybe
¶Si a
es un monoide, entonces Maybe a
también lo es y está definido por
Por ejemplo,
Just [2,3] `mappend` Just [7,5,8]
Just [2,3,7,5,8]
Just [2,3] `mappend` Nothing
Just [2,3]
Nothing `mappend` Just [7,5,8]
Just [7,5,8]
Nothing `mappend` Nothing
Nothing
Just (Sum 3) `mappend` Just (Sum 4)
Just (Sum {getSum = 7})
Si a
y b
son monoides el tipo de sus pares tambien lo es:
([1,2],[3]) `mappend` ([5],[6,7])
([1,2,5],[3,6,7])
Ordering
¶Ordering es un monoide y está definido por
Por ejemplo,
LT `mappend` GT
LT
GT `mappend` LT
GT
mempty `mappend` LT
LT
mempty `mappend` GT
GT
Otros ejemplos de monoide definidos (con la correspondiente operación y elemento neutro) son
Con Maybe los semigrupos se pueden externder a monoides como se muestra a continuación.
Los monoides First y Last están definidos en Data.Monoide por
:m - Data.Semigroup
First Nothing <> First (Just 10) <> First (Just 1)
First {getFirst = Just 10}
Last (Just 2) <> Last (Just 1) <> Last Nothing
Last {getLast = Just 1}
Si b es un monoide, entonces ls funciones de a en b forman un monoide y está definido por
Para ejemplo, se borra Data.List.NonEmpty, y se tiene
:m - Data.List.NonEmpty
(map (+2) <> map (*5)) [1,4]
[3,6,5,20]
La clase de los plegables está definida por
Su información se puede ver con
:info Foldable
Las listas están definidas como plegables:
Los opcionales están definidos como plegables:
Por ejemplo,
:m - GHC.Base
foldr (+) 1 (Just 3)
4
foldr (*) 0 Nothing
0
maximum [Just 2, Nothing, Just 7, Just 3]
Just 7
la librería Debug.SimpleReflect permite escribir expresiones con variables. Para usarla, en primer lugar se instala con
:! stack install simple-reflect
A continuación se reinicia el núcleo y, a continuación, se importa con
import Debug.SimpleReflect
Algunos ejemplos son
foldr f x [a,b,c]
f a (f b (f c x))
foldr (-) 0 [1..5] :: Expr
1 - (2 - (3 - (4 - (5 - 0))))
foldr (+) 1 (Just 3) :: Expr
3 + 1
Los functores abstraen la idea de aplicar una función a cada elemento de una estructura.
La clase de los functores está definida por
Se deben de cumplir las siguientes leyes
(<$>) es una abreviatura de fmap definida como sigue
Maybe es un functor definido por
Por ejemplo,
fmap (+2) (Just 3)
Just 5
fmap (+2) Nothing
Nothing
Las listas son functores definidas como sigue
Por ejemplo,
fmap (*2) [3,2,5]
[6,4,10]
fmap (*2) []
[]
(*2) <$> [3,2,5]
[6,4,10]
Para cada r
, las funciones de dominio r
es un functor definido por
Por ejemplo,
((+4) <$> (*5)) 2
14
(+4) <$> (*5) $ 2
14
fmap (+4) (*5) 2
14
La idea de los functores aplicativos es generalizar los functores a cualquier número de argumentos.
La clase de los functores aplicativos está definida por
Además, se deben de cumplir las siguientes leyes
Maybe es un functor aplicativo definido por
Por ejemplo,
Just (+3) <*> Just 9
Just 12
pure (+3) <*> Just 10
Just 13
Just (++"abc") <*> Nothing
Nothing
pure (++"es todo") <*> pure "esto "
"esto es todo"
Las listas son functores aplicativos definido por
Por ejemplo,
[(*2), (+3)] <*> [1, 2, 3]
[2,4,6,4,5,6]
[(*2), (+3)] <*> pure 5
[10,8]
(*) <$> [2,5,4] <*> [8,1]
[16,2,40,5,32,4]
(+) <$> [2,5,4] <*> [8,1]
[10,3,13,6,12,5]
(,) <$> [2,5,4] <*> [8,1]
[(2,8),(2,1),(5,8),(5,1),(4,8),(4,1)]
[(+),(*)] <*> [1,2] <*> [3,4]
[4,5,5,6,3,4,6,8]
Para cada r
, las funciones de dominio r
es un functor aplicativo definido por
Por ejemplo,
(+) <$> (7-) <*> (*3) $ 5
17
(+) <$> (7-) <*> (*3) $ 5 :: Expr
7 - 5 + 5 * 3
(*) <$> (7+) <*> (*3) $ 5
180
(*) <$> (7+) <*> (*3) $ 5 :: Expr
(7 + 5) * (5 * 3)
(\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5
[8.0,10.0,2.5]
En el módulo Control.Applicative están definidas las funciones de elevación. La definición de liftA2
es
Para usarla, se importa el módulo
import Control.Applicative (liftA2)
Por ejemplo,
liftA2 (*) (Just 5) (Just 3)
Just 15
es equivalente a
(*) <$> Just 5 <*> Just 3
Just 15
Ejemplo de definición con aplicativos: mayusculaOdigito c
se verifica si c
es una letra mayúscula o un dígito.
import Data.Char (isUpper, isDigit)
import Control.Applicative (liftA2)
mayusculaOdigito :: Char -> Bool
mayusculaOdigito = liftA2 (||) isUpper isDigit
Por ejemplo,
mayusculaOdigito 'A'
True
mayusculaOdigito '3'
True
mayusculaOdigito 'a'
False
Una definición equivalente, sin liftA2, es
mayusculaOdigito' :: Char -> Bool
mayusculaOdigito' = (||) <$> isUpper <*> isDigit
map mayusculaOdigito' "A3a"
[True,True,False]
Se define el tipo de los usuarios por
data Usuario = Usuario
{ nombre :: String
, apellido :: String
, correo :: String
} deriving Show
y el de los perfiles por
type Perfil = [(String, String)]
Por ejemplo.
perfil1, perfil2 :: Perfil
perfil1 =
[ ("nombre" , "Ana" )
, ("apellido" , "Luna" )
, ("correo" , "aluna@us.es")
]
perfil2 =
[ ("nonbre" , "Luis" )
, ("apellido" , "Sol" )
, ("correo" , "lsol@us.es")
]
usuario p
es el usuario correspondiente al perfil p
.
usuario :: Perfil -> Maybe Usuario
usuario p = Usuario
<$> lookup "nombre" p
<*> lookup "apellido" p
<*> lookup "correo" p
Por ejemplo,
usuario perfil1
Just (Usuario {nombre = "Ana", apellido = "Luna", correo = "aluna@us.es"})
usuario perfil2
Nothing
Una definición alternativa, sin argumentos, es
import Control.Applicative (liftA3)
usuario' :: Perfil -> Maybe Usuario
usuario' = liftA3 (liftA3 Usuario)
(lookup "nombre")
(lookup "apellido")
(lookup "correo")
Por ejemplo,
usuario' perfil1
Just (Usuario {nombre = "Ana", apellido = "Luna", correo = "aluna@us.es"})
usuario' perfil2
Nothing
La clase de los functores alternativos está definida en Control.Applicative por
y tiene que cumplir las siguientes leyes
Se importa el módulo con
import Control.Applicative
Se puede ver la información de la clase con
:info Alternative
Maybe es un functor alternativo definido por
Por ejemplo,
Nothing <|> Just 3 <|> empty <|> Just 5
Just 3
Las lista son functores alternativos definidos por
Por ejemplo,
[] <|> [1,2,3] <|> [4]
[1,2,3,4]
La clase Traversable está definida en el módulo Data.Traversable por
Maybe es un iterable definido por
Por ejemplo, sea mitad
la función definida por
mitad :: Int -> Maybe Int
mitad x = if even x then Just (x `div` 2) else Nothing
entonces
traverse mitad (Just 6)
Just (Just 3)
traverse mitad (Just 7)
Nothing
traverse mitad Nothing
Just Nothing
Las listas son iterables definidos por
Por ejemplo,
traverse mitad [2,4,8]
Just [1,2,4]
traverse mitad [2,3,8]
Nothing
Otro ejemplo,
anterior :: Int -> Maybe Int
anterior n = if n > 0 then Just (n-1) else Nothing
traverse anterior [2,5,4]
Just [1,4,3]
traverse anterior [2,0,4]
Nothing
Algunas instancias se pueden derivar automáticamente. Por ejemplo,
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
data Arbol a = Hoja | Nodo a (Arbol a) (Arbol a)
deriving (Functor, Foldable, Traversable, Show)
ejArbol1, ejArbol2 :: Arbol Int
ejArbol1 = Nodo 2 (Nodo 4 Hoja Hoja) (Nodo 6 Hoja Hoja)
ejArbol2 = Nodo 2 (Nodo 3 Hoja Hoja) (Nodo 6 Hoja Hoja)
fmap (+1) ejArbol1
Nodo 3 (Nodo 5 Hoja Hoja) (Nodo 7 Hoja Hoja)
foldr (+) 3 ejArbol1
15
length ejArbol1
3
6 `elem` ejArbol1
True
7 `elem` ejArbol1
False
maximum ejArbol1
6
minimum ejArbol1
2
sum ejArbol1
12
product ejArbol1
48
import Data.Foldable
toList ejArbol1
[2,4,6]
traverse mitad ejArbol1
Just (Nodo 1 (Nodo 2 Hoja Hoja) (Nodo 3 Hoja Hoja))
traverse mitad ejArbol2
Nothing