{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", " Tema 30: Monoides, functores, aplicativos y plegables\n", " \n", "\n", "----------\n", "\n", "[José A. Alonso](https://www.cs.us.es/~jalonso) \n", "[Departamento de Ciencias de la Computación e I.A.](https://www.cs.us.es) \n", "[Universidad de Sevilla](http://www.us.es) \n", "Sevilla, 26 de agosto de 2019" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Nota:__ La versión interactiva de este tema se encuentra en [Binder](https://mybinder.org/v2/gh/jaalonso/Temas_interactivos_de_PF_con_Haskell/master?urlpath=lab/tree/temas/Tema-30.ipynb)." ] }, { "cell_type": "markdown", "metadata": { "toc-hr-collapsed": true }, "source": [ "# Semigrupos y monoides" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Semigrupos\n", "\n", "En el preludio está definida la clase de los semigrupos por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "class Semigroup m where\n", " (<>) :: m -> m -> m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Los semigrupos deben de cumplir la ley asociativa:" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "(x <> y) <> z ≡ x <> (y <> z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se puede ver la información de los semigrupos con" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": {}, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
" ], "text/plain": [ "class Semigroup a where\n", " (<>) :: a -> a -> a\n", " GHC.Base.sconcat :: GHC.Base.NonEmpty a -> a\n", " GHC.Base.stimes :: Integral b => b -> a -> a\n", " {-# MINIMAL (<>) #-}\n", " \t-- Defined in ‘GHC.Base’\n", "instance Semigroup Display -- Defined in ‘IHaskell.Types’\n", "instance Semigroup (Either a b) -- Defined in ‘Data.Either’\n", "instance Semigroup [a] -- Defined in ‘GHC.Base’\n", "instance Semigroup Ordering -- Defined in ‘GHC.Base’\n", "instance Semigroup a => Semigroup (Maybe a) -- Defined in ‘GHC.Base’\n", "instance Semigroup a => Semigroup (IO a) -- Defined in ‘GHC.Base’\n", "instance Semigroup b => Semigroup (a -> b) -- Defined in ‘GHC.Base’\n", "instance (Semigroup a, Semigroup b, Semigroup c, Semigroup d, Semigroup e) => Semigroup (a, b, c, d, e) -- Defined in ‘GHC.Base’\n", "instance (Semigroup a, Semigroup b, Semigroup c, Semigroup d) => Semigroup (a, b, c, d) -- Defined in ‘GHC.Base’\n", "instance (Semigroup a, Semigroup b, Semigroup c) => Semigroup (a, b, c) -- Defined in ‘GHC.Base’\n", "instance (Semigroup a, Semigroup b) => Semigroup (a, b) -- Defined in ‘GHC.Base’\n", "instance Semigroup () -- Defined in ‘GHC.Base’" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":info Semigroup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ejemplos de semigrupos:\n", "+ Las lista con la concatenación (++).\n", "+ Los tipos ordenados con el máximo (max).\n", "+ Los tipos ordenados con el mínimo (min)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En los semigrupos están definidas las funciones" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "sconcat :: NonEmpty a -> a\n", "stimes :: Integral b => b -> a -> a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "tales que\n", "+ `sconcat xs` aplica la operación del semigrupo a todos los elementos de la lista no vacía `xs`.\n", "+ `stimes n x` aplica n veces la operación del semigrupo con el elemento `x`. \n", "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2,5,3,4,7,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import GHC.Base\n", "import Data.List.NonEmpty\n", "\n", "sconcat (fromList [[2,5],[3],[4,7,6]])" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1,6,1,6,1,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "stimes 3 [1,6]" ] }, { "cell_type": "markdown", "metadata": { "toc-hr-collapsed": true }, "source": [ "## Ejemplos de semigrupos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En [Data.Semigroup](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Semigroup.html) se encuentran las definiciones de distintos semigrupos. Por ejemplo," ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": {}, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
" ], "text/plain": [ "class Semigroup a where\n", " (<>) :: a -> a -> a\n", " sconcat :: NonEmpty a -> a\n", " stimes :: Integral b => b -> a -> a\n", " {-# MINIMAL (<>) #-}\n", " \t-- Defined in ‘GHC.Base’\n", "instance Monoid m => Semigroup (WrappedMonoid m) -- Defined in ‘Data.Semigroup’\n", "instance Semigroup a => Semigroup (Option a) -- Defined in ‘Data.Semigroup’\n", "instance Ord a => Semigroup (Min a) -- Defined in ‘Data.Semigroup’\n", "instance Ord a => Semigroup (Max a) -- Defined in ‘Data.Semigroup’\n", "instance Semigroup (Last a) -- Defined in ‘Data.Semigroup’\n", "instance Semigroup (First a) -- Defined in ‘Data.Semigroup’\n", "instance Num a => Semigroup (Sum a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Num a => Semigroup (Product a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Semigroup (Endo a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Semigroup a => Semigroup (Dual a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Semigroup Data.Semigroup.Any -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Semigroup All -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Semigroup Display -- Defined in ‘IHaskell.Types’\n", "instance Semigroup (Either a b) -- Defined in ‘Data.Either’\n", "instance Semigroup [a] -- Defined in ‘GHC.Base’\n", "instance Semigroup Ordering -- Defined in ‘GHC.Base’\n", "instance Semigroup (NonEmpty a) -- Defined in ‘GHC.Base’\n", "instance Semigroup a => Semigroup (Maybe a) -- Defined in ‘GHC.Base’\n", "instance Semigroup a => Semigroup (IO a) -- Defined in ‘GHC.Base’\n", "instance Semigroup b => Semigroup (a -> b) -- Defined in ‘GHC.Base’\n", "instance (Semigroup a, Semigroup b, Semigroup c, Semigroup d, Semigroup e) => Semigroup (a, b, c, d, e) -- Defined in ‘GHC.Base’\n", "instance (Semigroup a, Semigroup b, Semigroup c, Semigroup d) => Semigroup (a, b, c, d) -- Defined in ‘GHC.Base’\n", "instance (Semigroup a, Semigroup b, Semigroup c) => Semigroup (a, b, c) -- Defined in ‘GHC.Base’\n", "instance (Semigroup a, Semigroup b) => Semigroup (a, b) -- Defined in ‘GHC.Base’\n", "instance Semigroup () -- Defined in ‘GHC.Base’" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import Data.Semigroup\n", "\n", ":info Semigroup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### El semigrupo de las listas" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "El semigrupo de las listas está definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Semigroup [a] where\n", " (<>) = (++)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[3,5,2,4,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[3,5] <> [2,4,6]" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1,6,2,1,6,2,1,6,2]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "3 `stimes` [1,6,2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Semigrupos numéricos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Los semigrupos numéricos están definidos en [Data.Semigroup](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Semigroup.html) por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "newtype Sum a = Sum { getSum :: a }\n", "newtype Product a = Product { getProduct :: a }\n", "\n", "instance Num a => Semigroup (Sum a) where\n", " Sum x <> Sum y = Sum (x + y)\n", "\n", "instance Num a => Semigroup (Product a) where\n", " Product x <> Product y = Product (x * y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Sum {getSum = 8}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "3 <> 5 :: Sum Int" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Product {getProduct = 15}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "3 <> 5 :: Product Int" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Otros semigrupos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Otros ejemplos de semigrupos definidos (con la correspondiente operación) son" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "newtype Max a = Max { getMax :: a } -- max\n", "newtype Min a = Min { getMin :: a } -- min\n", "newtype Any = Any { getAny :: Bool } -- ||\n", "newtype All = All { getAll :: Bool } -- &&" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Max {getMax = 10}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Max 3 <> Max 10 <> Max 2" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Min {getMin = 2}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Min 3 <> Min 10 <> Min 2" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Any {getAny = True}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Any True <> Any False <> Any True" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "All {getAll = False}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "All True <> All False <> All True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Monoides" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "También está definida la clase de los monoides por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "class Semigroup m => Monoid m where\n", " mempty :: m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se puede ver con" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": {}, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
" ], "text/plain": [ "class Semigroup a => Monoid a where\n", " mempty :: a\n", " mappend :: a -> a -> a\n", " mconcat :: [a] -> a\n", " {-# MINIMAL mempty #-}\n", " \t-- Defined in ‘GHC.Base’\n", "instance Monoid m => Monoid (WrappedMonoid m) -- Defined in ‘Data.Semigroup’\n", "instance Semigroup a => Monoid (Option a) -- Defined in ‘Data.Semigroup’\n", "instance (Ord a, Bounded a) => Monoid (Min a) -- Defined in ‘Data.Semigroup’\n", "instance (Ord a, Bounded a) => Monoid (Max a) -- Defined in ‘Data.Semigroup’\n", "instance Num a => Monoid (Sum a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Num a => Monoid (Product a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Monoid (Endo a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Monoid a => Monoid (Dual a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Monoid Data.Semigroup.Any -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Monoid All -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Monoid Display -- Defined in ‘IHaskell.Types’\n", "instance Monoid [a] -- Defined in ‘GHC.Base’\n", "instance Monoid Ordering -- Defined in ‘GHC.Base’\n", "instance Semigroup a => Monoid (Maybe a) -- Defined in ‘GHC.Base’\n", "instance Monoid a => Monoid (IO a) -- Defined in ‘GHC.Base’\n", "instance Monoid b => Monoid (a -> b) -- Defined in ‘GHC.Base’\n", "instance (Monoid a, Monoid b, Monoid c, Monoid d, Monoid e) => Monoid (a, b, c, d, e) -- Defined in ‘GHC.Base’\n", "instance (Monoid a, Monoid b, Monoid c, Monoid d) => Monoid (a, b, c, d) -- Defined in ‘GHC.Base’\n", "instance (Monoid a, Monoid b, Monoid c) => Monoid (a, b, c) -- Defined in ‘GHC.Base’\n", "instance (Monoid a, Monoid b) => Monoid (a, b) -- Defined in ‘GHC.Base’\n", "instance Monoid () -- Defined in ‘GHC.Base’" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":info Monoid" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Los monoides deben de cumplir las leyes asociativa y neutro:" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "1. (x <> y) <> z ≡ x <> (y <> z)\n", "2. x <> mempty ≡ x\n", "3. mempty <> x ≡ x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ejemplo de monoide:\n", "+ Las lista con la concatenación (++) y la lista vacía como neutro." ] }, { "cell_type": "markdown", "metadata": { "toc-hr-collapsed": true }, "source": [ "## Ejemplos de monoides" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En [Data.Monoids](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Monoid.html) se encuentran las definiciones de monides. Por ejemplo," ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": {}, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
" ], "text/plain": [ "class Semigroup a => Monoid a where\n", " mempty :: a\n", " mappend :: a -> a -> a\n", " mconcat :: [a] -> a\n", " {-# MINIMAL mempty #-}\n", " \t-- Defined in ‘GHC.Base’\n", "instance Monoid m => Monoid (WrappedMonoid m) -- Defined in ‘Data.Semigroup’\n", "instance Semigroup a => Monoid (Option a) -- Defined in ‘Data.Semigroup’\n", "instance (Ord a, Bounded a) => Monoid (Min a) -- Defined in ‘Data.Semigroup’\n", "instance (Ord a, Bounded a) => Monoid (Max a) -- Defined in ‘Data.Semigroup’\n", "instance Monoid (Data.Monoid.Last a) -- Defined in ‘Data.Monoid’\n", "instance Monoid (Data.Monoid.First a) -- Defined in ‘Data.Monoid’\n", "instance (Applicative f, Monoid a) => Monoid (Ap f a) -- Defined in ‘Data.Monoid’\n", "instance Num a => Monoid (Sum a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Num a => Monoid (Product a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Monoid (Endo a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Monoid a => Monoid (Dual a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Monoid Data.Semigroup.Any -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Alternative f => Monoid (Alt f a) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Monoid All -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Monoid Display -- Defined in ‘IHaskell.Types’\n", "instance Monoid [a] -- Defined in ‘GHC.Base’\n", "instance Monoid Ordering -- Defined in ‘GHC.Base’\n", "instance Semigroup a => Monoid (Maybe a) -- Defined in ‘GHC.Base’\n", "instance Monoid a => Monoid (IO a) -- Defined in ‘GHC.Base’\n", "instance Monoid b => Monoid (a -> b) -- Defined in ‘GHC.Base’\n", "instance (Monoid a, Monoid b, Monoid c, Monoid d, Monoid e) => Monoid (a, b, c, d, e) -- Defined in ‘GHC.Base’\n", "instance (Monoid a, Monoid b, Monoid c, Monoid d) => Monoid (a, b, c, d) -- Defined in ‘GHC.Base’\n", "instance (Monoid a, Monoid b, Monoid c) => Monoid (a, b, c) -- Defined in ‘GHC.Base’\n", "instance (Monoid a, Monoid b) => Monoid (a, b) -- Defined in ‘GHC.Base’\n", "instance Monoid () -- Defined in ‘GHC.Base’" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import Data.Monoid\n", "\n", ":info Monoid" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La clase de los monoides está definida por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "class Monoid a where\n", " mempty :: a\n", " mappend :: a -> a -> a\n", " mconcat :: [a] -> a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cumpliendo las siguientes propiedades" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "1. mappend x (mappend y z) = mappend (mappend x y)\n", "2. mappend mempty x = x\n", "3. mappend x mempty = x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### El monoide de las listas" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Está definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Monoid [a] where\n", " mempty = []\n", " l1 `mappend` l2 = l1 ++ l2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1,2,5,6,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1,2] `mappend` [5,6,4]" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1,2]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1,2] `mappend` mempty" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[3,2,5,9,0,7]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mconcat [[3,2],[5],[9,0,7]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### El monoide `Maybe`\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si `a`es un monoide, entonces `Maybe a` también lo es y está definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Monoid a => Monoid (Maybe a) where\n", " mempty = Nothing\n", "\n", " Nothing `mappend` x = x\n", " x `mappend` Nothing = x\n", " Just x `mappend` Just y = Just (x `mappend` y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just [2,3,7,5,8]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just [2,3] `mappend` Just [7,5,8]" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just [2,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just [2,3] `mappend` Nothing" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just [7,5,8]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing `mappend` Just [7,5,8]" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing `mappend` Nothing" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just (Sum {getSum = 7})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just (Sum 3) `mappend` Just (Sum 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### El monoide de los pares" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si `a` y `b` son monoides el tipo de sus pares tambien lo es:" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance (Monoid a, Monoid b) => Monoid (a,b) where\n", " mempty = ( mempty, mempty)\n", " (a1,b1) `mappend` (a2,b2) = (a1 `mappend` a2, b1 `mappend` b2)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "([1,2,5],[3,6,7])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "([1,2],[3]) `mappend` ([5],[6,7])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### El monoide `Ordering`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ordering es un monoide y está definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Monoid Ordering where\n", " mempty = EQ\n", " LT `mappend` _ = LT\n", " EQ `mappend` y = y\n", " GT `mappend` _ = GT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "LT `mappend` GT" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "GT `mappend` LT" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mempty `mappend` LT" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mempty `mappend` GT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Otros monoides" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Otros ejemplos de monoide definidos (con la correspondiente operación y elemento neutro) son" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "newtype Sum a = Sum { getSum :: a } -- (+) 0\n", "newtype Product a = Product { getProduct :: a } -- (*) 1\n", "newtype Max a = Max { getMax :: a } -- max\n", "newtype Min a = Min { getMin :: a } -- min\n", "newtype Any = Any { getAny :: Bool } -- || False\n", "newtype All = All { getAll :: Bool } -- && True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### De semigrupos a monoides" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Con Maybe los semigrupos se pueden externder a monoides como se muestra a continuación." ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Semigroup a => Semigroup (Maybe a) where\n", " Nothing <> b = b\n", " a <> Nothing = a\n", " Just a <> Just b = Just (a <> b)\n", "\n", "instance Semigroup a => Monoid (Maybe a) where\n", " mempty = Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Los monoides First y Last" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Los monoides First y Last están definidos en Data.Monoide por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "newtype First a = First { getFirst :: Maybe a }\n", " deriving (Eq, Ord, Read, Show)\n", "\n", "instance Monoid (First a) where\n", " mempty = First Nothing\n", " r@(First (Just _)) `mappend` _ = r\n", " First Nothing `mappend` r = r\n", "\n", "newtype Last a = Last { getLast :: Maybe a }\n", " deriving (Eq, Ord, Read, Show)\n", "\n", "instance Monoid (Last a) where\n", " mempty = Last Nothing\n", " _ `mappend` r@(Last (Just _)) = r\n", " r `mappend` Last Nothing = r" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "First {getFirst = Just 10}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":m - Data.Semigroup\n", "\n", "First Nothing <> First (Just 10) <> First (Just 1)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Last {getLast = Just 1}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Last (Just 2) <> Last (Just 1) <> Last Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### El monoide de las funciones" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si b es un monoide, entonces ls funciones de a en b forman un monoide y está definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Monoid b => Monoid (a -> b) where\n", " mempty _ = mempty\n", " mappend f g x = f x `mappend` g x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para ejemplo, se borra Data.List.NonEmpty, y se tiene" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ ":m - Data.List.NonEmpty" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[3,6,5,20]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(map (+2) <> map (*5)) [1,4]" ] }, { "cell_type": "markdown", "metadata": { "toc-hr-collapsed": true }, "source": [ "# Plegables (o *Foldable*)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La clase de los plegables está definida por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "class Foldable t where\n", " fold :: Monoid m => t m -> m\n", " foldMap :: Monoid m => (a -> m) -> t a -> m\n", " foldr :: (a -> b -> b) -> b -> t a -> b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Su información se puede ver con" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": {}, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
" ], "text/plain": [ "class Foldable (t :: * -> *) where\n", " Data.Foldable.fold :: Monoid m => t m -> m\n", " foldMap :: Monoid m => (a -> m) -> t a -> m\n", " IHaskellPrelude.foldr :: (a -> b -> b) -> b -> t a -> b\n", " Data.Foldable.foldr' :: (a -> b -> b) -> b -> t a -> b\n", " foldl :: (b -> a -> b) -> b -> t a -> b\n", " Data.Foldable.foldl' :: (b -> a -> b) -> b -> t a -> b\n", " foldr1 :: (a -> a -> a) -> t a -> a\n", " foldl1 :: (a -> a -> a) -> t a -> a\n", " Data.Foldable.toList :: t a -> [a]\n", " null :: t a -> Bool\n", " length :: t a -> Int\n", " elem :: Eq a => a -> t a -> Bool\n", " maximum :: Ord a => t a -> a\n", " minimum :: Ord a => t a -> a\n", " sum :: Num a => t a -> a\n", " product :: Num a => t a -> a\n", " {-# MINIMAL foldMap | foldr #-}\n", " \t-- Defined in ‘Data.Foldable’\n", "instance Foldable [] -- Defined in ‘Data.Foldable’\n", "instance Foldable Sum -- Defined in ‘Data.Foldable’\n", "instance Foldable Product -- Defined in ‘Data.Foldable’\n", "instance Foldable NonEmpty -- Defined in ‘Data.Foldable’\n", "instance Foldable Maybe -- Defined in ‘Data.Foldable’\n", "instance Foldable Last -- Defined in ‘Data.Foldable’\n", "instance Foldable First -- Defined in ‘Data.Foldable’\n", "instance Foldable (Either a) -- Defined in ‘Data.Foldable’\n", "instance Foldable Dual -- Defined in ‘Data.Foldable’\n", "instance Foldable f => Foldable (Ap f) -- Defined in ‘Data.Foldable’\n", "instance Foldable f => Foldable (Alt f) -- Defined in ‘Data.Foldable’\n", "instance Foldable ((,) a) -- Defined in ‘Data.Foldable’" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":info Foldable" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Las listas como plegables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Las listas están definidas como plegables:" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Foldable [] where\n", " foldr :: (a -> b -> b) -> b -> [a] -> b\n", " foldr _ z [] = z\n", " foldr f z (x:xs) = x `f` foldr f z xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Los opcionales como plegables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Los opcionales están definidos como plegables:" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Foldable Maybe where\n", " foldr :: (a -> b -> b) -> b -> Maybe a -> b\n", " foldr _ z Nothing = z\n", " foldr f z (Just x) = f x z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":m - GHC.Base\n", "\n", "foldr (+) 1 (Just 3)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldr (*) 0 Nothing" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just 7" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "maximum [Just 2, Nothing, Just 7, Just 3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Análisis con Debug.SimpleReflect" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "la librería [Debug.SimpleReflect](http://hackage.haskell.org/package/simple-reflect-0.3.3/docs/Debug-SimpleReflect.html) permite escribir expresiones con variables. Para usarla, en primer lugar se instala con" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":! stack install simple-reflect" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A continuación se reinicia el núcleo y, a continuación, se importa con" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "import Debug.SimpleReflect" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Algunos ejemplos son" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "f a (f b (f c x))" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldr f x [a,b,c]" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1 - (2 - (3 - (4 - (5 - 0))))" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldr (-) 0 [1..5] :: Expr " ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3 + 1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldr (+) 1 (Just 3) :: Expr" ] }, { "cell_type": "markdown", "metadata": { "toc-hr-collapsed": true }, "source": [ "# Functores" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Los functores abstraen la idea de aplicar una función a cada elemento de una estructura." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La clase de los functores está definida por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "class Functor f where \n", " fmap :: (a -> b) -> f a -> f b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se deben de cumplir las siguientes leyes" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "1. fmap id ≡ id\n", "2. fmap (f . g) ≡ fmap f . fmap g" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(<$>) es una abreviatura de fmap definida como sigue" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "infixl 4 <$>\n", "(<$>) :: Functor f => (a -> b) -> f a -> f b\n", "(<$>) = fmap" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## El functor Maybe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Maybe es un functor definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Functor Maybe where\n", " fmap :: (a -> b) -> Maybe a -> Maybe b\n", " fmap f (Just x) =\n", " fmap f Nothing =" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just 5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (+2) (Just 3)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (+2) Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## El functor lista" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Las listas son functores definidas como sigue" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Functor [] where\n", " fmap :: (a -> b) -> [a] -> [b]\n", " fmap = map" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[6,4,10]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (*2) [3,2,5]" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (*2) [] " ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[6,4,10]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(*2) <$> [3,2,5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## El functor de las funciones" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para cada `r`, las funciones de dominio `r` es un functor definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Functor ((->) r) where\n", " fmap :: (a -> b) -> (r -> a) -> (r -> b)\n", " fmap = (.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "14" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "((+4) <$> (*5)) 2 " ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "14" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(+4) <$> (*5) $ 2 " ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "14" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (+4) (*5) 2" ] }, { "cell_type": "markdown", "metadata": { "toc-hr-collapsed": true }, "source": [ "# Functores aplicativos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La idea de los functores aplicativos es generalizar los functores a cualquier número de argumentos." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La clase de los functores aplicativos está definida por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "class Functor f => Applicative f where \n", " pure :: a -> f a\n", " (<*>) :: f (a -> b) -> f a -> f b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Además, se deben de cumplir las siguientes leyes" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "1. identidad\n", " pure id <*> v ≡ v\n", "\n", "2. composición\n", " pure (.) <*> u <*> v <*> w ≡ u <*> (v <*> w)\n", "\n", "3. homomorfismo\n", " pure f <*> pure x ≡ pure (f x)\n", "\n", "4. intercambio\n", " u <*> pure y ≡ pure ($ y) <*> u" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Maybe como aplicativo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Maybe es un functor aplicativo definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Applicative Maybe where\n", " pure :: a -> Maybe a\n", " pure = Just\n", " \n", " (<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b\n", " Nothing <*> _ = Nothing\n", " Just f <*> mx = fmap f mx" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just 12" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just (+3) <*> Just 9" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just 13" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pure (+3) <*> Just 10" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just (++\"abc\") <*> Nothing" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"esto es todo\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pure (++\"es todo\") <*> pure \"esto \"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Listas como aplicativos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Las listas son functores aplicativos definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Applicative [] where\n", " pure :: a -> [a]\n", " pure x = [x]\n", "\n", " (<*>) :: [a -> b] -> [a] -> [b]\n", " fs <*> xs = [f x | f <- fs, x <- xs]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2,4,6,4,5,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[(*2), (+3)] <*> [1, 2, 3]" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[10,8]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[(*2), (+3)] <*> pure 5" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[16,2,40,5,32,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(*) <$> [2,5,4] <*> [8,1]" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[10,3,13,6,12,5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(+) <$> [2,5,4] <*> [8,1]" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(2,8),(2,1),(5,8),(5,1),(4,8),(4,1)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(,) <$> [2,5,4] <*> [8,1]" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[4,5,5,6,3,4,6,8]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[(+),(*)] <*> [1,2] <*> [3,4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Funciones como aplicativos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para cada `r`, las funciones de dominio `r` es un functor aplicativo definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Applicative ((->) r) where\n", " pure :: a -> r -> a\n", " pure x = \\_ -> x\n", "\n", " (<*>) :: (r -> a -> b) -> (r -> a) -> r -> b\n", " f <*> g = \\x -> f x (g x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "17" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(+) <$> (7-) <*> (*3) $ 5" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7 - 5 + 5 * 3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(+) <$> (7-) <*> (*3) $ 5 :: Expr" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "180" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(*) <$> (7+) <*> (*3) $ 5" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(7 + 5) * (5 * 3)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(*) <$> (7+) <*> (*3) $ 5 :: Expr" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[8.0,10.0,2.5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Las funciones de elevación" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En el módulo [Control.Applicative](http://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Applicative.html) están definidas las funciones de elevación. La definición de `liftA2` es" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "liftA2 f x y = f <$> x <*> y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para usarla, se importa el módulo" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [], "source": [ "import Control.Applicative (liftA2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just 15" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "liftA2 (*) (Just 5) (Just 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "es equivalente a" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just 15" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(*) <$> Just 5 <*> Just 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ejemplo de definición con aplicativos: `mayusculaOdigito c` se verifica si `c` es una letra mayúscula o un dígito." ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [], "source": [ "import Data.Char (isUpper, isDigit)\n", "import Control.Applicative (liftA2)\n", "\n", "mayusculaOdigito :: Char -> Bool\n", "mayusculaOdigito = liftA2 (||) isUpper isDigit" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mayusculaOdigito 'A'" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mayusculaOdigito '3'" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mayusculaOdigito 'a'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Una definición equivalente, sin liftA2, es" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [], "source": [ "mayusculaOdigito' :: Char -> Bool\n", "mayusculaOdigito' = (||) <$> isUpper <*> isDigit" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[True,True,False]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map mayusculaOdigito' \"A3a\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Estilo aplicativo de programación" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se define el tipo de los usuarios por" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [], "source": [ "data Usuario = Usuario\n", " { nombre :: String\n", " , apellido :: String\n", " , correo :: String\n", " } deriving Show" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "y el de los perfiles por" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [], "source": [ "type Perfil = [(String, String)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo." ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [], "source": [ "perfil1, perfil2 :: Perfil\n", "perfil1 = \n", " [ (\"nombre\" , \"Ana\" )\n", " , (\"apellido\" , \"Luna\" )\n", " , (\"correo\" , \"aluna@us.es\")\n", " ]\n", "perfil2 = \n", " [ (\"nonbre\" , \"Luis\" )\n", " , (\"apellido\" , \"Sol\" )\n", " , (\"correo\" , \"lsol@us.es\")\n", " ] " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`usuario p` es el usuario correspondiente al perfil `p`." ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [], "source": [ "usuario :: Perfil -> Maybe Usuario\n", "usuario p = Usuario\n", " <$> lookup \"nombre\" p\n", " <*> lookup \"apellido\" p\n", " <*> lookup \"correo\" p" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just (Usuario {nombre = \"Ana\", apellido = \"Luna\", correo = \"aluna@us.es\"})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "usuario perfil1" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "usuario perfil2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Una definición alternativa, sin argumentos, es" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [], "source": [ "import Control.Applicative (liftA3)\n", "\n", "usuario' :: Perfil -> Maybe Usuario\n", "usuario' = liftA3 (liftA3 Usuario)\n", " (lookup \"nombre\")\n", " (lookup \"apellido\")\n", " (lookup \"correo\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just (Usuario {nombre = \"Ana\", apellido = \"Luna\", correo = \"aluna@us.es\"})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "usuario' perfil1" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "usuario' perfil2" ] }, { "cell_type": "markdown", "metadata": { "toc-hr-collapsed": true }, "source": [ "# Functores alternativos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La clase de los functores alternativos está definida en [Control.Applicative](http://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Applicative.html#g:2) por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "class Applicative f => Alternative f where\n", " empty :: f a\n", " (<|>) :: f a -> f a -> f a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "y tiene que cumplir las siguientes leyes" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "empty <|> x = x\n", "x <|> empty = x\n", "(x <|> y) <|> z = x <|> (y <|> z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se importa el módulo con" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [], "source": [ "import Control.Applicative" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se puede ver la información de la clase con" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": {}, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
" ], "text/plain": [ "class Applicative f => Alternative (f :: * -> *) where\n", " empty :: f a\n", " (<|>) :: f a -> f a -> f a\n", " some :: f a -> f [a]\n", " many :: f a -> f [a]\n", " {-# MINIMAL empty, (<|>) #-}\n", " \t-- Defined in ‘GHC.Base’\n", "instance Alternative ZipList -- Defined in ‘Control.Applicative’\n", "instance GHC.Base.MonadPlus m => Alternative (WrappedMonad m) -- Defined in ‘Control.Applicative’\n", "instance (Control.Arrow.ArrowZero a, Control.Arrow.ArrowPlus a) => Alternative (WrappedArrow a b) -- Defined in ‘Control.Applicative’\n", "instance Alternative f => Alternative (Ap f) -- Defined in ‘Data.Monoid’\n", "instance Alternative f => Alternative (Alt f) -- Defined in ‘base-4.12.0.0:Data.Semigroup.Internal’\n", "instance Alternative [] -- Defined in ‘GHC.Base’\n", "instance Alternative Maybe -- Defined in ‘GHC.Base’\n", "instance Alternative IO -- Defined in ‘GHC.Base’\n", "instance [safe] Control.Monad.Trans.Error.Error e => Alternative (Either e) -- Defined in ‘Control.Monad.Trans.Error’" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":info Alternative" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Maybe como alternativo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Maybe es un functor alternativo definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Alternative Maybe where\n", " empty :: Maybe a\n", " empty = Nothing\n", "\n", " (<|>) :: Maybe a -> Maybe a -> Maybe a\n", " Nothing <|> r = r\n", " l <|> _ = l" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just 3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing <|> Just 3 <|> empty <|> Just 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Lista como alternativo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Las lista son functores alternativos definidos por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Alternative [] where\n", " empty :: [a]\n", " empty = []\n", " \n", " (<|>) :: [a] -> [a] -> [a]\n", " (<|>) = (++)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[] <|> [1,2,3] <|> [4]" ] }, { "cell_type": "markdown", "metadata": { "toc-hr-collapsed": true }, "source": [ "# Iterables (en inglés, *Traversable*)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La clase Traversable está definida en el módulo [Data.Traversable](https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "class (Functor t, Foldable t) => Traversable t where\n", " traverse :: Applicative f => (a -> f b) -> t a -> f (t b)\n", " sequenceA :: Applicative f => t (f a) -> f (t a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Maybe como iterable" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Maybe es un iterable definido por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Traversable Maybe where\n", " traverse :: Applicative f => (a -> f b) -> Maybe a -> f (Maybe b)\n", " traverse _ Nothing = pure Nothing\n", " traverse f (Just x) = Just <$> f x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo, sea ` mitad` la función definida por" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [], "source": [ "mitad :: Int -> Maybe Int\n", "mitad x = if even x then Just (x `div` 2) else Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "entonces" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just (Just 3)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "traverse mitad (Just 6)" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "traverse mitad (Just 7)" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "traverse mitad Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Las listas como iterable" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Las listas son iterables definidos por" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "instance Traversable [] where\n", " traverse :: Applicative f => (a -> f b) -> [a] -> f [b]\n", " traverse f = foldr consF (pure [])\n", " where \n", " consF x ys = (:) <$> f x <*> ys" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por ejemplo," ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just [1,2,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "traverse mitad [2,4,8]" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "traverse mitad [2,3,8]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Otro ejemplo," ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [], "source": [ "anterior :: Int -> Maybe Int\n", "anterior n = if n > 0 then Just (n-1) else Nothing" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just [1,4,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "traverse anterior [2,5,4]" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "traverse anterior [2,0,4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Derivaciones" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Algunas instancias se pueden derivar automáticamente. Por ejemplo," ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [], "source": [ "{-# LANGUAGE DeriveFunctor #-} \n", "{-# LANGUAGE DeriveFoldable #-} \n", "{-# LANGUAGE DeriveTraversable #-} \n", "\n", "data Arbol a = Hoja | Nodo a (Arbol a) (Arbol a)\n", " deriving (Functor, Foldable, Traversable, Show)" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [], "source": [ "ejArbol1, ejArbol2 :: Arbol Int\n", "ejArbol1 = Nodo 2 (Nodo 4 Hoja Hoja) (Nodo 6 Hoja Hoja)\n", "ejArbol2 = Nodo 2 (Nodo 3 Hoja Hoja) (Nodo 6 Hoja Hoja)" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nodo 3 (Nodo 5 Hoja Hoja) (Nodo 7 Hoja Hoja)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (+1) ejArbol1" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldr (+) 3 ejArbol1" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "length ejArbol1" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "6 `elem` ejArbol1" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "7 `elem` ejArbol1" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "maximum ejArbol1" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "minimum ejArbol1" ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "12" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sum ejArbol1" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "48" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "product ejArbol1" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2,4,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import Data.Foldable\n", "toList ejArbol1" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Just (Nodo 1 (Nodo 2 Hoja Hoja) (Nodo 3 Hoja Hoja))" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "traverse mitad ejArbol1" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "traverse mitad ejArbol2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Referencias" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "+ [All about monoids](http://comonad.com/reader/wp-content/uploads/2009/07/AllAboutMonoids.pdf) por Edward Kmett\n", "+ [Applicative functors](https://pbrisbin.com/posts/applicative_functors/) por Pat Brisbin\n", "+ [¡Aprende Haskell por el bien de todos!](http://aprendehaskell.es/main.html) de Miran Lipovača.\n", " + [Cap. 11: Funtores, funtores aplicativos y monoides](http://aprendehaskell.es/content/Funtores.html).\n", " + [Cap. 12: Un puñado de mónadas](http://aprendehaskell.es/content/Funtores.html).\n", "+ [Functors, applicatives, and monads in pictures](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html).\n", "+ [Haskell wiki](https://en.wikibooks.org/wiki/Haskell):\n", " + [Foldable](https://en.wikibooks.org/wiki/Haskell/Foldable).\n", " + [Traversable](https://en.wikibooks.org/wiki/Haskell/Traversable).\n", "+ [Haskell ITMO course at CTD](https://github.com/jagajaga/FP-Course-ITMO) de Dimitry Kovanikov y Arseniy Seroka.\n", " + [Lecture 4: Basic typeclasses: Monoid. Functor. Applicative](http://slides.com/fp-ctd/lecture-4).\n", "+ [Learn you a Haskell for great good!](https://mybinder.org/v2/gh/jamesdbrock/learn-you-a-haskell-notebook/master?urlpath=lab/tree/learn_you_a_haskell/00-preface.ipynb) por Miran Lipovača y James Brock.\n", " + [Chapter 11: Functors, applicative functors and monoids](https://mybinder.org/v2/gh/jamesdbrock/learn-you-a-haskell-notebook/master?urlpath=lab/tree/learn_you_a_haskell/11-functors-applicative-functors-and-monoids.ipynb)\n", " + [Chapter 12: A fistful of monads](https://mybinder.org/v2/gh/jamesdbrock/learn-you-a-haskell-notebook/master?urlpath=lab/tree/learn_you_a_haskell/12-a-fistful-of-monads.ipynb)\n", "+ [Monoids and finger trees](https://apfelmus.nfshost.com/articles/monoid-fingertree.html) por Heinrich Apfelmus.\n", "+ [Monoids tour](https://www.schoolofhaskell.com/user/mgsloan/monoids-tour) por Michael Sloan.\n", "+ [Programming in Haskell](https://books.google.es/books?id=75C5DAAAQBAJ&lpg=PP1&dq=Programming%20in%20Haskell&pg=PP1#v=onepage&q&f=false) por G. Hutton.\n", " + Cap. 12: Monads and more.\n", " + Cap. 14: Foldables and friends.\n", "+ [Simple reflection of expressions](https://twanvl.nl/blog/haskell/simple-reflection-of-expressions) por Twan van Laarhoven.\n", "+ [Typeclassopedia](https://wiki.haskell.org/Typeclassopedia) por Brent Yorgey." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "name": "haskell", "pygments_lexer": "Haskell", "version": "8.6.5" } }, "nbformat": 4, "nbformat_minor": 4 }