Wikilibros eswikibooks https://es.wikibooks.org/wiki/Portada MediaWiki 1.47.0-wmf.24 first-letter Medio Especial Discusión Usuario Usuario discusión Wikilibros Wikilibros discusión Archivo Archivo discusión MediaWiki MediaWiki discusión Plantilla Plantilla discusión Ayuda Ayuda discusión Categoría Categoría discusión TimedText TimedText talk Módulo Módulo discusión Evento Evento discusión Plantilla:Portada/Encabezado 10 17829 436371 436360 2026-10-13T12:39:46Z Antimundo 74354 Quitar libro de visitas tras consenso 436371 wikitext text/x-wiki <div style="display:flex;flex-wrap:wrap;margin-bottom: 1rem;align-items:center;"> <div style="flex:2 1 30rem;min-width:20rem;"> <span style="font-size:1.5rem;line-height:1.5;"> Te damos la [[Wikilibros:Bienvenida|bienvenida]] a Wikilibros, </span> <br> la biblioteca colaborativa de contenido libre. <div class="plainlinks" style="margin-top:0.75rem;font-size:0.875rem;"> [[Wikilibros:Ayuda|Ayuda]] · [[Ayuda:Preguntas frecuentes|Preguntas frecuentes]] · [[Wikilibros:Café|Café comunitario]] </div> </div> <div style="flex:1 1 20rem;text-align:right;font-size:0.95rem;"> [[Wikilibros:Estadísticas|'''{{NUMBEROFARTICLES}}''' páginas]] en español, <br> en más de '''{{NUMBEROFBOOKS}}''' libros. </div> </div> 7i0nfck98xswdckswqlocfb645u4ss5 Wikilibros:Libro de visitas 4 17966 436369 427198 2026-10-13T12:34:15Z Antimundo 74354 Consenso en discusión 436369 wikitext text/x-wiki {{Museo}} {| width="100%" cellspacing="0" cellpadding="0" style="border:1px solid var(--border-color-progressive, #36c); color: var(--color-base); background:var(--background-color-progressive-subtle, #e8eeff); border-radius: 2px;" |- | colspan="2" style="color:var(--color-inverted, #fff); background:var(--border-color-progressive, #36c); padding:12px 15px;font-size: 120%;" | [[Archivo:Gartoon mimetypes txt2.svg|36px|sinmarco]] '''¡Te damos la bienvenida al libro de visitas de Wikilibros en español!''' |- | style="padding:1em;" | '''<span class="plainlinks">[http://es.wikibooks.org/w/wiki.phtml?title=Wikibooks:Libro_de_visitas&action=edit&section=new Pulsa aquí]</span>''' para dejarnos tus impresiones sobre Wikilibros. ¡Los comentarios y sugerencias constructivos nos ayudan a mejorar! Sé amable y respetuoso con los demás. Evita compartir información personal, spam, y cualquier contenido inapropiado. |} [[vi:Wikibooks:Guestbook for non-Vietnamese speakers]] == ¡Simplemente....fantástico...! == He quedado sorprendido por el caudal de conocimiento al cual los seres humanos estamos {{Nofirmado|186.144.162.155|23:40 14 ago 2011}} == ¡Mas imágenes! == Creo que están muy bien sus libros pero se ve necesario poner mas imágenes para tener mas claro lo que se hace. {{Nofirmado|190.53.229.124|21:45 20 ago 2011}} == Biblioteca carente == La verdad les falta mucho material, los pocos libros que tienen carecen de interés, ojalá y puedan mejorar, gracias. Enrique Garrido. {{Nofirmado|200.66.106.34|09:49 21 oct 2011}} Enrique te animamos a que completes aquellos puntos en los que crees que wikilibros puede mejorar. {{Nofirmado|88.0.243.127|15:59 21 ene 2012}} == Felicitaciones == Wikilibros me ayudó mucho en lo que es el inglés. Felicito a los que hicieron de Wikipedia, Wikilibros. Muchas felicitaciones. Atte. Maria C. Salamanca {{Nofirmado|190.110.152.229|00:38 13 ene 2012}} == Comentario del 2012 == Pero.., ¿de verdad es lo que parece? No me lo puedo creer..., me parece que es demasiado bueno para ser verdad esto de WIKILIBROS. MUCHÍSIMAS GRACIAS Y MUCHA SUERTE A TODOS. jose luis alvarez {{Nofirmado|88.151.16.21|18:21 8 feb 2012}} == Música popular == Me encanta que se pueda editar las páginas para colaborar con wikilibros como lo hice ahora. {{Nofirmado|186.60.26.119|21:54 9 may 2012}} == Me agrada Wikilibros == Muy interesante este proyecto de wikilibros, porque permite al usuario obtener información inmediata y libre. Felicitaciones. María Mercedes González Brand Julio 25/2012 --[[Especial:Contribuciones/181.129.254.200|181.129.254.200]] 02:12 26 jul 2012 (UTC) == ¿Y la literatura? == es muy interesante el compendio de libros que tiene wikilibros pero creo que le falta agregar obras literarias. no sé si eso sea posible pero si lo es quisiera que los agreguen. gracias (nancy guzmán) {{Nofirmado|190.236.19.165|01:15 24 ene 2013}} :Hola gracias por compartir tu opinión, pero Wikilibros no puede admitir obras literarias ya que su contenido se limita a libros de carácter pedagógico, ya sean manuales, guías o similares. Si buscas obras literarias ya existentes tienes a tu disposición el proyecto [[Manual wiki/Los proyectos Wikimedia/Wikisource|Wikisource]]. Si te refieres a la creación de obras literarias de forma colaborativa me temo que ningún proyecto de Wikimedia tiene esa función. Un saludo, --[[Usuario:Morza|Morza]] ([[Usuario Discusión:Morza|sono qui]]) 17:44 29 ene 2013 (UTC) == Temas de la Geociencia == Sería muy útil la creación de libros relativo a la Geociencia {{Nofirmado|Juananto~eswikibooks|21:00 28 feb 2013}} == ePub y odt == Por favor, incluyan la extensión de descarga en odt como Wikipedia y, además, ePub. La difusión será más alta. Gracias. {{Nofirmado|188.85.112.29|08:44 9 ago 2013}} == ¿Qué es esto? == De verdad que puedes encontrar cosas muy interesantes, y gracias a eso aprendí Coreano,...Pero...POR POCO ME VUELVO LOCA BUSCANDO UN LIBRO DE DISEÑO de modas!!!!, aunque sea alguno de confecciones.¿NO PODRÍAN COMPLACERME AMPLIANDO UN POCO TEMAS COMO ESTOS?(Diseño, dibujo, etc). Espero que me complazcan, Lo estoy esperando. Hae Na Kang {{Nofirmado|201.220.215.14|18:29 17 ene 2014}} == Genial la cantidad de informacion brindada a profesores y alumnos! == Hola desde Cuba! Pues muy bueno esta gran cantidad de informacion compartida para el mundo entero, gracias a la colaboracion de todos. Es uno de los pocos portales a los que puedo acceder desde aca, y en hora buena que llegue a todas partes del mundo. Felicidades por su concepcion de compartir la informacion entre todos, en la unidad esta la fuerza! {{Nofirmado|Vampgoth|02:54 28 may 2014}} == Hola == Wikilibros es estupenda porque aqui se encuentra todo el conocimiento necesario para un estudiante--[[Especial:Contribuciones/201.220.215.14|201.220.215.14]] 21:38 8 oct 2014 (UTC) == Seguimos. == Felicitaciones por el avance contínuo de los aportes abiertos !!!. {{Nofirmado|Guillspa|01:15 2 nov 2014}} == El conocimiento == De gran manera, podemos acercarnos al conocimiento por medio de esta base de datos. Gracias! [[Especial:Contribuciones/2806:104E:13:1993:3184:B3B:7CB0:7A35|2806:104E:13:1993:3184:B3B:7CB0:7A35]] 13:32 21 sep 2023 (UTC) == Ediciones == me encanta que se pueda editar las páginas para colaborar con el proyecto y desarrollar el wikilibro que se está editando gracias [[Especial:Contribuciones/2803:33C0:7507:A014:4C7B:6EAE:3C1:9310|2803:33C0:7507:A014:4C7B:6EAE:3C1:9310]] 17:41 8 oct 2023 (UTC) == Muy interesante == Muy choro Muy bueno [[Especial:Contribuciones/190.215.245.184|190.215.245.184]] 21:32 14 dic 2023 (UTC) 01kbnvy0to5hyjt2od2e9uts6ilxuio 436370 436369 2026-10-13T12:34:27Z Antimundo 74354 436370 wikitext text/x-wiki {{Museo}} {| width="100%" cellspacing="0" cellpadding="0" style="border:1px solid var(--border-color-progressive, #36c); color: var(--color-base); background:var(--background-color-progressive-subtle, #e8eeff); border-radius: 2px;" |- | colspan="2" style="color:var(--color-inverted, #fff); background:var(--border-color-progressive, #36c); padding:12px 15px;font-size: 120%;" | [[Archivo:Gartoon mimetypes txt2.svg|36px|sinmarco]] '''¡Te damos la bienvenida al libro de visitas de Wikilibros en español!''' |- | style="padding:1em;" | '''<span class="plainlinks">[http://es.wikibooks.org/w/wiki.phtml?title=Wikibooks:Libro_de_visitas&action=edit&section=new Pulsa aquí]</span>''' para dejarnos tus impresiones sobre Wikilibros. ¡Los comentarios y sugerencias constructivos nos ayudan a mejorar! Sé amable y respetuoso con los demás. Evita compartir información personal, spam, y cualquier contenido inapropiado. |} [[vi:Wikibooks:Guestbook for non-Vietnamese speakers]] == ¡Simplemente....fantástico...! == He quedado sorprendido por el caudal de conocimiento al cual los seres humanos estamos {{Nofirmado|186.144.162.155|23:40 14 ago 2011}} == ¡Mas imágenes! == Creo que están muy bien sus libros pero se ve necesario poner mas imágenes para tener mas claro lo que se hace. {{Nofirmado|190.53.229.124|21:45 20 ago 2011}} == Biblioteca carente == La verdad les falta mucho material, los pocos libros que tienen carecen de interés, ojalá y puedan mejorar, gracias. Enrique Garrido. {{Nofirmado|200.66.106.34|09:49 21 oct 2011}} Enrique te animamos a que completes aquellos puntos en los que crees que wikilibros puede mejorar. {{Nofirmado|88.0.243.127|15:59 21 ene 2012}} == Felicitaciones == Wikilibros me ayudó mucho en lo que es el inglés. Felicito a los que hicieron de Wikipedia, Wikilibros. Muchas felicitaciones. Atte. Maria C. Salamanca {{Nofirmado|190.110.152.229|00:38 13 ene 2012}} == Comentario del 2012 == Pero.., ¿de verdad es lo que parece? No me lo puedo creer..., me parece que es demasiado bueno para ser verdad esto de WIKILIBROS. MUCHÍSIMAS GRACIAS Y MUCHA SUERTE A TODOS. jose luis alvarez {{Nofirmado|88.151.16.21|18:21 8 feb 2012}} == Música popular == Me encanta que se pueda editar las páginas para colaborar con wikilibros como lo hice ahora. {{Nofirmado|186.60.26.119|21:54 9 may 2012}} == Me agrada Wikilibros == Muy interesante este proyecto de wikilibros, porque permite al usuario obtener información inmediata y libre. Felicitaciones. María Mercedes González Brand Julio 25/2012 --[[Especial:Contribuciones/181.129.254.200|181.129.254.200]] 02:12 26 jul 2012 (UTC) == ¿Y la literatura? == es muy interesante el compendio de libros que tiene wikilibros pero creo que le falta agregar obras literarias. no sé si eso sea posible pero si lo es quisiera que los agreguen. gracias (nancy guzmán) {{Nofirmado|190.236.19.165|01:15 24 ene 2013}} :Hola gracias por compartir tu opinión, pero Wikilibros no puede admitir obras literarias ya que su contenido se limita a libros de carácter pedagógico, ya sean manuales, guías o similares. Si buscas obras literarias ya existentes tienes a tu disposición el proyecto [[Manual wiki/Los proyectos Wikimedia/Wikisource|Wikisource]]. Si te refieres a la creación de obras literarias de forma colaborativa me temo que ningún proyecto de Wikimedia tiene esa función. Un saludo, --[[Usuario:Morza|Morza]] ([[Usuario Discusión:Morza|sono qui]]) 17:44 29 ene 2013 (UTC) == Temas de la Geociencia == Sería muy útil la creación de libros relativo a la Geociencia {{Nofirmado|Juananto~eswikibooks|21:00 28 feb 2013}} == ePub y odt == Por favor, incluyan la extensión de descarga en odt como Wikipedia y, además, ePub. La difusión será más alta. Gracias. {{Nofirmado|188.85.112.29|08:44 9 ago 2013}} == ¿Qué es esto? == De verdad que puedes encontrar cosas muy interesantes, y gracias a eso aprendí Coreano,...Pero...POR POCO ME VUELVO LOCA BUSCANDO UN LIBRO DE DISEÑO de modas!!!!, aunque sea alguno de confecciones.¿NO PODRÍAN COMPLACERME AMPLIANDO UN POCO TEMAS COMO ESTOS?(Diseño, dibujo, etc). Espero que me complazcan, Lo estoy esperando. Hae Na Kang {{Nofirmado|201.220.215.14|18:29 17 ene 2014}} == Genial la cantidad de informacion brindada a profesores y alumnos! == Hola desde Cuba! Pues muy bueno esta gran cantidad de informacion compartida para el mundo entero, gracias a la colaboracion de todos. Es uno de los pocos portales a los que puedo acceder desde aca, y en hora buena que llegue a todas partes del mundo. Felicidades por su concepcion de compartir la informacion entre todos, en la unidad esta la fuerza! {{Nofirmado|Vampgoth|02:54 28 may 2014}} == Hola == Wikilibros es estupenda porque aqui se encuentra todo el conocimiento necesario para un estudiante--[[Especial:Contribuciones/201.220.215.14|201.220.215.14]] 21:38 8 oct 2014 (UTC) == Seguimos. == Felicitaciones por el avance contínuo de los aportes abiertos !!!. {{Nofirmado|Guillspa|01:15 2 nov 2014}} == El conocimiento == De gran manera, podemos acercarnos al conocimiento por medio de esta base de datos. Gracias! [[Especial:Contribuciones/2806:104E:13:1993:3184:B3B:7CB0:7A35|2806:104E:13:1993:3184:B3B:7CB0:7A35]] 13:32 21 sep 2023 (UTC) == Ediciones == me encanta que se pueda editar las páginas para colaborar con el proyecto y desarrollar el wikilibro que se está editando gracias [[Especial:Contribuciones/2803:33C0:7507:A014:4C7B:6EAE:3C1:9310|2803:33C0:7507:A014:4C7B:6EAE:3C1:9310]] 17:41 8 oct 2023 (UTC) == Muy interesante == Muy choro Muy bueno [[Especial:Contribuciones/190.215.245.184|190.215.245.184]] 21:32 14 dic 2023 (UTC) h7gyh46kiexoamfkrcdtif4qqh58o3n Wikilibros discusión:Libro de visitas 5 18872 436372 436368 2026-10-13T12:45:59Z Antimundo 74354 /* Consenso para archivado de esta página */ Respuesta 436372 wikitext text/x-wiki == Formato == Me he tomado la libertad de realizarle unas pequeñas modificaciones en el formato en pro. [[Usuario:Oleinad|Oleinad]] 08:52 30 ene 2008 (UTC) ---- Este podria ser un proyecto a destacar y en la que todos los amigos del mundo lo hagan posible. ¿Que es lo mas grande de su tierra? Hasta lo luego. --[[Especial:Contributions/190.53.81.59|190.53.81.59]] 02:34 2 mar 2008 (UTC) ---- He firmado y limpiado la página. También me he permitido cambiar un poquito el estilo de la caja de bienvenida e introducción (centrado, rayas, correcciones menores, etc.). Espero que quede así a gusto de todos. — [[Usuario:Ketamino|Ketamino]] 14:29 22 may 2009 (UTC) ==Discutiendo el formato de bienvenida== *Sobre el ''Centrado'': tienes razón en cuanto a la resolución. *Las ''rayas'', gramaticalmente no es necesario para nada. *Las ''mayúsculas'', solo en palabras propias, para lo demás existen símbolos ortográficos. -- Saludos, [[Usuario:Oleinad|Ole<b>i</b>nad]] <sup style="color=#33f933">(opina [[Usuario_Discusión:Oleinad|aquí]])</sup> 21:11 25 may 2009 (UTC) :Ok, de acuerdo contigo, pero las rayas no son signos gramaticales, son tipográficos. Sirven para diferenciar mínimamente esa frase referente al (futuro) archivo e igualar la longitud de la línea respecto a la inmediatamente anterior. Con un poco de buena voluntad, si me la respetas, ni «para ti ni para mi», sino para los dos ;). Saludos. --[[Usuario:Ketamino|Ketamino]] 01:15 26 may 2009 (UTC) ::Estas de acuerdo en que se centra el contenido del cuadro para que resalte respecto al demás texto, por lo que '''esta centrado el contenido primordial que no interesa que los usuarios lean'''. En la linea en donde colocas las rayas no es primordial por lo no debe estar centrado, por lo tanto no es necesario ningún otro símbolo. -- Saludos, [[Usuario:Oleinad|Ole<b>i</b>nad]] <sup style="color=#33f933">(opina [[Usuario_Discusión:Oleinad|aquí]])</sup> 18:17 26 may 2009 (UTC) :::El centrado no es «para que resalte», es porque tipográficamente es un horror eso a la izquierda, respecto al título de la caja, que sí aceptas centrado. Soy editor, sé de lo que hablo, respétalo. PD. Por cierto, «discucion» es discusión. Cuida también la sintaxis, no se entiende tu argumentación. --[[Usuario:Ketamino|Ketamino]] 01:42 27 may 2009 (UTC) == felicitaciones== muy buena la página. {{Nofirmado|200.72.40.99|14:31 17 feb 2011}} == Consenso para archivado de esta página == Esta página está enlazada en la portada de Wikilibros desde hace prácticamente 20 años. El objetivo de esta página es solicitar «comentarios y sugerencias», sin embargo en todos estos años tan solo se han hecho 17 comentarios y la página (antes de que yo la arreglase en junio de este año) estaba en un estado de abandono evidente, llena de ediciones sin sentido, comentarios sin firmar, etc. Me gustaría archivar esta página, y retirar su enlace de la portada, para que si alguien tiene un «comentario o sugerencia» se haga en el Café o en la página de discusión correspondiente a ese comentario. No veo que ni otras wikis en español, ni las ediciones de Wikibooks en otros idiomas, incluyan esta clase «libro de visitas», y personalmente no le encuentro ninguna utilidad. Los comentarios que ahí aparecen no son particularmente útiles y además hay una cantidad extremadamente baja de ellos para la cantidad de años que esta página ha estado listada en la portada. Insisto en que no solicito su borrado. Solicito el colocar la plantilla {{ep|Museo}} y retirarla de la portada por los motivos mencionados anteriormente. Menciono a @[[Usuario:Mazbel|Mazbel]] para escuchar su opinión. Saludos. [[Usuario:Antimundo|Antimundo]] ([[Usuario discusión:Antimundo|discusión]]) 10:00 13 oct 2026 (UTC) : Por lo general, cuando uno presenta un comentario o sugerencia sobre alguna página, puede hacerla directamente en el Café, por tanto, no hay problema en quitar el enlace de esta página en la portada y dejar esta misma, con la plantilla {{tl|museo}}. Sería bueno, eso sí, que en la portada, se mencione que, en caso de «comentario o sugerencia», el usuario pueda hacerlo en la página correspondiente (café). Saludos! [[Usuario:Mazbel|<span style="font-weight:bold; font-family:cursive, serif;">Mazbel</span>]] [[Usuario discusión:Mazbel|<span style="font-size:small; font-family:cursive, serif;">(Discusión)</span>]] 11:26 13 oct 2026 (UTC) ::Estoy intentando «actualizar» un poco la portada, tomando como referencia el diseño de esWikipedia. De momento, he cambiado el enlace a «Café comunitario» para dar algo de contexto a quienes no sepan qué es el Café. Creo que añadir explicaciones sobre cada apartado en la portada generaría demasiado texto y podría hacerla un poco abrumadora. Si lo ves bien, prefiero mantener un diseño más sencillo para el encabezado como el de esWikipedia. [[Usuario:Antimundo|Antimundo]] ([[Usuario discusión:Antimundo|discusión]]) 12:45 13 oct 2026 (UTC) fuwqnp5yx5ifj2kn44c6sgsglhg368u Temario Oposición Profesor FP Informática/Tema 02. Elementos funcionales de un ordenador digital. Arquitectura. 0 27685 436395 396972 2026-10-13T20:08:08Z ~2026-55042-54 128009 /* Unidad Aritmético-lógica (ALU) */ 436395 wikitext text/x-wiki =Estructura básica de un ordenador= Históricamente los ordenadores eran computadores con un programa fijado pero en la actualidad los ordenadores siguen un modelo basado en programas de almacenamiento, de esta manera se tienen: *'''Computadora de programa fijado (Fixed Program Computer)''': Actualmente algunas máquinas todavía utilizan este módelo, las calculadoras, por ejemplo. *'''Computadora de programa almacenado (Stored Program Computer)''': En estas máquinas existe una memoria que sirve para almacenar programas por lo que son reprogramables sin necesidad de rediseñarlas por completo. Éste último modelo también es conocido como [[:m:w:en:Von_Neumann_architecture|Arquitectura Von Neumann]]. '''Arquitectura John Von Neummann''' Fue diseñada en 1946 por el matemático [[:m:w:en:John_von_Neumann|John Von Neumann]] y es un modelo que utiliza la memoria tanto para almacenar instrucciones como datos. Estos computadores implementan [[:m:w:es:M%C3%A1quina_de_Turing#M.C3.A1quina_Universal_de_Turing|la máquina Universal de Turing]] ya que permiten crear aplicaciones capaces de interpretar otras aplicaciones. La idea de esta arquitectura es la de conectar de forma fija y coordinar el funcionamiento de los componentes de un ordenador con una unidad central de proceso (CPU). La CPU dispone de un conjunto de instrucciones que permiten manejar la máquina y además estas instrucciones se pueden almacenar en la memoria del sistema como el resto de datos. ==Componentes de un ordenador. Modelo de máquina sencilla de Von Neumman== Una máquina [[:m:w:en:Von_Neumann_architecture|Von Neumman]] está compuesta por cuatro partes: ===Unidad de Control (UC)=== Es la unidad que se encarga de leer las instrucciones secuencialmente. Las instrucciones las obtiene de la memoria principal. ===Unidad Aritmético-lógica (ALU)=== Realiza las operaciones matemáticas fundamentales (suma, resta, AND, OR, etc.). Es conocida como ALU (UAL en español). ===Memoria principal=== Es donde se almacenan los datos mediante celdas identificadas por direcciones ===Unidad de control de entrada/salida=== Es la encargada de conectar la máquina con el exterior. Permite la transferencia de datos desde la máquina a los periféricos. [[Imatge:Adcfig2.gif]] La comunicación entre los diferentes componentes se realiza usando '''buses o canales'''. Los buses son los caminos físicos a través de los cuales las instrucciones y los datos circulan entre los diferentes componentes de la máquina. ==Funcionamiento== Veamos un ejemplo de como se ejecuta un programa en un ordenador actual. El siguiente gráfico nos muestra como es el proceso: Supongamos un programa al que llamaramos '''holaMundo.sh''' y que una vez ejecutado nos saludara con la frase "¡Hola mundo!". Según el gráfico el primer paso del proceso de ejecución del programa es utilizar un dispositivo de entrada. En este caso utilizaremos el teclado y mediante una terminal de linea de comandos ejecutaremos: $ holaMundo.sh Conviene destacar que no siempre será un dispositivo de entrada el que iniciara el programa ya que los programas pueden ser iniciados por otros programas. Con esta orden le indicamos a la computadora que debe ejecutar un programa llamado holaMundo.sh y que deberá buscar las instrucciones de las que se compone el programa en la memoria. La CPU irá leyendo las instrucciones de la memoria de forma secuencial y una vez que tenga un resultado lo mostrará por algún dispositivo de salida. (por ejemplo, la pantalla). '''NOTA''': Se han obviado los pasos de interpretación del lenguaje de programación y otros aspectos. =La unidad central de proceso (CPU)= Para mas detalle consultar el tema 3: Los computadores actuales tienen un componente que se llama la unidad dcentral de procesamiento.(conocida por el término inglés [[:m:w:en:CPU|Central processing unit]] o CPU). La CPU esta compuesta por los siguientes componentes: La unidad aritmético-lógica (ALU): Es la encargada de realizar las operaciones básicas La Unidad de Control: Es la encargada de coordinar y gestionar el resto de elementos de la CPU. Registros: Son elementos de memoria que contienen información relativa al programa que se está ejecutando. La forma, diseño e implementación de las CPU ha cambiado mucho durante la evolución de la informática pero su funcionamiento continúa siendo el mismo desde su definición funcional en la arquitectura de Von Neumman. ==Lenguaje máquina== Las CPU ejecutan programas que leen de la memória principal. Pero estos programas no son escritos en lenguajes de alto nivel sino que son interpretados por la máquina utilizando un lenguaje muy restringido y de bajo nivel llamado lenguaje máquina. Este lenguaje es muy sencillo y se limita a un conjunto de instrucciones muy limitado que comunmente se llama juego de instrucciones del computador. Cada CPU tiene su conjunto de instrucciones propio. Las CPU pueden ser de propósito general o estar diseñadas para un proposito concreto y por tanto tener un conjunto de instrucciones muy diferentes. De cualquier manera suelen cumplir los siguientes requisitos: *Realizan una única y sencilla operación. *Utilizan un número fijo de operandos y con una representación numérica fija. *La codificación de las instrucciones es lo más simple y sitemática posible de tal forma que el interprete del lenguaje máquina pueda ser también sencillo. *Las instrucciones son independientes y autocontenidas. Contienen todos los datos necesarios para ejecutarse y no dependen de otras instrucciones. Y las podemos clasificar en:: *'''Instrucciones de transferencia de datos''': Leen o escriben datos de una memoria. *'''Instrucciones aritméticas''': Realizan operaciones aritméticas utilizando la ALU. *'''Instrucciones lógicas''': Realizan operaciones lógicas utilizando la ALU. *'''Instrucciones de desplazamiento''': Realizan operaciones de desplazamiento. *'''Instrucciones de transferencia de control''': Actualizan el registro PC de la CPU administrando el flujo de ejecución del programa (llamadas a subrutinas). *'''Instrucciones I/O''': Administran los comandos de entrada salida. Las instrucciones tienen un '''formato rígido''' definido por el conjunto de instrucciones del sistema. Una instrucción tiene dos partes: *'''Código de la operación''': es descodificado por la unidad de control *'''Operandos''': Los datos sobre los cuales se ejecutará una operación. Hay operaciones que no tienen datos, operaciones unarias y operaciones binarias. *Todas las instrucciones son '''imperativas''' La arquitectura mas utilizada en PC de sobremesa es la [[:m:w:en:X86-16|x86]] de Intel y compatibles (com por ejemplo las CPU de AMD). Esta arquitectura apareció en 1978 con intel y ha ido evolucionando Intel 286, Intel 386, Pentium,Pentium 2, Pentium 3, Itanium hasta los actuales Pentium 4. Se han desarrollado y se continúan desarollando extensiones del lenguaje máquina. Un par de ejemplos son: *[[:m:w:en:MMX_%28instruction_set%29|MMX d'Intel]] *[[:m:w:en:3DNow%21|3DNow]] Conviene destacar que nosrmalmente es difícil encontrar software y sistemas operativos que soporten las nuevas instrucciones. '''Propiedades''': *Las instrucciones son independientes y autocontenidas. Contienen todos los datos necesarios para ejecutarse y no dependen de otras instrucciones. *Instrucciones sistemáticas (facilita su decodificación). El decodificador ha de ser sencillo (no puede ser tan complicado como un compilador de un lenguaje de alto nivel). *Es el único lenguaje que entiende la máquina. *Utiliza código binario, octal o hexadecimal. *Fueron los '''primeros lenguajes de programación''' *Cada máquina (CPU) tiene su propio lenguaje y conjunto de instrucciones. Poco portable. Es necesario conocer el conjunto de instrucciones de la máquina para poder programarla.Diferentes arquitecturas. *Actualmente se utilizan mucho más los lenguajes de alto nivel(mas faciles de aprender y utilizar por que son más cercanos al lenguaje natural) **La unidad de controlo utilizando el descodificador es la encargada de convertir las instrucciones en código interpretable por la CPU. *Los datos utilizados son referenciados por su posición en la memória. Es responsabilidad del programador controlar el mapa de memoria y la asignación de memoria a cada dato. *Las instrucciones tiene un formato rígido definido por el conjunto de instrucciones del sistema. Una instrucción tiene dos partes: **Código de la operación: Es descodificado por la unidad de control **Operandos *Todas las instrucciones son '''imperativas''' *No se pueden utilizar comentarios ==La unidad aritmético-lógica (ALU)== Las ALU son las encargadas de realizar las operaciones aritmético-lógicas que le proporciona la unidad de control. Las operaciones son muy sencillas hasta el punto de que muchos procesadores disponen de un simple sumador/restador y el resto de operaciones se realizan mediante la descomposición en operaciones sencillas de otras más complejas. Las ALU tiene el siguiente esquema: [[file:ALU_symbol.svg|200px|thumb]] La forma de V se utiliza tipicamente para representarla donde los extremos de la v son los operandos y el vértice el resultado. La comunicación entre la CPU y la ALU se realiza mediante los siguientes registros: *'''A y B''': Son los registros que contienen los operandos. Tambien se llaman '''Registros de entrada'''. *'''R''': Es el registro que contiene el resultado. Los registros A, B y R normalmente se almacenan en un banco de registros. *'''F''': Es el registro que indica que '''tipo de operación''' se realiza sobre los operandos A y B. *'''D''': Es el '''registro de estado del resultado'''. También conocido como '''registro de estado''' (Status Register). Se utiliza para indicar estados especiales como '''bits de arrastre''', '''divisiones por cero''' o ''''desbordamientos/overflows'''. Las operaciones que suelen hacer las ALU son: *Operaciones aritméticas con números enteros (suma, resta y a veces multiplicaciones y divisiones). *Operaciones lógicas binarías *Operaciones de desplazamiento de bits: Desplazamientos a derecha o izquierda de bits (equivaliendo a multiplicar o a dividir por 2). Estas operaciones se suelen realizar mediante circuitos electrónicos que pueden ser circuitos combinacionales o circuitos secuenciales según utilizan registros de memoria o no. Los operandos normalmente se representan en complemento a dos (Ver complemento a 2 en el tema 10) y las operaciones son con números enteros. Las operaciones se pueden clasificar en: *'''Operacions unarias''': Un solo operando (operación de negación) *'''Operacions binaria''': 2 operadores (suma, resta, división, etc.) Las operaciones pueden ser: *'''Operador paralelo''': La operación se realiza con todos los bits del operador a la vez. *'''Operador serie''': la operación se realiza dígito a dígito. j6x4lb8j9lglzx4zb38q8dlbzxjjbn0 436396 436395 2026-10-13T20:10:15Z ~2026-55042-54 128009 /* La unidad central de proceso (CPU) */ 436396 wikitext text/x-wiki =Estructura básica de un ordenador= Históricamente los ordenadores eran computadores con un programa fijado pero en la actualidad los ordenadores siguen un modelo basado en programas de almacenamiento, de esta manera se tienen: *'''Computadora de programa fijado (Fixed Program Computer)''': Actualmente algunas máquinas todavía utilizan este módelo, las calculadoras, por ejemplo. *'''Computadora de programa almacenado (Stored Program Computer)''': En estas máquinas existe una memoria que sirve para almacenar programas por lo que son reprogramables sin necesidad de rediseñarlas por completo. Éste último modelo también es conocido como [[:m:w:en:Von_Neumann_architecture|Arquitectura Von Neumann]]. '''Arquitectura John Von Neummann''' Fue diseñada en 1946 por el matemático [[:m:w:en:John_von_Neumann|John Von Neumann]] y es un modelo que utiliza la memoria tanto para almacenar instrucciones como datos. Estos computadores implementan [[:m:w:es:M%C3%A1quina_de_Turing#M.C3.A1quina_Universal_de_Turing|la máquina Universal de Turing]] ya que permiten crear aplicaciones capaces de interpretar otras aplicaciones. La idea de esta arquitectura es la de conectar de forma fija y coordinar el funcionamiento de los componentes de un ordenador con una unidad central de proceso (CPU). La CPU dispone de un conjunto de instrucciones que permiten manejar la máquina y además estas instrucciones se pueden almacenar en la memoria del sistema como el resto de datos. ==Componentes de un ordenador. Modelo de máquina sencilla de Von Neumman== Una máquina [[:m:w:en:Von_Neumann_architecture|Von Neumman]] está compuesta por cuatro partes: ===Unidad de Control (UC)=== Es la unidad que se encarga de leer las instrucciones secuencialmente. Las instrucciones las obtiene de la memoria principal. ===Unidad Aritmético-lógica (ALU)=== Realiza las operaciones matemáticas fundamentales (suma, resta, AND, OR, etc.). Es conocida como ALU (UAL en español). ===Memoria principal=== Es donde se almacenan los datos mediante celdas identificadas por direcciones ===Unidad de control de entrada/salida=== Es la encargada de conectar la máquina con el exterior. Permite la transferencia de datos desde la máquina a los periféricos. [[Imatge:Adcfig2.gif]] La comunicación entre los diferentes componentes se realiza usando '''buses o canales'''. Los buses son los caminos físicos a través de los cuales las instrucciones y los datos circulan entre los diferentes componentes de la máquina. ==Funcionamiento== Veamos un ejemplo de como se ejecuta un programa en un ordenador actual. El siguiente gráfico nos muestra como es el proceso: Supongamos un programa al que llamaramos '''holaMundo.sh''' y que una vez ejecutado nos saludara con la frase "¡Hola mundo!". Según el gráfico el primer paso del proceso de ejecución del programa es utilizar un dispositivo de entrada. En este caso utilizaremos el teclado y mediante una terminal de linea de comandos ejecutaremos: $ holaMundo.sh Conviene destacar que no siempre será un dispositivo de entrada el que iniciara el programa ya que los programas pueden ser iniciados por otros programas. Con esta orden le indicamos a la computadora que debe ejecutar un programa llamado holaMundo.sh y que deberá buscar las instrucciones de las que se compone el programa en la memoria. La CPU irá leyendo las instrucciones de la memoria de forma secuencial y una vez que tenga un resultado lo mostrará por algún dispositivo de salida. (por ejemplo, la pantalla). '''NOTA''': Se han obviado los pasos de interpretación del lenguaje de programación y otros aspectos. =La unidad central de proceso (CPU)= Para mas detalle consultar el tema 3: Los computadores actuales tienen un componente que se llama la unidad central de procesamiento.(conocida por el término inglés [[:m:w:en:CPU|Central processing unit]] o CPU). La CPU esta compuesta por los siguientes componentes: La unidad aritmético-lógica (ALU): Es la encargada de realizar las operaciones básicas La Unidad de Control: Es la encargada de coordinar y gestionar el resto de elementos de la CPU. Registros: Son elementos de memoria que contienen información relativa al programa que se está ejecutando. La forma, diseño e implementación de las CPU ha cambiado mucho durante la evolución de la informática pero su funcionamiento continúa siendo el mismo desde su definición funcional en la arquitectura de Von Neumman. ==Lenguaje máquina== Las CPU ejecutan programas que leen de la memória principal. Pero estos programas no son escritos en lenguajes de alto nivel sino que son interpretados por la máquina utilizando un lenguaje muy restringido y de bajo nivel llamado lenguaje máquina. Este lenguaje es muy sencillo y se limita a un conjunto de instrucciones muy limitado que comunmente se llama juego de instrucciones del computador. Cada CPU tiene su conjunto de instrucciones propio. Las CPU pueden ser de propósito general o estar diseñadas para un proposito concreto y por tanto tener un conjunto de instrucciones muy diferentes. De cualquier manera suelen cumplir los siguientes requisitos: *Realizan una única y sencilla operación. *Utilizan un número fijo de operandos y con una representación numérica fija. *La codificación de las instrucciones es lo más simple y sitemática posible de tal forma que el interprete del lenguaje máquina pueda ser también sencillo. *Las instrucciones son independientes y autocontenidas. Contienen todos los datos necesarios para ejecutarse y no dependen de otras instrucciones. Y las podemos clasificar en:: *'''Instrucciones de transferencia de datos''': Leen o escriben datos de una memoria. *'''Instrucciones aritméticas''': Realizan operaciones aritméticas utilizando la ALU. *'''Instrucciones lógicas''': Realizan operaciones lógicas utilizando la ALU. *'''Instrucciones de desplazamiento''': Realizan operaciones de desplazamiento. *'''Instrucciones de transferencia de control''': Actualizan el registro PC de la CPU administrando el flujo de ejecución del programa (llamadas a subrutinas). *'''Instrucciones I/O''': Administran los comandos de entrada salida. Las instrucciones tienen un '''formato rígido''' definido por el conjunto de instrucciones del sistema. Una instrucción tiene dos partes: *'''Código de la operación''': es descodificado por la unidad de control *'''Operandos''': Los datos sobre los cuales se ejecutará una operación. Hay operaciones que no tienen datos, operaciones unarias y operaciones binarias. *Todas las instrucciones son '''imperativas''' La arquitectura mas utilizada en PC de sobremesa es la [[:m:w:en:X86-16|x86]] de Intel y compatibles (com por ejemplo las CPU de AMD). Esta arquitectura apareció en 1978 con intel y ha ido evolucionando Intel 286, Intel 386, Pentium,Pentium 2, Pentium 3, Itanium hasta los actuales Pentium 4. Se han desarrollado y se continúan desarollando extensiones del lenguaje máquina. Un par de ejemplos son: *[[:m:w:en:MMX_%28instruction_set%29|MMX d'Intel]] *[[:m:w:en:3DNow%21|3DNow]] Conviene destacar que nosrmalmente es difícil encontrar software y sistemas operativos que soporten las nuevas instrucciones. '''Propiedades''': *Las instrucciones son independientes y autocontenidas. Contienen todos los datos necesarios para ejecutarse y no dependen de otras instrucciones. *Instrucciones sistemáticas (facilita su decodificación). El decodificador ha de ser sencillo (no puede ser tan complicado como un compilador de un lenguaje de alto nivel). *Es el único lenguaje que entiende la máquina. *Utiliza código binario, octal o hexadecimal. *Fueron los '''primeros lenguajes de programación''' *Cada máquina (CPU) tiene su propio lenguaje y conjunto de instrucciones. Poco portable. Es necesario conocer el conjunto de instrucciones de la máquina para poder programarla.Diferentes arquitecturas. *Actualmente se utilizan mucho más los lenguajes de alto nivel(mas faciles de aprender y utilizar por que son más cercanos al lenguaje natural) **La unidad de controlo utilizando el descodificador es la encargada de convertir las instrucciones en código interpretable por la CPU. *Los datos utilizados son referenciados por su posición en la memória. Es responsabilidad del programador controlar el mapa de memoria y la asignación de memoria a cada dato. *Las instrucciones tiene un formato rígido definido por el conjunto de instrucciones del sistema. Una instrucción tiene dos partes: **Código de la operación: Es descodificado por la unidad de control **Operandos *Todas las instrucciones son '''imperativas''' *No se pueden utilizar comentarios ==La unidad aritmético-lógica (ALU)== Las ALU son las encargadas de realizar las operaciones aritmético-lógicas que le proporciona la unidad de control. Las operaciones son muy sencillas hasta el punto de que muchos procesadores disponen de un simple sumador/restador y el resto de operaciones se realizan mediante la descomposición en operaciones sencillas de otras más complejas. Las ALU tiene el siguiente esquema: [[file:ALU_symbol.svg|200px|thumb]] La forma de V se utiliza tipicamente para representarla donde los extremos de la v son los operandos y el vértice el resultado. La comunicación entre la CPU y la ALU se realiza mediante los siguientes registros: *'''A y B''': Son los registros que contienen los operandos. Tambien se llaman '''Registros de entrada'''. *'''R''': Es el registro que contiene el resultado. Los registros A, B y R normalmente se almacenan en un banco de registros. *'''F''': Es el registro que indica que '''tipo de operación''' se realiza sobre los operandos A y B. *'''D''': Es el '''registro de estado del resultado'''. También conocido como '''registro de estado''' (Status Register). Se utiliza para indicar estados especiales como '''bits de arrastre''', '''divisiones por cero''' o ''''desbordamientos/overflows'''. Las operaciones que suelen hacer las ALU son: *Operaciones aritméticas con números enteros (suma, resta y a veces multiplicaciones y divisiones). *Operaciones lógicas binarías *Operaciones de desplazamiento de bits: Desplazamientos a derecha o izquierda de bits (equivaliendo a multiplicar o a dividir por 2). Estas operaciones se suelen realizar mediante circuitos electrónicos que pueden ser circuitos combinacionales o circuitos secuenciales según utilizan registros de memoria o no. Los operandos normalmente se representan en complemento a dos (Ver complemento a 2 en el tema 10) y las operaciones son con números enteros. Las operaciones se pueden clasificar en: *'''Operacions unarias''': Un solo operando (operación de negación) *'''Operacions binaria''': 2 operadores (suma, resta, división, etc.) Las operaciones pueden ser: *'''Operador paralelo''': La operación se realiza con todos los bits del operador a la vez. *'''Operador serie''': la operación se realiza dígito a dígito. o45akjm2n4sk90dwax02vyh37phmat3 436397 436396 2026-10-13T20:11:22Z ~2026-55042-54 128009 /* Lenguaje máquina */ 436397 wikitext text/x-wiki =Estructura básica de un ordenador= Históricamente los ordenadores eran computadores con un programa fijado pero en la actualidad los ordenadores siguen un modelo basado en programas de almacenamiento, de esta manera se tienen: *'''Computadora de programa fijado (Fixed Program Computer)''': Actualmente algunas máquinas todavía utilizan este módelo, las calculadoras, por ejemplo. *'''Computadora de programa almacenado (Stored Program Computer)''': En estas máquinas existe una memoria que sirve para almacenar programas por lo que son reprogramables sin necesidad de rediseñarlas por completo. Éste último modelo también es conocido como [[:m:w:en:Von_Neumann_architecture|Arquitectura Von Neumann]]. '''Arquitectura John Von Neummann''' Fue diseñada en 1946 por el matemático [[:m:w:en:John_von_Neumann|John Von Neumann]] y es un modelo que utiliza la memoria tanto para almacenar instrucciones como datos. Estos computadores implementan [[:m:w:es:M%C3%A1quina_de_Turing#M.C3.A1quina_Universal_de_Turing|la máquina Universal de Turing]] ya que permiten crear aplicaciones capaces de interpretar otras aplicaciones. La idea de esta arquitectura es la de conectar de forma fija y coordinar el funcionamiento de los componentes de un ordenador con una unidad central de proceso (CPU). La CPU dispone de un conjunto de instrucciones que permiten manejar la máquina y además estas instrucciones se pueden almacenar en la memoria del sistema como el resto de datos. ==Componentes de un ordenador. Modelo de máquina sencilla de Von Neumman== Una máquina [[:m:w:en:Von_Neumann_architecture|Von Neumman]] está compuesta por cuatro partes: ===Unidad de Control (UC)=== Es la unidad que se encarga de leer las instrucciones secuencialmente. Las instrucciones las obtiene de la memoria principal. ===Unidad Aritmético-lógica (ALU)=== Realiza las operaciones matemáticas fundamentales (suma, resta, AND, OR, etc.). Es conocida como ALU (UAL en español). ===Memoria principal=== Es donde se almacenan los datos mediante celdas identificadas por direcciones ===Unidad de control de entrada/salida=== Es la encargada de conectar la máquina con el exterior. Permite la transferencia de datos desde la máquina a los periféricos. [[Imatge:Adcfig2.gif]] La comunicación entre los diferentes componentes se realiza usando '''buses o canales'''. Los buses son los caminos físicos a través de los cuales las instrucciones y los datos circulan entre los diferentes componentes de la máquina. ==Funcionamiento== Veamos un ejemplo de como se ejecuta un programa en un ordenador actual. El siguiente gráfico nos muestra como es el proceso: Supongamos un programa al que llamaramos '''holaMundo.sh''' y que una vez ejecutado nos saludara con la frase "¡Hola mundo!". Según el gráfico el primer paso del proceso de ejecución del programa es utilizar un dispositivo de entrada. En este caso utilizaremos el teclado y mediante una terminal de linea de comandos ejecutaremos: $ holaMundo.sh Conviene destacar que no siempre será un dispositivo de entrada el que iniciara el programa ya que los programas pueden ser iniciados por otros programas. Con esta orden le indicamos a la computadora que debe ejecutar un programa llamado holaMundo.sh y que deberá buscar las instrucciones de las que se compone el programa en la memoria. La CPU irá leyendo las instrucciones de la memoria de forma secuencial y una vez que tenga un resultado lo mostrará por algún dispositivo de salida. (por ejemplo, la pantalla). '''NOTA''': Se han obviado los pasos de interpretación del lenguaje de programación y otros aspectos. =La unidad central de proceso (CPU)= Para mas detalle consultar el tema 3: Los computadores actuales tienen un componente que se llama la unidad central de procesamiento.(conocida por el término inglés [[:m:w:en:CPU|Central processing unit]] o CPU). La CPU esta compuesta por los siguientes componentes: La unidad aritmético-lógica (ALU): Es la encargada de realizar las operaciones básicas La Unidad de Control: Es la encargada de coordinar y gestionar el resto de elementos de la CPU. Registros: Son elementos de memoria que contienen información relativa al programa que se está ejecutando. La forma, diseño e implementación de las CPU ha cambiado mucho durante la evolución de la informática pero su funcionamiento continúa siendo el mismo desde su definición funcional en la arquitectura de Von Neumman. ==Lenguaje máquina== Las CPU ejecutan programas que leen de la memoria principal. Pero estos programas no son escritos en lenguajes de alto nivel sino que son interpretados por la máquina utilizando un lenguaje muy restringido y de bajo nivel llamado lenguaje máquina. Este lenguaje es muy sencillo y se limita a un conjunto de instrucciones muy limitado que comunmente se llama juego de instrucciones del computador. Cada CPU tiene su conjunto de instrucciones propio. Las CPU pueden ser de propósito general o estar diseñadas para un proposito concreto y por tanto tener un conjunto de instrucciones muy diferentes. De cualquier manera suelen cumplir los siguientes requisitos: *Realizan una única y sencilla operación. *Utilizan un número fijo de operandos y con una representación numérica fija. *La codificación de las instrucciones es lo más simple y sitemática posible de tal forma que el interprete del lenguaje máquina pueda ser también sencillo. *Las instrucciones son independientes y autocontenidas. Contienen todos los datos necesarios para ejecutarse y no dependen de otras instrucciones. Y las podemos clasificar en:: *'''Instrucciones de transferencia de datos''': Leen o escriben datos de una memoria. *'''Instrucciones aritméticas''': Realizan operaciones aritméticas utilizando la ALU. *'''Instrucciones lógicas''': Realizan operaciones lógicas utilizando la ALU. *'''Instrucciones de desplazamiento''': Realizan operaciones de desplazamiento. *'''Instrucciones de transferencia de control''': Actualizan el registro PC de la CPU administrando el flujo de ejecución del programa (llamadas a subrutinas). *'''Instrucciones I/O''': Administran los comandos de entrada salida. Las instrucciones tienen un '''formato rígido''' definido por el conjunto de instrucciones del sistema. Una instrucción tiene dos partes: *'''Código de la operación''': es descodificado por la unidad de control *'''Operandos''': Los datos sobre los cuales se ejecutará una operación. Hay operaciones que no tienen datos, operaciones unarias y operaciones binarias. *Todas las instrucciones son '''imperativas''' La arquitectura mas utilizada en PC de sobremesa es la [[:m:w:en:X86-16|x86]] de Intel y compatibles (com por ejemplo las CPU de AMD). Esta arquitectura apareció en 1978 con intel y ha ido evolucionando Intel 286, Intel 386, Pentium,Pentium 2, Pentium 3, Itanium hasta los actuales Pentium 4. Se han desarrollado y se continúan desarollando extensiones del lenguaje máquina. Un par de ejemplos son: *[[:m:w:en:MMX_%28instruction_set%29|MMX d'Intel]] *[[:m:w:en:3DNow%21|3DNow]] Conviene destacar que nosrmalmente es difícil encontrar software y sistemas operativos que soporten las nuevas instrucciones. '''Propiedades''': *Las instrucciones son independientes y autocontenidas. Contienen todos los datos necesarios para ejecutarse y no dependen de otras instrucciones. *Instrucciones sistemáticas (facilita su decodificación). El decodificador ha de ser sencillo (no puede ser tan complicado como un compilador de un lenguaje de alto nivel). *Es el único lenguaje que entiende la máquina. *Utiliza código binario, octal o hexadecimal. *Fueron los '''primeros lenguajes de programación''' *Cada máquina (CPU) tiene su propio lenguaje y conjunto de instrucciones. Poco portable. Es necesario conocer el conjunto de instrucciones de la máquina para poder programarla.Diferentes arquitecturas. *Actualmente se utilizan mucho más los lenguajes de alto nivel(mas faciles de aprender y utilizar por que son más cercanos al lenguaje natural) **La unidad de controlo utilizando el descodificador es la encargada de convertir las instrucciones en código interpretable por la CPU. *Los datos utilizados son referenciados por su posición en la memória. Es responsabilidad del programador controlar el mapa de memoria y la asignación de memoria a cada dato. *Las instrucciones tiene un formato rígido definido por el conjunto de instrucciones del sistema. Una instrucción tiene dos partes: **Código de la operación: Es descodificado por la unidad de control **Operandos *Todas las instrucciones son '''imperativas''' *No se pueden utilizar comentarios ==La unidad aritmético-lógica (ALU)== Las ALU son las encargadas de realizar las operaciones aritmético-lógicas que le proporciona la unidad de control. Las operaciones son muy sencillas hasta el punto de que muchos procesadores disponen de un simple sumador/restador y el resto de operaciones se realizan mediante la descomposición en operaciones sencillas de otras más complejas. Las ALU tiene el siguiente esquema: [[file:ALU_symbol.svg|200px|thumb]] La forma de V se utiliza tipicamente para representarla donde los extremos de la v son los operandos y el vértice el resultado. La comunicación entre la CPU y la ALU se realiza mediante los siguientes registros: *'''A y B''': Son los registros que contienen los operandos. Tambien se llaman '''Registros de entrada'''. *'''R''': Es el registro que contiene el resultado. Los registros A, B y R normalmente se almacenan en un banco de registros. *'''F''': Es el registro que indica que '''tipo de operación''' se realiza sobre los operandos A y B. *'''D''': Es el '''registro de estado del resultado'''. También conocido como '''registro de estado''' (Status Register). Se utiliza para indicar estados especiales como '''bits de arrastre''', '''divisiones por cero''' o ''''desbordamientos/overflows'''. Las operaciones que suelen hacer las ALU son: *Operaciones aritméticas con números enteros (suma, resta y a veces multiplicaciones y divisiones). *Operaciones lógicas binarías *Operaciones de desplazamiento de bits: Desplazamientos a derecha o izquierda de bits (equivaliendo a multiplicar o a dividir por 2). Estas operaciones se suelen realizar mediante circuitos electrónicos que pueden ser circuitos combinacionales o circuitos secuenciales según utilizan registros de memoria o no. Los operandos normalmente se representan en complemento a dos (Ver complemento a 2 en el tema 10) y las operaciones son con números enteros. Las operaciones se pueden clasificar en: *'''Operacions unarias''': Un solo operando (operación de negación) *'''Operacions binaria''': 2 operadores (suma, resta, división, etc.) Las operaciones pueden ser: *'''Operador paralelo''': La operación se realiza con todos los bits del operador a la vez. *'''Operador serie''': la operación se realiza dígito a dígito. qzhq8qqhek284nfmzzcs3m1fbsftuz2 436398 436397 2026-10-13T20:13:49Z ~2026-55042-54 128009 /* Lenguaje máquina */ 436398 wikitext text/x-wiki =Estructura básica de un ordenador= Históricamente los ordenadores eran computadores con un programa fijado pero en la actualidad los ordenadores siguen un modelo basado en programas de almacenamiento, de esta manera se tienen: *'''Computadora de programa fijado (Fixed Program Computer)''': Actualmente algunas máquinas todavía utilizan este módelo, las calculadoras, por ejemplo. *'''Computadora de programa almacenado (Stored Program Computer)''': En estas máquinas existe una memoria que sirve para almacenar programas por lo que son reprogramables sin necesidad de rediseñarlas por completo. Éste último modelo también es conocido como [[:m:w:en:Von_Neumann_architecture|Arquitectura Von Neumann]]. '''Arquitectura John Von Neummann''' Fue diseñada en 1946 por el matemático [[:m:w:en:John_von_Neumann|John Von Neumann]] y es un modelo que utiliza la memoria tanto para almacenar instrucciones como datos. Estos computadores implementan [[:m:w:es:M%C3%A1quina_de_Turing#M.C3.A1quina_Universal_de_Turing|la máquina Universal de Turing]] ya que permiten crear aplicaciones capaces de interpretar otras aplicaciones. La idea de esta arquitectura es la de conectar de forma fija y coordinar el funcionamiento de los componentes de un ordenador con una unidad central de proceso (CPU). La CPU dispone de un conjunto de instrucciones que permiten manejar la máquina y además estas instrucciones se pueden almacenar en la memoria del sistema como el resto de datos. ==Componentes de un ordenador. Modelo de máquina sencilla de Von Neumman== Una máquina [[:m:w:en:Von_Neumann_architecture|Von Neumman]] está compuesta por cuatro partes: ===Unidad de Control (UC)=== Es la unidad que se encarga de leer las instrucciones secuencialmente. Las instrucciones las obtiene de la memoria principal. ===Unidad Aritmético-lógica (ALU)=== Realiza las operaciones matemáticas fundamentales (suma, resta, AND, OR, etc.). Es conocida como ALU (UAL en español). ===Memoria principal=== Es donde se almacenan los datos mediante celdas identificadas por direcciones ===Unidad de control de entrada/salida=== Es la encargada de conectar la máquina con el exterior. Permite la transferencia de datos desde la máquina a los periféricos. [[Imatge:Adcfig2.gif]] La comunicación entre los diferentes componentes se realiza usando '''buses o canales'''. Los buses son los caminos físicos a través de los cuales las instrucciones y los datos circulan entre los diferentes componentes de la máquina. ==Funcionamiento== Veamos un ejemplo de como se ejecuta un programa en un ordenador actual. El siguiente gráfico nos muestra como es el proceso: Supongamos un programa al que llamaramos '''holaMundo.sh''' y que una vez ejecutado nos saludara con la frase "¡Hola mundo!". Según el gráfico el primer paso del proceso de ejecución del programa es utilizar un dispositivo de entrada. En este caso utilizaremos el teclado y mediante una terminal de linea de comandos ejecutaremos: $ holaMundo.sh Conviene destacar que no siempre será un dispositivo de entrada el que iniciara el programa ya que los programas pueden ser iniciados por otros programas. Con esta orden le indicamos a la computadora que debe ejecutar un programa llamado holaMundo.sh y que deberá buscar las instrucciones de las que se compone el programa en la memoria. La CPU irá leyendo las instrucciones de la memoria de forma secuencial y una vez que tenga un resultado lo mostrará por algún dispositivo de salida. (por ejemplo, la pantalla). '''NOTA''': Se han obviado los pasos de interpretación del lenguaje de programación y otros aspectos. =La unidad central de proceso (CPU)= Para mas detalle consultar el tema 3: Los computadores actuales tienen un componente que se llama la unidad central de procesamiento.(conocida por el término inglés [[:m:w:en:CPU|Central processing unit]] o CPU). La CPU esta compuesta por los siguientes componentes: La unidad aritmético-lógica (ALU): Es la encargada de realizar las operaciones básicas La Unidad de Control: Es la encargada de coordinar y gestionar el resto de elementos de la CPU. Registros: Son elementos de memoria que contienen información relativa al programa que se está ejecutando. La forma, diseño e implementación de las CPU ha cambiado mucho durante la evolución de la informática pero su funcionamiento continúa siendo el mismo desde su definición funcional en la arquitectura de Von Neumman. ==Lenguaje máquina== Las CPU ejecutan programas que leen de la memoria principal. Pero estos programas no son escritos en lenguajes de alto nivel sino que son interpretados por la máquina utilizando un lenguaje muy restringido y de bajo nivel llamado lenguaje máquina. Este lenguaje es muy sencillo y se limita a un conjunto de instrucciones muy limitado que comunmente se llama juego de instrucciones del computador. Cada CPU tiene su conjunto de instrucciones propio. Las CPU pueden ser de propósito general o estar diseñadas para un proposito concreto y por tanto tener un conjunto de instrucciones muy diferentes. De cualquier manera suelen cumplir los siguientes requisitos: *Realizan una única y sencilla operación. *Utilizan un número fijo de operandos y con una representación numérica fija. *La codificación de las instrucciones es lo más simple y sitemática posible de tal forma que el interprete del lenguaje máquina pueda ser también sencillo. *Las instrucciones son independientes y autocontenidas. Contienen todos los datos necesarios para ejecutarse y no dependen de otras instrucciones. Y las podemos clasificar en:: *'''Instrucciones de transferencia de datos''': Leen o escriben datos de una memoria. *'''Instrucciones aritméticas''': Realizan operaciones aritméticas utilizando la ALU. *'''Instrucciones lógicas''': Realizan operaciones lógicas utilizando la ALU. *'''Instrucciones de desplazamiento''': Realizan operaciones de desplazamiento. *'''Instrucciones de transferencia de control''': Actualizan el registro PC de la CPU administrando el flujo de ejecución del programa (llamadas a subrutinas). *'''Instrucciones I/O''': Administran los comandos de entrada salida. Las instrucciones tienen un '''formato rígido''' definido por el conjunto de instrucciones del sistema. Una instrucción tiene dos partes: *'''Código de la operación''': es descodificado por la unidad de control *'''Operandos''': Los datos sobre los cuales se ejecutará una operación. Hay operaciones que no tienen datos, operaciones unarias y operaciones binarias. *Todas las instrucciones son '''imperativas''' La arquitectura mas utilizada en PC de sobremesa es la [[:m:w:en:X86-16|x86]] de Intel y compatibles (com por ejemplo las CPU de AMD). Esta arquitectura apareció en 1978 con intel y ha ido evolucionando Intel 286, Intel 386, Pentium,Pentium 2, Pentium 3, Itanium hasta los actuales Pentium 4. Se han desarrollado y se continúan desarollando extensiones del lenguaje máquina. Un par de ejemplos son: *[[:m:w:en:MMX_%28instruction_set%29|MMX d'Intel]] *[[:m:w:en:3DNow%21|3DNow]] Conviene destacar que normalmente es difícil encontrar software y sistemas operativos que soporten las nuevas instrucciones. '''Propiedades''': *Las instrucciones son independientes y autocontenidas. Contienen todos los datos necesarios para ejecutarse y no dependen de otras instrucciones. *Instrucciones sistemáticas (facilita su decodificación). El decodificador ha de ser sencillo (no puede ser tan complicado como un compilador de un lenguaje de alto nivel). *Es el único lenguaje que entiende la máquina. *Utiliza código binario, octal o hexadecimal. *Fueron los '''primeros lenguajes de programación''' *Cada máquina (CPU) tiene su propio lenguaje y conjunto de instrucciones. Poco portable. Es necesario conocer el conjunto de instrucciones de la máquina para poder programarla.Diferentes arquitecturas. *Actualmente se utilizan mucho más los lenguajes de alto nivel(mas faciles de aprender y utilizar por que son más cercanos al lenguaje natural) **La unidad de controlo utilizando el descodificador es la encargada de convertir las instrucciones en código interpretable por la CPU. *Los datos utilizados son referenciados por su posición en la memória. Es responsabilidad del programador controlar el mapa de memoria y la asignación de memoria a cada dato. *Las instrucciones tiene un formato rígido definido por el conjunto de instrucciones del sistema. Una instrucción tiene dos partes: **Código de la operación: Es descodificado por la unidad de control **Operandos *Todas las instrucciones son '''imperativas''' *No se pueden utilizar comentarios ==La unidad aritmético-lógica (ALU)== Las ALU son las encargadas de realizar las operaciones aritmético-lógicas que le proporciona la unidad de control. Las operaciones son muy sencillas hasta el punto de que muchos procesadores disponen de un simple sumador/restador y el resto de operaciones se realizan mediante la descomposición en operaciones sencillas de otras más complejas. Las ALU tiene el siguiente esquema: [[file:ALU_symbol.svg|200px|thumb]] La forma de V se utiliza tipicamente para representarla donde los extremos de la v son los operandos y el vértice el resultado. La comunicación entre la CPU y la ALU se realiza mediante los siguientes registros: *'''A y B''': Son los registros que contienen los operandos. Tambien se llaman '''Registros de entrada'''. *'''R''': Es el registro que contiene el resultado. Los registros A, B y R normalmente se almacenan en un banco de registros. *'''F''': Es el registro que indica que '''tipo de operación''' se realiza sobre los operandos A y B. *'''D''': Es el '''registro de estado del resultado'''. También conocido como '''registro de estado''' (Status Register). Se utiliza para indicar estados especiales como '''bits de arrastre''', '''divisiones por cero''' o ''''desbordamientos/overflows'''. Las operaciones que suelen hacer las ALU son: *Operaciones aritméticas con números enteros (suma, resta y a veces multiplicaciones y divisiones). *Operaciones lógicas binarías *Operaciones de desplazamiento de bits: Desplazamientos a derecha o izquierda de bits (equivaliendo a multiplicar o a dividir por 2). Estas operaciones se suelen realizar mediante circuitos electrónicos que pueden ser circuitos combinacionales o circuitos secuenciales según utilizan registros de memoria o no. Los operandos normalmente se representan en complemento a dos (Ver complemento a 2 en el tema 10) y las operaciones son con números enteros. Las operaciones se pueden clasificar en: *'''Operacions unarias''': Un solo operando (operación de negación) *'''Operacions binaria''': 2 operadores (suma, resta, división, etc.) Las operaciones pueden ser: *'''Operador paralelo''': La operación se realiza con todos los bits del operador a la vez. *'''Operador serie''': la operación se realiza dígito a dígito. aav0xldn2279dhhhvnj7gqfqaj69e7p Mantenimiento y Montaje de Equipos Informáticos/Tema 2/Conectores 0 38624 436474 427612 2026-10-14T07:25:21Z ~2026-55384-72 128016 /* USB: */ 436474 wikitext text/x-wiki {{#ifeq:{{SUBPAGENAME}} |Texto completo || {{Mantenimiento y Montaje de Equipos Informáticos|tono=nube}} }} ==Conectores Eléctricos== Se verán en el tema 5 ==Conectores informáticos== Son conectores, también llamados puertos, que '''transmiten información''' entre el dispositivo y el computador. Pueden contener cables para la alimentación eléctrica. ===Conectores externos=== [[Archivo:conectores traseros.png|left|thumb|conectores externos, situados en la parte trasera de la caja.]] Son los conectores que comunican al computador con diferentes periféricos externos al PC desde el monitor a una red LAN o impresora. Los conectores están situados en la parte trasera del computador y soldados a la placa base del computador. Son de alta fiabilidad. Los más comunes son: ====RED==== Par trenzado o '''[//es.wikipedia.org/wiki/RJ45 RJ-45]'''es una interfaz física comúnmente usada para conectar redes de cableado estructurado. ====Audio==== * '''Naranja''': Conector de salida de los altavoces centrales y del amplificador de graves. Configuración de audio de 5.1/7.1 canales. * '''Negro''': Conector de salida de los altavoces traseros. Configuración de audio de 4/5.1/7.1 canales. * '''Gris''': Conector de salida de los altavoces laterales. Configuración de audio de 7.1 canales. * '''Verde''':Conector de salida de línea. Es el conector de salida de '''línea predeterminado'''. Utiliza este conector de audio para unos auriculares, etc. * '''Rosa''': Conector de entrada de micrófono. Es el conector de entrada de '''micrófono predeterminado'''. * '''Azul''': Conector de entrada de línea. Utiliza este conector de audio para dispositivos mp3, otro micrófono, etc. ====USB:==== El [//es.wikipedia.org/wiki/USB Universal Serial Bus ('''USB''')] (bus universal en serie BUS) es un estándar industrial desarrollado en los años 1990 que define los cables, conectores y protocolos usados en un bus para conectar, comunicar y proveer de alimentación eléctrica entre computadores y periféricos y dispositivos electrónicos. Los dispositivos USB se clasifican según su velocidad de transferencia de datos y prestaciones en varios tipos: FIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII {| class=wikitable style="text-align:center; margin-left:auto; margin-right:auto;" |- ! colspan="2" scope="col" | Connectors ! scope="col" | USB 1.0<br>1996 ! scope="col" | USB 1.1<br>1998 ! scope="col" | USB 2.0<br>2001 ! scope="col" | USB 2.0<br>Revised ! scope="col" | [//es.wikipedia.org/wiki/USB_3.0 USB 3.0]<br>2011 ! scope="col" | [//es.wikipedia.org/wiki/USB_3.0#USB_3.1_y_USB_3.2 USB 3.1]<br>2014 ! scope="col" | [//es.wikipedia.org/wiki/USB_3.0#USB_3.1_y_USB_3.2 USB 3.2]<br>2017 ! scope="col" | [//es.wikipedia.org/wiki/Universal_Serial_Bus#USB_4 USB4]<br>2019 |- ! colspan="2" scope="row" | Data rate | colspan="2" | 1.5Mbit/s<br>(''Low Speed'')<br>12Mbit/s<br>(''Full Speed'') | colspan="2" | 1.5Mbit/s<br>(''Low Speed'')<br>12Mbit/s<br>(''Full Speed'')<br>480Mbit/s<br>(''High Speed'') | 5 Gbit/s<br>(''SuperSpeed'') | 10 Gbit/s<br>(''SuperSpeed+'') | 20 Gbit/s<br>(''SuperSpeed+'') | 40 Gbit/s<br>(''SuperSpeed+, Thunderbolt 3 and 4'') |- ! rowspan="3" scope="row" | Standard ! A | colspan="4" | Type A<br>[[File:USB Type-A receptacle.svg|75px]] | colspan="2" | Type A<br>[[File:USB 3.0 Type-A receptacle blue.svg|75px]] | colspan="2" | Obsoleto |- ! B | colspan="4" | Type B<br>[[File:USB Type-B receptacle.svg|x60px]] | colspan="2" | Type B<br>[[File:USB 3.0 Type-B receptacle blue.svg|x75px]] | colspan="2" | Obsoleto |- ! C | colspan="2" | No disponible | colspan="7" | [//es.wikipedia.org/wiki/USB-C '''Type C'''] <br>[[File:USB Type-C Receptacle Pinout.svg|219x219px]] |- ! rowspan="3" scope="row" | Mini ! A | rowspan="2" | No disponible | colspan="3" | Mini A<br>[[File:USB Mini-A receptacle.svg|75x75px]] | rowspan="3" colspan="5" | Obsoleto |- ! B | colspan="3" | Mini B<br>[[File:USB Mini-B receptacle.svg|67x67px]] |- ! AB | colspan="3" | No disponible | Mini AB<br>[[File:USB_Mini-AB_receptacle.svg|60px]] |- style="height: 6em;" ! rowspan="3" scope="row" | Micro ! A | colspan="8" | No disponible |- ! B | colspan="3" rowspan="2" | No disponible | Micro B<br>[[File:USB Micro-B receptacle.svg|alt=|frameless|75x75px]] | colspan="2" | Micro B<br>[[File:USB 3.0 Micro-B receptacle.svg|frameless|117x117px]] | colspan="2" | Obsoleto |- ! AB | Micro AB<br>[[File:USB Micro-AB receptacle.svg|75x75px]] | colspan="4" | Obsoleto |- ! colspan="2" scope="row" | Conectores ! scope="col" | USB 1.0<br>1996 ! scope="col" | USB 1.1<br>1998 ! scope="col" | USB 2.0<br>2001 ! scope="col" | USB 2.0<br>Revised ! scope="col" | USB 3.0<br>2011 ! scope="col" | USB 3.1<br>2014 ! scope="col" | USB 3.2<br>2017 ! scope="col" | USB4<br>2019 |} ====[//es.wikipedia.org/wiki/Video_Graphics_Array VGA]==== O D-sub 15 de tres hileras de 15 pines se encuentra en la mayoría de las tarjetas gráficas, monitores de computadoras, y otros dispositivos de vídeo. Está cayendo en desuso por ser analógico y soportar menor resolución que el DVI-D. No se debe confundir con puerto Serie (dos filas de pines). [[Archivo:DVI Connector Types.svg|thumb|left|Conector DVI (vista del enchufe macho).]] ====[//es.wikipedia.org/wiki/Digital_Visual_Interface DVI (Digital Visual Interface)]==== Es un conector de vídeo diseñado para obtener la máxima calidad de visualización posible en pantallas digitales, tales como los monitores LCD de pantalla plana y los proyectores digitales. posee pins para transmitir las señales digitales nativas de DVI. En los sistemas de doble enlace, se proporcionan pins adicionales para la segunda señal. También puede tener pins para transmitir las señales analógicas del estándar VGA. Esta característica se incluyó para dar un carácter universal al conector. Los conectores que la implementan admiten monitores de ambos tipos (analógico o digital). Se clasifican en tres tipos en función de qué señales admiten: {| class="wikitable" ! tipo conector || Señal |- | DVI-D | sólo digital |- | DVI-A | sólo analógica |- | DVI-I | digital y analógica |} ====[//es.wikipedia.org/wiki/HDMI High-Definition Multimedia Interface o '''HDMI''']==== [[File:HDMI Connector Pinout.svg|thumb|Conector HDMI]] Es una interfaz multimedia de alta definición, es una norma de audio y vídeo digital cifrado sin compresión apoyada por la industria para que sea el sustituto del euroconector. HDMI provee una interfaz entre cualquier fuente de audio y vídeo digital como podría ser un sintonizador TDT, un reproductor de Blu-ray, un Tablet PC, un computador o un receptor A/V, y monitor de audio/vídeo digital compatible, como un televisor digital (DTV). Permite el uso de vídeo computarizado, mejorado o de alta definición, así como audio digital multicanal en un único cable. ====[//es.wikipedia.org/wiki/DisplayPort '''DisplayPort''']==== [[File:DisplayPort Connector.svg|thumb|Conector DisplayPort]] [[File:DisplayPort-rid.jpg|thumb|DisplayPort]] DisplayPort es una interfaz digital estándar de dispositivos desarrollado por la Asociación de Estándares Electrónicos de Vídeo (VESA). Libre de licencias y cánones, define un tipo de interconexión destinado a la transmisión de Vídeo entre una computadora y su monitor con una resolución máxima 4k x 2K (4096 x 2160). '''Opcionalmente permite la transmisión de Audio''' para su uso por ejemplo en sistemas de cine en casa con un máximo de 8 canales sin compresión 192 kHz, 24-bit, '''y la transmisión de Datos''', por ejemplo USB. ====[https://es.wikipedia.org/wiki/TOSLINK TOSLINK]==== Es una conexión de fibra óptica, que se basa en la utilización de señales ópticas en lugar de señales eléctricas. Se utiliza generalmente para la interconexión de equipos de audio, aunque admite diferentes formatos, tanto físicos como de datos. Ventajas: El ruido electromagnético no afecta a la transmisión, tampoco radia ruido electromagnético, fácil de montar y conectar. ====[[w:Thunderbolt_(bus)|Thunderbolt]]==== Es el nombre comercial utilizado por Intel para designar a un tipo de interfaz de conexión de alta velocidad. En su versión Thunderbolt 5 publicada en 2024.<ref>[https://newsroom.intel.com/client-computing/intel-introduces-thunderbolt-5-standard ''Intel Introduces Thunderbolt 5 Connectivity Standard''] - 12 de septiembre de 2023 (''en inglés'').</ref> ofrece un ancho de banda de 80Gbit/s. Aunque nació bajo el nombre clave ''Ligit Peak'' planteando el uso de fibra óptica, se consolidó en el mercado masivo usando cables de cobre y adoptando el conector USB-C. Se creó con el propósito de reemplazar a las antiguas versiones de USB, FireWire y HDMI. En definitiva, si no funciona un periférico con los conectores frontales, se debe comprobar con los traseros. Si no funcionara, se debiera probar otro similar y/o comprobar en el BIOS que estos conectores están habilitados. ===Conectores internos=== [[Imagen:intel-pcb.png|left|thumb|conectores internos, vista de una placa base Intel.]] Son conectores situados en el interior del computador, suelen estar en la placa base y en algún dispositivo (discos duros, reproductores DVD, lectores de tarjetas,....). Sirven para la transferencia de información entre la placa base y el dispositivo. También existen otros conectores para el conexionado de la placa base con los conectores frontales de la caja (audio, usb, interruptores de alimentación y led's). Los conectores de la memoria RAM y del procesador, se verán en sendos apartados. [[File:SATA ports.jpg|thumb|SATA ports]] * '''[//es.wikipedia.org/wiki/Sata Serial ATA o SATA]''' es una interfaz de transferencia de datos entre la placa base y algunos dispositivos de almacenamiento, como puede ser el '''disco duro, lectores''' y regrabadores de CD/DVD/BR, Unidades de Estado Sólido u otros dispositivos de altas prestaciones que están siendo todavía desarrollados. SATA sustituye al '''[//en.wikipedia.org/wiki/Parallel_ATA P-ATA]'''. SATA proporciona '''mayores velocidades''' de transferencia de datos, conexionado '''punto a punto''' y utiliza un cable con una '''longitud máxima de 1 metro'''. : :Se comercializan dispositivos SATA II, a 300 MB/s de velocidades de transmisión, también conocida como Serial ATA-300 y los SATA III con tasas de transferencias de hasta 750 MB/s, son compatibles entre ellos y siempre se aplicará la velocidad menor de transferencia soportada..... [[Archivo:MSATA SSD vs. 2.5" SATA drive.JPG|thumb|left|mSATA SSD vs. disco 2.5" SATA]] * '''mSATA o mini-SATA''' es una interfaz, variante de la interfaz SATA. La interfaz mSATA es muy usada en computadoras portátiles y también en placas base actuales. Se conectan discos SSD. La interfaz mSATA se divide en dos partes: interfaz eléctrica similar al MOLEX sin alimentanción de 12V y la interfaz SATA. Existen cables para convertir una SATA y MOLEX en un mSATA. El conector mSATA es similar en apariencia a una interfaz Mini-Tarjeta PCI Express, pero no es compatible eléctricamente ni en las señales de datos. * [//es.wikipedia.org/wiki/Peripheral_Component_Interconnect PCI] es la versión antigua del PCI Express. Su uso es limitado a unos pocos dispositivos. No se puede utilizar en tarjetas de expansión de vídeo de alta definición o ampliación de USB 3.0 o Giga Ethernet pues su caudal es escaso. [[Archivo:PCI-E & PCI slots on DFI LanParty nF4 SLI-DR 20050531.jpg|right|250px|thumb|Slots PCI Express (de arriba a abajo: x4, x16, x1 y x16), comparado con uno tradicional PCI de 32 bits, tal como se ven en la placa [[w:DFI|DFI]] LanParty nF4 Ultra-D]] * [//es.wikipedia.org/wiki/PCIe '''PCI Express o PCIe''']: cada ranura de expansión lleva uno, dos, cuatro, ocho o dieciséis carriles de datos entre la placa base y las tarjetas conectadas. El número de carriles se escribe con una x de prefijo (x1 para un carril simple y x16 para una tarjeta con dieciséis carriles); x16 de 500MB/s dan un máximo [[w:ancho de banda|ancho de banda]] de 8 GB/s en cada dirección para PCIE 2.x. En el uso más común de x16 para el PCIE 1.1 proporciona un ancho de banda de 4 GB/s (250 MB/s x 16) en cada dirección. En comparación con otros buses, un carril simple es aproximadamente el doble de rápido que el PCI normal; una ranura de cuatro carriles, tiene un ancho de banda comparable a la versión más rápida de PCI-X 1.0, y ocho carriles tienen un ancho de banda comparable a la versión más rápida de AGP. : :Una ranura PCi Express 3.0 tiene 1 GB/s direccional y 2 GB/s bidireccional, por lo que logran en el caso de x16 un máximo teórico de 16 GB/s direccionales y 32 GB/s bidireccion :Se usa para todo tipo de tarjetas de expansión (tarjetas de red, ampliación puertos, tarjetas vídeo,etc...). [[File:M.2 and mSATA SSDs comparison.jpg|thumb|upright=1.4|left|Comparación de un SSD tipo mSATA a la izquierda con uno M.2, 2242 SSD]] * [//es.wikipedia.org/wiki/M.2 '''M.2''']:Utiliza la ranura física PCI Express Mini Card y sus conexiones. Las especificaciones de M.2 son más flexibles, lo que permite diferentes longitudes y anchos de los módulos. El estándar M.2 es más idóneo que el mSATA para las unidades de estado sólido en general. j33pugb595h6ez1dk64fevpvgoj9sk9 Haskell 0 42615 436373 436312 2026-10-13T16:31:23Z ~2026-54787-13 128006 /* Tutoriales que se han incorporado al Wikilibro de Haskell */ 436373 wikitext text/x-wiki __NOTOC__ {{traduciendo|en|Haskell}} [[Archivo:Haskell-Logo.svg|derecha|sinmarco]] {{Versión imprimible}} {{Colección}} {{Versión en PDF}} {{Buscar en libro}} '''Haskell''' es un [[w:Programación founcional|lenguaje de programación puramente funcional]]. Se diferencia de varias formas: * Haskell es un lenguaje de programación funcional ''puro''. Cuando se invoca una función usando los mismos argumentos devolverá el mismo resultado cada vez. * Haskell es ''perezoso''. Las evaluaciones se realizan solo cuando es necesario. * Haskell proporciona un sistema de tipado moderno con características sofisticadas, como clases de tipos y tipos de datos algebraicos generalizados (muy pronto, términos como estos van a sonar bien fuera del lenguaje). * Haskell provee un sistema tipográfico moderno con funciones de última generación, como clases tipográficas y tipos de datos algebraicos generalizados (muy pronto, términos como esos saldrán fácilmente de su boca). Haskell es divertido de usar porque tratar con funciones puras hace que el código sea mucho más fácil de razonar, y el sistema de tipo avanzado ayuda a detectar errores tontos y profundos. El objetivo de este libro es introducir al lector, sea este un estudiante o persona interesada en aprender el lenguaje Haskell, en dos aspectos; en primer lugar, al lenguaje Haskell desde las características más básicas a las avanzadas y en segundo lugar a la programación en general. Se insta que los programadores avanzados sean particularmente pacientes con este proceso. Es muy probable que los lenguajes con los que están familiarizados difieran mucho de Haskell, y los hábitos que provienen de esos lenguajes pueden dificultar el proceso de entendimiento de cómo se maneja la sintaxis; Haskell es simple, pero diferente. Aprender a ver el mundo a través de la perspectiva matemática de un programador funcional es una aventura en un mundo nuevo y desafiante, que trae conocimiento valioso más allá de los límites de cualquier lenguaje. Teniendo en cuenta todos estos aspectos, este libro también pretende ser una herramienta y ayuda a la hora de programar en Haskell. == Descripción general == El libro está dividido en tres secciones: una unidad para principiantes, una unidad avanzada y una sección llamada Haskell en la práctica. La última sección, que cubre casos prácticos de uso, depende únicamente de la unidad para principiantes. Los programadores experimentados pueden leer la descripción general para evaluar rápidamente qué hace que Haskell sea único y diferente de otros lenguajes. {{Información|¡Por favor contribuya! Cualquiera, independientemente de la competencia de Haskell, puede ayudar. Dedique cinco minutos a mejorar un módulo y ahorre horas de tiempo a otra persona. Revisar la [[/Notas para contribuyentes|notas para las contribuyentes]]. Incluso si te confundes con una página, cuéntanoslo en la página de discusión (Por supuesto, cortesmente).}} == Unidad para principiantes == Esta sección le presenta los conceptos básicos de Haskell y algunas bibliotecas de uso común. Al final de esta unidad, debería poder escribir programas simples de Haskell. La mayoría de los capítulos incluyen ejercicios, con soluciones, para su práctica. {|width="100%" cellspacing="0" cellpadding="15" valign="top" style="margin:0.8em 0;border:1px solid MediumAquamarine;background:honeydew" |valign="top" style="border-right: 1px dashed MediumAquamarine"| === Principios básicos de Haskell === *{{Capítulo de Haskell/Principios básicos de Haskell|sep= *}} |valign="top" style="border-right: 1px dashed MediumAquamarine"| === Haskell nivel elemental === *{{Capítulo de Haskell/Haskell elemental|sep= *}} |valign="top" style="border-right: 1px dashed MediumAquamarine"| === Haskell nivel intermedio === *{{Capítulo de Haskell/Haskell intermedio|sep= *}} |valign="top"| === Mónadas === *{{Capítulo de Haskell/Mónadas|sep=*|subsep=&nbsp;-&nbsp;|breaksep= **|firstsep= *}} |} == Unidades avanzadas == Esta sección presenta conceptos de programación funcional más amplios, como diferentes estructuras de datos y teoría de tipos. También cubrirá temas más prácticos como la concurrencia. {|width="100%" cellspacing="0" cellpadding="15" valign="top" style="margin:0.8em 0; border: 1px solid SlateBlue; background:#F7F7FF" |valign="top" style="border-right: 1px dashed SlateBlue"| === Haskell avanzado === *{{Capítulo de Haskell/Haskell avanzado|sep= *}} |valign="top" style="border-right:1px dashed SlateBlue"| === Diversión con tipos === *{{Capítulo de Haskell/Diversión con tipos|sep= *}} |valign="top" style="border-right:1px dashed SlateBlue"| === Teoría ampliada === *{{Capítulo de Haskell/Teoría ampliada|sep= *}} |valign="top"| === Rendimiento de Haskell === *{{Capítulo de Haskell/Rendimiento de Haskell|sep= *}} |} == Haskell en la práctica == Los problemas cotidianos de trabajar con Haskell incluyen elementos como conocer las bibliotecas estándar, crear interfaces gráficas y trabajar con bases de datos. Debería poder saltar directamente a esta sección desde la unidad para principiantes. {|width="100%" cellspacing="0" cellpadding="15" style="margin-top:0.8em;margin-bottom:0.8em;border:1px solid gold;background:cornsilk" |valign="top" style="border-right: 1px dashed gold"| === Bibliotecas de referencia === *{{Capítulo de Haskell/Bibliotecas de referencia|sep=*|subsep=&nbsp;-&nbsp;|breaksep= **|firstsep= *}} |valign="top" style="border-right: 1px dashed gold"| === Prácticas generales === *{{Capítulo de Haskell/Prácticas generales|sep= *}} |valign="top"| === Tareas especializadas === *{{Capítulo de Haskell/Tareas especializadas|sep= *}} |} == Apéndices == * [[/Azúcar sintáctico/]] * [[/Soluciones|Respuestas a ejercicios]] * [[/Historia de Haskell/]] * [[/Implementaciones de Haskell/]] * [[/Autores/|Autores y agradecimientos]] (original en inglés) == Acerca del libro == * [[/Notas para los contribuyentes/]] * [[/Convenciones de estilo/]] * [[/To do/]] * [[:Categoría:Haskell/Plantillas|Plantillas para el wikilibro de Haskell]] * [[/Módulos experimentales/|Módulos experimentales]] * [[/Lista de tópicos/|Lista de tópicos]] == Otros tutoriales de Haskell == * [http://www.haskell.org/haskellwiki/Meta-tutorial Haskell Meta-tutorial] - the tutorial to find other tutorials * [http://www.learnyouahaskell.com/Chapters Learn You a Haskell for Great Good] - Tutorial aimed at beginners who may have experience in imperative programming languages but haven't programmed in a functional language before. Freely available online under a CC-BY-NC-SA license; also released as a conventional book. * [http://book.realworldhaskell.org/ Real World Haskell] - an O'Reilly book, available online at no charge (CC-BY-NC license). Built around case studies of practical applications. == Tutoriales de Haskell que se han incorporado a Wikilibros == Los siguientes tutoriales pueden leerse de forma independiente, pero su contenido ya se ha importado y adaptado a Wikilibros: * [[Write Yourself a Scheme in 48 Hours]] &mdash; An alternate approach to teaching Haskell (and perhaps Scheme), aimed at a more advanced audience (though not necessarily one that knows how to program!) * [[/YAHT|Yet Another Haskell Tutorial]] &mdash; is aimed at beginners and takes a practical approach to things. == Recursos adicionales == * [http://www.haskell.org/haskellwiki/Learning_Haskell Learning Haskell] en haskell.org * [http://dev.stephendiehl.com/hask/ What I Wish I Knew When Learning Haskell] - A wide-ranging collection of concise summaries of many intermediate and advanced Haskell topics. Released in the public domain. {{Estantes|Lenguajes de programación}} 5e85dikn19pf2r0hf5spaepb4a6p9hh Plantilla:Navigate 10 58912 436472 383377 2026-10-13T23:25:14Z MABot 20834 Robot: corrección de redirección doble a [[Plantilla:Navegador compacto]] 436472 wikitext text/x-wiki #REDIRECCIÓN [[Plantilla:Navegador compacto]] qpsrvg8dxt3s8f6qf0b9iabcws2k4s2 Haskell/Variables y funciones 0 64935 436486 436345 2026-10-14T11:46:16Z Luciérnaga de conocimiento 119502 /* Parámetros múltiples */ 436486 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} ''Todos los ejemplos de este capítulo pueden guardarse en un archivo y ejecutarse usando GHC. No incluyas `Prelude>` en ninguno de los ejemplos. Cuando aparezca este comando, significa que el código siguiente se ejecutó en GHCi. Cuando no aparezca el comando, basta con copiar el código completo.'' == Variables == En el capítulo anterior, usamos GHCi como una calculadora, algo que resulta práctico únicamente para cálculos breves. Para realizar cálculos más complejos y escribir un programa en Haskell, generalmente tenemos que seguir y comprobar los resultados intermedios. Podemos hacerlo asignando nombres. A estos nombres los llamamos ''variables''. Cuando se ejecuta un programa, cada variable se sustituye por un ''valor'' al que hace referencia. Por ejemplo, considera lo siguiente: {{Haskell/GHCi|1= Prelude> 3.141592653 * 5^2 78.539816325 }} Este fue el cálculo aproximado del área de un círculo de radio <code>5</code>, de acuerdo con la fórmula <math>A = \pi r^2</math>. Por supuesto, resulta bastante laborioso escribir todos los dígitos de <math>\pi \approx 3,141592653</math>, o incluso recordar los primeros cinco. Por eso, para evitar repeticiones, podemos hacer que la máquina recuerde estos valores una sola vez, de modo que podamos utilizarlos siempre que sea necesario. Esta abstracción nos permite concentrarnos en otras partes más importantes. En este ejemplo, Haskell ya dispone de una variable llamada <code>pi</code> que almacena más de 15 dígitos de <math>\pi</math>, lo que hace que el código sea más claro y, además, nos permite obtener resultados más precisos. {{Haskell/GHCi|1= Prelude> pi 3.141592653589793 Prelude> pi * 5^2 78.53981633974483 }} Observa cómo la variable <code>pi</code> y su valor, <code>3.141592653589793</code>, se sustituyen mutuamente en los cálculos. == Archivos de código en Haskell == Además de realizar cálculos ocasionalmente en GHCi, puedes guardar tu código en un archivo Haskell, que es un archivo de texto con la extensión <tt>.hs</tt>. Se recomienda utilizar un editor de texto diseñado para la programación (consulta el [[w:Editor de texto|artículo de Wikipedia sobre editores de texto]]). La gran mayoría de ellos permiten ''resaltar la sintaxis'', es decir, colorear el código fuente para facilitar su lectura. Vim y Emacs son dos opciones bastante populares entre los programadores. Para mantener todo organizado, crea también un directorio nuevo (una carpeta) en tu computadora para guardar los archivos Haskell que vayas creando al resolver los ejercicios de este libro. Vamos, crea un directorio llamado <tt>HaskellWikibook</tt>, por ejemplo, y dentro de él un archivo llamado <tt>Varfun.hs</tt> que contenga el siguiente código: <syntaxhighlight lang = "haskell"> r = 5.0 </syntaxhighlight> Este programa define la variable <code>r</code> con el valor <code>5.0</code>. Nota: asegúrate de que no haya espacios en blanco al comienzo de las líneas, porque Haskell distingue los espacios de indentación. Después, abre una terminal en el directorio <tt>HaskellWikibook</tt> que creaste, ejecuta GHCi y carga el archivo <tt>Varfun.hs</tt> usando el comando <code>:load</code>: {{Haskell/GHCi|1= Prelude> :load Varfun.hs [1 of 1] Compiling Main ( Varfun.hs, interpreted ) Ok, modules loaded: Main. }} Conviene saber que <code>:load</code> puede abreviarse como <code>:l</code>: <code>:l Varfun.hs</code>. Si GHCi devuelve un error como <code>Could not find module 'Varfun.hs'</code>, significa que probablemente te encuentras en el directorio equivocado. Prueba el comando <code>:cd</code> para cambiar el directorio actual desde GHCi (por ejemplo, <code>:cd HaskellWikibook</code>). Después de cargar el archivo en la sesión de GHCi, verás que la línea de comandos cambia de "Prelude" a "*Main". Ahora puedes utilizar la variable <code>r</code> que definiste en el archivo. {{Haskell/GHCi|1= *Main> r 5.0 *Main> pi * r^2 78.53981633974483 }} Acabamos de calcular el área de un círculo de radio 5,0 utilizando la fórmula <math>\pi r^2</math>. Lo que hicimos fue utilizar <code>r</code>, que definimos en el archivo <tt>Varfun.hs</tt>, y <code>pi</code>, que ya está definida en otro lugar. Ahora vamos a facilitar el acceso a este valor definiendo también un nombre para él. Abre nuevamente el archivo y cambia su contenido por lo siguiente: <syntaxhighlight lang = "haskell"> r = 5.0 area = pi * r ^ 2 </syntaxhighlight> Guárdalo y vuelve a cargarlo en GHCi mediante <code>:reload</code>, o simplemente <code>:r</code>: {{Haskell/GHCi|1= *Main> :reload Compiling Main ( Varfun.hs, interpreted ) Ok, modules loaded: Main. *Main> }} Ahora tenemos dos variables: <code>r</code> y <code>area</code>. {{Haskell/GHCi|1= *Main> area 78.53981633974483 *Main> area / r 15.707963267948966 }} {{nota|La palabra <code>let</code> es la que deberíamos utilizar para definir nuevas variables directamente en GHCi, sin necesidad de editar un archivo de código. Aunque hacerlo de esta manera puede resultar práctico, no siempre es lo más conveniente para tareas complejas. En esos casos, es preferible utilizar archivos.}} == Comentarios == Además del propio código, los archivos de código fuente pueden contener textos conocidos como ''comentarios'', que el compilador ignora. En Haskell existen dos tipos de comentarios. El primero comienza con <code>--</code> y se extiende hasta el final de la línea: <syntaxhighlight lang = "haskell"> x = 5 -- x es 5. y = 6 -- y es 6. -- z = 7 -- z no está definida. </syntaxhighlight> En este caso, <code>x</code> e <code>y</code> están efectivamente definidas en el código, pero <code>z</code> no lo está. El segundo tipo de comentario está delimitado por <code>{- ... -}</code> y puede abarcar varias líneas: <syntaxhighlight lang = "haskell"> answer = 2 * {- bloque de comentario de varias líneas y... -} 3 {- comentario en medio de la línea -} * 7 </syntaxhighlight> Los comentarios se utilizan para explicar partes de un programa o realizar anotaciones sobre cualquier aspecto del contexto. Es importante señalar que abusar de los comentarios puede, en realidad, dificultar la lectura del propio código. Además, los comentarios desactualizados también pueden generar confusiones. == Variables en lenguajes imperativos == Los lectores familiarizados con los lenguajes de programación imperativos notarán que las variables en Haskell se comportan de manera muy diferente a las variables en C, por ejemplo. Si no tienes experiencia previa en programación, puedes saltarte esta sección, aunque puede ayudarte a comprender las situaciones en las que se compara el comportamiento de Haskell con el de otros lenguajes. Muchos libros sobre Haskell lo hacen, por ejemplo. La ''programación imperativa'' trata las variables como posiciones en la memoria de la computadora. Este enfoque está relacionado con el funcionamiento básico de una computadora. Los programas imperativos indican explícitamente a la computadora lo que debe hacer. Los lenguajes de alto nivel, aunque pueden estar bastante alejados de estos conceptos, todavía conservan parte de ellos en el sentido de que siguen utilizando este patrón de programación «paso a paso». En contraste, la ''programación funcional'' ofrece una manera diferente de pensar, utilizando términos matemáticos y dejando de lado las instrucciones directas a la computadora, que solo son traducidas por el compilador a algo que la máquina pueda interpretar y procesar. Veamos un ejemplo. El siguiente código no funcionaría en Haskell: <syntaxhighlight lang = "haskell"> r = 5 r = 2 </syntaxhighlight> Un programador de lenguajes imperativos interpretaría esto como: primero, definir <code>r = 5</code> y después redefinirla como <code>r = 2</code>. En Haskell, sin embargo, el compilador respondería con un error: «múltiples declaraciones de <code>r</code>». Dentro de un ámbito determinado, una variable de Haskell se define una sola vez y su definición no puede cambiar. Las variables de Haskell parecen ''invariables'', pero en realidad se comportan como variables matemáticas. En Matemáticas nunca vemos que una variable cambie de valor dentro de un mismo problema, y lo mismo sucede en Haskell. Más precisamente, las variables de Haskell son ''inmutables'': sus valores solo varían de acuerdo con los datos que introducimos en el programa. No podemos definir <code>r</code> de dos maneras diferentes en un mismo código, pero podemos cambiar su valor modificando el archivo. Cambiemos el código anterior por lo siguiente: <syntaxhighlight lang = "haskell"> r = 2.0 area = pi * r ^ 2 </syntaxhighlight> Por supuesto, funcionará. Hemos cambiado <code>r</code> en el único lugar donde está definida, y eso modifica su valor en el resto del programa. En problemas reales, los programas en Haskell dejan ''algunos'' valores sin definir en el código. Esos valores se definen más adelante, cuando el programa obtiene datos de un archivo o de una acción externa. Por ahora, sin embargo, nos limitaremos a definir variables internamente. Aprenderemos sobre las interacciones con el mundo exterior en capítulos posteriores. Veamos otro ejemplo de la gran diferencia entre Haskell y los lenguajes imperativos: <syntaxhighlight lang = "haskell"> r = r + 1 </syntaxhighlight> En lugar de «incrementar el valor de <code>r</code>», es decir, actualizar el valor almacenado en la memoria, este código en Haskell constituye una definición recursiva de <code>r</code>, o sea, la define en términos de sí misma. No te preocupes, pues explicaremos la [[Haskell/Recursión|recursión]] más adelante. En este caso concreto, si <code>r</code> ya hubiera sido definida con algún valor, el compilador devolvería un mensaje de error, como explicamos anteriormente. Si <code>r</code> se hubiera definido con el valor 5, escribir <code>r = r + 1</code> sería equivalente a intentar afirmar que <math>5 = 5 + 1</math> en Matemáticas, lo cual es evidentemente incorrecto. Como los valores no cambian dentro de un mismo programa, las variables pueden definirse en el orden que queramos. Por ejemplo, los dos códigos siguientes son exactamente equivalentes: {|border="0" |- ||<syntaxhighlight lang = "haskell"> y = x * 2 x = 3 </syntaxhighlight> ||<syntaxhighlight lang = "haskell"> x = 3 y = x * 2 </syntaxhighlight> |} En Haskell no existe la noción de que «<code>x</code> se declaró antes que <code>y</code>», o viceversa. Por supuesto, para utilizar <code>y</code> seguimos necesitando el valor de <code>x</code>, pero el orden no nos importa, ni tampoco al compilador. == Funciones == Modificar el programa cada vez que queramos calcular el área de un círculo diferente resulta tedioso y nos limita a un círculo por vez. Podríamos calcular dos áreas duplicando todos los cálculos en nuestro código y definiendo <code>r2</code> y <code>area2</code><ref group=nota>Podemos observar aquí que los nombres de las variables pueden contener números y letras. Sin embargo, toda variable ''debe'' comenzar con una letra minúscula, seguida de otras letras (también mayúsculas), números, guiones bajos (_) o apóstrofos (').</ref> <syntaxhighlight lang = "haskell"> r = 5 area = pi * r ^ 2 r2 = 3 area2 = pi * r2 ^ 2 </syntaxhighlight> Para evitar esta repetición constante, es preferible añadir simplemente una ''función'' para calcular el área y aplicarla a diferentes radios. Una ''función'' recibe un ''argumento'' (o ''parámetro'') y devuelve un resultado, exactamente igual que en Matemáticas. En Haskell, las funciones se definen de la misma manera que las variables, salvo que debemos indicar sus argumentos en el lado izquierdo de la expresión. Por ejemplo, el siguiente código define <code>area</code>, cuyo argumento es <code>r</code>: <syntaxhighlight lang = "haskell"> area r = pi * r ^ 2 </syntaxhighlight> Observa la sintaxis: primero aparece el nombre de la función (<code>area</code>), seguido de un espacio en blanco y de su argumento (<code>r</code>). Después aparece el signo <code>=</code> y la definición de la función, que utiliza el argumento de entrada en su cálculo. Ahora podemos probar diferentes valores del radio y ''llamar'' a la función con ellos. Guarda el código anterior en un archivo, ábrelo en GHCi y prueba lo siguiente: {{Haskell/GHCi|1= *Main> area 5 78.53981633974483 *Main> area 3 28.274333882308138 *Main> area 17 907.9202768874502 }} Acabamos de ''llamar'' a la función con diferentes valores y hemos calculado las áreas de tres círculos distintos. Matemáticamente, el cálculo del área puede representarse mediante <math>A(r) = \pi \cdot r ^ 2</math>. Para calcular las áreas, escribiríamos <math>A(5) = 78.54</math> o <math>A(3) = 28.27</math>. Haskell también permite utilizar paréntesis, pero por convención suelen omitirse para que el código tenga menos símbolos y sea más legible. Aun así, los paréntesis se utilizan para agrupar ''expresiones'' (cualquier código que devuelva un valor) que deben calcularse en conjunto. Observa cómo se interpretan de manera diferente las siguientes expresiones: <syntaxhighlight lang = "haskell"> 5 * 3 + 2 -- 15 + 2 = 17 (la multiplicación se realiza antes que la suma) 5 * (3 + 2) -- 5 * 5 = 25 (los paréntesis obligan a calcular primero la suma) area 5 * 3 -- (area 5) * 3 area (5 * 3) -- area 15 </syntaxhighlight> Del mismo modo que la multiplicación se realiza antes que la suma, observa que las aplicaciones de funciones en Haskell tienen una precedencia mayor que cualquier operador, ya sea <code>+</code> o <code>*</code>, por ejemplo. == Evaluación de expresiones == ¿Qué sucede exactamente cuando introducimos una expresión en GHCi? Después de pulsar «Enter», se evalúa. Esto significa que cada función se sustituye por su definición y se realizan los cálculos hasta que queda un único valor final. Por ejemplo, al ejecutar <code>area 5</code>, sucede lo siguiente: <syntaxhighlight lang=haskell> area 5 -- todas las apariciones de 'r' a la derecha se sustituyen por '5' pi * 5 ^ 2 -- 'pi' se sustituye por un valor aproximado 3.141592653589793 * 5 ^ 2 -- se realiza la operación de potenciación '5 ^ 2' 3.141592653589793 * 25 -- se realiza la operación de multiplicación 78.53981633974483 -- resultado final </syntaxhighlight> Cuando utilizamos GHCi, el resultado de ''aplicar'' o ''llamar'' a la función, siguiendo el procedimiento anterior, aparece en la pantalla. Veamos ahora algunas funciones más: <syntaxhighlight lang = "haskell"> duplicar x = 2 * x cuadruplicar x = double (double x) cuadrado x = x * x mitad x = x / 2 </syntaxhighlight> {{Haskell/Ejercicios|1= * Explica cómo evaluaría GHCi <code>cuadruplicar 5</code>. * Define una función que reste 12 a la mitad de su argumento. }} == Parámetros múltiples == Las funciones también pueden tener más de un argumento. Por ejemplo, para calcular el área de un rectángulo necesitamos su longitud y su anchura: <syntaxhighlight lang = "haskell"> areaRectang a b = a * b </syntaxhighlight> {{Haskell/GHCi|1= *Main> areaRectang 5 10 50 }} Otro ejemplo es el área de un triángulo, <math>A = \frac{bh}{2}</math>: <syntaxhighlight lang="haskell"> areaTriang b h = (b * h) / 2 </syntaxhighlight> {{Haskell/GHCi|1= *Main> areaTriang 3 9 13.5 }} Como puedes ver, los argumentos se separan mediante espacios. Por eso, a veces es necesario utilizar paréntesis para agrupar expresiones. Por ejemplo, no podemos escribir el cuádruple de un valor de la siguiente manera: <syntaxhighlight lang = "haskell"> cuadruple x = double double x -- error </syntaxhighlight> Esto aplicaría la función <code>double</code> a dos argumentos: <code>double</code> y <code>x</code>. Sí es posible que las ''funciones sean argumentos de otras funciones'', como veremos más adelante, pero eso no es lo que queremos hacer aquí. Para que esta función funcione, necesitamos utilizar paréntesis: <syntaxhighlight lang = "haskell"> cuadruple x = double (double x) </syntaxhighlight> Los argumentos siempre se pasan en el orden en que aparecen. Por ejemplo: <syntaxhighlight lang = "haskell"> menos x y = x - y </syntaxhighlight> {{Haskell/GHCi|1= *Main> menos 10 5 5 *Main> menos 5 10 -5 }} Aquí, <code>menos 10 5</code> evalúa la expresión <code>10 - 5</code>, mientras que <code>menos 5 10</code> evalúa <code>5 - 10</code>, debido al cambio en el orden de los argumentos. {{Haskell/Ejercicios|1= * Escribe una función para calcular el volumen de una caja. * ¿Aproximadamente cuántas piedras se utilizaron para construir las famosas pirámides de Guiza? Pista: tendrás que estimar el volumen de la pirámide y el volumen de cada bloque de piedra. }} == Combinación de funciones == Por supuesto, puedes utilizar otras funciones para definir las tuyas, del mismo modo que ya lo has hecho utilizando la suma, <code>+</code>, o la multiplicación, <code>*</code>. De hecho, los operadores también se definen como funciones en Haskell. Por ejemplo, para calcular el área de un cuadrado, podemos reutilizar la función que calcula el área de un rectángulo: <syntaxhighlight lang = "haskell"> areaRetang a b = a * b areaCuad l = areaRetang l l </syntaxhighlight> {{Haskell/GHCi|1= *Main> areaCuad 5 25 }} Al fin y al cabo, un cuadrado es un rectángulo cuyos lados son iguales. {{Haskell/Ejercicios|1= * Escribe una función que calcule el volumen de un cilindro. El volumen de un cilindro es el área de su base, que es un círculo, multiplicada por la altura. Como ya has programado la función que calcula el área de un círculo en este capítulo, puedes reutilizarla aquí. }} == Definición de funciones locales == === Uso de <code>where</code> === Cuando definimos una función, a veces necesitamos calcular valores intermedios que son ''locales'' y se utilizan únicamente dentro de esa función. Considera la [[w:teorema de Herón|fórmula de Herón]]: <math>A = \sqrt{p(p-a)(p-b)(p-c)}</math>, donde <math>p</math> es la mitad del perímetro del triángulo. Este es el cálculo del área de un triángulo cuyos lados miden <code>a</code>, <code>b</code> y <code>c</code>: <syntaxhighlight lang = "haskell"> heron a b c = sqrt (s * (s - a) * (s - b) * (s - c)) where s = (a + b + c) / 2 </syntaxhighlight> La variable <code>s</code> representa la mitad del perímetro. Sería tedioso y propenso a errores tener que escribir <code>(a + b + c)/2</code> cada vez que necesitáramos este valor dentro de <code>herao</code>. <code>sqrt</code> calcula la raíz cuadrada de su argumento (recuerda que significa ''square root'' en inglés). Escribir las definiciones por separado no funcionaría. Observa: <syntaxhighlight lang = "haskell"> heron a b c = sqrt (p * (p - a) * (p - b) * (p - c)) p = (a + b + c) / 2 -- a, b y c no están definidas aquí </syntaxhighlight> El compilador señalaría que <code>a</code>, <code>b</code> y <code>c</code> solo están disponibles en el lado derecho de <code>herao</code>, mientras que la definición de <code>s</code> no forma parte del lado derecho de <code>herao</code>. Para incluirla allí, utilizamos la palabra clave <code>where</code>. Observa que tanto <code>where</code> como la definición de <code>s</code> están indentadas con cuatro espacios en blanco. Esto es importante para delimitar qué pertenece a cada bloque. Veamos otros ejemplos del uso de <code>where</code>: <syntaxhighlight lang = "haskell"> areaTriangTrig a b c = c * altura / 2 -- usando trigonometría where cosAlfa = (b ^ 2 + c ^ 2 - a ^ 2) / (2 * b * c) sinAlfa = sqrt (1 - cosAlfa ^ 2) altura = b * sinAlfa areaTriangHeron a b c = resultado -- usa la fórmula de Herón where resultado = sqrt (p * (p - a) * (p - b) * (p - c)) p = (a + b + c) / 2 </syntaxhighlight> == Ámbito == Observa atentamente el ejemplo anterior: fíjate en que utilizamos las variables <code>a</code>, <code>b</code> y <code>c</code> dos veces, en cada una de las funciones definidas. ¿Cómo es posible? Considera el siguiente procedimiento en GHCi: {{Haskell/GHCi|1= Prelude> let r = 0 Prelude> let area r = pi * r ^ 2 Prelude> area 5 78.53981633974483 }} Sería extraño que el resultado del área fuera <code>0</code> debido a la definición anterior de <code>let r = 0</code>. Esto no sucede porque, la segunda vez que escribimos <code>r</code>, nos referimos a una <code>r</code> ''diferente''. Puede parecer confuso, pero piensa en lo siguiente: muchas personas se llaman Juan y, aun así, en una situación en la que solo hay un Juan presente, podemos decir «Juan» sin ninguna confusión. En programación, esta noción de contexto se denomina ''[[w:Ámbito (informática)|ámbito]]''. No profundizaremos por ahora en los detalles técnicos relacionados con el ámbito, pero ten presente que el valor de un parámetro es exactamente el que se pasa a la función, independientemente del nombre que le hayas dado al definirla. Por lo tanto, elegir nombres apropiados para las variables ayuda considerablemente a que los seres humanos puedan leer el código. == Resumen == # Las variables almacenan valores, que pueden ser cualquier expresión en Haskell. # Las variables no cambian sus valores dentro de un mismo ámbito. # Las funciones son útiles para escribir programas reutilizables. # Las funciones pueden aceptar más de un parámetro. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] obbj6z7ybxsbqvf86robrcw3hb5mzg4 Números y Operaciones/Introducción/Generalidades 0 64947 436429 435982 2026-10-13T23:18:04Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Clasificación]] 436429 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Clasificación]] afxt7wgwjflyqc7fct82gktd2yf2u8p Números y Operaciones/Números Enteros 0 64953 436430 435994 2026-10-13T23:18:14Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Enteros]] 436430 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Enteros]] gtsp5v0pd41h1ncbj0wed1khkk6lpwt Números y Operaciones/Números Enteros/División 0 64954 436431 435996 2026-10-13T23:18:24Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Enteros/División]] 436431 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Enteros/División]] h3bqj9kerc806wjiwodqshyvwvrhdoq Números y Operaciones/Números Enteros/Introducción 0 64955 436432 435998 2026-10-13T23:18:34Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Enteros/Introducción]] 436432 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Enteros/Introducción]] bh94ws56edet82qs2p4kr6r1o7knpwk Números y Operaciones/Números Enteros/Números Pares e Impares 0 64956 436433 436000 2026-10-13T23:18:44Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Enteros/Números Pares e Impares]] 436433 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Enteros/Números Pares e Impares]] g34aejbclwosp1a7dwzwwvv57dj6d7m Números y Operaciones/Números Enteros/Operaciones Básicas 0 64957 436434 436002 2026-10-13T23:18:54Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Enteros/Operaciones Básicas]] 436434 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Enteros/Operaciones Básicas]] apkp65o0xz0mcwn6jf7wz8j0dljo84e Números y Operaciones/Números Enteros/Propiedades Aritméticas 0 64958 436435 436004 2026-10-13T23:19:04Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Enteros/Propiedades Aritméticas]] 436435 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Enteros/Propiedades Aritméticas]] ntkipsnimafl4eio6y5nfuoccjq5g6e Números y Operaciones/Números Enteros/Propiedades Fundamentales 0 64959 436436 436006 2026-10-13T23:19:14Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Enteros/Propiedades Fundamentales]] 436436 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Enteros/Propiedades Fundamentales]] mqmm5ozrbh8c9n9xq84t4zunu52z760 Números y Operaciones/Números Enteros/Resta o Sustracción 0 64960 436437 436008 2026-10-13T23:19:24Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Enteros/Resta o Sustracción]] 436437 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Enteros/Resta o Sustracción]] 75rgp4j7cbhsrug4j771o2583boyk6w Números y Operaciones/Números Enteros/Valor Absoluto 0 64961 436438 436010 2026-10-13T23:19:34Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Enteros/Valor Absoluto]] 436438 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Enteros/Valor Absoluto]] fa2kqcx7s4ghjr6fqwlol1nj6jk3r9j Números y Operaciones/Números Enteros B 0 64962 436439 436012 2026-10-13T23:19:44Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Enteros/Números Enteros B]] 436439 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Enteros/Números Enteros B]] 62gd0u4rt8ca0mubmngmpg8wzj414zy Números y Operaciones/Números Naturales 0 64963 436440 436014 2026-10-13T23:19:54Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales]] 436440 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales]] evnvilr6sz78d6yo5cpy20nbadanf20 Números y Operaciones/Números Naturales/División 0 64964 436441 436016 2026-10-13T23:20:04Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/División]] 436441 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/División]] 3wbsxegul0bucrgr8l8f4c502b5umj6 Números y Operaciones/Números Naturales/Introducción 0 64965 436442 436018 2026-10-13T23:20:14Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Introducción]] 436442 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Introducción]] 7tk5x4r18i2lwayjeltxat0eqi5caqt Números y Operaciones/Números Naturales/Menor o Mayor 0 64966 436443 436020 2026-10-13T23:20:24Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Menor o Mayor]] 436443 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Menor o Mayor]] pj7vuf3g20r9vuxt4363csw6cuu7ata Números y Operaciones/Números Naturales/Máximo Común Divisor 0 64967 436444 436022 2026-10-13T23:20:34Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Máximo Común Divisor]] 436444 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Máximo Común Divisor]] atvj2lsx7uzbs4ef0kax5clmjwijwyw Números y Operaciones/Números Naturales/Método para encontrar el MCM y el MCD 0 64968 436445 436024 2026-10-13T23:20:44Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/MCM y MCD]] 436445 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/MCM y MCD]] bhxo13kxx8wrx05nzihwhe3shzp5dpv Números y Operaciones/Números Naturales/Mínimo Común Múltiplo 0 64969 436446 436026 2026-10-13T23:20:54Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Mínimo Común Múltiplo]] 436446 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Mínimo Común Múltiplo]] t6tf69vh7au3zbz0wxt4m2wx9ta7zxq Números y Operaciones/Números Naturales/Números Pares e Impares 0 64970 436447 436028 2026-10-13T23:21:04Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Pares e Impares]] 436447 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Pares e Impares]] 20kbbjvfjcvycbb6w50cyopfpgi39xi Números y Operaciones/Números Naturales/Números Primos y Compuestos 0 64971 436448 436030 2026-10-13T23:21:14Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Primos y Compuestos]] 436448 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Primos y Compuestos]] 57sjh7zmrpku7enn9orlrdi7kgf37d8 Números y Operaciones/Números Naturales/Orden 0 64972 436449 436032 2026-10-13T23:21:24Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Orden]] 436449 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Orden]] s81njond9pvoj09j4d6pzctruj7cotc Números y Operaciones/Números Naturales/Propiedades Aritméticas 0 64973 436450 436034 2026-10-13T23:21:34Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Operaciones Combinadas]] 436450 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Operaciones Combinadas]] eey9bq656qg888e64fpogkud0jhwzst Números y Operaciones/Números Naturales/Propiedades básicas 0 64974 436451 436036 2026-10-13T23:21:44Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Propiedades básicas]] 436451 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Propiedades básicas]] 9p6rlbjrkuvxpat6tzdymwy52jctj1p Números y Operaciones/Números Naturales/Propiedades de la Adición y Multiplicación 0 64975 436452 436038 2026-10-13T23:21:54Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Propiedades de Adición y Multiplicación]] 436452 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Propiedades de Adición y Multiplicación]] q3j0pipkc8j6mkni59uglaqu9sms1m6 Números y Operaciones/Números Naturales/Resta o Sustracción 0 64976 436453 436040 2026-10-13T23:22:04Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Naturales/Sustracción]] 436453 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Naturales/Sustracción]] n5r9m7lg9ns1k9edlapiz8x12663efx Números y Operaciones/Números Naturales 2 0 64977 436454 436042 2026-10-13T23:22:14Z MABot 20834 Robot: corrección de redirección doble a [[Cursos/E M T/2º Administración - Matemáticas/Unidad 5]] 436454 wikitext text/x-wiki #REDIRECCIÓN [[Cursos/E M T/2º Administración - Matemáticas/Unidad 5]] n9gyb79m0oe1clvkx2gmwn4ujkvmx1f Números y Operaciones/Números Racionales 0 64978 436455 436044 2026-10-13T23:22:24Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales]] 436455 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales]] anu3nsn9zuhg1km8aeio2c354veqsxp Números y Operaciones/Números Racionales/Construcción 0 64979 436456 436046 2026-10-13T23:22:34Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Construcción]] 436456 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Construcción]] 76yrd0fpg0peo4xv1avaf26dg1ddqhb Números y Operaciones/Números Racionales/Ecuaciones 0 64980 436457 436048 2026-10-13T23:22:44Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Ecuaciones]] 436457 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Ecuaciones]] 82u2fc1wjisfvvspktvlevc1ob3wgmj Números y Operaciones/Números Racionales/Expresión decimal 0 64981 436458 436050 2026-10-13T23:22:54Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Expresión decimal]] 436458 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Expresión decimal]] bw4lwk3oto07odz3yz1t83vv0vymgij Números y Operaciones/Números Racionales/Historia 0 64982 436459 436052 2026-10-13T23:23:04Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Historia]] 436459 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Historia]] 9putoklddipxavnap3ptwsn341dk8nh Números y Operaciones/Números Racionales/Introducción 0 64983 436460 436054 2026-10-13T23:23:14Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Introducción]] 436460 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Introducción]] b2cphtvxjk37vlyvgvipazsg3byht46 Números y Operaciones/Números Racionales/Multiplicación 0 64984 436461 436056 2026-10-13T23:23:24Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Multiplicación]] 436461 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Multiplicación]] 2qvb7654m2p5ii5rr4k1qjt3noyjh8x Números y Operaciones/Números Racionales/Otras operaciones 0 64985 436462 436058 2026-10-13T23:23:34Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Otras operaciones]] 436462 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Otras operaciones]] piczwgysp4vbda8rcnq8u0h9f83j39f Números y Operaciones/Números Racionales/Potencias 0 64986 436463 436060 2026-10-13T23:23:44Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Potencias]] 436463 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Potencias]] 9uiv47c6y5t6pi84oe8vbmo73fy5vyb Números y Operaciones/Números Racionales/Potencias/Ejercicios 0 64987 436464 436062 2026-10-13T23:23:54Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Potencias/Ejercicios]] 436464 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Potencias/Ejercicios]] tlt1mxx6bb86aa8dp2koesdk5ormxpa Números y Operaciones/Números Racionales/Propiedades 0 64988 436465 436064 2026-10-13T23:24:04Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Propiedades]] 436465 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Propiedades]] lz0lz8u6fxv6hdlp9nxp1qchj3ut0tl Números y Operaciones/Números Racionales/Relaciones de equivalencia 0 64989 436466 436066 2026-10-13T23:24:14Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Relaciones de equivalencia]] 436466 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Relaciones de equivalencia]] bqbg6u4gwa6pcztbnghdv3wudb19d5m Números y Operaciones/Números Racionales/Representación decimal 0 64990 436467 436068 2026-10-13T23:24:24Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Representación decimal]] 436467 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Representación decimal]] h83kmqjhodm98p1tja1z7edmzh8yaj7 Números y Operaciones/Números Racionales/Suma 0 64991 436468 436070 2026-10-13T23:24:34Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Adición]] 436468 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Adición]] mvh4hhe2c7pe05lpduukkfs0byc3nzi Números y Operaciones/Números Racionales/Valor absoluto 0 64992 436469 436072 2026-10-13T23:24:44Z MABot 20834 Robot: corrección de redirección doble a [[Matemáticas/Números/Racionales/Valor absoluto]] 436469 wikitext text/x-wiki #REDIRECCIÓN [[Matemáticas/Números/Racionales/Valor absoluto]] 7ib2sjafxjqc3qz1zdhmdfuosrqj3rc Historia digital del Arte 0 65012 436399 436147 2026-10-13T23:13:04Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte]] 436399 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte]] ncq9jwjm4wv1z0h4x21r0balzz4gerw Discusión:Historia digital del Arte 1 65013 436470 436149 2026-10-13T23:24:54Z MABot 20834 Robot: corrección de redirección doble a [[Discusión:Historia del arte]] 436470 wikitext text/x-wiki #REDIRECCIÓN [[Discusión:Historia del arte]] plfdggs6zn1g8hkqusiy8wfg69dfbss Historia digital del Arte/Arte del próximo Oriente/Mesopotamia 0 65014 436400 436151 2026-10-13T23:13:14Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Arte del próximo Oriente/Mesopotamia]] 436400 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Arte del próximo Oriente/Mesopotamia]] tteu1uyfkq9ha8h0v535ju5s41eco0r Historia digital del Arte/Arte egipcio 0 65015 436401 436153 2026-10-13T23:13:24Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Arte egipcio]] 436401 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Arte egipcio]] qpnevtx317xpcsbkqjoh5d30rufiyyy Historia digital del Arte/Arte egipcio/Época Amarniana 0 65016 436402 436155 2026-10-13T23:13:34Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Arte egipcio/Época Amarniana]] 436402 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Arte egipcio/Época Amarniana]] qy13h3qvnvjea266zbp9cso8kvk40bs Historia digital del Arte/Arte griego 0 65017 436403 436157 2026-10-13T23:13:44Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Arte griego]] 436403 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Arte griego]] 8lyawhnubqd9uxbwqgokcerglmbv004 Historia digital del Arte/Arte gótico/ Origen y expansión del Arte gótico 0 65018 436404 436159 2026-10-13T23:13:54Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Arte gótico/ Origen y expansión del Arte gótico]] 436404 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Arte gótico/ Origen y expansión del Arte gótico]] a54qbn9semvxv24jk7zavgptwducely Historia digital del Arte/Arte gótico/ Realismo burgués en el siglo XV 0 65019 436405 436161 2026-10-13T23:14:04Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Arte gótico/ Realismo burgués en el siglo XV]] 436405 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Arte gótico/ Realismo burgués en el siglo XV]] dv5w7x031dt31uq44o9jxjkwnb10fb7 Historia digital del Arte/Arte islámico 0 65020 436406 436163 2026-10-13T23:14:14Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Arte islámico]] 436406 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Arte islámico]] d2qfmfgy1ftblcmlv7mlafifaivsaov Historia digital del Arte/Arte roma 0 65021 436407 436165 2026-10-13T23:14:24Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Arte roma]] 436407 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Arte roma]] 6bvnxc4jskocv0pz83x3xwjmtgows4i Historia digital del Arte/Arte románico 0 65022 436408 436167 2026-10-13T23:14:34Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Arte románico]] 436408 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Arte románico]] r1oe33wxetf8g88lutqbvw3k5riin9n Historia digital del Arte/Barroco 0 65023 436409 436169 2026-10-13T23:14:44Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Barroco]] 436409 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Barroco]] gdai9gd1r8lm1n0kf5tvekis5jba8js Historia digital del Arte/Barroco tardio y Rococó 0 65024 436410 436171 2026-10-13T23:14:54Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Barroco tardio y Rococó]] 436410 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Barroco tardio y Rococó]] 8we35paum8rxe8ga9ponkgp03w8qluq Historia digital del Arte/Bibliografía 0 65025 436411 436173 2026-10-13T23:15:04Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Bibliografía]] 436411 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Bibliografía]] f01wsco5uacn8siq8pf19njyrbqkays Historia digital del Arte/Conceptos elementales 0 65026 436412 436175 2026-10-13T23:15:14Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Conceptos elementales]] 436412 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Conceptos elementales]] sorqp1auqkf439ezwxyj6mq2ihad3j8 Historia digital del Arte/Impresionismo 0 65027 436413 436177 2026-10-13T23:15:24Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Impresionismo]] 436413 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Impresionismo]] rutzrbuzlfqv41asrg5uijdaqkmc0dt Historia digital del Arte/Introducción 0 65028 436414 436179 2026-10-13T23:15:34Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Introducción]] 436414 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Introducción]] 1am0yc4r0kzdqpur423dhmnr03uwudb Historia digital del Arte/Manierismo 0 65029 436415 436181 2026-10-13T23:15:44Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Manierismo]] 436415 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Manierismo]] hf456o9pjwis2f10li34nq5848lazor Historia digital del Arte/Neoclásico 0 65030 436416 436183 2026-10-13T23:15:54Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Neoclásico]] 436416 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Neoclásico]] fy3ls12lzujj0ln29jtmane6obisl06 Historia digital del Arte/Origen del Arte moderno 0 65031 436417 436185 2026-10-13T23:16:04Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Origen del Arte moderno]] 436417 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Origen del Arte moderno]] ozfgdlqrtnthl8zt4w1v4fufdvin34z Historia digital del Arte/Renacimiento 0 65032 436418 436187 2026-10-13T23:16:14Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Renacimiento]] 436418 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Renacimiento]] 2o8qdj4clvqc2mzqoqvc3ydgo5mezz1 Historia digital del Arte/Renacimiento/ Cinquecento 0 65033 436419 436189 2026-10-13T23:16:24Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Renacimiento/ Cinquecento]] 436419 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Renacimiento/ Cinquecento]] 66b2nbho5z6u3u2psirzl17itt58svm Historia digital del Arte/Renacimiento/ Quattrocento 0 65034 436420 436191 2026-10-13T23:16:34Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Renacimiento/ Quattrocento]] 436420 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Renacimiento/ Quattrocento]] c0w5ybpl1k7ew89fttd7v33elhd0j7y Historia digital del Arte/Romanticismo 0 65035 436421 436193 2026-10-13T23:16:44Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/Romanticismo]] 436421 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/Romanticismo]] 5ls2vfp78cs0mazumxfyrw064gcx8h7 Historia digital del Arte/ Arte del próximo Oriente/ Arte cretense 0 65036 436422 436195 2026-10-13T23:16:54Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/ Arte del próximo Oriente/ Arte cretense]] 436422 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/ Arte del próximo Oriente/ Arte cretense]] 8mu06piratfmac1bf0iu2cfmx4eaphu Historia digital del Arte/ Arte egipcio/El imperio antiguo 0 65037 436423 436197 2026-10-13T23:17:04Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/ Arte egipcio/El imperio antiguo]] 436423 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/ Arte egipcio/El imperio antiguo]] 2dshs1volaxpdruixmnowunla5m5hl0 Historia digital del Arte/ Arte egipcio/El imperio medio 0 65038 436424 436199 2026-10-13T23:17:14Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/ Arte egipcio/El imperio medio]] 436424 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/ Arte egipcio/El imperio medio]] 7zmylelezhslou6r0cpo3h1e4pcuyfl Historia digital del Arte/ Arte gótico/ Pintura del Trecento 0 65039 436425 436201 2026-10-13T23:17:24Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/ Arte gótico/ Pintura del Trecento]] 436425 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/ Arte gótico/ Pintura del Trecento]] k3tpr7njg5sgjhi1gt9yocbfce8e7cw Historia digital del Arte/ El arte prehistórico 0 65040 436426 436203 2026-10-13T23:17:34Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/ El arte prehistórico]] 436426 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/ El arte prehistórico]] mdrdicka2c68finyuywc5zk9rfgu9jm Historia digital del Arte/ El arte prehistórico/ Neolitico 0 65041 436427 436205 2026-10-13T23:17:44Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/ El arte prehistórico/ Neolitico]] 436427 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/ El arte prehistórico/ Neolitico]] mzvhub45dyz6j7oj9mlhsgn2ffyu9zn Historia digital del Arte/ El arte prehistórico/ Paleolitico 0 65042 436428 436207 2026-10-13T23:17:54Z MABot 20834 Robot: corrección de redirección doble a [[Historia del arte/ El arte prehistórico/ Paleolitico]] 436428 wikitext text/x-wiki #REDIRECCIÓN [[Historia del arte/ El arte prehistórico/ Paleolitico]] 4gy7zuq2h2nxln6ln7y4eqqr5ywhjjg Discusión:Historia digital del Arte/ Arte gótico/ Pintura del Trecento 1 65043 436471 436209 2026-10-13T23:25:04Z MABot 20834 Robot: corrección de redirección doble a [[Discusión:Historia del arte/ Arte gótico/ Pintura del Trecento]] 436471 wikitext text/x-wiki #REDIRECCIÓN [[Discusión:Historia del arte/ Arte gótico/ Pintura del Trecento]] l1jy4or21sg7treu64nbztvyafpikyw Haskell/Valores de verdad 0 65076 436376 436344 2026-10-13T16:45:27Z Luciérnaga de conocimiento 119502 /* Guardas y where */ 436376 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} == Igualdad y otras comparaciones == En el capítulo anterior, usamos signos de igualdad para definir variables y funciones en Haskell, como se muestra en el siguiente código: <syntaxhighlight lang = "haskell"> r = 5 </syntaxhighlight> Esto significa que, durante la evaluación del programa, todas las apariciones de <code>r</code> se sustituyen por <code>5</code> dentro del mismo ámbito. De manera similar, al evaluar <syntaxhighlight lang = "haskell"> f x = x + 3 </syntaxhighlight> todas las apariciones de <code>f</code> seguidas de un número (el argumento de <code>f</code>) se sustituyen por ese valor más tres. En Matemáticas, el signo de igualdad se usa de manera diferente. Consideremos el siguiente problema: {{Haskell/Ejercicios|1= Resuelve el siguiente problema: <math>x+3=5</math>. }} En este caso, el problema no consiste en representar <math>5</math> como si fuera <math>x+3</math>, o viceversa. En realidad, tenemos una ''proposición'' que afirma que un número <math>x</math>, al sumarle 3, da como resultado 5. Resolver la ecuación significa encontrar el valor de <math>x</math>, si existe alguno, que haga verdadera la proposición. En este sencillo ejemplo, una simple manipulación algebraica nos muestra que <math>x=2</math>, es decir, <math>2</math> es el número que satisface la proposición, ya que <math>2+3=5</math>. Comparar valores para comprobar si son iguales resulta bastante útil en programación. De hecho, es algo muy elemental y necesario. En Haskell, estas pruebas se parecen bastante a una ecuación. Sin embargo, como el signo igual ya se utiliza para definir algo, Haskell emplea un signo de igualdad ''doble'', <code>==</code>, para realizar comparaciones. Veamos: {{Haskell/GHCi|1= Prelude> 2 + 3 == 5 True }} Aquí, GHCi devuelve <code>True</code> (''verdadero'', en inglés) porque <math>2 + 3</math> es igual a <math>5</math>. ¿Y si la ecuación no fuera verdadera? {{Haskell/GHCi|1= Prelude> 7 + 3 == 5 False }} El resultado es <code>False</code> (''falso'', en inglés). Ahora vamos a utilizar la función <code>f</code> que definimos al comienzo de este capítulo: {{Haskell/GHCi|1= Prelude> let f x = x + 3 Prelude> f 2 == 5 True }} Como era de esperar. Puesto que <code>f 2</code> se evalúa como <code>2 + 3</code>, cuyo resultado es <code>5</code>, lo lógico es que <code>5 == 5</code> devuelva <code>True</code>. También podemos comparar dos valores numéricos para averiguar cuál es mayor. Haskell dispone de varios operadores para estas pruebas: <code><</code>, para ''menor que''; <code>></code>, para ''mayor que''; <code><=</code>, para ''menor o igual que''; y <code>>=</code>, para ''mayor o igual que''. Estas pruebas funcionan exactamente igual que <code>==</code>, que significa ''igual a''. Por ejemplo, podríamos utilizar <code><</code> junto con la función <code>area</code> del capítulo anterior para comprobar si un círculo de cierto radio tiene un área menor que otro valor. {{Haskell/GHCi|1= Prelude> let area r = pi * r ^ 2 Prelude> area 5 < 50 False }} == Valores booleanos == ¿Qué sucede cuando GHCi tiene que determinar si estas comparaciones son verdaderas o falsas? Consideremos un caso diferente. Si introducimos una expresión aritmética en GHCi, esta se ''evalúa'' y el resultado numérico aparece en pantalla: {{Haskell/GHCi|1= Prelude> 2 + 2 4 }} Si sustituimos la expresión aritmética por su valor final en una comparación, sucede algo parecido: {{Haskell/GHCi|1= Prelude> 2 == 2 True }} Mientras que el «4» devuelto en el primer caso representa el recuento de alguna cosa, «True» es un ''valor'' que representa la verdad de una determinada proposición. Estos valores se conocen como '''booleanos'''.<ref group=nota>Este término se eligió en homenaje al matemático británico [[w:George Boole|George Boole]].</ref> Naturalmente, solo hay dos valores posibles, que ya se nos han presentado: <code>True</code> y <code>False</code>. == Introducción a los tipos de datos == <code>True</code> y <code>False</code> son valores reales, no una simple analogía. En Haskell, los valores booleanos tienen el mismo ''estatus'' que los valores numéricos, y podemos manipularlos de manera similar. Veamos un ejemplo sencillo: {{Haskell/GHCi|1= Prelude> True == True True Prelude> True == False False }} En efecto, <code>True</code> es igual a <code>True</code>, y <code>True</code> no es igual a <code>False</code>. Ahora bien, ¿es posible determinar si <code>2</code> es igual a <code>True</code>? {{Haskell/GHCi|1= Prelude> 2 == True <interactive>:1:0: No instance for (Num Bool) arising from the literal ‘2’ at <interactive>:1:0 Possible fix: add an instance declaration for (Num Bool) In the first argument of ‘(==)’, namely ‘2’ In the expression: 2 == True In an equation for ‘it’: it = 2 == True }} El compilador produce un error. En realidad, la pregunta original ni siquiera tiene sentido. No se puede comparar un número con algo que no es un número, ni un valor booleano con algo que no es booleano. Haskell incorpora esta noción en el propio lenguaje, y el mensaje de error anterior, aunque es largo y un poco intimidante, expresa precisamente eso: a la izquierda de <code>==</code> hay un número (<code>Num</code>), por lo que se esperaba también un número a la derecha. Sin embargo, un valor booleano (<code>Bool</code>) no es un número, de modo que la prueba de igualdad falla. Queda claro, entonces, que los valores se agrupan en '''tipos''', que establecen los límites de lo que podemos o no hacer con ellos. <code>True</code> y <code>False</code> son valores del tipo <code>Bool</code>. El caso de <code>2</code> es algo más complicado, porque en informática existen varios tipos de números, al igual que en Matemáticas, aunque no sean equivalentes. Pero, en definitiva, sigue siendo un dato numérico (<code>Num</code>). En general, esta característica restrictiva resulta muy valiosa, porque podemos aprovecharla para controlar el comportamiento de nuestros programas y establecer reglas que impidan utilizarlos con tipos de datos que ''no tienen sentido''. De este modo, garantizamos el funcionamiento correcto de lo que desarrollamos. Volveremos a tratar los tipos más adelante, ya que constituyen una parte importante del lenguaje Haskell. == Operadores infijos == Una prueba de igualdad, como <code>2 == 2</code>, también es una expresión, al igual que una operación aritmética, como <code>2 + 2</code>. Ambos casos se evalúan básicamente de la misma manera. Cuando escribimos <code>2 == 2</code> en una sesión de GHCi, este «responde» con <code>True</code>, porque simplemente ha evaluado la expresión. En realidad, el operador <code>==</code> es una función de dos argumentos (el de la izquierda y el de la derecha de la igualdad). Sin embargo, su uso es diferente: Haskell permite escribir las funciones de dos argumentos como ''operadores infijos'', es decir, situados ''entre'' sus dos argumentos. Cuando el nombre de una función está formado por caracteres no alfanuméricos (<code>==</code>, por ejemplo), utilizarla como operador infijo es la forma más habitual. Si queremos utilizarla de la manera convencional, es decir, como un ''operador prefijo'' (con el nombre de la función antes de los argumentos), debemos encerrarla entre paréntesis. Veamos: {{Haskell/GHCi|1= Prelude> 4 + 9 == 13 True Prelude> (==) (4 + 9) 13 True }} También es posible transformar un valor booleano en otro mediante la ''negación''. <code>not</code> es la función de negación: transforma <code>True</code> en <code>False</code>, y <code>False</code> en <code>True</code>. {{Haskell/GHCi|1= Prelude> not (5 * 2 == 10) False }} Haskell ya dispone del operador de desigualdad: <code>/=</code>, que significa ''no es igual a''. Sin embargo, podríamos definirlo fácilmente mediante <code>not</code> y <code>==</code>: <syntaxhighlight lang = "haskell"> x /= y = not (x == y) </syntaxhighlight> Observa que podemos utilizar la notación de operadores infijos incluso al definirlos. Además, un operador puede definirse a partir de cualquier símbolo [[w:ASCII|ASCII]]. === Otras operaciones booleanas === Existen otras operaciones booleanas muy útiles y necesarias: ''o'' e ''y''. La operación ''A o B'' da como resultado verdadero si ''A'', ''B'' o ambos son verdaderos. En Haskell se representa mediante el operador <code>(||)</code>: {{Haskell/GHCi|1= Prelude> True {{!!}} True True Prelude> True {{!!}} False True Prelude> False {{!!}} True True Prelude> False {{!!}} False False }} La operación ''A y B'' da como resultado verdadero si ''A'' y ''B'' son verdaderos. En Haskell se representa mediante el operador <code>(&&)</code>: {{Haskell/GHCi|1= Prelude> True && True True Prelude> True && False False Prelude> False && True False Prelude> False && False False }} == Guardas == Los programas escritos en Haskell suelen utilizar operadores booleanos mediante una sintaxis muy cómoda y abreviada. Cuando una misma lógica se expresa con una sintaxis diferente, hablamos de [[w:Azúcar sintáctico|''azúcar sintáctico'']]. Esto significa que el código resulta más «agradable al paladar humano». Conviene recordar este término, porque aparece con frecuencia en los materiales sobre Haskell. Uno de estos «azúcares» son las ''guardas'', que utilizan valores booleanos para facilitar la implementación de funciones de una manera muy sencilla. Primero, vamos a implementar la función de valor absoluto para números reales, también llamada [[w:Función modular|función modular o módulo]]. Su definición matemática establece que, si un número es positivo, su módulo es el propio número; si es negativo, su módulo es su opuesto, es decir, cero menos ese número. <math>\text{abs}(x) = |x| = \begin{cases} x, & \mbox{si } x \ge 0 \\ -x, & \mbox{si } x < 0. \end{cases} </math> En esta función, la expresión utilizada para calcular <math>|x|</math> depende del propio valor de <math>x</math>. Si <math>x \ge 0</math> es verdadero, utilizamos la primera expresión; si es falso, utilizamos la segunda. Para expresar este proceso de decisión en Haskell, empleamos guardas. La función quedaría así:<ref group=nota>Esta función ya está disponible en las bibliotecas estándar de Haskell con el nombre <code>abs</code>. No es necesario volver a implementarla.</ref> <syntaxhighlight lang="haskell"> absoluto x | x < 0 = 0 - x | otherwise = x </syntaxhighlight> Es interesante observar que el código anterior se parece bastante a la definición matemática. Veámoslo por partes: * Comenzamos con una definición normal de una función. Primero escribimos el nombre, <code>absoluto</code>, y después especificamos la cantidad de argumentos que acepta: <code>x</code>. * En lugar de escribir el signo <code>=</code> y comenzar la definición, escribimos dos alternativas de comportamiento justo debajo.<ref group=nota>En realidad, podríamos escribirlo todo en una sola línea, como <code>f x | caso 1 | caso 2</code>, pero siempre es preferible dividir el código en varias líneas para facilitar la lectura.</ref> Estas alternativas son las que llamamos ''guardas''. Debemos recordar que la indentación (los espacios en blanco antes de <code>|</code>) no es opcional: sirve para indicar que las guardas pertenecen al ámbito de la definición de <code>absoluto</code>. * Cada guarda comienza con una barra vertical, <code>|</code>. Después de ella, escribimos una expresión que dé como resultado un valor booleano, también llamada condición booleana o ''predicado''. A continuación, aparece el resto de la definición, que comienza con <code>=</code>. La definición indicada en una guarda solo se evaluará si su predicado resulta ser <code>True</code>. * El caso de <code>otherwise</code> se evalúa cuando ninguna de las condiciones anteriores es verdadera. En este ejemplo, si <code>x</code> no es menor que cero, entonces necesariamente es mayor o igual que cero. Por tanto, el predicado de la última guarda podría ser <code>x >= 0</code>, pero lo habitual es utilizar <code>otherwise</code> para abarcar todos los casos que no se hayan contemplado en las guardas anteriores. {{nota|No hay nada mágico detrás de <code>otherwise</code>. En realidad, se define simplemente como <code>otherwise {{=}} True</code>. Esto significa que, siempre que se evalúe <code>otherwise</code>, devolverá <code>True</code> y, por tanto, se ejecutará la definición indicada en su guarda.}} Observa que escribimos <code>0 - x</code> en lugar de <code>-x</code>. Esto se debe a que <code>-</code> ''no es'' una función de un argumento que devuelva <code>0 - x</code>. En realidad, se trata de una abreviatura sintáctica. Aunque resulta muy útil, suele causar conflictos cuando se utiliza junto con la función <code>(-)</code>, que es el operador de resta. Haz una prueba en GHCi: calcula 3 menos -4 sin utilizar paréntesis. Para evitar este tipo de problemas, hemos decidido escribir explícitamente <code>0 - x</code>. == Guardas y <code>where</code> == Es bastante habitual utilizar <code>where</code> dentro de una función con guardas. Por ejemplo, para determinar cuántas soluciones reales tiene una [[w:Ecuación cuadrática|ecuación de segundo grado]] de la forma <math>ax^2+bx+c=0</math>, debemos calcular su discriminante, <math>\Delta = b^2 - 4ac</math>. Si es mayor que cero, hay dos raíces reales; si es igual a cero, hay una; y si es menor que cero, no hay ninguna. Una función de Haskell llamada <code>numSolsReais</code>, que calcule esta cantidad, podría escribirse así: <syntaxhighlight lang = "haskell"> numSolsReales a b c | delta > 0 = 2 | delta == 0 = 1 | otherwise = 0 where delta = b^2 - 4*a*c </syntaxhighlight> == Notas == <references group=nota/> {{AutoCat}} [[Categoría:Libro:Haskell]] pspr2hxhowd1h1t8xmdb107gv3dfbgc 436377 436376 2026-10-13T16:45:50Z Luciérnaga de conocimiento 119502 /* Guardas y where */ 436377 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} == Igualdad y otras comparaciones == En el capítulo anterior, usamos signos de igualdad para definir variables y funciones en Haskell, como se muestra en el siguiente código: <syntaxhighlight lang = "haskell"> r = 5 </syntaxhighlight> Esto significa que, durante la evaluación del programa, todas las apariciones de <code>r</code> se sustituyen por <code>5</code> dentro del mismo ámbito. De manera similar, al evaluar <syntaxhighlight lang = "haskell"> f x = x + 3 </syntaxhighlight> todas las apariciones de <code>f</code> seguidas de un número (el argumento de <code>f</code>) se sustituyen por ese valor más tres. En Matemáticas, el signo de igualdad se usa de manera diferente. Consideremos el siguiente problema: {{Haskell/Ejercicios|1= Resuelve el siguiente problema: <math>x+3=5</math>. }} En este caso, el problema no consiste en representar <math>5</math> como si fuera <math>x+3</math>, o viceversa. En realidad, tenemos una ''proposición'' que afirma que un número <math>x</math>, al sumarle 3, da como resultado 5. Resolver la ecuación significa encontrar el valor de <math>x</math>, si existe alguno, que haga verdadera la proposición. En este sencillo ejemplo, una simple manipulación algebraica nos muestra que <math>x=2</math>, es decir, <math>2</math> es el número que satisface la proposición, ya que <math>2+3=5</math>. Comparar valores para comprobar si son iguales resulta bastante útil en programación. De hecho, es algo muy elemental y necesario. En Haskell, estas pruebas se parecen bastante a una ecuación. Sin embargo, como el signo igual ya se utiliza para definir algo, Haskell emplea un signo de igualdad ''doble'', <code>==</code>, para realizar comparaciones. Veamos: {{Haskell/GHCi|1= Prelude> 2 + 3 == 5 True }} Aquí, GHCi devuelve <code>True</code> (''verdadero'', en inglés) porque <math>2 + 3</math> es igual a <math>5</math>. ¿Y si la ecuación no fuera verdadera? {{Haskell/GHCi|1= Prelude> 7 + 3 == 5 False }} El resultado es <code>False</code> (''falso'', en inglés). Ahora vamos a utilizar la función <code>f</code> que definimos al comienzo de este capítulo: {{Haskell/GHCi|1= Prelude> let f x = x + 3 Prelude> f 2 == 5 True }} Como era de esperar. Puesto que <code>f 2</code> se evalúa como <code>2 + 3</code>, cuyo resultado es <code>5</code>, lo lógico es que <code>5 == 5</code> devuelva <code>True</code>. También podemos comparar dos valores numéricos para averiguar cuál es mayor. Haskell dispone de varios operadores para estas pruebas: <code><</code>, para ''menor que''; <code>></code>, para ''mayor que''; <code><=</code>, para ''menor o igual que''; y <code>>=</code>, para ''mayor o igual que''. Estas pruebas funcionan exactamente igual que <code>==</code>, que significa ''igual a''. Por ejemplo, podríamos utilizar <code><</code> junto con la función <code>area</code> del capítulo anterior para comprobar si un círculo de cierto radio tiene un área menor que otro valor. {{Haskell/GHCi|1= Prelude> let area r = pi * r ^ 2 Prelude> area 5 < 50 False }} == Valores booleanos == ¿Qué sucede cuando GHCi tiene que determinar si estas comparaciones son verdaderas o falsas? Consideremos un caso diferente. Si introducimos una expresión aritmética en GHCi, esta se ''evalúa'' y el resultado numérico aparece en pantalla: {{Haskell/GHCi|1= Prelude> 2 + 2 4 }} Si sustituimos la expresión aritmética por su valor final en una comparación, sucede algo parecido: {{Haskell/GHCi|1= Prelude> 2 == 2 True }} Mientras que el «4» devuelto en el primer caso representa el recuento de alguna cosa, «True» es un ''valor'' que representa la verdad de una determinada proposición. Estos valores se conocen como '''booleanos'''.<ref group=nota>Este término se eligió en homenaje al matemático británico [[w:George Boole|George Boole]].</ref> Naturalmente, solo hay dos valores posibles, que ya se nos han presentado: <code>True</code> y <code>False</code>. == Introducción a los tipos de datos == <code>True</code> y <code>False</code> son valores reales, no una simple analogía. En Haskell, los valores booleanos tienen el mismo ''estatus'' que los valores numéricos, y podemos manipularlos de manera similar. Veamos un ejemplo sencillo: {{Haskell/GHCi|1= Prelude> True == True True Prelude> True == False False }} En efecto, <code>True</code> es igual a <code>True</code>, y <code>True</code> no es igual a <code>False</code>. Ahora bien, ¿es posible determinar si <code>2</code> es igual a <code>True</code>? {{Haskell/GHCi|1= Prelude> 2 == True <interactive>:1:0: No instance for (Num Bool) arising from the literal ‘2’ at <interactive>:1:0 Possible fix: add an instance declaration for (Num Bool) In the first argument of ‘(==)’, namely ‘2’ In the expression: 2 == True In an equation for ‘it’: it = 2 == True }} El compilador produce un error. En realidad, la pregunta original ni siquiera tiene sentido. No se puede comparar un número con algo que no es un número, ni un valor booleano con algo que no es booleano. Haskell incorpora esta noción en el propio lenguaje, y el mensaje de error anterior, aunque es largo y un poco intimidante, expresa precisamente eso: a la izquierda de <code>==</code> hay un número (<code>Num</code>), por lo que se esperaba también un número a la derecha. Sin embargo, un valor booleano (<code>Bool</code>) no es un número, de modo que la prueba de igualdad falla. Queda claro, entonces, que los valores se agrupan en '''tipos''', que establecen los límites de lo que podemos o no hacer con ellos. <code>True</code> y <code>False</code> son valores del tipo <code>Bool</code>. El caso de <code>2</code> es algo más complicado, porque en informática existen varios tipos de números, al igual que en Matemáticas, aunque no sean equivalentes. Pero, en definitiva, sigue siendo un dato numérico (<code>Num</code>). En general, esta característica restrictiva resulta muy valiosa, porque podemos aprovecharla para controlar el comportamiento de nuestros programas y establecer reglas que impidan utilizarlos con tipos de datos que ''no tienen sentido''. De este modo, garantizamos el funcionamiento correcto de lo que desarrollamos. Volveremos a tratar los tipos más adelante, ya que constituyen una parte importante del lenguaje Haskell. == Operadores infijos == Una prueba de igualdad, como <code>2 == 2</code>, también es una expresión, al igual que una operación aritmética, como <code>2 + 2</code>. Ambos casos se evalúan básicamente de la misma manera. Cuando escribimos <code>2 == 2</code> en una sesión de GHCi, este «responde» con <code>True</code>, porque simplemente ha evaluado la expresión. En realidad, el operador <code>==</code> es una función de dos argumentos (el de la izquierda y el de la derecha de la igualdad). Sin embargo, su uso es diferente: Haskell permite escribir las funciones de dos argumentos como ''operadores infijos'', es decir, situados ''entre'' sus dos argumentos. Cuando el nombre de una función está formado por caracteres no alfanuméricos (<code>==</code>, por ejemplo), utilizarla como operador infijo es la forma más habitual. Si queremos utilizarla de la manera convencional, es decir, como un ''operador prefijo'' (con el nombre de la función antes de los argumentos), debemos encerrarla entre paréntesis. Veamos: {{Haskell/GHCi|1= Prelude> 4 + 9 == 13 True Prelude> (==) (4 + 9) 13 True }} También es posible transformar un valor booleano en otro mediante la ''negación''. <code>not</code> es la función de negación: transforma <code>True</code> en <code>False</code>, y <code>False</code> en <code>True</code>. {{Haskell/GHCi|1= Prelude> not (5 * 2 == 10) False }} Haskell ya dispone del operador de desigualdad: <code>/=</code>, que significa ''no es igual a''. Sin embargo, podríamos definirlo fácilmente mediante <code>not</code> y <code>==</code>: <syntaxhighlight lang = "haskell"> x /= y = not (x == y) </syntaxhighlight> Observa que podemos utilizar la notación de operadores infijos incluso al definirlos. Además, un operador puede definirse a partir de cualquier símbolo [[w:ASCII|ASCII]]. === Otras operaciones booleanas === Existen otras operaciones booleanas muy útiles y necesarias: ''o'' e ''y''. La operación ''A o B'' da como resultado verdadero si ''A'', ''B'' o ambos son verdaderos. En Haskell se representa mediante el operador <code>(||)</code>: {{Haskell/GHCi|1= Prelude> True {{!!}} True True Prelude> True {{!!}} False True Prelude> False {{!!}} True True Prelude> False {{!!}} False False }} La operación ''A y B'' da como resultado verdadero si ''A'' y ''B'' son verdaderos. En Haskell se representa mediante el operador <code>(&&)</code>: {{Haskell/GHCi|1= Prelude> True && True True Prelude> True && False False Prelude> False && True False Prelude> False && False False }} == Guardas == Los programas escritos en Haskell suelen utilizar operadores booleanos mediante una sintaxis muy cómoda y abreviada. Cuando una misma lógica se expresa con una sintaxis diferente, hablamos de [[w:Azúcar sintáctico|''azúcar sintáctico'']]. Esto significa que el código resulta más «agradable al paladar humano». Conviene recordar este término, porque aparece con frecuencia en los materiales sobre Haskell. Uno de estos «azúcares» son las ''guardas'', que utilizan valores booleanos para facilitar la implementación de funciones de una manera muy sencilla. Primero, vamos a implementar la función de valor absoluto para números reales, también llamada [[w:Función modular|función modular o módulo]]. Su definición matemática establece que, si un número es positivo, su módulo es el propio número; si es negativo, su módulo es su opuesto, es decir, cero menos ese número. <math>\text{abs}(x) = |x| = \begin{cases} x, & \mbox{si } x \ge 0 \\ -x, & \mbox{si } x < 0. \end{cases} </math> En esta función, la expresión utilizada para calcular <math>|x|</math> depende del propio valor de <math>x</math>. Si <math>x \ge 0</math> es verdadero, utilizamos la primera expresión; si es falso, utilizamos la segunda. Para expresar este proceso de decisión en Haskell, empleamos guardas. La función quedaría así:<ref group=nota>Esta función ya está disponible en las bibliotecas estándar de Haskell con el nombre <code>abs</code>. No es necesario volver a implementarla.</ref> <syntaxhighlight lang="haskell"> absoluto x | x < 0 = 0 - x | otherwise = x </syntaxhighlight> Es interesante observar que el código anterior se parece bastante a la definición matemática. Veámoslo por partes: * Comenzamos con una definición normal de una función. Primero escribimos el nombre, <code>absoluto</code>, y después especificamos la cantidad de argumentos que acepta: <code>x</code>. * En lugar de escribir el signo <code>=</code> y comenzar la definición, escribimos dos alternativas de comportamiento justo debajo.<ref group=nota>En realidad, podríamos escribirlo todo en una sola línea, como <code>f x | caso 1 | caso 2</code>, pero siempre es preferible dividir el código en varias líneas para facilitar la lectura.</ref> Estas alternativas son las que llamamos ''guardas''. Debemos recordar que la indentación (los espacios en blanco antes de <code>|</code>) no es opcional: sirve para indicar que las guardas pertenecen al ámbito de la definición de <code>absoluto</code>. * Cada guarda comienza con una barra vertical, <code>|</code>. Después de ella, escribimos una expresión que dé como resultado un valor booleano, también llamada condición booleana o ''predicado''. A continuación, aparece el resto de la definición, que comienza con <code>=</code>. La definición indicada en una guarda solo se evaluará si su predicado resulta ser <code>True</code>. * El caso de <code>otherwise</code> se evalúa cuando ninguna de las condiciones anteriores es verdadera. En este ejemplo, si <code>x</code> no es menor que cero, entonces necesariamente es mayor o igual que cero. Por tanto, el predicado de la última guarda podría ser <code>x >= 0</code>, pero lo habitual es utilizar <code>otherwise</code> para abarcar todos los casos que no se hayan contemplado en las guardas anteriores. {{nota|No hay nada mágico detrás de <code>otherwise</code>. En realidad, se define simplemente como <code>otherwise {{=}} True</code>. Esto significa que, siempre que se evalúe <code>otherwise</code>, devolverá <code>True</code> y, por tanto, se ejecutará la definición indicada en su guarda.}} Observa que escribimos <code>0 - x</code> en lugar de <code>-x</code>. Esto se debe a que <code>-</code> ''no es'' una función de un argumento que devuelva <code>0 - x</code>. En realidad, se trata de una abreviatura sintáctica. Aunque resulta muy útil, suele causar conflictos cuando se utiliza junto con la función <code>(-)</code>, que es el operador de resta. Haz una prueba en GHCi: calcula 3 menos -4 sin utilizar paréntesis. Para evitar este tipo de problemas, hemos decidido escribir explícitamente <code>0 - x</code>. == Guardas y <code>where</code> == Es bastante habitual utilizar <code>where</code> dentro de una función con guardas. Por ejemplo, para determinar cuántas soluciones reales tiene una [[w:Ecuación cuadrática|ecuación de segundo grado]] de la forma <math>ax^2+bx+c=0</math>, debemos calcular su discriminante, <math>\Delta = b^2 - 4ac</math>. Si es mayor que cero, hay dos raíces reales; si es igual a cero, hay una; y si es menor que cero, no hay ninguna. Una función de Haskell llamada <code>numSolsReales</code>, que calcule esta cantidad, podría escribirse así: <syntaxhighlight lang = "haskell"> numSolsReales a b c | delta > 0 = 2 | delta == 0 = 1 | otherwise = 0 where delta = b^2 - 4*a*c </syntaxhighlight> == Notas == <references group=nota/> {{AutoCat}} [[Categoría:Libro:Haskell]] jp0gon7se8yihj1adkfl2z4aa7f0pzj Haskell/Tipos básicos 0 65086 436374 2026-10-13T16:42:29Z Luciérnaga de conocimiento 119502 Página creada con «{{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación…» 436374 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{nota|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | Juan | Román | Calle del Livramento, 77, Cortiço, Río de Janeiro | 218444 |- | Severino | Retirante | Av. Capibaribe-Río, 1025, Recife | 813586 |} Los campos de cada registro contienen valores diferentes. <tt>Juan</tt> es un valor, mientras que <tt>Calle del Livramento, 77, Cortiço, Río de Janeiro</tt> y <tt>218444</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Ejercicio|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanoVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanoVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) {{=}} 2</code>. # <code>({{!!}})</code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x {{!!}} y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> [[en:Haskell/Type basics]] 26sgp9rev6j9f6c1h9bak276owf7fdg 436375 436374 2026-10-13T16:43:06Z Luciérnaga de conocimiento 119502 436375 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{nota|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | Juan | Román | Calle del Livramento, 77, Cortiço, Río de Janeiro | 218444 |- | Severino | Retirante | Av. Capibaribe-Río, 1025, Recife | 813586 |} Los campos de cada registro contienen valores diferentes. <tt>Juan</tt> es un valor, mientras que <tt>Calle del Livramento, 77, Cortiço, Río de Janeiro</tt> y <tt>218444</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Ejercicio|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanoVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanoVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) {{=}} 2</code>. # <code>({{!!}})</code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x {{!!}} y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] 5thcw1ulmgyfh091hudl2m0vle99180 436378 436375 2026-10-13T16:47:00Z Luciérnaga de conocimiento 119502 /* Ejemplo: abrirVentana */ 436378 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{nota|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | Juan | Román | Calle del Livramento, 77, Cortiço, Río de Janeiro | 218444 |- | Severino | Retirante | Av. Capibaribe-Río, 1025, Recife | 813586 |} Los campos de cada registro contienen valores diferentes. <tt>Juan</tt> es un valor, mientras que <tt>Calle del Livramento, 77, Cortiço, Río de Janeiro</tt> y <tt>218444</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Ejercicio|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) {{=}} 2</code>. # <code>({{!!}})</code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x {{!!}} y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] dw2827vhiavnl02gghzy98erx2gaisn 436379 436378 2026-10-13T16:47:30Z Luciérnaga de conocimiento 119502 /* Caracteres y cadenas de caracteres */ 436379 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{nota|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | Juan | Román | Calle del Livramento, 77, Cortiço, Río de Janeiro | 218444 |- | Severino | Retirante | Av. Capibaribe-Río, 1025, Recife | 813586 |} Los campos de cada registro contienen valores diferentes. <tt>Juan</tt> es un valor, mientras que <tt>Calle del Livramento, 77, Cortiço, Río de Janeiro</tt> y <tt>218444</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicio|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) {{=}} 2</code>. # <code>({{!!}})</code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x {{!!}} y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] frp6pu29vmsfyv87quj8waqeghpkaui 436380 436379 2026-10-13T16:47:48Z Luciérnaga de conocimiento 119502 /* Caracteres y cadenas de caracteres */ 436380 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{nota|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | Juan | Román | Calle del Livramento, 77, Cortiço, Río de Janeiro | 218444 |- | Severino | Retirante | Av. Capibaribe-Río, 1025, Recife | 813586 |} Los campos de cada registro contienen valores diferentes. <tt>Juan</tt> es un valor, mientras que <tt>Calle del Livramento, 77, Cortiço, Río de Janeiro</tt> y <tt>218444</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicios|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) {{=}} 2</code>. # <code>({{!!}})</code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x {{!!}} y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] pjuwzc8vtrh00a1ktum1tad25w4pdxo 436381 436380 2026-10-13T16:56:24Z Luciérnaga de conocimiento 119502 /* Introducción */ 436381 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{nota|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | José | Martínez | Calle de Embajadores, 77, Lavapiés, Madrid, España. | 184344 |- | Juan | José | Carrer de la Marina, 1025, El Poblenou, Barcelona, España. | 724931 |} Los campos de cada registro contienen valores diferentes. <tt>José</tt> es un valor, mientras que <tt>Calle de Embajadores, 77, Lavapiés, Madrid, España</tt> y <tt>184344</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicios|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) {{=}} 2</code>. # <code>({{!!}})</code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x {{!!}} y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] jqqu0smcwuldm8p0u92vc0b751kyx8b 436388 436381 2026-10-13T17:36:10Z Luciérnaga de conocimiento 119502 436388 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{Nota al margen|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | José | Martínez | Calle de Embajadores, 77, Lavapiés, Madrid, España. | 184344 |- | Juan | José | Carrer de la Marina, 1025, El Poblenou, Barcelona, España. | 724931 |} Los campos de cada registro contienen valores diferentes. <tt>José</tt> es un valor, mientras que <tt>Calle de Embajadores, 77, Lavapiés, Madrid, España</tt> y <tt>184344</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicios|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) {{=}} 2</code>. # <code>({{!!}})</code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x {{!!}} y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] 4czuogofcpvq7lvi9iddz38mgqdkaje 436393 436388 2026-10-13T17:48:27Z Luciérnaga de conocimiento 119502 /* Introducción */ 436393 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{Nota al margen|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | José | Martínez | Calle de Embajadores, 77, Lavapiés, Madrid, España. | 184344 |- | Juan | Rodríguez | Carrer de la Marina, 1025, El Poblenou, Barcelona, España. | 724931 |} Los campos de cada registro contienen valores diferentes. <tt>José</tt> es un valor, mientras que <tt>Calle de Embajadores, 77, Lavapiés, Madrid, España</tt> y <tt>184344</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicios|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) {{=}} 2</code>. # <code>({{!!}})</code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x {{!!}} y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] 67xaf3rqnt9b6n69n8w02a9pd9fam9m 436476 436393 2026-10-14T10:28:08Z Luciérnaga de conocimiento 119502 /* Ejemplo: abrirVentana */ 436476 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{Nota al margen|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | José | Martínez | Calle de Embajadores, 77, Lavapiés, Madrid, España. | 184344 |- | Juan | Rodríguez | Carrer de la Marina, 1025, El Poblenou, Barcelona, España. | 724931 |} Los campos de cada registro contienen valores diferentes. <tt>José</tt> es un valor, mientras que <tt>Calle de Embajadores, 77, Lavapiés, Madrid, España</tt> y <tt>184344</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicios|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) {{=}} 2</code>. # ||: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x || y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] nkcegdz7ua2ncvddbpyl5iskys9ga9j 436477 436476 2026-10-14T10:29:16Z Luciérnaga de conocimiento 119502 /* Ejemplo: abrirVentana */ 436477 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{Nota al margen|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | José | Martínez | Calle de Embajadores, 77, Lavapiés, Madrid, España. | 184344 |- | Juan | Rodríguez | Carrer de la Marina, 1025, El Poblenou, Barcelona, España. | 724931 |} Los campos de cada registro contienen valores diferentes. <tt>José</tt> es un valor, mientras que <tt>Calle de Embajadores, 77, Lavapiés, Madrid, España</tt> y <tt>184344</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicios|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) {{=}} 2</code>. # <code>||</code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x <code>||</code> y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] fhf74iwi7li097zbb84r6h0yzjugzfr 436478 436477 2026-10-14T10:31:02Z Luciérnaga de conocimiento 119502 /* Ejemplo: abrirVentana */ 436478 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{Nota al margen|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | José | Martínez | Calle de Embajadores, 77, Lavapiés, Madrid, España. | 184344 |- | Juan | Rodríguez | Carrer de la Marina, 1025, El Poblenou, Barcelona, España. | 724931 |} Los campos de cada registro contienen valores diferentes. <tt>José</tt> es un valor, mientras que <tt>Calle de Embajadores, 77, Lavapiés, Madrid, España</tt> y <tt>184344</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicios|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 {{=}} -4</code>; <code>negativo (-2) <code>=</code> 2</code>. # <code>||</code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y {{=}} not x <code>||</code> y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] diovl3jn32s4fq3humqq97gzjyqj889 436479 436478 2026-10-14T10:36:45Z Luciérnaga de conocimiento 119502 /* Ejemplo: abrirVentana */ 436479 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{Nota al margen|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | José | Martínez | Calle de Embajadores, 77, Lavapiés, Madrid, España. | 184344 |- | Juan | Rodríguez | Carrer de la Marina, 1025, El Poblenou, Barcelona, España. | 724931 |} Los campos de cada registro contienen valores diferentes. <tt>José</tt> es un valor, mientras que <tt>Calle de Embajadores, 77, Lavapiés, Madrid, España</tt> y <tt>184344</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicios|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 = -4</code>; <code>negativo (-2) = 2</code>. # <code><nowiki>||</nowiki></code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y = not x <nowiki>||</nowiki> y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] nq1lvgw4rmh9zfi6zl2ykj8o99zma9e 436480 436479 2026-10-14T10:41:24Z Luciérnaga de conocimiento 119502 /* Ejemplo: abrirVentana */ 436480 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{Nota al margen|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | José | Martínez | Calle de Embajadores, 77, Lavapiés, Madrid, España. | 184344 |- | Juan | Rodríguez | Carrer de la Marina, 1025, El Poblenou, Barcelona, España. | 724931 |} Los campos de cada registro contienen valores diferentes. <tt>José</tt> es un valor, mientras que <tt>Calle de Embajadores, 77, Lavapiés, Madrid, España</tt> y <tt>184344</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicios|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === {{Nota al margen|side=derecha|Una biblioteca es una colección de código común utilizado por muchos programas.}} A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 = -4</code>; <code>negativo (-2) = 2</code>. # <code><nowiki>||</nowiki></code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y = not x <nowiki>||</nowiki> y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] plc9s597oe5t1bkixnywscyoijz7ncz 436482 436480 2026-10-14T10:44:07Z Luciérnaga de conocimiento 119502 /* Ejemplo: abrirVentana */ 436482 wikitext text/x-wiki {{Haskell/Minitoc|capítulo=Principios básicos de Haskell}} En programación, los '''tipos''' se utilizan para agrupar valores similares, con características comunes, dentro de una determinada categoría. En Haskell, su sistema de tipos es una herramienta poderosa que permite evitar muchos errores triviales en un programa. == Introducción == Cuando se crea un programa, generalmente hay que trabajar con distintas entidades. Por ejemplo, consideremos la operación <math>2 + 3</math>. ¿Qué son 2 y 3? Números. ¿Y el signo más que aparece entre ellos? No es un número, sino un símbolo que representa la operación de suma que queremos realizar entre 2 y 3. Ahora imaginemos un programa que nos pide nuestro nombre y nos saluda con un mensaje que dice «Hola». Ni nuestro nombre ni el mensaje son números, sino palabras. Por lo tanto, son texto. En programación, cuando nos referimos a texto, generalmente hablamos de ''String'', que significa «hilo» o «cadena» en inglés. Es una forma abreviada de decir «cadena de caracteres». {{Nota al margen|En Haskell, existe una regla según la cual el nombre de cualquier tipo debe comenzar con una letra mayúscula. A partir de aquí, seguiremos siempre esta regla.}} Una base de datos es un buen ejemplo para ilustrar los distintos tipos de datos que existen. Imaginemos que tenemos una tabla de contactos, como una agenda. Su contenido podría ser el siguiente: {|class="wikitable" | Primer nombre | Apellido | Dirección postal | Número de teléfono |- | José | Martínez | Calle de Embajadores, 77, Lavapiés, Madrid, España. | 184344 |- | Juan | Rodríguez | Carrer de la Marina, 1025, El Poblenou, Barcelona, España. | 724931 |} Los campos de cada registro contienen valores diferentes. <tt>José</tt> es un valor, mientras que <tt>Calle de Embajadores, 77, Lavapiés, Madrid, España</tt> y <tt>184344</tt> son otros dos. En cuanto a sus tipos, «Primer nombre» y «Apellido» contienen texto, por lo que sus valores son del tipo String. A primera vista, también podríamos clasificar «Dirección postal» como texto. Sin embargo, existen distintas formas de interpretar una dirección como esta. La primera palabra podría ser el nombre de un edificio o de una calle, mientras que los números que aparecen a continuación podrían indicar el número del inmueble o de su parcela. Las palabras siguientes podrían corresponder al barrio o a la ciudad. También es posible que la dirección comience con «Apartado postal», en cuyo caso ni siquiera indicaría un domicilio de entrega, sino únicamente un apartado de correspondencia en una oficina postal. Por lo tanto, cada parte de una dirección tiene un significado propio. En principio, podríamos decir que una dirección es un String. De hecho, lo es, pero al tratarla únicamente como texto perderíamos información importante. Cuando definimos algo como String, indicamos que se trata de una simple secuencia de caracteres. En cambio, si podemos incluirlo en una categoría más específica, dispondremos de mucha más información sobre su contenido. Por ejemplo, si sabemos que un String representa una dirección, podremos interpretarlo y manipularlo de una manera más apropiada. La misma lógica puede aplicarse a los números de teléfono. Podemos especificar un tipo llamado NumeroTelefono y, cuando encontremos un valor de ese tipo, podremos buscar patrones como los códigos de área o de país. Otra razón para no tratarlos como números comunes es que no tiene sentido realizar operaciones aritméticas con ellos: ¿qué esperamos obtener al multiplicar un NumeroTelefono por 100 o dividirlo por la mitad? Es posible que el resultado ni siquiera sea un número de teléfono válido. Además, no es aceptable perder, omitir ni redondear sus cifras por ese mismo motivo. === La utilidad de los tipos === Pero ¿por qué nos ayuda a programar separar y categorizar las cosas? Una vez definidos los tipos, podemos especificar qué operaciones se pueden realizar con sus valores y cuáles no. Esto resulta especialmente útil para evitar errores en programas extensos. == Usando el comando interactivo <code>:type</code> == Exploremos el funcionamiento de los tipos mediante una sesión de GHCi. Podemos consultar el tipo de una expresión con el comando <code>:type</code> o su abreviatura <code>:t</code>. Probémoslo con valores booleanos y expresiones booleanas: {{Haskell/GHCi|1= Prelude> :type True True :: Bool Prelude> :type False False :: Bool Prelude> :t (3 < 5) (3 < 5) :: Bool }} El símbolo <tt>::</tt> aparece en todos los resultados. Se lee como «es del tipo» y va seguido de una ''firma de tipo'' o ''anotación de tipo''. El comando <code>:t</code> nos muestra que los valores verdadero (<code>True</code>) y falso (<code>False</code>) son del tipo <code>Bool</code>. Incluso las expresiones tienen un tipo definido: cuando se evalúan, producen un valor de tipo <code>Bool</code>. Quizá esto ya esté claro, pero conviene señalar que <code>Bool</code> es el tipo de dato elegido para representar valores de verdadero o falso. Por ejemplo, permite indicar si se encontró un valor o si se pulsó un botón. == Caracteres y ''cadenas de caracteres'' == Probemos ahora <code>:t</code> con otros valores. En Haskell, un único carácter se representa encerrándolo entre comillas simples: {{Haskell/GHCi|1= Prelude> :t 'H' 'H' :: Char }} Por lo tanto, los caracteres son del tipo <code>Char</code>, abreviatura de ''character'' (carácter, en inglés). Para representar una cadena de caracteres en lugar de uno solo, debemos utilizar comillas dobles: {{Haskell/GHCi|1= Prelude> :t "Hola, mundo" "Hola, mundo" :: [Char] }} De nuevo aparece <code>Char</code>, pero esta vez entre corchetes. <code>[Char]</code> significa una ''lista'' de caracteres. Haskell considera que toda cadena de caracteres es una lista. Las listas son muy importantes en Haskell, y hablaremos de ellas más adelante. {{Haskell/Ejercicios|1= # Usa <code>:type</code> seguido de un valor como <code>"H"</code> en GHCi. Observa que aquí utilizamos comillas dobles. ¿Qué sucede y por qué? # Usa <code>:type</code> seguido de un valor como <code>'Hola, mundo'</code> en GHCi. Observa que aquí utilizamos comillas simples. ¿Qué sucede y por qué? }} Haskell nos permite crear ''sinónimos de tipos'', que funcionan como los sinónimos del lenguaje humano: palabras diferentes que tienen el mismo significado. Estos sinónimos son nombres alternativos para los tipos. Por ejemplo, <tt>String</tt> está definido como sinónimo de <tt>[Char]</tt>, por lo que podemos utilizar uno u otro indistintamente: <syntaxhighlight lang="haskell"> "Hola, mundo" :: String </syntaxhighlight> El código anterior es perfectamente válido. Para las personas, resulta mucho más claro leer <code>String</code> que leer <code>[Char]</code> e interpretar que se trata de una lista de caracteres y, por lo tanto, de una cadena de caracteres. == Tipos de las funciones == Hasta ahora hemos visto los tipos de algunos valores (cadenas de caracteres, valores booleanos, caracteres, etc.). También hemos visto que los tipos nos ayudan a categorizar y describir determinados valores de una forma más conveniente. Ahora veremos qué hace que el sistema de tipos de Haskell sea tan importante y poderoso: las ''funciones'' también tienen tipos. === Ejemplo === Podemos invertir un valor booleano mediante la función <code>not</code>, como ya vimos. Para conocer el tipo de una función, debemos tener en cuenta dos cosas: los tipos de sus argumentos y el tipo del valor que devuelve, es decir, el tipo de su salida. En el caso de <code>not</code>, es muy sencillo: recibe un valor de tipo <code>Bool</code> y devuelve otro del mismo tipo. En Haskell, utilizamos la siguiente notación: <syntaxhighlight lang="haskell"> not :: Bool -> Bool </syntaxhighlight> Esta línea puede leerse así: «<tt>not</tt> es del tipo que recibe valores de tipo <tt>Bool</tt> y devuelve valores de tipo <tt>Bool</tt>». En otras palabras, es una función que transforma un valor de tipo <tt>Bool</tt> (la entrada) en otro de tipo <tt>Bool</tt> (la salida). En GHCi también podemos utilizar <code>:t</code> con las funciones: {{Haskell/GHCi|1= Prelude> :t not not :: Bool -> Bool }} Por lo tanto, el tipo de una función se describe mediante el tipo de su entrada y el de su salida. === Ejemplo: <code>chr</code> y <code>ord</code> === La representación del texto siempre ha sido un problema para las computadoras. En su nivel más básico, una computadora solo puede interpretar datos binarios: unos y ceros. Para representar un texto, cada carácter debe convertirse primero en un número, que después puede expresarse en forma binaria para que la computadora lo interprete. La conversión a binario es una tarea sencilla para la máquina, por lo que aquí solo nos interesa la conversión de un carácter a su representación numérica. Una forma sencilla de realizar esta tarea sería enumerar todos los caracteres posibles. Por ejemplo, podríamos hacer corresponder la letra «a» con el número 1, la «b» con el 2, la «c» con el 3, y así sucesivamente. Haskell ya dispone de una codificación de caracteres, y sería tedioso consultar su tabla cada vez que necesitáramos conocer los valores numéricos. Por comodidad, contamos con dos funciones que realizan estas conversiones: <code>chr</code> y <code>ord</code>.<ref group="nota">No es exactamente así como funcionan <code>chr</code> y <code>ord</code>, pero esta descripción es válida por ahora.</ref> Investiguemos sus tipos: <syntaxhighlight lang="haskell"> chr :: Int -> Char ord :: Char -> Int </syntaxhighlight> Como ya sabemos qué significa <code>Char</code>, expliquemos <code>Int</code>. Este tipo representa números enteros y es solo uno de los varios tipos numéricos definidos en Haskell. De hecho, Haskell dispone de más de un tipo para representar números enteros. La firma de tipo de <code>chr</code> nos indica que recibe un argumento de tipo <code>Int</code>, es decir, un número entero, y produce un resultado de tipo <code>Char</code>. En el caso de <code>ord</code>, sucede lo contrario: recibe un argumento de tipo <code>Char</code> y devuelve uno de tipo <code>Int</code>. Al analizar ambas firmas, queda claro cuál de las funciones convierte un carácter en un número (<code>ord</code>) y cuál realiza la operación inversa (<code>chr</code>). Veamos algunos ejemplos. Estas funciones no están disponibles de forma predeterminada al abrir una sesión de GHCi, por lo que debemos cargar el módulo <code>Data.Char</code>, donde están definidas, mediante el comando <code>:module Data.Char</code> o su abreviatura <code>:m</code>. {{Haskell/GHCi|1= Prelude> :m Data.Char Prelude Data.Char> chr 97 'a' Prelude Data.Char> chr 98 'b' Prelude Data.Char> ord 'c' 99 }} === Funciones con más de un argumento === ¿Cuál sería el tipo de una función con más de un argumento? <syntaxhighlight lang="haskell"> xor p q = (p || q) && not (p && q) </syntaxhighlight> <code>xor</code> es la función lógica «o exclusivo», que evalúa si exactamente uno de sus argumentos es <code>True</code>. Si ambos son verdaderos o ambos son falsos, devuelve <code>False</code>. El método básico para describir el tipo de una función con varios argumentos consiste en escribir los tipos de cada argumento en secuencia (primero el de <code>p</code>, después el de <code>q</code>, por ejemplo) y añadir al final el tipo del resultado. Después, basta con intercalar los tipos mediante el símbolo <code>-></code>.<ref group="nota">Trataremos este tema con más detalle en el capítulo sobre [[Haskell/Funciones de alta orden|funciones de orden superior]].</ref> <ol> <li> Escribe los tipos de los argumentos de entrada en secuencia y, al final, el tipo de salida. La definición de <code>xor</code> nos indica que sus argumentos se evalúan como <code>True</code> o <code>False</code>. Por lo tanto, ambos deben ser del tipo <code>Bool</code>. La salida también es de tipo <code>Bool</code>: <pre> Bool Bool Bool ^^^^ p es de tipo Bool ^^^^ q es de tipo Bool ^^^^ la salida también es de tipo Bool </pre> </li> <li> Intercala los tipos mediante <code>-></code>: <pre>Bool -> Bool -> Bool</pre> </li> </ol> Por lo tanto, la firma de tipo de <code>xor</code> es: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool </syntaxhighlight> Puede leerse así: «<tt>xor</tt> es del tipo que recibe dos valores <tt>Bool</tt> y devuelve un valor <tt>Bool</tt>». === Ejemplo: <code>abrirVentana</code> === {{Nota al margen|margen=derecha|Una biblioteca es una colección de código común utilizado por muchos programas.}} A medida que profundizamos en nuestros conocimientos de programación, algunos términos técnicos se vuelven habituales. Uno de ellos es el de ''bibliotecas'', que son conjuntos de código destinados a un propósito determinado y que pueden utilizarse en distintos programas. Uno de los tipos de bibliotecas más comunes es el de las GUI (''<u>G</u>raphical <u>U</u>ser <u>I</u>nterface'', o «interfaz gráfica de usuario», en español). En este tipo de bibliotecas podemos encontrar funciones para crear botones y menús, así como para gestionar la interacción con el ratón y el teclado. Una posible función sería <code>abrirVentana</code>, que probablemente abriría una ventana nueva al ejecutarse. La firma de tipo de una función como esta podría ser: <syntaxhighlight lang="haskell"> abrirVentana :: TituloVentana -> TamanioVentana -> Ventana </syntaxhighlight> No necesitamos conocer en profundidad la definición de estos tipos, pero sus nombres nos ayudan a entender qué se necesita para crear una ventana. También es muy probable que estén definidos en la propia biblioteca que contiene la función <code>abrirVentana</code>. Analicémoslo por partes. Recordemos lo que explicamos en la [[#Funciones con más de un argumento|sección anterior]]: el último tipo descrito en la firma de una función es el de su salida. Por lo tanto, ¿cuántos argumentos necesita <code>abrirVentana</code>? Dos. ¿Cuáles son sus tipos? <code>TituloVentana</code> y <code>TamanioVentana</code>. ¿Y cuál es el tipo de la salida? <code>Ventana</code>. Así, podemos entender que «<code>abrirVentana</code> es una función que crea una ventana nueva y que, para hacerlo, necesita un título y un tamaño». Observa que no es necesario haber utilizado una función para comprender qué hace. Cuando utilizamos tipos cuyos nombres son suficientemente claros, la firma de la función nos da pistas sobre su funcionamiento. Es una buena práctica probar las funciones con <code>:t</code> en GHCi antes de utilizarlas. Cuanto más lo hagas, mejor será tu intuición para interpretar los tipos de las funciones. {{Haskell/Ejercicios|1= Intenta deducir las anotaciones de tipo de las siguientes funciones. Si alguna de ellas involucra números, supón que son del tipo Int. # <code>negativo</code>: una función que recibe un Int y devuelve su opuesto (con el signo invertido). Ejemplos: <code>negativo 4 = -4</code>; <code>negativo (-2) = 2</code>. # <code><nowiki>||</nowiki></code>: la función lógica «o». Recibe dos valores booleanos y comprueba si al menos uno de ellos es verdadero. Si es así, devuelve verdadero; de lo contrario, devuelve falso. # <code>diasEnElMes</code>: una función que calcula la cantidad de días de un mes determinado. Recibe un valor que indica si el año es bisiesto y el número del mes, del 1 al 12. # <code>f x y = not x <nowiki>||</nowiki> y</code> # <code>g x = (2*x - 1)^2</code> }} == Anotaciones de tipo en archivos de código == Ahora que ya comprendemos un poco qué son los tipos y cómo pueden ayudarnos, veamos cómo incluir este tipo de anotaciones en el código fuente de un programa. Consideremos la función <code>xor</code> que definimos anteriormente. Si queremos definirla en un archivo y anotar su tipo, basta con escribir lo siguiente: <syntaxhighlight lang="haskell"> xor :: Bool -> Bool -> Bool xor p q = (p || q) && not (p && q) </syntaxhighlight> Eso es todo. Para mayor claridad, es habitual colocar la anotación inmediatamente antes de la definición de la función, por lo que se recomienda seguir esta convención. Además de facilitar la comprensión del funcionamiento de una función, las anotaciones de tipo también son fundamentales para que el compilador interprete correctamente el código, como veremos a continuación. === Inferencia de tipos === Como dijimos antes, las funciones tienen tipos. Es importante destacar que ''siempre'' los tienen. Entonces, ¿cómo pudimos escribir las funciones del capítulo anterior sin especificar ninguna firma? Esto es posible porque el compilador puede ''inferir'' el tipo de una función basándose en las operaciones que realiza. Veamos el siguiente caso: <syntaxhighlight lang="haskell"> ehL c = c == 'l' </syntaxhighlight> <code>ehL</code> es una función que recibe un argumento, <code>c</code>, y devuelve el resultado de la expresión <code>c == 'l'</code>. Aunque no hayamos anotado explícitamente el tipo de <code>c</code> ni el de la salida, podemos intentar deducir la firma de tipo de <code>ehL</code>. Veamos cómo hacerlo: * Es una función que recibe un argumento, <code>c</code>. Toda función en Haskell tiene una salida. <pre> _ -> _ </pre> * La salida es el resultado de la operación <code>c == 'l'</code>. Como ya vimos, <code>(==)</code> es un operador de comparación de igualdad, y estas comparaciones suelen producir una respuesta de verdadero o falso, que puede representarse mediante el tipo <code>Bool</code>. De hecho, ya vimos el operador <code>(==)</code> en [[Haskell/Verdadero o falso|Verdadero o falso]], por lo que sabemos con certeza que su resultado es de tipo <code>Bool</code>. <pre> _ -> Bool </pre> * El operador <code>(==)</code> compara <code>c</code> con <code>'l'</code>. No tiene sentido comparar valores de tipos incompatibles. El valor <code>'l'</code> es de tipo <code>Char</code>, por lo que <code>c</code> también debe ser de ese tipo. <pre> Char -> Bool </pre> Si quieres comprobar lo que acabamos de deducir, define <code>ehL</code> en GHCi sin especificar una firma y utiliza después <code>:t</code> para consultar su tipo: {{Haskell/GHCi|1= Prelude> let ehL c = c == 'l' Prelude> :t ehL ehL :: Char -> Bool }} El compilador realiza un proceso bastante parecido para determinar el tipo de una función. Sin embargo, el resultado puede no ser la firma que esperas o deseas, e incluso puede ocurrir que no consiga determinar una firma válida. Por eso, se recomienda escribir siempre las anotaciones de tipo de las funciones: <syntaxhighlight lang="haskell"> ehL :: Char -> Bool ehL c = c == 'l' </syntaxhighlight> También existen otras buenas razones para incluirlas en el código: * '''Documentación:''' como ya vimos, las anotaciones de tipo ayudan a comprender qué hace una función. ''Documentar'' un programa consiste precisamente en describir qué hacen sus partes y cómo funcionan. Por supuesto, los comentarios también ayudan, pero en Haskell las anotaciones de tipo son tan importantes como ellos. * '''''Depuración'' (''debugging''):''' muchos errores de los programas, conocidos como ''bugs'', se producen al utilizar una función de manera incorrecta. Las anotaciones de tipo ayudan tanto al programador como al compilador a comprobar si una función se está utilizando correctamente, verificando los tipos de las entradas y salidas. Cuando declaramos las anotaciones y se produce un error de incompatibilidad de tipos, el compilador muestra un mensaje que indica dónde está el problema. Si omitimos los tipos, el compilador puede inferir una firma que funcione en una parte del programa, pero que provoque otros errores inesperados durante la ejecución o errores de compilación en otras partes. Veamos un ejemplo sencillo de depuración. Prueba lo siguiente en GHCi: {{Haskell/GHCi|1= Prelude> "hola" ++ " mundo" "hola mundo" Prelude> "hola" + " mundo" <interactive>:2:1: error: * No instance for (Num [Char]) arising from a use of `+' * In the expression: "hola" + " mundo" In an equation for `it': it = "hola" + " mundo" }} Acabamos de introducir el operador <code>(++)</code>, que sirve para concatenar las dos cadenas de caracteres <code>"hola"</code> y <code>" mundo"</code>. Si, por un error de escritura, utilizamos <tt>+</tt> en lugar de <tt>++</tt>, aparecerá el mensaje de error anterior. En resumen, el mensaje nos indica que el operador <code>(+)</code> solo está definido para valores pertenecientes a la clase de tipos <code>Num</code>, mientras que estamos intentando aplicarlo a valores de tipo <code>[Char]</code>, es decir, cadenas de caracteres. Es importante señalar que la comprobación de tipos no evita por completo todos los errores. De hecho, todavía podemos escribir una función de una manera que no produzca el resultado deseado. Sin embargo, reflexionar sobre las operaciones que podemos realizar con cada tipo de dato nos ayuda a razonar mejor sobre la lógica de nuestros programas. == Notas == <references group="nota"/> {{AutoCat}} [[Categoría:Libro:Haskell]] 1dc2lcsimtseqrrscbbrmx44rmrcn48 Categoría:Wikilibros:Plantillas de sustitución de caracteres 14 65088 436385 2026-10-13T17:12:44Z Luciérnaga de conocimiento 119502 Página creada con «[[Categoría:Plantillas de utilidad]]» 436385 wikitext text/x-wiki [[Categoría:Plantillas de utilidad]] mxtuoknlmsbun74hqiins3tlxf1bjsi Categoría:Plantillas de utilidad 14 65089 436386 2026-10-13T17:13:21Z Luciérnaga de conocimiento 119502 Página creada con «[[Categoría:Wikilibros:Plantillas]]» 436386 wikitext text/x-wiki [[Categoría:Wikilibros:Plantillas]] d4d3mdquvv1bbrqmhqw5pvvzr4cjecx Plantilla:Nota al margen 10 65090 436389 2026-10-13T17:38:50Z Luciérnaga de conocimiento 119502 Página creada con «<div bgcolor="#f0f0ff" style="float:{{{side|left}}}; width:17ex; border: 1px solid #C6C9FF; padding: 1em; margin:1em; font-style:italic;">{{{1}}}</div><noinclude>{{Documentación}}[[Categoría:Plantillas de información|{{PAGENAME}}}]]</noinclude>» 436389 wikitext text/x-wiki <div bgcolor="#f0f0ff" style="float:{{{side|left}}}; width:17ex; border: 1px solid #C6C9FF; padding: 1em; margin:1em; font-style:italic;">{{{1}}}</div><noinclude>{{Documentación}}[[Categoría:Plantillas de información|{{PAGENAME}}}]]</noinclude> 01ely9nb1p98d7zrih4a0q5ymzz6ke6 436481 436389 2026-10-14T10:43:14Z Luciérnaga de conocimiento 119502 436481 wikitext text/x-wiki <div bgcolor="#f0f0ff" style="float:{{{side|izquierda}}}; width:17ex; border: 1px solid #C6C9FF; padding: 1em; margin:1em; font-style:italic;">{{{1}}}</div><noinclude>{{Documentación}}[[Categoría:Plantillas de información|{{PAGENAME}}}]]</noinclude> 8noh49tngyhqh0yz677zmw5pdrmc6lg 436483 436481 2026-10-14T10:48:14Z Luciérnaga de conocimiento 119502 436483 wikitext text/x-wiki <div bgcolor="#f0f0ff" style="float:{{{margen|izquierda}}}; width:17ex; border: 1px solid #C6C9FF; padding: 1em; margin:1em; font-style:italic;">{{{1}}}</div><noinclude>{{Documentación}}[[Categoría:Plantillas de información|{{PAGENAME}}}]]</noinclude> 63uhe5oig2ebipidrt8m8g6w9d2y8k8 436484 436483 2026-10-14T10:51:15Z Luciérnaga de conocimiento 119502 436484 wikitext text/x-wiki <div style="float:{{#switch:{{lc:{{{margen|izquierda}}}}}|derecha|right=right|#default=left}}; background-color:#f0f0ff; width:17ex; border:1px solid #C6C9FF; padding:1em; margin:1em; font-style:italic;">{{{1}}}</div><noinclude>{{Documentación}}[[Categoría:Plantillas de información]]</noinclude> si4p2ufy00edbyna8vt0m6mmwoshzwb 436485 436484 2026-10-14T10:52:51Z Luciérnaga de conocimiento 119502 436485 wikitext text/x-wiki <div style="float:{{#switch:{{lc:{{{margen|izquierda}}}}}|derecha|right=right|#default=left}}; width:17ex; border:1px solid #C6C9FF; padding:1em; margin:1em; font-style:italic;">{{{1}}}</div><noinclude>{{Documentación}}[[Categoría:Plantillas de información]]</noinclude> roixc6nj9c3lxxrcae914efh524ah0e Plantilla:Nota al margen/uso 10 65091 436390 2026-10-13T17:40:26Z Luciérnaga de conocimiento 119502 Página creada con «<templatedata> { "params": { "1": { "label": "content", "description": "Content of the note.", "type": "string", "required": true }, "side": { "description": "Which side of the page to display this note at.", "type": "string", "suggestedvalues": [ "left", "right" ], "default": "left" } }, "description": "Display a small note on either the left or right side of the screen." } </templatedata>» 436390 wikitext text/x-wiki <templatedata> { "params": { "1": { "label": "content", "description": "Content of the note.", "type": "string", "required": true }, "side": { "description": "Which side of the page to display this note at.", "type": "string", "suggestedvalues": [ "left", "right" ], "default": "left" } }, "description": "Display a small note on either the left or right side of the screen." } </templatedata> qjp4hweqn5chu5i4s243lut7t9tdtcb 436391 436390 2026-10-13T17:41:20Z Luciérnaga de conocimiento 119502 436391 wikitext text/x-wiki <templatedata> { "params": { "1": { "label": "contenido", "description": "Contenido de la nota.", "type": "string", "required": true }, "side": { "description": "En qué lado de la página se mostrará esta nota.", "type": "string", "suggestedvalues": [ "izquierda", "derecha" ], "default": "izquierda" } }, "description": "Muestra una pequeña nota en el lado izquierdo o derecho de la pantalla." } </templatedata> br17tujnol6nw4w14qgdr4t39o56vdb 436392 436391 2026-10-13T17:41:42Z Luciérnaga de conocimiento 119502 436392 wikitext text/x-wiki <templatedata> { "params": { "1": { "label": "contenido", "description": "Contenido de la nota.", "type": "string", "required": true }, "margen": { "description": "En qué lado de la página se mostrará esta nota.", "type": "string", "suggestedvalues": [ "izquierda", "derecha" ], "default": "izquierda" } }, "description": "Muestra una pequeña nota en el lado izquierdo o derecho de la pantalla." } </templatedata> m9i5p0c6452acssblbktfgh97buoich Usuario discusión:Luciérnaga de conocimiento 3 65093 436473 2026-10-13T23:41:33Z Mazbel 57931 Página creada con «{{subst:Bienvenida}}~~~~ == Creación de plantillas == Hola, Luciérnaga de conocimiento: Te escribo respecto de la creación y utilización de plantillas, principalmente en el libro Haskell. Es necesario revisar su utilidad, evitar duplicaciones y respetar las decisiones comunitarias de borrado. '''1. Plantillas redundantes y necesidad de justificar su creación''' En el caso de [[:Plantilla:Haskell/Minitoc]], en el resumen de borrado, señalé que existe :Plant…» 436473 wikitext text/x-wiki {| width="100%" cellspacing="0" cellpadding="6" style="font-size:95%; line-height: 15px; color: var(--color-base); background:var(--background-color-option-yellow, #faf6ed); border: 1px solid var(--border-color-subtle, #faecc8); border-radius: 2px;" |- | colspan="4" style="color: var(--color-base);background: var(--background-color-content-removed, #faecc8)" |<span style="font-size: 120%">'''Hola, {{BASEPAGENAME}}.''' Te damos la bienvenida a Wikilibros en español</span> |- | colspan="4" | Gracias por participar en el proyecto. Esperamos que la colaboración te resulte agradable y que aproveches tu estancia por aquí. |- | colspan="4" | [[w:Wikilibros|Wikilibros]] es una colección de libros de texto de contenido libre que surgió en julio de 2003; desde entonces se han establecido varios principios definidos por la comunidad. Por favor, tómate un tiempo para '''explorar los temas siguientes''', antes de comenzar a editar en Wikilibros. |- | align="right" | <imagemap> Imagen:Crystal Clear app lassist.png|30x30px default [[Wikilibros:Bienvenida]] desc none </imagemap> | [[Wikilibros:Bienvenida|'''Página de bienvenida''']]. | align="right" | <imagemap> Imagen:Nuvola apps kpdf2.svg|30x30px default [[Wikilibros:Manual]] desc none </imagemap> | [[Manual de Wikilibros|'''Manual'''.<br>Manual general de Wikilibros]]. |- | width="8%" align="right" | <imagemap> Imagen:Crystal Clear app kedit.png|30x30px default [[Wikilibros:Zona de pruebas]] desc none </imagemap> | width="38%" | [[Wikilibros:Zona de pruebas|'''Zona de pruebas'''.<br>Para que realices pruebas de edición]]. | align="right" | <imagemap> Imagen:Nuvola apps khelpcenter.png|30x30px default [[Ayuda:Cómo empezar una página]] desc none </imagemap> | [[Wikilibros:Ayuda|'''Ayuda'''<br />Página con enlaces útiles. ]] |- | align="right" | <imagemap> Imagen:Nuvola apps important yellow.svg|30x30px default [[Wikilibros:Lo que Wikilibros no es]] desc none </imagemap> | [[Wikilibros:Lo que Wikilibros no es|'''Cosas que no se deben hacer'''.<br>Resumen de errores más comunes a evitar]]. | width="8%" align="right" | <imagemap> Imagen:Crystal Clear action apply.png|30x30px default [[w:Ayuda:Cómo se edita una página]] desc none </imagemap> | [[Manual wiki/Edición|'''Aprende a editar páginas'''.<br>Guía de edición de páginas wiki]]. |- | align="right" | <imagemap> Imagen:Crystal Clear app file-manager.png|30x30px default [[Wikilibros:Políticas y orientaciones]] desc none </imagemap> | [[Wikilibros:Políticas y orientaciones|'''Las políticas de Wikilibros'''.<br>Estándares y reglas aprobadas por la comunidad]]. | align="right" | <imagemap> Imagen:Nuvola apps ksirc.png|30x30px default [[Manual wiki]] desc none </imagemap> | [[Ayuda:Preguntas frecuentes|'''Preguntas frecuentes'''.<br>Lo que mucha gente pregunta]]. |- | align="right" | <imagemap> Imagen:Nuvola apps kteatime.png|30x30px default [[Wikilibros:Café]] desc none </imagemap> | [[Wikilibros:Café|'''Café'''.<br>Donde puedes preguntar a otros wikilibristas]]. | align="right" | <imagemap> Imagen:Curly Brackets.svg|30x30px default [[w:Ayuda:Referencia rápida]] desc none </imagemap> | [[w:Ayuda:Referencia rápida| '''Referencia rápida'''.<br>Un resumen sobre cómo utilizar el wikicódigo]] |- | colspan="4" style="color: var(--color-base); border-top:1px solid var(--background-color-content-removed, #faecc8);" | [[Imagen:Signature button.png|right]] Ésta es tu '''página de discusión''', en la cual puedes recibir mensajes de otros wikilibristas. Para contactar con otro wikilibrista debes escribirle '''en su página de discusión''', o usar [[Plantilla:Respuesta a|la plantilla de respuesta]]; si no, no será notificado. Al final del mensaje debes firmar escribiendo cuatro <code><nowiki>~~~~</nowiki></code> o presionando el botón mostrado en la imagen. Recuerda que ''los libros no se firman''. |} Esperamos que pases buenos momentos en Wikilibros. [[Usuario:Mazbel|<span style="font-weight:bold; font-family:cursive, serif;">Mazbel</span>]] [[Usuario discusión:Mazbel|<span style="font-size:small; font-family:cursive, serif;">(Discusión)</span>]] 23:41 13 oct 2026 (UTC) == Creación de plantillas == Hola, Luciérnaga de conocimiento: Te escribo respecto de la creación y utilización de plantillas, principalmente en el libro Haskell. Es necesario revisar su utilidad, evitar duplicaciones y respetar las decisiones comunitarias de borrado. '''1. Plantillas redundantes y necesidad de justificar su creación''' En el caso de [[:Plantilla:Haskell/Minitoc]], en el resumen de borrado, señalé que existe [[:Plantilla:+ÍndiceSección]] para el mismo propósito. Antes de crear otra plantilla, comprueba si la existente permite resolver lo que necesitas. Si presenta alguna limitación, explica en la discusión del libro qué función adicional buscas y por qué no puede incorporarse a la solución disponible. Respecto de [[:Plantilla:Color de texto por defecto]], [[:Plantilla:Encabezado3 oculto]] y [[:Plantilla:Haskell/GHCi]], conviene justificar qué ventaja concreta aporta cada una. Una plantilla puede ser útil para reutilizar código o mantener un formato uniforme; sin embargo, cuando únicamente introduce un fragmento sencillo que puede escribirse directamente y no aporta una ventaja de mantenimiento, corresponde valorar su simplificación. El objetivo es mantener el libro comprensible y fácil de editar para cualquier colaborador. Por favor, documenta la finalidad de estas plantillas y revisa si existen alternativas antes de crear otras equivalentes. '''2. Consultas de borrado''' [[:Plantilla:!!]] fue incluida expresamente en [[Wikilibros:Consultas de borrado/Archivo/2026#Cinco plantillas sin uso y obsoletas|la consulta de borrado sobre cinco plantillas sin uso y obsoletas]], cerrada el 19 de julio de 2026 con resultado de eliminar. La decisión también abarcó sus páginas de uso. No corresponde recrear una plantilla manteniendo el problema que motivó su eliminación. Si consideras que existe una necesidad técnica nueva que justifica recuperarla, solicita su restauración en el [[WL:TAB|tablón de bibliotecarios]] y explica qué ha cambiado respecto de los argumentos de la consulta. La recreación de páginas borradas por consulta, en contra del criterio que llevó a su eliminación, está contemplada en el criterio G10 de [[Wikilibros:Política de borrado]]. '''3. Ejercicios y enfoque del proyecto''' En cuanto a [[:Plantilla:Haskell/Ejercicios]], es importante distinguir la pertinencia de los ejercicios de la necesidad de utilizar una plantilla para presentarlos. Wikilibros está orientado a la elaboración colaborativa de libros de texto, manuales y otros materiales instructivos. Los ejercicios pueden formar parte de un libro cuando complementan sus explicaciones y mantienen una relación clara con los contenidos. Si el propósito es desarrollar un curso con actividades, evaluaciones, seguimiento o participación de estudiantes, conviene valorar [[:v:Portada|Wikiversidad]], cuyo enfoque resulta más adecuado para ese tipo de proyecto educativo. En cualquier caso, la creación de una plantilla específica debe aportar una utilidad que justifique su mantenimiento. Te pido que revises estas cuestiones y que, antes de volver a crear una plantilla eliminada o introducir otra que duplique una función existente, plantees la necesidad en la página de discusión correspondiente o en el [[WL:Café|café]], así podremos acordar una solución útil para el libro y coherente con las decisiones de la comunidad. Saludos. --[[Usuario:Mazbel|<span style="font-weight:bold; font-family:cursive, serif;">Mazbel</span>]] [[Usuario discusión:Mazbel|<span style="font-size:small; font-family:cursive, serif;">(Discusión)</span>]] 23:41 13 oct 2026 (UTC) behlez922vwc5xpg4rnptzcb85deyu4 436475 436473 2026-10-14T07:26:41Z Antimundo 74354 /* Creación de plantillas */ Respuesta 436475 wikitext text/x-wiki {| width="100%" cellspacing="0" cellpadding="6" style="font-size:95%; line-height: 15px; color: var(--color-base); background:var(--background-color-option-yellow, #faf6ed); border: 1px solid var(--border-color-subtle, #faecc8); border-radius: 2px;" |- | colspan="4" style="color: var(--color-base);background: var(--background-color-content-removed, #faecc8)" |<span style="font-size: 120%">'''Hola, {{BASEPAGENAME}}.''' Te damos la bienvenida a Wikilibros en español</span> |- | colspan="4" | Gracias por participar en el proyecto. Esperamos que la colaboración te resulte agradable y que aproveches tu estancia por aquí. |- | colspan="4" | [[w:Wikilibros|Wikilibros]] es una colección de libros de texto de contenido libre que surgió en julio de 2003; desde entonces se han establecido varios principios definidos por la comunidad. Por favor, tómate un tiempo para '''explorar los temas siguientes''', antes de comenzar a editar en Wikilibros. |- | align="right" | <imagemap> Imagen:Crystal Clear app lassist.png|30x30px default [[Wikilibros:Bienvenida]] desc none </imagemap> | [[Wikilibros:Bienvenida|'''Página de bienvenida''']]. | align="right" | <imagemap> Imagen:Nuvola apps kpdf2.svg|30x30px default [[Wikilibros:Manual]] desc none </imagemap> | [[Manual de Wikilibros|'''Manual'''.<br>Manual general de Wikilibros]]. |- | width="8%" align="right" | <imagemap> Imagen:Crystal Clear app kedit.png|30x30px default [[Wikilibros:Zona de pruebas]] desc none </imagemap> | width="38%" | [[Wikilibros:Zona de pruebas|'''Zona de pruebas'''.<br>Para que realices pruebas de edición]]. | align="right" | <imagemap> Imagen:Nuvola apps khelpcenter.png|30x30px default [[Ayuda:Cómo empezar una página]] desc none </imagemap> | [[Wikilibros:Ayuda|'''Ayuda'''<br />Página con enlaces útiles. ]] |- | align="right" | <imagemap> Imagen:Nuvola apps important yellow.svg|30x30px default [[Wikilibros:Lo que Wikilibros no es]] desc none </imagemap> | [[Wikilibros:Lo que Wikilibros no es|'''Cosas que no se deben hacer'''.<br>Resumen de errores más comunes a evitar]]. | width="8%" align="right" | <imagemap> Imagen:Crystal Clear action apply.png|30x30px default [[w:Ayuda:Cómo se edita una página]] desc none </imagemap> | [[Manual wiki/Edición|'''Aprende a editar páginas'''.<br>Guía de edición de páginas wiki]]. |- | align="right" | <imagemap> Imagen:Crystal Clear app file-manager.png|30x30px default [[Wikilibros:Políticas y orientaciones]] desc none </imagemap> | [[Wikilibros:Políticas y orientaciones|'''Las políticas de Wikilibros'''.<br>Estándares y reglas aprobadas por la comunidad]]. | align="right" | <imagemap> Imagen:Nuvola apps ksirc.png|30x30px default [[Manual wiki]] desc none </imagemap> | [[Ayuda:Preguntas frecuentes|'''Preguntas frecuentes'''.<br>Lo que mucha gente pregunta]]. |- | align="right" | <imagemap> Imagen:Nuvola apps kteatime.png|30x30px default [[Wikilibros:Café]] desc none </imagemap> | [[Wikilibros:Café|'''Café'''.<br>Donde puedes preguntar a otros wikilibristas]]. | align="right" | <imagemap> Imagen:Curly Brackets.svg|30x30px default [[w:Ayuda:Referencia rápida]] desc none </imagemap> | [[w:Ayuda:Referencia rápida| '''Referencia rápida'''.<br>Un resumen sobre cómo utilizar el wikicódigo]] |- | colspan="4" style="color: var(--color-base); border-top:1px solid var(--background-color-content-removed, #faecc8);" | [[Imagen:Signature button.png|right]] Ésta es tu '''página de discusión''', en la cual puedes recibir mensajes de otros wikilibristas. Para contactar con otro wikilibrista debes escribirle '''en su página de discusión''', o usar [[Plantilla:Respuesta a|la plantilla de respuesta]]; si no, no será notificado. Al final del mensaje debes firmar escribiendo cuatro <code><nowiki>~~~~</nowiki></code> o presionando el botón mostrado en la imagen. Recuerda que ''los libros no se firman''. |} Esperamos que pases buenos momentos en Wikilibros. [[Usuario:Mazbel|<span style="font-weight:bold; font-family:cursive, serif;">Mazbel</span>]] [[Usuario discusión:Mazbel|<span style="font-size:small; font-family:cursive, serif;">(Discusión)</span>]] 23:41 13 oct 2026 (UTC) == Creación de plantillas == Hola, Luciérnaga de conocimiento: Te escribo respecto de la creación y utilización de plantillas, principalmente en el libro Haskell. Es necesario revisar su utilidad, evitar duplicaciones y respetar las decisiones comunitarias de borrado. '''1. Plantillas redundantes y necesidad de justificar su creación''' En el caso de [[:Plantilla:Haskell/Minitoc]], en el resumen de borrado, señalé que existe [[:Plantilla:+ÍndiceSección]] para el mismo propósito. Antes de crear otra plantilla, comprueba si la existente permite resolver lo que necesitas. Si presenta alguna limitación, explica en la discusión del libro qué función adicional buscas y por qué no puede incorporarse a la solución disponible. Respecto de [[:Plantilla:Color de texto por defecto]], [[:Plantilla:Encabezado3 oculto]] y [[:Plantilla:Haskell/GHCi]], conviene justificar qué ventaja concreta aporta cada una. Una plantilla puede ser útil para reutilizar código o mantener un formato uniforme; sin embargo, cuando únicamente introduce un fragmento sencillo que puede escribirse directamente y no aporta una ventaja de mantenimiento, corresponde valorar su simplificación. El objetivo es mantener el libro comprensible y fácil de editar para cualquier colaborador. Por favor, documenta la finalidad de estas plantillas y revisa si existen alternativas antes de crear otras equivalentes. '''2. Consultas de borrado''' [[:Plantilla:!!]] fue incluida expresamente en [[Wikilibros:Consultas de borrado/Archivo/2026#Cinco plantillas sin uso y obsoletas|la consulta de borrado sobre cinco plantillas sin uso y obsoletas]], cerrada el 19 de julio de 2026 con resultado de eliminar. La decisión también abarcó sus páginas de uso. No corresponde recrear una plantilla manteniendo el problema que motivó su eliminación. Si consideras que existe una necesidad técnica nueva que justifica recuperarla, solicita su restauración en el [[WL:TAB|tablón de bibliotecarios]] y explica qué ha cambiado respecto de los argumentos de la consulta. La recreación de páginas borradas por consulta, en contra del criterio que llevó a su eliminación, está contemplada en el criterio G10 de [[Wikilibros:Política de borrado]]. '''3. Ejercicios y enfoque del proyecto''' En cuanto a [[:Plantilla:Haskell/Ejercicios]], es importante distinguir la pertinencia de los ejercicios de la necesidad de utilizar una plantilla para presentarlos. Wikilibros está orientado a la elaboración colaborativa de libros de texto, manuales y otros materiales instructivos. Los ejercicios pueden formar parte de un libro cuando complementan sus explicaciones y mantienen una relación clara con los contenidos. Si el propósito es desarrollar un curso con actividades, evaluaciones, seguimiento o participación de estudiantes, conviene valorar [[:v:Portada|Wikiversidad]], cuyo enfoque resulta más adecuado para ese tipo de proyecto educativo. En cualquier caso, la creación de una plantilla específica debe aportar una utilidad que justifique su mantenimiento. Te pido que revises estas cuestiones y que, antes de volver a crear una plantilla eliminada o introducir otra que duplique una función existente, plantees la necesidad en la página de discusión correspondiente o en el [[WL:Café|café]], así podremos acordar una solución útil para el libro y coherente con las decisiones de la comunidad. Saludos. --[[Usuario:Mazbel|<span style="font-weight:bold; font-family:cursive, serif;">Mazbel</span>]] [[Usuario discusión:Mazbel|<span style="font-size:small; font-family:cursive, serif;">(Discusión)</span>]] 23:41 13 oct 2026 (UTC) :Concuerdo con @[[Usuario:Mazbel|Mazbel]] y te agradezco que escribas este comentario porque iba a escribir yo lo mismo. Un saludo e igualmente le doy la bienvenida cordialmente a @[[Usuario:Luciérnaga de conocimiento|Luciérnaga de conocimiento]] [[Usuario:Antimundo|Antimundo]] ([[Usuario discusión:Antimundo|discusión]]) 07:26 14 oct 2026 (UTC) c4t2d6oj3suk3mnue6r9flh8jeb37g1