├── styles └── Vocab │ └── Base │ ├── accept.txt │ └── reject.txt ├── Homework ├── Homework15 │ ├── hye.yml │ ├── CHANGELOG.md │ ├── README.md │ ├── Homework15.cabal │ ├── LICENSE │ ├── src │ │ └── Homework15B.hs │ └── app │ │ └── Homework15A.hs ├── Homework14 │ └── ForestGameHWSolution │ │ ├── CHANGELOG.md │ │ ├── src │ │ ├── User │ │ │ └── Actions │ │ │ │ ├── Battle.hs │ │ │ │ └── Move.hs │ │ └── Forest │ │ │ └── Level1.hs │ │ ├── ForestGameHWSolution.cabal │ │ ├── LICENSE │ │ └── app │ │ └── Main.hs ├── Homework01 │ └── Homework01.hs ├── Homework07 │ └── Homework07.hs ├── Homework02 │ └── Homework02.hs ├── Homework16 │ └── README.md ├── Homework04 │ └── Homework04.hs ├── Homework17 │ ├── Semigroup.hs │ └── Monoid.hs ├── Homework05 │ └── Homework05.hs ├── Homework03 │ └── Homework03.hs ├── Homework10 │ └── Homework.hs ├── Homework09 │ ├── 2-Forest.hs │ └── 1-Maze.hs ├── Homework19 │ └── Homework19.hs ├── Homework08 │ └── Homework.hs ├── Homework06 │ └── Homework06.hs ├── Homework11 │ └── Homework.hs └── Homework18 │ └── Homework.hs ├── lessons ├── rise.css ├── 12-Installing-Haskell-and-first-program.ipynb └── 01-Introduction-to-haskell.ipynb ├── .devcontainer ├── devcontainer.json └── Dockerfile ├── images ├── BST.png ├── monadchy.png ├── diagram_IO_api.png ├── monoid_form_1.png ├── monoid_form_2.png ├── monoid_form_3.png ├── monoid_form_4.png ├── monoid_form_5.png ├── monoid_form_6.png ├── diagram_IO_screen.png ├── abstraction_pyramid.png ├── diagram_IO_getChar.png ├── diagram_IO_getLine.png ├── diagram_IO_keyboard.png ├── diagram_IO_putStrLn.png ├── diagram_IO_something.png ├── diagram_IO_putStrLn_Hi.png ├── diagram_purefunc_lame.png ├── diagram_purefunc_max6.png ├── haskell98_typeclasses.gif ├── diagram_purefunc_generic.png ├── distributed_computation.png ├── monad-applicative-functor.png ├── input-output.svg └── diagrams.drawio ├── .vale.ini ├── .vscode └── settings.json ├── CHANGELOG.md ├── .jupyter └── lab │ └── user-settings │ └── @jupyterlab │ └── apputils-extension │ └── themes.jupyterlab-settings ├── .gitpod.Dockerfile ├── .gitignore ├── ES-translation ├── Tarea │ ├── Tarea01 │ │ └── Tarea01.hs │ └── Tarea02 │ │ └── Tarea02.hs ├── PreguntasFrecuentes.md ├── README.md └── Lecciones │ └── 01-Introducción-a-haskell.ipynb ├── Changes.md ├── .gitpod.yml ├── Dockerfile ├── FAQ.md ├── LICENSE └── README.md /styles/Vocab/Base/accept.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /styles/Vocab/Base/reject.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Homework/Homework15/hye.yml: -------------------------------------------------------------------------------- 1 | cradle: 2 | cabal: 3 | 4 | -------------------------------------------------------------------------------- /lessons/rise.css: -------------------------------------------------------------------------------- 1 | div.input_area { 2 | font-size: 1.2em; 3 | } -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { "dockerfile": "Dockerfile" } 3 | } 4 | -------------------------------------------------------------------------------- /images/BST.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/BST.png -------------------------------------------------------------------------------- /images/monadchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/monadchy.png -------------------------------------------------------------------------------- /images/diagram_IO_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_IO_api.png -------------------------------------------------------------------------------- /images/monoid_form_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/monoid_form_1.png -------------------------------------------------------------------------------- /images/monoid_form_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/monoid_form_2.png -------------------------------------------------------------------------------- /images/monoid_form_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/monoid_form_3.png -------------------------------------------------------------------------------- /images/monoid_form_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/monoid_form_4.png -------------------------------------------------------------------------------- /images/monoid_form_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/monoid_form_5.png -------------------------------------------------------------------------------- /images/monoid_form_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/monoid_form_6.png -------------------------------------------------------------------------------- /images/diagram_IO_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_IO_screen.png -------------------------------------------------------------------------------- /.vale.ini: -------------------------------------------------------------------------------- 1 | StylesPath = styles 2 | 3 | MinAlertLevel = suggestion 4 | Vocab = Base 5 | 6 | [*] 7 | BasedOnStyles = Vale 8 | -------------------------------------------------------------------------------- /images/abstraction_pyramid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/abstraction_pyramid.png -------------------------------------------------------------------------------- /images/diagram_IO_getChar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_IO_getChar.png -------------------------------------------------------------------------------- /images/diagram_IO_getLine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_IO_getLine.png -------------------------------------------------------------------------------- /images/diagram_IO_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_IO_keyboard.png -------------------------------------------------------------------------------- /images/diagram_IO_putStrLn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_IO_putStrLn.png -------------------------------------------------------------------------------- /images/diagram_IO_something.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_IO_something.png -------------------------------------------------------------------------------- /images/diagram_IO_putStrLn_Hi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_IO_putStrLn_Hi.png -------------------------------------------------------------------------------- /images/diagram_purefunc_lame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_purefunc_lame.png -------------------------------------------------------------------------------- /images/diagram_purefunc_max6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_purefunc_max6.png -------------------------------------------------------------------------------- /images/haskell98_typeclasses.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/haskell98_typeclasses.gif -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "haskell.manageHLS": "GHCup", 3 | "haskell.ghcupExecutablePath": "/home/gitpod/.ghcup/bin/ghcup" 4 | } 5 | -------------------------------------------------------------------------------- /images/diagram_purefunc_generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/diagram_purefunc_generic.png -------------------------------------------------------------------------------- /images/distributed_computation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/distributed_computation.png -------------------------------------------------------------------------------- /images/monad-applicative-functor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/haskell-course/HEAD/images/monad-applicative-functor.png -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Revision history for haskell-bootcamp 2 | 3 | ## 0.1.0.0 -- YYYY-mm-dd 4 | 5 | * First version. Released on an unsuspecting world. 6 | -------------------------------------------------------------------------------- /Homework/Homework15/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Revision history for Homework15 2 | 3 | ## 0.1.0.0 -- YYYY-mm-dd 4 | 5 | * First version. Released on an unsuspecting world. 6 | -------------------------------------------------------------------------------- /Homework/Homework14/ForestGameHWSolution/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Revision history for ForestModule 2 | 3 | ## 0.1.0.0 -- YYYY-mm-dd 4 | 5 | * First version. Released on an unsuspecting world. 6 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/jamesdbrock/ihaskell-notebook:master 2 | # FROM ghcr.io/ihaskell/ihaskell-notebook:master 3 | 4 | RUN pip install RISE 5 | 6 | # RUN stack install Decimal http-conduit ansi-terminal 7 | -------------------------------------------------------------------------------- /.jupyter/lab/user-settings/@jupyterlab/apputils-extension/themes.jupyterlab-settings: -------------------------------------------------------------------------------- 1 | { 2 | // Theme 3 | // @jupyterlab/apputils-extension:themes 4 | // Theme manager settings. 5 | // ************************************* 6 | "theme": "JupyterLab Light" 7 | } 8 | -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-base 2 | 3 | # Install ghcup and add it to path 4 | RUN sudo curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh 5 | ENV PATH="/home/gitpod/.ghcup/bin:${PATH}" 6 | 7 | # Install hls and stack 8 | RUN ghcup install hls 9 | RUN ghcup install stack 10 | 11 | # Install zlib 12 | RUN sudo apt install -y zlib1g 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-* 3 | cabal-dev 4 | *.o 5 | *.hi 6 | *.hie 7 | *.chi 8 | *.chs.h 9 | *.dyn_o 10 | *.dyn_hi 11 | .hpc 12 | .hsenv 13 | .cabal-sandbox/ 14 | cabal.sandbox.config 15 | *.prof 16 | *.aux 17 | *.hp 18 | *.eventlog 19 | .stack-work/ 20 | cabal.project.local 21 | cabal.project.local~ 22 | .HTF/ 23 | .ghc.environment.* 24 | 25 | # JupyterLab 26 | .ipynb_checkpoints 27 | lab/.ipynb_checkpoints 28 | 29 | 30 | # Custom 31 | scripts 32 | .DS_Store -------------------------------------------------------------------------------- /Homework/Homework14/ForestGameHWSolution/src/User/Actions/Battle.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | {-# LANGUAGE NamedFieldPuns #-} 3 | {-# LANGUAGE RecordWildCards #-} 4 | 5 | module User.Actions.Battle where 6 | 7 | data Golem = Golem { gAttack :: Int, gHp :: Int } deriving Show 8 | data Player = Player { pAttack :: Int, pHp :: Int } deriving (Show) 9 | data Battle = Fight | RunAway deriving (Show, Read) 10 | 11 | battle :: IO Bool 12 | battle = undefined -------------------------------------------------------------------------------- /Homework/Homework15/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Homework 3 | 4 | 1. Read the [Data.Maybe](https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Maybe.html) and [Data.Either](https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Either.html) modules. 5 | 2. Read the [Control.Exception](https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Exception.html) module skipping the "Asynchronous Exceptions" section. 6 | 3. Solve Homework15A (inside `app` directory) and Homework15B (inside `src`). -------------------------------------------------------------------------------- /Homework/Homework14/ForestGameHWSolution/src/User/Actions/Move.hs: -------------------------------------------------------------------------------- 1 | module User.Actions.Move (AvailableMoves (..), move) where 2 | 3 | import Forest.Level1 (Forest (..)) 4 | 5 | data AvailableMoves = GoForward | GoLeft | GoRight deriving (Show, Read) 6 | 7 | move :: Num a => (a, Forest a) -> AvailableMoves -> (a, Forest a) 8 | move (s, FoundExit) _ = (s, FoundExit) 9 | move (s, Trail a x _ _) GoLeft = (s - a, x) 10 | move (s, Trail a _ x _) GoForward = (s - a, x) 11 | move (s, Trail a _ _ x) GoRight = (s - a, x) -------------------------------------------------------------------------------- /ES-translation/Tarea/Tarea01/Tarea01.hs: -------------------------------------------------------------------------------- 1 | -- Las respuestas están en la rama: solutions-es 2 | 3 | -- Pregunta 1 4 | -- Escribe un comentario multilinea. 5 | 6 | -- Pregunta 2 7 | -- Define una función que toma un valor y lo mulitplica por 3. 8 | 9 | -- Pregunta 3 10 | -- Define una función que calcule el área de un círculo. 11 | 12 | -- Pregunta 4 13 | -- Define una función que calcule el volúmen de un cilindro usando la función previa. 14 | 15 | -- Pregunta 5 16 | -- Define una función que chequee si el volúmen de un cilindro es mayor o igual a 42. 17 | -------------------------------------------------------------------------------- /Changes.md: -------------------------------------------------------------------------------- 1 | # Proposed changes for next iteration/version 2 | 3 | The idea of this file is to have a single source of truth as to which changes we want to do when we update the course. 4 | Feel free to provide a PR with more proposed changes! 5 | 6 | ## In general 7 | 8 | 9 | ## Lesson 1 10 | 11 | ### Lecture 12 | - 13 | ### Homework 14 | - 15 | 16 | ## Lesson 2 17 | 18 | ### Lecture 19 | - Add short explanation about type classes after polymorphysm. Just enough so the students don't struggle until we cover them properly on the type classes lesson. 20 | - 21 | 22 | ### Homework 23 | -------------------------------------------------------------------------------- /Homework/Homework01/Homework01.hs: -------------------------------------------------------------------------------- 1 | 2 | -- Question 1 3 | -- Write a multiline comment below. 4 | 5 | -- Question 2 6 | -- Define a function that takes a value and multiplies it by 3. 7 | 8 | -- Question 3 9 | -- Define a function that calculates the area of a circle. 10 | 11 | -- Question 4 12 | -- Define a function that calculates the volume of a cylinder by composing the previous function together with the height of the cylinder. 13 | 14 | -- Question 5 15 | -- Define a function that takes the height and radius of a cylinder and checks if the volume is greater than or equal to 42. 16 | 17 | -------------------------------------------------------------------------------- /Homework/Homework14/ForestGameHWSolution/ForestGameHWSolution.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 3.0 2 | name: ForestGame 3 | version: 0.1.0.0 4 | license: MIT 5 | license-file: LICENSE 6 | author: Robertino Martinez 7 | maintainer: robertino.martinez@iohk.io 8 | category: Game 9 | build-type: Simple 10 | extra-doc-files: CHANGELOG.md 11 | 12 | 13 | executable ForestModule 14 | import: warnings 15 | main-is: Main.hs 16 | other-modules: Forest.Level1 17 | , User.Actions.Move 18 | build-depends: base ^>=4.16.4.0 19 | , random 20 | hs-source-dirs: app 21 | default-language: Haskell2010 22 | -------------------------------------------------------------------------------- /Homework/Homework15/Homework15.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 3.0 2 | name: Homework15 3 | version: 0.1.0.0 4 | license: MIT 5 | license-file: LICENSE 6 | author: Robertino Martinez 7 | maintainer: robertino.martinez@iohk.io 8 | build-type: Simple 9 | extra-doc-files: CHANGELOG.md 10 | 11 | common warnings 12 | ghc-options: -Wall 13 | 14 | executable Homework15A 15 | import: warnings 16 | main-is: Homework15A.hs 17 | build-depends: base ^>=4.16.4.0 18 | hs-source-dirs: app 19 | default-language: Haskell2010 20 | 21 | 22 | library 23 | import: warnings 24 | build-depends: base ^>=4.16.4.0 25 | hs-source-dirs: src 26 | exposed-modules: Homework15B 27 | default-language: Haskell2010 28 | -------------------------------------------------------------------------------- /Homework/Homework07/Homework07.hs: -------------------------------------------------------------------------------- 1 | -- Question 1 2 | -- Investigate the `Bounded` type class. What behaviours it provides? 3 | 4 | 5 | -- Question 2 6 | -- The types Int and Word bellong to the same type classes. What is the difference 7 | -- between them? Check maybe the maxBound and minBound parameter for both types. 8 | 9 | 10 | -- Question 3 11 | -- Investigate the `Enum` type class. What behaviours provides? 12 | 13 | 14 | -- Question 4 15 | -- Add the most general type signatures possible to the functions below. 16 | -- Then uncomment the functions and try to compile. 17 | 18 | --f1 x y z = show (x / y) ++ z 19 | 20 | --f2 x = if x == maxBound then minBound else succ x 21 | 22 | 23 | -- Question 5 24 | -- Investigate the numeric type classes to figure out which behaviors they provide to change between numeric types. 25 | -------------------------------------------------------------------------------- /Homework/Homework02/Homework02.hs: -------------------------------------------------------------------------------- 1 | 2 | -- Question 1 3 | -- Add the type signatures for the functions below and then remove the comments and try to compile. 4 | -- (Use the types presented in the lecture.) 5 | 6 | -- f1 x y z = x ** (y/z) 7 | 8 | -- f2 x y z = sqrt (x/y - z) 9 | 10 | -- f3 x y = [x == True] ++ [y] 11 | 12 | -- f4 x y z = x == (y ++ z) 13 | 14 | 15 | -- Question 2 16 | -- Why should we define type signatures of functions? How can they help you? How can they help others? 17 | 18 | 19 | -- Question 3 20 | -- Why should you define type signatures for variables? How can they help you? 21 | 22 | 23 | -- Question 4 24 | -- Are there any functions in Haskell that let you transform one type to the other? Try googling for the answer. 25 | 26 | -- Question 5 27 | -- Can you also define in Haskell list of lists? Did we showed any example of that? How would you access the inner 28 | -- most elements? 29 | -------------------------------------------------------------------------------- /Homework/Homework14/ForestGameHWSolution/src/Forest/Level1.hs: -------------------------------------------------------------------------------- 1 | module Forest.Level1 (Forest (..), level1forest) where 2 | 3 | data Forest a = FoundExit | Trail a (Forest a) (Forest a) (Forest a) deriving (Show) 4 | 5 | level1forest :: (Num a, Ord a) => Forest a 6 | level1forest = 7 | Trail 8 | 3_000 9 | ( Trail 10 | 7_000 11 | (Trail 3_000 FoundExit FoundExit FoundExit) 12 | (Trail 4_000 FoundExit FoundExit FoundExit) 13 | (Trail 5_000 FoundExit FoundExit FoundExit) 14 | ) 15 | ( Trail 16 | 3_000 17 | (Trail 3_000 FoundExit FoundExit FoundExit) 18 | (Trail 9_000 FoundExit FoundExit FoundExit) 19 | (Trail 5_000 FoundExit FoundExit FoundExit) 20 | ) 21 | ( Trail 22 | 5_000 23 | (Trail 3_000 FoundExit FoundExit FoundExit) 24 | (Trail 4_000 FoundExit FoundExit FoundExit) 25 | (Trail 1_000 FoundExit FoundExit FoundExit) 26 | ) 27 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | 4 | vscode: 5 | extensions: 6 | - haskell.haskell 7 | - justusadam.language-haskell 8 | 9 | github: 10 | prebuilds: 11 | # enable for the default branch (defaults to true) 12 | main: true 13 | # enable for all branches in this repo (defaults to false) 14 | branches: true 15 | # enable for pull requests coming from this repo (defaults to true) 16 | pullRequests: true 17 | # enable for pull requests coming from forks (defaults to false) 18 | pullRequestsFromForks: false 19 | # add a check to pull requests (defaults to true) 20 | addCheck: true 21 | # add a "Review in Gitpod" button as a comment to pull requests (defaults to false) 22 | addComment: false 23 | # add a "Review in Gitpod" button to the pull request's description (defaults to false) 24 | addBadge: false 25 | -------------------------------------------------------------------------------- /Homework/Homework16/README.md: -------------------------------------------------------------------------------- 1 | # SECTION'S FINAL PROJECT 2 | 3 | Congratulations!! 🥳🎉 you finished the "GAINING INDEPENDENCE" section of the course. 🔥 That means you should be able to build a simple project from start to finish by yourself. So, that's what you're going to do for this homework! 😃 4 | 5 | Create and configure a new project using Cabal, and build a Tic-Tac-Toe game that follows these requirements: 6 | 7 | ## Requirements: 8 | 9 | - It has to be a CLI executable with the business logic implemented as a library. 10 | - There has to be a single-player (against the machine) and a multiplayer (two-player) mode. 11 | - The machine has to play randomly. 12 | - The board has to be printed nicely on the console. 13 | - Use any library you want. However, the provided solution will only use the `random` library and Haskell features explained up until now. So, if you're tempted to use more advanced features, you're likely overcomplicating it. 14 | 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/jamesdbrock/ihaskell-notebook:master 2 | 3 | # set up a user whose uid is 1000. It is bad practice to run processes in containers as root, and on binder we do not allow root container processes. 4 | ARG NB_USER=rober 5 | ARG NB_UID=1000 6 | ENV USER ${NB_USER} 7 | ENV NB_UID ${NB_UID} 8 | ENV HOME /home/${NB_USER} 9 | 10 | USER root 11 | 12 | # ---- As advised here: 13 | # https://github.com/binder-project/binder/issues/50 14 | RUN pip install jupyter_client 15 | 16 | # ---- Install RISE extension 17 | RUN pip install RISE 18 | 19 | # ---- Install Jypyter themes 20 | RUN pip install jupyterthemes 21 | #RUN jt -t gruvboxd -T -N -kl 22 | 23 | # ---- Add non-root user 24 | #RUN adduser --disabled-password \ 25 | # --gecos "Default user" \ 26 | # --uid ${NB_UID} \ 27 | # ${NB_USER} 28 | 29 | # Make sure the contents of our repo are in ${HOME} 30 | COPY . ${HOME} 31 | 32 | RUN chown -R ${NB_UID} ${HOME} 33 | USER ${NB_USER} 34 | 35 | RUN stack install Decimal http-conduit 36 | -------------------------------------------------------------------------------- /ES-translation/Tarea/Tarea02/Tarea02.hs: -------------------------------------------------------------------------------- 1 | 2 | -- Pregunta 1 3 | -- Agrega la firma de tipos para las funciones debajo, luego remueve los comentarios e intenta compilar. 4 | 5 | -- f1 x y z = x ** (y/z) 6 | 7 | -- f2 x y z = sqrt (x/y - z) 8 | 9 | -- f3 x y z = x:((show y) ++ z) 10 | 11 | -- f4 x y z = [x > y] ++ [z] 12 | 13 | -- f5 x y z = x == (y ++ z) 14 | 15 | -- Pregunta 2 16 | -- ¿Son inmutables realmente todas las variables de Haskell? Intentá googlear la respuesta. 17 | 18 | -- Pregunta 3 19 | -- ¿Por qué deberíamos definir el tipo de las funciones? ¿Cómo podrían ayudarte? ¿Cómo podrian ayudar a otros? 20 | 21 | -- Pregunta 4 22 | -- ¿Por qué deberías definir las firmas de tipos para variables? ¿Cómo pueden ayudarte? 23 | 24 | -- Pregunta 5 25 | -- ¿Hay alguna función en Haskell que te deje transformar un tipo por otro? Intentá googlear la respuesta. 26 | 27 | -- Pregunta 6 28 | -- ¿Cómo escribirías la función producto de nuestra lección para que trabaje con Int y Double? ¿El código compila? 29 | 30 | -- Pregunta 7 31 | -- ¿Podés definir en Haskell una lista de listas? ¿Dimos algún ejemplo de eso? ¿Cómo accederías a los elementos internos? 32 | -------------------------------------------------------------------------------- /Homework/Homework15/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Robertino Martinez 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Homework/Homework14/ForestGameHWSolution/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Robertino Martinez 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Homework/Homework04/Homework04.hs: -------------------------------------------------------------------------------- 1 | -- Question 1 2 | -- Lets say you have the nested values defined bellow. How would you get the value of 3 | -- 4 by using only pattern matching in a function? 4 | 5 | nested :: [([Int], [Int])] 6 | nested = [([1,2],[3,4]), ([5,6],[7,8])] 7 | 8 | -- Question 2 9 | -- Write a function that takes a list of elements of any type and, if the list has 3 or more elements, it 10 | -- removes them. Else, it does nothing. Do it two times, one with multiple function definitions and one with 11 | -- case expressions. 12 | 13 | 14 | -- Question 3 15 | -- Create a function that takes a 3-element tuple (all of type Integer) and adds them together 16 | 17 | 18 | -- Question 4 19 | -- Implement a function that returns True if a list is empty and False otherwise. 20 | 21 | 22 | -- Question 5 23 | -- Write the implementation of the tail function using pattern matching. But, instead of failing if 24 | -- the list is empty, return an empty list. 25 | 26 | 27 | -- Question 6 28 | -- write a case expression wrapped in a function that takes an Int and adds one if it's even. Otherwise does nothing. 29 | -- (Use the `even` function to check if the number is even.) 30 | 31 | -------------------------------------------------------------------------------- /Homework/Homework14/ForestGameHWSolution/app/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | 3 | module Main where 4 | 5 | import Forest.Level1 (Forest (..), level1forest) 6 | import System.Random (randomRIO) 7 | import User.Actions.Move (AvailableMoves, move) 8 | import User.Actions.Battle (battle) 9 | 10 | main :: IO () 11 | main = do 12 | startingStamina <- randomRIO @Int (10_000, 20_000) 13 | putStrLn "\nYou're traped in a Forest, try to scape! Remember that you loose stamina with each step you take." 14 | gameLoop (startingStamina, level1forest) 15 | where 16 | gameLoop (_, FoundExit) = putStrLn "YOU'VE FOUND THE EXIT!!" 17 | gameLoop (s, _) | s <= 0 = putStrLn "You ran out of stamina and died -.-!" 18 | gameLoop (s, forest) = do 19 | let continueLoop = do 20 | putStrLn $ "\nYou have " ++ show s ++ " stamina, and you're still inside the Forest. Choose a path, brave adventurer: GoLeft, GoRight, or GoForward." 21 | selectedMove <- getLine 22 | gameLoop $ move (s, forest) (read @AvailableMoves selectedMove) 23 | battleDice <- randomRIO @Int (0, 3) 24 | case battleDice of 25 | 2 -> do 26 | r <- battle 27 | if r then continueLoop else return () 28 | _ -> continueLoop -------------------------------------------------------------------------------- /Homework/Homework17/Semigroup.hs: -------------------------------------------------------------------------------- 1 | import Data.Monoid 2 | import Data.Semigroup 3 | 4 | -------------------------------------------------------------------------------------------- 5 | ---------------------------------------- NonEmpty ------------------------------------------ 6 | 7 | -- This is the type of a NonEmpty list: 8 | data NonEmpty a = a :| [a] deriving (Show, Eq) 9 | 10 | l1, l2, l3 :: NonEmpty Int 11 | l1 = 1 :| [2, 3] 12 | l2 = 4 :| [5, 6] 13 | l3 = 7 :| [8, 9] 14 | 15 | l4, l5 :: NonEmpty Char 16 | l4 = 'h' :| "ello" 17 | l5 = 'w' :| "orld" 18 | 19 | --- As you can see, because we alwas have to provide the first `a`, we can never have an empty list. 20 | -- Now, define a Semigroup instance for NonEmpty: 21 | 22 | -- TODO: Define the Semigroup instance for NonEmpty 23 | 24 | -- Test it out 25 | 26 | --- >>> l1 <> l2 27 | -- 1 :| [2,3,4,5,6] 28 | 29 | --- >>> l4 <> l5 30 | -- 'h' :| "elloworld" 31 | 32 | --- >>> (l1 <> l2) <> l3 == l1 <> (l2 <> l3) 33 | -- True 34 | 35 | -- Now, try to define a Monoid instance for NonEmpty. 36 | -- You'll notice that it's not possible to do so because there's 37 | -- no identity element for NonEmpty. 38 | 39 | -- instance Monoid (NonEmpty a) where 40 | -- mempty = undefined 41 | 42 | -------------------------------------------------------------------------------- /Homework/Homework05/Homework05.hs: -------------------------------------------------------------------------------- 1 | -- Create a higher-order function that takes 3 parameters: A function and the two parameters that that function takes, and 2 | -- flips the order of the parameters. 3 | -- For example this: `(/) 6 2` returns `3`. But this: `flip' (/) 6 2` returns `0.3333333333` 4 | 5 | 6 | -- Create the `uncurry'` function that converts a curried function to a function on pairs. So this: `(+) 1 2` that returns `3` can be written as 7 | -- `uncurry' (+) (1,2)` (with the two different arguments inside a pair). 8 | 9 | 10 | -- Create the `curry'` function that converts an uncurried function to a curried function. So this: `fst (1,2)` that returns `1` can be written as 11 | -- `curry' fst 1 2` (with the tuple converted into two different arguments). 12 | 13 | 14 | -- Use higher-order functions, partial application, and point-free style to create a function that checks if a word has an uppercase letter. 15 | -- Start with using just higher-order functions and build from there. 16 | 17 | 18 | -- Create the `count` function that takes a team ("Red", "Blue", or "Green") and returns the amount of votes the team has inside `votes`. 19 | 20 | votes :: [String] 21 | votes = ["Red", "Blue", "Green", "Blue", "Blue", "Red"] 22 | 23 | -- Create a one-line function that filters `cars` by brand and then checks if there are any left. 24 | 25 | cars :: [(String,Int)] 26 | cars = [("Toyota",0), ("Nissan",3), ("Ford",1)] 27 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ### I can't run the code cells in the VSCode instance! 4 | 5 | As of now, the Haskell kernel is configured to work only with a Binder instance. The VSCode instance is only for the homework. Mainly because we didn't want to encourage students to use their free 50 hs/month of GitPod while going through the lesson if there's a free and open-source alternative (Binder). 6 | 7 | ### There's no binder button! 8 | 9 | If that's the case, that lesson hasn't been published yet. When it's finalized and recorded, we'll add the button. 10 | 11 | ### Do I have to pay anything to use all these resources? 12 | 13 | NO! 14 | - The content is free and open source. 15 | - The cloud service we use to host the interactive versions is also free. 16 | - And the GitPod instance we use to run the VSCode has a free tier of 50 hs/month, which we believe will be more than enough. In the case you need more than 50 hs/month to do the homework, you can clone the repo and run everything locally (You can install everything using [GHCup](https://www.haskell.org/ghcup/). 17 | 18 | ### I want something to be different! 19 | 20 | Tell us! We want this to be an easy and fun way to learn Haskell. If the community want's something different, we'll adapt the course! 21 | 22 | ### I have other questions! 23 | 24 | - About Haskell? Look for the Haskell channel in [IOG's technical community](https://discord.com/invite/cmveaxuzBn). We'll be there to answer questions! 25 | - About the repo or the course itself? Create an [issue](https://github.com/input-output-hk/haskell-course/issues)! 26 | -------------------------------------------------------------------------------- /Homework/Homework03/Homework03.hs: -------------------------------------------------------------------------------- 1 | -- Question 1 2 | -- Write a function that checks if the monthly consumption of an electrical device is bigger, equal, or smaller than the maximum allowed and 3 | -- returns a message accordingly. 4 | -- The function has to take the hourly consumption of an electrical device, the hours of daily use, and the maximum monthly consumption allowed. 5 | -- (Monthly usage = consumption (kW) * hours of daily use (h) * 30 days). 6 | 7 | 8 | -- Question 2 9 | -- Prelude: 10 | -- We use the function `show :: a -> String` to transform any type into a String. 11 | -- So `show 3` will produce `"3"` and `show (3 > 2)` will produce `"True"`. 12 | 13 | -- In the previous function, return the excess/savings of consumption as part of the message. 14 | 15 | 16 | -- Question 3 17 | -- Write a function that showcases the advantages of using let expressions to split a big expression into smaller ones. 18 | -- Then, share it with other students in Canvas. 19 | 20 | 21 | -- Question 4 22 | -- Write a function that takes in two numbers and returns their quotient such that it is not greater than 1. 23 | -- Return the number as a string, and in case the divisor is 0, return a message why the division is not 24 | -- possible. To implement this function using both guards and if-then-else statements. 25 | 26 | 27 | -- Question 5 28 | -- Write a function that takes in two numbers and calculates the sum of square roots for the product and quotient 29 | -- of those numbers. Write the function such that you use a where block inside a let expression and a 30 | -- let expression inside a where block. 31 | -------------------------------------------------------------------------------- /ES-translation/PreguntasFrecuentes.md: -------------------------------------------------------------------------------- 1 | # Preguntas Frecuentes 2 | 3 | ### ¡No puedo correr las celdas de código en VsCode! 4 | 5 | Por ahora, el kernell de Haskell está configurado para funcionar solo en Binder. La instancia de VSCode está solo para las tareas. Principalmente porque no queríamos promover que los estudiantes gasten sus 50 hs mensuales de GitPod mientras recorrían la lección siendo que existe una alternativa gratuita y open-source (Binder). 6 | ### ¡No está el botón de Binder! 7 | 8 | En ese caso, la lección todavía no se ha publicado. Cuando esté terminada y grabada, añadiremos el botón. 9 | ### Debo pagar algo para usar todos estos recursos? 10 | 11 | NO! 12 | - El contenido es gratuito y open-source. 13 | - The cloud service we use to host the interactive versions is also free. 14 | - El servicio en la nube que usamos para hostear las versiones interactivas también es gratuito. 15 | - La instancia de GitPod que usamos para correr el VSCode tiene una versión gratis de 50 hs/mes, lo cual creemos más que suficiente. En el caso que necesites más de 50 hs/mes para realizar la tarea, podes clonar el repo y correr todo localmente (podés instalar todo usando [GHCup](https://www.haskell.org/ghcup/) 16 | ### ¡Quiero que algo sea diferente! 17 | 18 | ¡Contanos! Queremos que este sea una forma fácil y divertidad de aprender Haskell. ¡Si la comunidad desea algo diferente, vamos a adaptar el curso! 19 | 20 | ### ¡Tengo otras preguntas! 21 | 22 | - ¿Sobre Haskell? Buscá el canal de Haskell en [IOG's technical community](https://discord.com/invite/cmveaxuzBn). ¡Estaremos ahí para responder las preguntas! (en inglés obvio) 23 | - ¿Sobre el repo o el curso en sí mismo? ¡Crea un issue en [issue](https://github.com/input-output-hk/haskell-course/issues)! (en inglés también) 24 | -------------------------------------------------------------------------------- /Homework/Homework15/src/Homework15B.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ScopedTypeVariables #-} 2 | 3 | module Homework15B where 4 | 5 | -------------------------------------------------------------------------------- 6 | -------------------------------------------------------------------------------- 7 | -- IMPORTANT: Read the README.md file before completing the homework. 8 | -------------------------------------------------------------------------------- 9 | -------------------------------------------------------------------------------- 10 | 11 | -- 1. Write a function that takes a list and returns the head if the list is not empty. 12 | -- If the list is empty, return Nothing. 13 | 14 | headMaybe :: [a] -> Maybe a 15 | headMaybe = undefined 16 | 17 | -- 2. Write a function that takes a list of Maybe values and returns a list of all the Just values. 18 | -- If there are no Just values, return an empty list. 19 | 20 | catMaybes :: [Maybe a] -> [a] 21 | catMaybes = undefined 22 | 23 | -- 3. Write a function that tries to read from a file and returns the contents of the file. 24 | -- If the file does not exist, return Nothing. 25 | 26 | readFileMaybe :: FilePath -> IO (Maybe String) 27 | readFileMaybe = undefined 28 | 29 | -- 4. Write a function that checks all the requirements for a password using the 30 | -- Either type with a custom data type for errors. 31 | -- The requirements are: 32 | -- - The password must be at least 10 characters long. 33 | -- - The password must contain at least one digit. 34 | -- - The password must contain at least one uppercase letter. 35 | -- - The password must contain at least one lowercase letter. 36 | 37 | data PasswordError = WrongConstructor 38 | 39 | passwordLongEnough :: String -> Either PasswordError String 40 | passwordLongEnough = undefined 41 | 42 | passwordHasDigit :: String -> Either PasswordError String 43 | passwordHasDigit = undefined 44 | 45 | passwordHasUppercase :: String -> Either PasswordError String 46 | passwordHasUppercase = undefined 47 | 48 | passwordHasLowercase :: String -> Either PasswordError String 49 | passwordHasLowercase = undefined 50 | 51 | passwordRequirements :: String -> Either PasswordError String 52 | passwordRequirements = undefined 53 | -------------------------------------------------------------------------------- /Homework/Homework10/Homework.hs: -------------------------------------------------------------------------------- 1 | {- 2 | -- Question 1 -- 3 | Continuing with the logistics software of the lesson: 4 | 1. After using the `Container` type class for a while, you realize that it might need a few adjustments: 5 | - First, write down the `Container` type class and its instances, same as we did in the lesson 6 | (try to do it without looking and check at the end or if you get stuck). 7 | - Then, add a function called `unwrap` that gives you back the value inside a container. 8 | 2. Create an instance for the `MailedBox` data type. 9 | - The MailedBox data type represents a box sent through the mail. 10 | - The parameter `t` is a tag with a person's identifier 11 | - The parameter `d` is the person's details (address,etc). 12 | - The parameter `a` is the content of the MailedBox 13 | -} 14 | 15 | data MailedBox t d a = EmptyMailBox t d | MailBoxTo t d a 16 | 17 | -- Question 2 -- 18 | -- Create instances for Show, Eq, and Ord for these three data types (use 19 | -- automatic deriving whenever possible): 20 | 21 | data Position = Intern | Junior | Senior | Manager | Chief 22 | 23 | data Experience = Programming | Managing | Leading 24 | 25 | type Address = String 26 | 27 | data Salary = USD Double | EUR Double 28 | 29 | data Relationship 30 | = Contractor Position Experience Salary Address 31 | | Employee Position Experience Salary Address 32 | 33 | data Pokemon = Pokemon 34 | { pName :: String, 35 | pType :: [String], 36 | pGeneration :: Int, 37 | pPokeDexNum :: Int 38 | } 39 | 40 | charizard = Pokemon "Charizard" ["Fire", "Flying"] 1 6 41 | 42 | venusaur = Pokemon "Venusaur" ["Grass", "Poison"] 1 3 43 | 44 | -- Question 3 -- EXTRA CREDITS 45 | -- Uncomment the next code and make it work (Google what you don't know). 46 | 47 | -- -- Team memeber experience in years 48 | -- newtype Exp = Exp Double 49 | -- 50 | -- -- Team memeber data 51 | -- type TeamMember = (String, Exp) 52 | -- 53 | -- -- List of memeber of the team 54 | -- team :: [TeamMember] 55 | -- team = [("John", Exp 5), ("Rick", Exp 2), ("Mary", Exp 6)] 56 | -- 57 | -- -- Function to check the combined experience of the team 58 | -- -- This function applied to `team` using GHCi should work 59 | -- combineExp :: [TeamMember] -> Exp 60 | -- combineExp = foldr ((+) . snd) 0 61 | -------------------------------------------------------------------------------- /Homework/Homework09/2-Forest.hs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | **************************** IMPORTANT **************************** 4 | 5 | Solve this homework after completing and checking the "Maze" one. 6 | 7 | ******************************************************************* 8 | 9 | We're going to build on top of the "Maze" challenge by coding a similar 10 | but a bit more complicated game. 11 | 12 | It works the same as the "Maze" game, with the difference that the player 13 | is now in a forest. Because we're in a forest, there are no walls. And, 14 | if you walk long enough, you're guaranteed to find the exit. 15 | 16 | So, what's the challenge in playing this game? The challenge lies in that 17 | now we have "stamina." Stamina is a number (we start with 10). And, each 18 | time the player makes a move, its stamina gets reduced by the amount of work 19 | needed to cross the current trail (represented by a number contained in the 20 | value constructor). 21 | 22 | The data types and functions are pretty much the same, with a few caveats: 23 | 24 | - We don't have walls. 25 | - We don't want to choose a specific numeric type, but we want to make sure 26 | we can do basic numeric operations regardless of the type we pass to the functions. 27 | - Because now we have to keep track of the player's stamina, we'll need to 28 | move it around with our current forest. This would be an awesome use case 29 | for monads, but because we don't know how to use them yet, a "(stamina, forest)" 30 | pair will have to do. 31 | 32 | Using GHCi, like the "Maze" game, this game should look like this: 33 | 34 | *Main> solveForest testForest [] 35 | "You have 10 stamina, and you're still inside the Forest. Choose a path, brave adventurer: GoLeft, GoRight, or GoForward." 36 | *Main> solveForest testForest [GoForward ] 37 | "You have 7 stamina, and you're still inside the Forest. Choose a path, brave adventurer: GoLeft, GoRight, or GoForward." 38 | *Main> solveForest testForest [GoForward, GoForward] 39 | "You have 4 stamina, and you're still inside the Forest. Choose a path, brave adventurer: GoLeft, GoRight, or GoForward." 40 | *Main> solveForest testForest [GoForward, GoForward, GoLeft ] 41 | "You ran out of stamina and died -.-!" 42 | *Main> solveForest testForest [GoForward, GoLeft , GoRight] 43 | "YOU'VE FOUND THE EXIT!!" 44 | -} 45 | -------------------------------------------------------------------------------- /Homework/Homework15/app/Homework15A.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | -------------------------------------------------------------------------------- 4 | -------------------------------------------------------------------------------- 5 | -- IMPORTANT: Read the README.md file before completing the homework. 6 | -------------------------------------------------------------------------------- 7 | -------------------------------------------------------------------------------- 8 | 9 | -- This is a CLI application that allows the user to manage a TODO list. 10 | -- The user can add and remove items from the list and save and load the list 11 | -- from a file. 12 | -- It's a working prototype, but it has some bugs. Specifically, it doesn't 13 | -- handle errors very well. Your mission, should you choose to accept it, is to 14 | -- fix those bugs and make the application more robust. Hint: Try to interact 15 | -- with the application in unexpected ways and see what happens! You should be 16 | -- able to find and fix 3 bugs. 17 | 18 | printTodoItem :: (Int, String) -> IO () 19 | printTodoItem (n, todo) = putStrLn (show n ++ ": " ++ todo) 20 | 21 | prompt :: [String] -> IO () 22 | prompt todos = do 23 | putStrLn "" 24 | putStrLn "Current TODO list:" 25 | foldr (\x k -> printTodoItem x >> k) (return ()) (zip [0 ..] todos) 26 | command <- getLine 27 | interpretCommand command todos 28 | 29 | delete :: Int -> [a] -> [a] 30 | delete 0 (_ : as) = as 31 | delete _ [] = [] 32 | delete n (a : as) = a : delete (n - 1) as 33 | 34 | interpretCommand :: String -> [String] -> IO () 35 | interpretCommand cmd todos = case cmd of 36 | "q" -> return () 37 | ('+' : ' ' : todo) -> prompt (todo : todos) 38 | ('-' : ' ' : num) -> prompt $ delete (read num) todos 39 | ('s' : ' ' : fn) -> 40 | writeFile fn (show todos) 41 | ('l' : ' ' : fn) -> readFile fn >>= prompt . read 42 | _ -> do 43 | putStrLn ("Invalid command: `" ++ cmd ++ "`") 44 | prompt todos 45 | 46 | printCommands :: IO () 47 | printCommands = do 48 | putStrLn "Commands:" 49 | putStrLn "+ - Add a TODO entry" 50 | putStrLn "- - Delete the numbered entry" 51 | putStrLn "s - Save the current list of TODOs" 52 | putStrLn "l - Load the saved list of TODOs" 53 | putStrLn "q - Quit without saving" 54 | 55 | main :: IO () 56 | main = do 57 | printCommands 58 | prompt [] 59 | -------------------------------------------------------------------------------- /Homework/Homework19/Homework19.hs: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------------------------------- 2 | ---------------------------------------- Question 1 ------------------------------------------------ 3 | {- 4 | -- Write the specialized types of all the `Functor` and `Applicative` operators 5 | -- in the `result` expression. 6 | -} 7 | 8 | eDiv :: Float -> Float -> Either String Float 9 | eDiv x 0 = Left "division by zero" 10 | eDiv x y = Right (x / y) 11 | 12 | 13 | 14 | -- ? 15 | -- | 16 | result :: Either String Float -- | 17 | result = (\x y z -> x * y + z) <$> (3 `eDiv` 2) <*> Right 2 <*> pure 4 18 | -- | |_______________________________________________ 19 | -- | | 20 | -- ? | 21 | -- | 22 | -- _______________________________________________________| 23 | -- | 24 | -- ? 25 | 26 | 27 | 28 | ---------------------------------------------------------------------------------------------------- 29 | ---------------------------------------- Question 2 ------------------------------------------------ 30 | {- 31 | - Implement all these Applicative functions 32 | -} 33 | 34 | -- Conditional execution of 'Applicative' expressions. For example, 35 | -- > when debug (putStrLn "Debugging") 36 | -- will output the string @Debugging@ if the Boolean value @debug@ 37 | -- is 'True', and otherwise do nothing. 38 | when :: (Applicative f) => Bool -> f () -> f () 39 | when p s = undefined 40 | 41 | -- The reverse of 'when'. 42 | unless :: (Applicative f) => Bool -> f () -> f () 43 | unless p s = undefined 44 | 45 | -- Like 'replicateM', but discards the result. 46 | replicateM_ :: (Applicative m) => Int -> m a -> m () 47 | replicateM_ n action = undefined 48 | 49 | 50 | -------------------------------------------------------------------------------------------------------- 51 | -- NEXT LESSON IS GOING TO BE ABOUT USING APPLICATIVE ON A REAL WORLD PROBLEM. SO, WE'LL CUT IT HERE. 52 | -------------------------------------------------------------------------------------------------------- 53 | -------------------------------------------------------------------------------- /Homework/Homework08/Homework.hs: -------------------------------------------------------------------------------- 1 | -- This homework is around creating Haskell types that represent wines from over the world. 2 | 3 | -- Question 1 4 | -- Different wines are made from different grapes, there are around 10000 varieties over the world! 5 | -- Create a type synonym called "Grape" for the different grape names as strings. 6 | -- Additionally, use this type synonym for the grapes: "Sangiovese", "Cabernet-sauvignon", "Merlot" and "Garnacha". 7 | 8 | -- Question 2 9 | -- The most famous regions that export wine are located in France, Italy and Spain. 10 | -- Each of these countries is divided up in smaller regions. 11 | -- These smaller regions are known for a certain style, for example the Champagne region in France 12 | -- Create a type synonym called "Region" for wine region given their country and region as a tuple of strings. 13 | -- Additionally, use this type synonym for the regions: Bordeaux in France, Tuscany in Italy and Rioja in Spain. 14 | 15 | -- Question 3 16 | -- A wine is either one of three kinds, these are red, white or rose wine. 17 | -- Besides its kind, each wine also has a given alcohol level. 18 | -- Create a data type called "Kind" that represents these three kinds, with each capturing the level of alcohol. 19 | -- Additionally, use this data type for the examples: red wine with 14.5% alcohol, white wine with 13% alcohol 20 | -- and Rose wine with 12% alcohol. 21 | 22 | -- Question 4 23 | -- In the world of wines, bottles display all of the above information for the consumer on its label. 24 | -- Create a record type called "Label" that captures the grapes that are in a whine, the region its from, 25 | -- and it's kind. Notice that some wines are a blended combination of multiple grapes! 26 | -- Additionally, create for each of the described wine below a label. 27 | 28 | -- Larrosa Rose is a rose wine from the region Rioja. It is made from the Garnacha grape and 29 | -- has a alcohol level of 14%. 30 | 31 | -- Castiglioni is a red wine from the region of Tuscany. It is made from the grape Sangiovese and 32 | -- has an alcohol level of 12.5%. 33 | 34 | -- Bordeaux is known for its red wine, these are mainly a blend between Cabernet-sauvignon and Merlot. 35 | -- Create a Label for the wine "Le Petit Haut Lafitte" that has an alcohol percentage 13.5%. 36 | 37 | -- Question 5 38 | -- Write a function `containsGrape` that takes a list of Labels and a Grape and returns a boolean. 39 | -- The function should check if the there exists a wine in the Label that contains this Grape. 40 | 41 | -- This is a test list for the `containsGrape` function with an grape that is not in the list. 42 | grapeList = [larrosaRose,castiglioni,lePetitHaitLafitte] 43 | newGrape = "Pinot Noir" -------------------------------------------------------------------------------- /Homework/Homework09/1-Maze.hs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | **************************** IMPORTANT **************************** 4 | 5 | This week is a two-step homework. First, you have to solve the 6 | "Maze" challenge, and then the "Forest" challenge. The challenges 7 | are in two separate files in both the homework and solution, so 8 | you can check the solution for the first "Maze" challenge without 9 | spoilers of the "Forest" one. Make sure to check the solution for 10 | "Maze" (and only "Maze," I see you 🥸👀) before starting with the 11 | "Forest" challenge! 12 | 13 | ******************************************************************* 14 | 15 | Today, you'll build the simplest and most basic game imaginable. 16 | It'll be a maze game where the player has to write a list of moves, and the game will perform them 17 | and tell the player where it ends up. Then, the player can change the moves and check again until it 18 | finds the exit. 19 | 20 | To play the game, the player will open GHCi, load this file, and run a "solveMaze" function that 21 | takes a maze and a list of moves and returns a String with the resulting state. 22 | 23 | It should look like this: 24 | 25 | *Main> solveMaze testMaze [] 26 | "You're still inside the maze. Choose a path, brave adventurer: GoLeft, GoRight, or GoForward." 27 | *Main> solveMaze testMaze [GoLeft] 28 | "You've hit a wall!" 29 | *Main> solveMaze testMaze [GoForward] 30 | "You're still inside the maze. Choose a path, brave adventurer: GoLeft, GoRight, or GoForward." 31 | *Main> solveMaze testMaze [GoForward, GoRight] 32 | "You've hit a wall!" 33 | *Main> solveMaze testMaze [GoForward, GoLeft] 34 | "YOU'VE FOUND THE EXIT!!" 35 | 36 | How are you going to achieve this? You can try it on your own, but here you have a 37 | step-by-step just in case: 38 | 39 | 1. Write two data types. One for the moves (Move) you can make, and another for the maze (Maze). 40 | (Use the example above to figure them out.) 41 | 42 | 2. Write a function called "move" that takes a maze and a move and returns the maze after the move. 43 | 44 | 3. Write a "testMaze" value of type "Maze" and test the "move" function in GHCi. 45 | 46 | 4. Write the "solveMaze" function that will take a maze and a list of moves and returns the maze 47 | after making those moves. 48 | 49 | 5. If you test the "solveMaze" function, you'll see that each time you try to solve the maze, 50 | it'll print the whole maze for the player to see. To avoid that, write a "showCurrentChoice" function 51 | that takes a maze and returns a different string depending on if you hit a wall, found the exit, or 52 | still need to make another choice. 53 | 54 | 6. Adapt adapt "solveMaze" function to use "showCurrentChoice" and play with your new game using GHCi! :D 55 | -} 56 | -------------------------------------------------------------------------------- /Homework/Homework06/Homework06.hs: -------------------------------------------------------------------------------- 1 | -- Question 1 2 | -- Write a function called `repeat'` that takes a value and creates an infinite list with 3 | -- the value provided as every element of the list. 4 | -- 5 | -- >>> repeat 17 6 | --[17,17,17,17,17,17,17,17,17... 7 | 8 | 9 | -- Question 2 10 | -- Using the `repeat'` function and the `take` function we defined in the lesson (comes with Haskell), 11 | -- create a function called `replicate'` that takes a number `n` and a value `x` and creates a list 12 | -- of length `n` with `x` as the value of every element. (`n` has to be Integer.) 13 | -- 14 | -- >>> replicate 0 True 15 | -- [] 16 | -- >>> replicate (-1) True 17 | -- [] 18 | -- >>> replicate 4 True 19 | -- [True,True,True,True] 20 | 21 | 22 | -- Question 3 23 | -- Write a function called `concat'` that concatenates a list of lists. 24 | -- 25 | -- >>> concat' [[1,2],[3],[4,5,6]] 26 | -- [1,2,3,4,5,6] 27 | 28 | 29 | -- Question 4 30 | -- Write a function called `zip'` that takes two lists and returns a list of 31 | -- corresponding pairs (zips them) like this: 32 | -- 33 | -- >>> zip' [1, 2] ['a', 'b'] 34 | -- [(1,'a'),(2,'b')] 35 | -- 36 | -- If one input list is shorter than the other, excess elements of the longer 37 | -- list are discarded, even if one of the lists is infinite: 38 | -- 39 | -- >>> zip' [1] ['a', 'b'] 40 | -- [(1,'a')] 41 | -- >>> zip' [1, 2] ['a'] 42 | -- [(1,'a')] 43 | -- >>> zip' [] [1..] 44 | -- [] 45 | -- >>> zip' [1..] [] 46 | -- [] 47 | 48 | 49 | 50 | -- Question 5 51 | -- Create a function called `zipWith'` that generalises `zip'` by zipping with a 52 | -- function given as the first argument, instead of a tupling function. 53 | -- 54 | -- > zipWith' (,) xs ys == zip' xs ys 55 | -- > zipWith' f [x1,x2,x3..] [y1,y2,y3..] == [f x1 y1, f x2 y2, f x3 y3..] 56 | -- 57 | -- For example, `zipWith' (+)` is applied to two lists to produce the list of 58 | -- corresponding sums: 59 | -- 60 | -- >>> zipWith (+) [1, 2, 3] [4, 5, 6] 61 | -- [5,7,9] 62 | 63 | 64 | -- Question 6 65 | -- Write a function called `takeWhile'` that takes a precate and a list and 66 | -- returns the list up until an element that doesn't satisfy the predicate. 67 | -- 68 | -- >>> takeWhile (< 3) [1,2,3,4,1,2,3,4] 69 | -- [1,2] 70 | -- >>> takeWhile (< 9) [1,2,3] 71 | -- [1,2,3] 72 | -- >>> takeWhile (< 0) [1,2,3] 73 | -- [] 74 | 75 | 76 | -- Question 7 (More difficult) 77 | -- Write a function that takes in an integer n, calculates the factorial n! and 78 | -- returns a string in the form of 1*2* ... *n = n! where n! is the actual result. 79 | 80 | 81 | -- Question 8 82 | -- Below you have defined some beer prices in bevogBeerPrices and your order list in 83 | -- orderList + the deliveryCost. Write a function that takes in an order and calculates 84 | -- the cost including delivery. Assume that the two lists have the beers in the same order. 85 | 86 | bevogBeerPrices :: [(String, Double)] 87 | bevogBeerPrices = 88 | [ ("Tak", 6.00), 89 | ("Kramah", 7.00), 90 | ("Ond", 8.50), 91 | ("Baja", 7.50) 92 | ] 93 | 94 | orderList :: [(String, Double)] 95 | orderList = 96 | [ ("Tak", 5), 97 | ("Kramah", 4), 98 | ("Ond", 7) 99 | ] 100 | 101 | deliveryCost :: Double 102 | deliveryCost = 8.50 103 | -------------------------------------------------------------------------------- /Homework/Homework11/Homework.hs: -------------------------------------------------------------------------------- 1 | import Data.List 2 | import System.CPUTime (getCPUTime) 3 | import System.Directory (doesFileExist, listDirectory) 4 | import Text.XHtml (thead) 5 | 6 | {- 7 | We imported some functions that you'll need to complete the homework. 8 | FilePath is just a synonym for String. Although, make sure to follow the standard path 9 | representation when using them (https://en.wikipedia.org/wiki/Path_(computing). 10 | getCPUTime :: IO Integer 11 | doesFileExist :: FilePath -> IO Bool 12 | listDirectory :: FilePath -> IO [FilePath] 13 | You can hover over the functions to know what they do. 14 | -} 15 | 16 | {- 17 | -- Question 1 -- 18 | Define an IO action that counts the number of all enteries in the current directory 19 | and prints it to the terminal inside a string message. 20 | (hidden files are not included) 21 | -} 22 | 23 | -- listFiles :: IO () 24 | 25 | {- 26 | -- Question 2 -- 27 | Write an IO action that asks the user to type something, then writes the message 28 | to a file called msg.txt, and after that, it reads the text from the msg.txt 29 | file and prints it back. Use the writeFile and readFile functions. 30 | -} 31 | 32 | -- createMsg :: IO () 33 | 34 | 35 | {- 36 | -- Context for Questions 3 and 4 -- 37 | In cryptography, prime numbers (positive integers only divisible by themselves and 1) play a fundamental 38 | role in providing unbreakable mathematical structures. These structures, in turn, are leveraged to 39 | establish secure encryption. 40 | But, generating primes is a computational straining problem, as we will measure in the following exercise. 41 | This is because, to know whether a number is a prime number, you first need to know all the previous primes 42 | and then check that they are not a divisor of this number. So, this problem gets bigger and bigger! 43 | Our lead cryptographer provided us with 3 different algorithms (primes1, primes2, and primes3). All three 44 | correctly produce a list of all the prime numbers until a limit (that we provide as a parameter). 45 | Our job is not to understand these algorithms but to measure which is the fastest and print the largest 46 | prime number below our limit. Do it step by step, starting with question 3. 47 | -} 48 | 49 | primes1 :: Integer -> [Integer] 50 | primes1 m = sieve [2 .. m] 51 | where 52 | sieve [] = [] 53 | sieve (p : xs) = p : sieve [x | x <- xs, x `mod` p > 0] 54 | 55 | primes2 :: Integer -> [Integer] 56 | primes2 m = sieve [2 .. m] 57 | where 58 | sieve (x : xs) = x : sieve (xs \\ [x, x + x .. m]) 59 | sieve [] = [] 60 | 61 | primes3 :: Integer -> [Integer] 62 | primes3 m = turner [2 .. m] 63 | where 64 | turner [] = [] 65 | turner (p : xs) = p : turner [x | x <- xs, x < p * p || rem x p /= 0] 66 | 67 | {- 68 | -- Question 3 -- 69 | Define an IO action that takes an IO action as input and calculates the time it takes to execute. 70 | Use the getCPUTime :: IO Integer function to get the CPU time before and after the IO action. 71 | The CPU time here is given in picoseconds (which is 1/1000000000000th of a second). 72 | -} 73 | 74 | -- timeIO :: IO a -> IO () 75 | 76 | 77 | {- 78 | -- Question 4 -- 79 | Write an action that retrieves a value from the standard input, parses it as an integer, 80 | and compares the time all three algorithms take to produce the largest prime before the 81 | limit. Print the number and time to the standard output. 82 | -} 83 | 84 | -- benchmark :: IO () 85 | 86 | {- 87 | -- Question 5 -- EXTRA CREDITS -- (In case the previous ones were too easy) 88 | Write a program that prints the directory tree structure from the current folder. 89 | Below you can see an example output of how such a structure looks like: 90 | . 91 | ├── foo1.hs 92 | ├── foo2.hs 93 | ├── bar1 94 | ├── bar2 95 | ├── foo3.hs 96 | ├── foo4.hs 97 | └── bar3 98 | └── foo5.hs 99 | └── bar5 100 | ├── bar6 101 | ├── foo6.hs 102 | └── foo7.hs 103 | HINT: You can use the function doesFileExist, which takes in a FilePath and returns 104 | True if the argument file exists and is not a directory, and False otherwise. 105 | -} 106 | -------------------------------------------------------------------------------- /images/input-output.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | Created with Sketch. 10 | 11 | 12 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Homework/Homework18/Homework.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE InstanceSigs #-} 2 | 3 | import Data.Functor 4 | import Data.Semigroup (Sum (..)) 5 | 6 | ---------------------------------------------------------------------------------------------------- 7 | ---------------------------------------- QUESTION 1 ------------------------------------------------ 8 | {- 9 | Question 1: Implement the Functor instance for a RoseTree type. 10 | A RoseTree is a tree where each node can have any number of children. 11 | It's a bit more complicated than the binary tree we saw in the lecture, 12 | but here's the trick to solve it: Follow the types! 13 | -} 14 | 15 | data RoseTree a = RoseNode a [RoseTree a] deriving (Eq, Show) 16 | 17 | exampleRoseTree :: RoseTree Int 18 | exampleRoseTree = RoseNode 1 [RoseNode 2 [], RoseNode 3 [RoseNode 4 []]] 19 | 20 | -- TODO: Implement the Functor instance for RoseTree 21 | 22 | -- Test it out: 23 | 24 | --- >>> fmap (+1) exampleRoseTree 25 | -- RoseNode 2 [RoseNode 3 [],RoseNode 4 [RoseNode 5 []]] 26 | 27 | --- >>> (*2) <$> exampleRoseTree 28 | -- RoseNode 2 [RoseNode 4 [],RoseNode 6 [RoseNode 8 []]] 29 | 30 | --- >>> exampleRoseTree $> "Yes!" 31 | -- RoseNode "Yes!" [RoseNode "Yes!" [],RoseNode "Yes!" [RoseNode "Yes!" []]] 32 | 33 | --- >>> (id <$> exampleRoseTree) == id exampleRoseTree 34 | -- True 35 | 36 | ---------------------------------------------------------------------------------------------------- 37 | ---------------------------------- QUESTION 2 - Introduction --------------------------------------- 38 | {- 39 | You're writing an app that queries the database for a list of items (products) and then performs 40 | several transformation on these items, such as calculating the taxes, adding them to get the 41 | total value, etc. The database query that returns the list of items might fail, so the result of 42 | the query is wrapped in a Maybe. 43 | -} 44 | 45 | -- Type representing an item (product) 46 | newtype Item a = Item {getItem :: (String, Sum a)} deriving (Eq, Show) 47 | 48 | exampleItem :: Item Double 49 | exampleItem = Item ("Phone", Sum 50.0) 50 | 51 | -- Type of the result of the database query 52 | type DBResponse = Maybe [Item Double] 53 | 54 | -- Example of a database query result 55 | dbResult :: DBResponse 56 | dbResult = Just [Item ("Phone", Sum 50.0), Item ("Glasses", Sum 30.0)] 57 | 58 | ---------------------------------------------------------------------------------------------------- 59 | ---------------------------------------- QUESTION 2 A ---------------------------------------------- 60 | {- 61 | Write the Functor instance for Item. 62 | -} 63 | 64 | -- TODO 65 | 66 | -- >>> fmap (*2) exampleItem 67 | -- Item {getItem = ("Phone",Sum {getSum = 100.0})} 68 | 69 | -- >>> fmap id exampleItem == id exampleItem 70 | -- True 71 | 72 | ---------------------------------------------------------------------------------------------------- 73 | ---------------------------------------- QUESTION 2 B ---------------------------------------------- 74 | {- 75 | Write a function that gives you all the items of the list for free (price = 0.0). 76 | -} 77 | 78 | giveForFree :: DBResponse -> DBResponse 79 | giveForFree = undefined -- TODO 80 | 81 | -- >>> giveForFree dbResult 82 | -- Just [Item {getItem = ("Phone",Sum {getSum = 0.0})},Item {getItem = ("Glasses",Sum {getSum = 0.0})}] 83 | 84 | ---------------------------------------------------------------------------------------------------- 85 | ---------------------------------------- QUESTION 2 C ---------------------------------------------- 86 | {- 87 | Write a function that changes the products prices by applying a tax of 20%. 88 | -} 89 | 90 | applyTaxes :: DBResponse -> DBResponse 91 | applyTaxes = undefined -- TODO 92 | 93 | -- >>> applyTaxes dbResult 94 | -- Just [Item {getItem = ("Phone",Sum {getSum = 60.0})},Item {getItem = ("Glasses",Sum {getSum = 36.0})}] 95 | 96 | ---------------------------------------------------------------------------------------------------- 97 | ---------------------------------------- QUESTION 2 D ---------------------------------------------- 98 | {- 99 | Write a function that marks the products as discounted (in the name) and applies the discount (that 100 | goes from 0.0 to 1.0) to the price. 101 | -} 102 | 103 | markOnSale :: Double -> DBResponse -> DBResponse 104 | markOnSale perc = undefined -- TODO 105 | 106 | -- >>> markOnSale 0.3 dbResult 107 | -- Just [Item {getItem = ("Phone (30.0% Discount)",Sum {getSum = 35.0})},Item {getItem = ("Glasses (30.0% Discount)",Sum {getSum = 21.0})}] 108 | 109 | ---------------------------------------------------------------------------------------------------- 110 | ---------------------------------------- QUESTION 2 E ---------------------------------------------- 111 | {- 112 | Write a function that returns a pair with the list of items as first argument and the total price 113 | as second argument. 114 | -} 115 | 116 | listItemsWithFinalPrice :: DBResponse -> Maybe ([String], Double) 117 | listItemsWithFinalPrice = undefined -- TODO 118 | 119 | -- >>> listItemsWithFinalPrice dbResult 120 | -- Just (["Phone","Glasses"],80.0) 121 | 122 | -- >>> listItemsWithFinalPrice . applyTaxes . markOnSale 0.3 $ dbResult 123 | -- Just (["Phone (30.0% Discount)","Glasses (30.0% Discount)"],67.2) 124 | -------------------------------------------------------------------------------- /Homework/Homework17/Monoid.hs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------------------- 2 | ----------------------------------------- Boolean ------------------------------------------ 3 | {- 4 | - Define all possible Monoid instances for `Bool` same as we did for `Int` and `Ord` 5 | - in the lesson. 6 | -} 7 | 8 | newtype All = All { getAll :: Bool } deriving (Show, Eq) 9 | newtype Any = Any { getAny :: Bool } deriving (Show, Eq) 10 | 11 | -- TODO: Define the Semigroup and Monoid instances for All and Any 12 | 13 | -- Test it out 14 | 15 | --- >>> All True <> All False <> All True 16 | -- All {getAll = False} 17 | 18 | --- >>> (All False <> All True) <> All True == All False <> (All True <> All True) 19 | -- True 20 | 21 | --- >>> Any True <> Any False <> Any True 22 | -- Any {getAny = True} 23 | 24 | --- >>> (Any False <> Any True) <> Any True == Any False <> (Any True <> Any True) 25 | -- True 26 | 27 | --- >>> All True <> mempty == All True 28 | -- True 29 | 30 | --- >>> mempty <> All True == All True 31 | -- True 32 | 33 | --- >>> Any False <> mempty == Any False 34 | -- True 35 | 36 | --- >>> mempty <> Any False == Any False 37 | -- True 38 | 39 | --- >>> mconcat [All True, All False, All True] == foldr (<>) mempty [All True, All False, All True] 40 | -- True 41 | 42 | -------------------------------------------------------------------------------------------- 43 | -------------------------------------------- Log -------------------------------------------- 44 | {- 45 | - Suppose you have a system where you want to collect logs from various parts of your application. 46 | - You could define a Log type and make it a Monoid, where mempty is an empty log and mappend 47 | - combines two logs. 48 | -} 49 | 50 | newtype Log = Log [String] deriving (Show, Eq) 51 | 52 | -- TODO: Define the Semigroup and Monoid instances for Log 53 | 54 | -- Test it out 55 | log1, log2, log3 :: Log 56 | log1 = Log ["Hello"] 57 | log2 = Log ["World"] 58 | log3 = Log ["!"] 59 | 60 | --- >>> log1 <> log2 61 | -- Log ["Hello","World"] 62 | 63 | --- >>> mconcat [log1, log2, log3] 64 | -- Log ["Hello","World","!"] 65 | 66 | --- >>> mconcat [log1, log2, log3] == log1 <> log2 <> log3 67 | -- True 68 | 69 | --- >>> mempty <> log1 == log1 70 | -- True 71 | 72 | --- >>> log1 <> mempty == log1 73 | -- True 74 | 75 | --- >>> (log1 <> log2) <> log3 == log1 <> (log2 <> log3) 76 | -- True 77 | 78 | --- >>> mconcat [log1, log2, log3] == foldr (<>) mempty [log1, log2, log3] 79 | -- True 80 | 81 | -------------------------------------------------------------------------------------------- 82 | ------------------------------------------ Config ------------------------------------------ 83 | {- 84 | - Suppose you have a configuration that can be overridden. 85 | - You could define a Config type and make it a Monoid, where mempty is a default configuration 86 | - and mappend overrides the configuration. 87 | -} 88 | 89 | data Config = Config { port :: Int, host :: String } deriving (Show, Eq) 90 | 91 | -- TODO: Define the Semigroup and Monoid instances for Config 92 | 93 | -- Test it out 94 | 95 | c1, c2, c3 :: Config 96 | c1 = Config { port = 8080, host = "localhost" } 97 | c2 = Config { port = 3000, host = "localhost" } 98 | c3 = Config { port = 4000, host = "example.com" } 99 | 100 | --- >>> c1 <> c2 101 | -- Config {port = 3000, host = "localhost"} 102 | 103 | --- >>> c1 <> c2 <> c3 104 | -- Config {port = 4000, host = "example.com"} 105 | 106 | --- >>> mconcat [c1, c2, c3] 107 | -- Config {port = 4000, host = "example.com"} 108 | 109 | --- >>> mempty <> c3 110 | -- Config {port = 4000, host = "example.com"} 111 | 112 | --- >>> c3 <> mempty 113 | -- Config {port = 4000, host = "example.com"} 114 | 115 | --- >>> (c1 <> c2) <> c3 == c1 <> (c2 <> c3) 116 | -- True 117 | 118 | --- >>> mconcat [c1, c2, c3] == foldr (<>) mempty [c1, c2, c3] 119 | -- True 120 | 121 | -------------------------------------------------------------------------------------------- 122 | ---------------------------------------- Emergency ----------------------------------------- 123 | {- 124 | - Suppose you have a system that can raise emergencies of various severities. 125 | - We'll use the same `Severity` type as in the lesson, but now it's going to be part of the 126 | - `Emergency` type. The `Emergency` type has to contain: 127 | - 1. A Severity 128 | - 2. A description of the emergency 129 | - 3. A flag indicating whether the emergency has been resolved 130 | - Once you define the type, make it a Monoid. 131 | -} 132 | 133 | data Severity = Low | Medium | High | Critical deriving (Show, Eq, Ord) 134 | 135 | -- TODO: Define the Semigroup and Monoid instances for Severity 136 | 137 | -- TODO: Define the Emergency type 138 | 139 | -- TODO: Define the Semigroup and Monoid instances for Emergency 140 | 141 | -- Test it out 142 | 143 | e1, e2, e3, e4 :: Emergency 144 | e1 = Emergency Low "You left the lights on; " (All True) 145 | e2 = Emergency Medium "You might have left the stove on, but you're not sure; " (All False) 146 | e3 = Emergency High "The building is on fire; " (All True) 147 | e4 = Emergency Critical "You mother-in-law is coming over!; " (All False) 148 | 149 | --- >>> e1 <> e2 150 | -- Emergency {severity = Medium, description = "You left the lights on; You might have left the stove on, but you're not sure; ", resolved = All {getAll = False}} 151 | 152 | --- >>> e1 <> e2 <> e3 <> e4 153 | -- Emergency {severity = Critical, description = "You left the lights on; You might have left the stove on, but you're not sure; The building is on fire; You mother-in-law is coming over!; ", resolved = All {getAll = False}} 154 | 155 | --- >>> e1 <> e2 <> e3 <> e4 == mconcat [e1, e2, e3, e4] 156 | -- True 157 | 158 | --- >>> mempty <> e4 159 | -- Emergency {severity = Critical, description = "You mother-in-law is coming over!; ", resolved = All {getAll = False}} 160 | 161 | --- >>> e4 <> mempty 162 | -- Emergency {severity = Critical, description = "You mother-in-law is coming over!; ", resolved = All {getAll = False}} 163 | 164 | --- >>> e3 <> mempty == e3 165 | -- True 166 | 167 | --- >>> mempty <> e3 == e3 168 | -- True 169 | 170 | --- >>> (e1 <> e2) <> e3 == e1 <> (e2 <> e3) 171 | -- True 172 | 173 | --- >>> mconcat [e1, e2, e3, e4] == foldr (<>) mempty [e1, e2, e3, e4] 174 | -- True 175 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /ES-translation/README.md: -------------------------------------------------------------------------------- 1 | # Curso de Haskell - Traducción de la comunidad 2 | 3 | [🇺🇸 version (original)](https://github.com/input-output-hk/haskell-course) 4 | 5 | >Esta es una traducción hecha del curso de Haskell de IOG, supervisada por Robertino 😇, con el objetivo de ofrecer a la comunidad hispana de Haskell/Cardano una herramienta de aprendizaje en su idioma. Cualquier miembro de la comunidad puede realizar su aporte/sugerencia. 6 | >>Todos los comentarios de la traducción estarán en bloques como estos. 7 | 8 | **Este curso está diseñado para que sus estudiantes puedan aprender Haskell desde cero, hasta tener el conocimiento necesario para trabajar con Marlowe y Plutus.** El curso en sí no contiene explicaciones sobre Marlowe o Plutus. Por lo tanto, si deseas aprender Haskell para otros usos, ¡podés hacerlo! 😃 9 | 10 | Para más información, continúa leyendo o mira este video introductorio: 11 | [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://youtu.be/H1vbUKMKvnM) 12 | 13 | 14 | ## ¿Cuánto debo estudiar si solo deseo usar Marlowe/Plutus? 15 | En el [índice](#lo-que-cubriremos), hay puntos de control bien marcados (para ambos Marlowe y Haskell) donde consideramos que ya sabes suficiente Haskell como para usar esa tecnología. 16 | 17 | ## Cómo leer/mirar las lecciones 18 | 19 | Para recorrer las lecciones interactivas, dirigite a la lección que desees dentro de "[Lo que cubriremos](#lo-que-cubriremos)" y clickeá sobre el botón que se ve como el de debajo de este párrafo. Si la página carga con "500: Internal Server Error" simplemente volvé a cargarla y debería andar bien. En la parte superior, vas a observar una consola que muestra el progreso en preparar tu lección interactiva. Durante este tiempo, podés explorar la lección que se estará mostrando de forma no interactiva. 20 | 21 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/vamanfredi/haskell-course/HEAD?labpath=%2Flessons%2F01-Introducci%C3%B3n-a-haskell.ipynb) 22 | 23 | Para ver el video, clickea el botón que se ve como éste: 24 | 25 | [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://youtu.be/H1vbUKMKvnM) 26 | 27 | ## Para realizar la ejercitación 28 | 29 | 2. Cloná este repositorio. 30 | 3. Crea una cuenta de [GitPod](https://www.gitpod.io/). 31 | 4. Hacé click en este botón para crear un entorno de desarrollo remoto: [![Visual Studio Code](https://img.shields.io/badge/Visual%20Studio%20Code-0078d7.svg?style=flat&logo=visual-studio-code&logoColor=white)](https://gitpod.io/#https://github.com/input-output-hk/haskell-course) 32 | 5. Selecciona la carpeta `Tarea/TareaXX` con la tarea que desees completar. 33 | 6. Seguí las instrucciones del archivo app/Main.hs. 34 | 8. ¡Revisá las soluciones dentro de la rama "solutions-es"! 35 | 36 | #### Estructura del Repositorio 37 | 38 | Haskell-Course/ES-translation 39 | | | 40 | | |---- Tarea 41 | | | 42 | | |---- Tarea01 (Tarea para clase 01) 43 | | |---- Tarea02 (Tarea para clase 02) 44 | | ... 45 | | 46 | |-------- lecciones (Lecciones en formato Jupyter Notebook. Se accede a través de Binder.) 47 | | 48 | |---- 1-Introducción-a-haskell 49 | |---- 2-Funciones-Tipos-de-Datos-y-Signatures 50 | 51 | Todo el resto puede ser ignorado 52 | 53 | ## Interactuar con otros estudiantes 54 | (ambos en inglés) 55 | - [Canvas](https://iohk.instructure.com/enroll/3BAAGG) 56 | - [IOG's technical community (revisá el canal #ask-haskell!)](https://discord.gg/inputoutput) 57 | 58 | ## Preguntas Frecuentes 59 | 60 | [Preguntas Frecuentes](PreguntasFrecuentes.md) 61 | 62 | ## Lo que cubriremos 63 | 64 | **Esta es una guía provisional. Los cambios pueden (y van a ser) hechos a medida que avancemos con el curso y obtengamos feedback de los estudiantes** 65 | 66 | **Si no hay botones en una lección, todavía no está publicada.** 67 | 68 | ### 1. Intro y herramientas [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/vamanfredi/haskell-course-ES/HEAD?labpath=ES-translation%2FLecciones%2F01-Introducci%C3%B3n-a-haskell.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://youtu.be/pkU8eiNZipQ) 69 | 70 | - Introducción al curso y las clases 71 | - Qué cubriremos 72 | - Estructura del repositorio 73 | - Introducción a Haskell 74 | - Cómo abrir y usar JupyterLab 75 | - Programación funcional pura 76 | - Sintaxis básica 77 | - Sistema de Tipos en Haskell 78 | - Laziness 79 | >no hay una traducción para esto, viene de "lazy evaluation", refiere a una forma particular de evaluar expresiones que se explica más adelante 80 | - GHC y GHCi 81 | - GitPod 82 | - Cómo abrir y usar GitPod 83 | - Ejemplo de cómo completar un ejercicio de tarea. 84 | 85 | 86 | ### 2. Tipos de datos, Firmas y Polimorfismo[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F02-Functions-Data-Types-and-Signatures.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://youtu.be/RABzYje2d2A) 87 | 88 | 89 | - Introducción a los tipos 90 | - Firmas de tipos 91 | - Firmas de funciones 92 | - Variables en Haskell 93 | - Parámetros en funciones 94 | - Nombres/Definiciones 95 | - Funciones Infijas y Prefijas (Infix y Prefix) 96 | - Tipos de datos en profundidad 97 | - Int, Integer 98 | - Float, Double 99 | - Rational 100 | - Bool 101 | - Char 102 | - Listas 103 | - Strings 104 | - Tuplas (Tuples) + Tuplas VS Listas 105 | - Valores polimórficos y variables de tipo 106 | 107 | ### 3. Condicionales y construcciones auxiliares 108 | 109 | - If-then-else 110 | - Guardas (Guards) 111 | - `let` 112 | - `where` 113 | - ¿Debería usar `let` o `where`? 114 | - Tips a tener en cuenta 115 | 116 | ### 4. Pattern matching y Case 117 | 118 | - Qué es pattern matching 119 | - Pattern matching en 120 | - Implementación de funciones 121 | - Listas 122 | - Tuplas 123 | - Case 124 | 125 | ### 5. Mejorando y combinando funciones 126 | 127 | - Funciones de orden superior 128 | - La función `filter` 129 | - La función `any` 130 | - Funciones Lambda 131 | - Precedencia y asociatividad 132 | - Funciones currificadas (curried) 133 | - Aplicación parcial 134 | - Composición y aplicación de funciones 135 | - Operador `.` 136 | - Operador `$` 137 | ### 6. Recursion 138 | 139 | - Concepto 140 | - Ejemplos 141 | 142 | ### 7. Manipulando listas 143 | 144 | - `zip` 145 | - `map` 146 | - `foldl`, `foldr` 147 | - `scan` 148 | 149 | ### 8. Introducción a los Type Classes 150 | 151 | - ¿Qué son los type classes? 152 | - Type classes más comunes 153 | - `Eq` 154 | - `Ord` 155 | - `Integral` 156 | - `Floating` 157 | - `Num` 158 | - Mencionando `Read`, `Show`, `Enum`, `Bounded`, and `Foldable`. 159 | - Restricciones de classes con ejemplo 160 | 161 | ### 9. Creando tipos 162 | 163 | - Sinónimos de tipos 164 | - Cómo definir sinónimos de tipos 165 | - Por qué usar sinónimos de tipos 166 | - Definir nuevos tipos 167 | - `data` 168 | - Parámetros por valor 169 | - Pattern matching en tipos 170 | - Record syntax 171 | - Parametrizar tipos 172 | - Parametrizar sinónimos de tipos 173 | - Parametrizar nuevos tipos 174 | - Honorable mención de `newType` 175 | 176 | ### 10. Creando Type Classes e Instanciación 177 | 178 | - Repaso de Type Classes 179 | - El type class `Eq` 180 | - Definiendo la type class `Eq` 181 | - Definiendo una instancia para la type class `Eq` 182 | - Mejorando la type class `Eq` (minimal complete definition) 183 | - Defining an instance for a parameterize type. 184 | - El type class `Ord` 185 | - Explorando el type class `Ord` (Subclases) 186 | - Deriving 187 | - Ejemplo completo 188 | 189 | ### 11. IO Básico (Entrada y salida) 190 | 191 | - Necesitamos efecto 192 | - Qué es IO 193 | - main + putStrLn + componiendo otras funciones 194 | - `>>` 195 | - `>>=` 196 | - do notation 197 | - `do` 198 | - `<-` 199 | - `let` 200 | - Algunos ejemplos 201 | - Leer/Escribir en consola 202 | - Leer/Escribir un archivo 203 | 204 | ### 12. Bits y Bytes 205 | 206 | - Agrupando bits y bites 207 | - Haskell y los bytes 208 | - Byte strings estrictos y lazy 209 | - Ejemplo 210 | 211 | ### 13. Pragmas, Módulos y Cabal 212 | 213 | - Prelude 214 | - pragmas/extensiones 215 | - Visión general de módulos base 216 | - Importando módulos base 217 | - Algunos módulos 218 | - Data.Char 219 | - Data.Tuple 220 | - Data.Array 221 | - Creando tus propios módulos 222 | - Cabal 223 | - Qué es y por qué usarlo 224 | - Archivo cabal 225 | - Usando bibliotecas externas con Cabal 226 | 227 | ### 14. Aprendiendo por tu cuenta y Map 228 | 229 | - Usar GHCi para descubrir más 230 | - Hoogle 231 | - HaskellWiki 232 | - Enseñando el módulo Map 233 | 234 | ### 15. Either y Maybe (sólo uso práctico) 235 | 236 | - Maybe 237 | - Por qué y cuando usar Maybe 238 | - Syntaxis 239 | - Ejemplos 240 | - Either 241 | - Por qué y cuando usar Either 242 | - Syntax 243 | - Ejemplos 244 | - Proyectos usando Maybe e IO 245 | 246 | ### 16. Aeson 247 | 248 | - Aeson 249 | 250 | --- 251 | 252 | #### YA ESTÁS PREPARADO PARA MARLOWE! 🥳🎉 (Seguí avanzando para Plutus.) 253 | 254 | --- 255 | >Más adelante el resto de la traducción😪 256 | ### 17. Monoid 257 | 258 | - Basic idea (definition without details) 259 | - Intuitive examples 260 | - Extracting the pattern 261 | - Complete definition (with all the details/laws) 262 | 263 | ### 18. Functor 264 | 265 | - Basic idea (definition without details) 266 | - Intuitive examples 267 | - Extracting the pattern 268 | - Complete definition (with all the details/laws) 269 | 270 | ### 19. Applicative 271 | 272 | - Basic idea (definition without details) 273 | - Intuitive examples 274 | - Extracting the pattern 275 | - Complete definition (with all the details/laws) 276 | 277 | ### 20. Monad 278 | 279 | - Basic idea (definition without details) 280 | - Intuitive examples 281 | - Extracting the pattern 282 | - Complete definition (with all the details/laws) 283 | - `do` notation in general 284 | 285 | ### 21. Reader Monad 286 | 287 | - Incentive/Motivation 288 | - Binding strategy (see [here](https://wiki.haskell.org/All_About_Monads#The_Reader_monad)) 289 | - Definition 290 | - Examples 291 | 292 | ### 22. Writer Monad 293 | 294 | - Incentive/Motivation 295 | - Binding strategy 296 | - Definition 297 | - Examples 298 | 299 | ### 23. State Monad 300 | 301 | - Incentive/Motivation 302 | - Binding strategy 303 | - Definition 304 | - Examples 305 | 306 | ### 24. Monadic functions / Operating with Monads 307 | 308 | - liftM 309 | - sequence and sequence_ 310 | - mapM and mapM_ 311 | - filterM 312 | - foldM 313 | 314 | ### 25. Transformers 315 | 316 | - TODO 317 | -------------------------------------------------------------------------------- /lessons/12-Installing-Haskell-and-first-program.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "# Installing Haskell and creating our first program" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": { 17 | "slideshow": { 18 | "slide_type": "skip" 19 | } 20 | }, 21 | "source": [ 22 | "We're starting to get used to coding in Haskell, and we're ready to get serious. In the next series of lessons, we'll learn how to manage our development environment, create Haskell Projects, deal with errors, and solve problems in general. Basic skills every Haskell developer must have.\n", 23 | "\n", 24 | "This is the first lesson in the series. And it's a short one. Where we're going to set up our local development environment and compile our first program." 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": { 30 | "slideshow": { 31 | "slide_type": "slide" 32 | } 33 | }, 34 | "source": [ 35 | "## Outline\n", 36 | "\n", 37 | "* Installing Haskell\n", 38 | " - Installing GHCup\n", 39 | " - Installing GHC, Cabal, Stack, and HLS with GHCup\n", 40 | " - Installing VSCode Extensions\n", 41 | "* Creating our first program\n", 42 | " - Writing the simplest Haskell program\n", 43 | " - Compiling and running our program" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": { 49 | "slideshow": { 50 | "slide_type": "slide" 51 | } 52 | }, 53 | "source": [ 54 | "## Installing Haskell Tooling (On all operating systems)" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "Feel free to ignore this section if you don't want to install anything locally and want to keep using the online dev environment." 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "We need a way to transform our Haskell source code into native code that our computer is able to run. And for that, we need a compiler. The most widely used Haskell compier is GHC. Let's learn how to install and use it." 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "GHCi (the interactive environment) already comes with GHC. There are a few different ways to install GHC. We could download it directly from its website. But there are better options. For example:" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "- The [Stack](https://docs.haskellstack.org/en/stable/ ) tool." 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "- The [GHCup](https://www.haskell.org/ghcup/#) comand line tool" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "We'll use GHCup because Stack does more than just installing the Haskell tooling and I want to go step by step. But feel free to use Stack if you prefer it.\n", 97 | "\n", 98 | "So, to install our tools, go to the [GHCup website](https://www.haskell.org/ghcup/#) and run the command it shows you on the terminal.\n", 99 | "\n", 100 | "You can click on \"Show all platforms\" if your OS is not shown.\n", 101 | "\n", 102 | "Once you run the command, it will ask you a few questions about what do you want to install. Make sure to have installed—at least—GHC, Haskell Language Server, and cabal-install." 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": {}, 108 | "source": [ 109 | "And that's it! We have everything we need! Asuming, of course, that you have a text editor. And Mirosoft Word doesn't count.\n", 110 | "\n", 111 | "If you don't have one, install [VSCode](https://code.visualstudio.com/). It's the most widely used and very friendly.\n", 112 | "\n", 113 | "If VSCode ofers to install extensions, say yes. Else, search for \"Haskell\" in the extensions pan and isntall the two most downloaded ones." 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "OK! Enough with the setup! Let's compile our first program!" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "### Compiling Haskell programs" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "In this section, we will show how you can compile simple Haskell files, later we will show how you can compile a more complex project using Cabal." 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "In the previous lesson, we saw one of the shortest Haskell programs you could write. This one:" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 1, 147 | "metadata": {}, 148 | "outputs": [], 149 | "source": [ 150 | "main :: IO ()\n", 151 | "main = putStrLn \"Hello World!\"" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": [ 158 | "A simple program that prints \"Hello World\" on the standard output. Remember that all your programs have to have an action called main that functions as the entry point of your program.\n", 159 | "\n", 160 | "OK! Let's compile this bad boy!" 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "metadata": {}, 166 | "source": [ 167 | "Because we're on this Jupyter Notebook, we'll have to use some command line tools to make it happen. But you can just write a file with the `.hs` extension and compile it using `ghc` (without the `:!` prepended) like I show at the end." 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": {}, 173 | "source": [ 174 | "So. first, we'll save the main action in a file with this command:" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "metadata": { 181 | "scrolled": true 182 | }, 183 | "outputs": [ 184 | { 185 | "data": { 186 | "text/plain": [] 187 | }, 188 | "metadata": {}, 189 | "output_type": "display_data" 190 | } 191 | ], 192 | "source": [ 193 | ":!echo \"main = putStrLn \\\"Hello World!\\\"\" >> hello.hs" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "If we look for a haskell file, we see that it's there:" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": null, 206 | "metadata": { 207 | "scrolled": true 208 | }, 209 | "outputs": [ 210 | { 211 | "data": { 212 | "text/plain": [ 213 | "hello.hs" 214 | ] 215 | }, 216 | "metadata": {}, 217 | "output_type": "display_data" 218 | } 219 | ], 220 | "source": [ 221 | ":!ls | grep 'hello'" 222 | ] 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "metadata": {}, 227 | "source": [ 228 | "And, if we check its contents, we find only the main action:" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": null, 234 | "metadata": {}, 235 | "outputs": [ 236 | { 237 | "data": { 238 | "text/plain": [ 239 | "main = putStrLn \"Hello World!\"" 240 | ] 241 | }, 242 | "metadata": {}, 243 | "output_type": "display_data" 244 | } 245 | ], 246 | "source": [ 247 | ":!cat hello.hs" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "metadata": {}, 253 | "source": [ 254 | "Ok, let's compile it! \n", 255 | "\n", 256 | "To compile a file, the only thing we need to do is to pass the file path as an argument to the ghc command line tool:" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "metadata": { 263 | "scrolled": false 264 | }, 265 | "outputs": [ 266 | { 267 | "data": { 268 | "text/plain": [ 269 | "[1 of 1] Compiling Main ( hello.hs, hello.o )\n", 270 | "Linking hello ..." 271 | ] 272 | }, 273 | "metadata": {}, 274 | "output_type": "display_data" 275 | } 276 | ], 277 | "source": [ 278 | ":!ghc hello.hs" 279 | ] 280 | }, 281 | { 282 | "cell_type": "markdown", 283 | "metadata": {}, 284 | "source": [ 285 | "Now, if we look for files named outSourcecode, we find three new files:" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": null, 291 | "metadata": { 292 | "scrolled": true 293 | }, 294 | "outputs": [ 295 | { 296 | "data": { 297 | "text/plain": [ 298 | "hello\n", 299 | "hello.hi\n", 300 | "hello.hs\n", 301 | "hello.o" 302 | ] 303 | }, 304 | "metadata": {}, 305 | "output_type": "display_data" 306 | } 307 | ], 308 | "source": [ 309 | ":!ls | grep 'hello'" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "- `hello.hs` is the file we created with the source code.\n", 317 | "- `hello.o` is an object file and `hello.hi` is an interface file that will link the object file to the libraries that come with GHC to produce an executable. We don't really care about those right now.\n", 318 | "- The one that we care about is `hello` (called `hello.exe` if we compile it on a Windows machine). This one is the acutal executable. A binary that we can run like like any other program.\n", 319 | "\n", 320 | "So, let's run it!:" 321 | ] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": null, 326 | "metadata": {}, 327 | "outputs": [ 328 | { 329 | "data": { 330 | "text/plain": [ 331 | "Hello World!" 332 | ] 333 | }, 334 | "metadata": {}, 335 | "output_type": "display_data" 336 | } 337 | ], 338 | "source": [ 339 | ":! ./hello" 340 | ] 341 | }, 342 | { 343 | "cell_type": "markdown", 344 | "metadata": {}, 345 | "source": [ 346 | "And boom! We compiled and run our own Haskell program! Congratulations!" 347 | ] 348 | }, 349 | { 350 | "cell_type": "markdown", 351 | "metadata": {}, 352 | "source": [ 353 | "Of course, because GHCi came with GHC, we can also load the `hello.hs` file to GHCI (`ghci`) and play arround with the main function." 354 | ] 355 | }, 356 | { 357 | "cell_type": "markdown", 358 | "metadata": {}, 359 | "source": [ 360 | "
\n", 361 | " GHCi the Haskell REPL allows you to load a Haskell file with the :l command. There, it is not relevant whether the file has a main function or not. Once the file is loaded into GHCi you can call any of the functions or types defined in the file and test them if they work as you expected. If you load a main.hs file into GHCi that imports some user-defined modules, they will also be included as in the compilation process.\n", 362 | "
" 363 | ] 364 | }, 365 | { 366 | "cell_type": "markdown", 367 | "metadata": {}, 368 | "source": [ 369 | "We've been coding in Haskell for a while now. But everything has been quite simple and short. That's why, if you've been doing your homework, we've been always writting our whole code in a single file.\n", 370 | "\n", 371 | "But what if we're making a more complex application? Like a website, a game, or a blockchain? How many thousands of unreadable code lines would that single file have? The naive solution is to split it into a bunch of files. But that stil doesn't solve many of problems we would have. That's why we use modules." 372 | ] 373 | }, 374 | { 375 | "cell_type": "markdown", 376 | "metadata": {}, 377 | "source": [ 378 | "## That's it for today!" 379 | ] 380 | } 381 | ], 382 | "metadata": { 383 | "celltoolbar": "Slideshow", 384 | "kernelspec": { 385 | "display_name": "Haskell", 386 | "language": "haskell", 387 | "name": "haskell" 388 | }, 389 | "language_info": { 390 | "codemirror_mode": "ihaskell", 391 | "file_extension": ".hs", 392 | "mimetype": "text/x-haskell", 393 | "name": "haskell", 394 | "pygments_lexer": "Haskell", 395 | "version": "8.10.7" 396 | } 397 | }, 398 | "nbformat": 4, 399 | "nbformat_minor": 4 400 | } 401 | -------------------------------------------------------------------------------- /images/diagrams.drawio: -------------------------------------------------------------------------------- 1 | 7Vnbbts4EP0aAe1DCt1oOY+2U7cFGmzaLLLFvhS0xEjcSKKXonzp1+9QIq0b3XWcOEmLIAgiDkdD8Zy5cBjLm2WbDxwvk0sWkdRy7WhjeReW6zq2M4Y/UrKtJcG5VwtiTiOl1Aiu6Q+i31TSkkak6CgKxlJBl11hyPKchKIjw5yzdVftlqXdVZc4JgPBdYjTofQvGomklo6R3cg/EhonemXHVjMZ1spKUCQ4YuuWyHtveTPOmKifss2MpBI8jUv93nzP7O7DOMnFIS+g+YJffdlubsLLL6tvwfif8yQ7U1ZWOC3VhtXHiq1GgLMyj4g04ljedJ1QQa6XOJSza+AcZInIUjV9y3KhSHR9GBegSfNYDmE0/GS9PuGCbFoitYUPhGVE8C2oqFnPU3Aqf/K0o6wbdpxzJUtazGgZVg4R70w3mMGDgu0eELoGCEcprDqN6KoD5ejfUpJdgXRWVChNQAEQ2VTg6Hl4itXfVOsfaciOsMBntR4POy8nQsgYmsjdu3OpUryLGYtTgpe0eBeyDMRhASrzW5zRVAJ+KbUI51js/9rFg/b8KV+WcrPaGnzDor8COFX+oEXe3Eiy3rbWqE12l1GoGMQVsVraCxfwY9GNCZzSOIfnELyecBBIb6eQYiZqIqNRJF+fcgIfjxeVKRvGS0YBb2kXTS10IW2VgtUbrEwXgrM7MmMpA7sXOcullVuapn1RKyxtNZ5rUr+yBRMMdgHsMq3MSl6FeO0lLpKAIukpaJ+voNpbQFX7CzzujCN3qsw/QhJwfNRNAq4hCYwNScA/VRLw/j+PkjyayIIkPSHFRUHDrps0eXbA0A3hEc5xl0iorNoBdGXyfgYviTqVbghuCzxkwE7LOEmxoKtufTQBqla4kk7c4g71Eviox0lRuZ56q13PeoZ2pO8zJDCPiRgYqvjdbft4yv29eX+RsvBOphzSzx2vFeH+e/6jFL9aSWi+0+AJr/XiyeuFP+6lCntYL9zAkPPGp6oX6LVeHFgvUP/Af2y9COznrRejAeXzMg8FZfnvmhPUhgyd6v3bvqBHnoeGJz73RCc+mm0vVt/Pv20+Xy28aPtnWP599yyd8wAvA6oHd87+U3bORggNnTOaji13BtzOAvj1pcP+psFx6oL5IF9xeuHmmxos7ynD7aU1WL3qeBC4z9Ng+Y/VYA0MPV7BNFJuaLDQFL2mhedLCyjo3ruY0sKprl2MLvLSjtEvNyv0j9FHZ4X+Mfqps8LwGJ3hzUheD8iBDUEog92130DHjTN5yKqa8dHbXy5jOKMjTtkPOyK6vfj2h/Ht2gYn9U4V4IEhwNtXaZqx5kJHcTe44Nl5yaS+CQKiPgEqspLYZztfUXc07dn73PoffsZ3vEPI1c5hwel9LH9MTvoYxPezQ3Bgb3AE8TBs/ulZZ4XmX8fe+/8A7Vddb9sgFP01fpnUyjZ2mjzmo0lfWm2t1O1toobYaNg3wjgf/fUDG2I7eGs7NZo2VZESOFwu5tzDCfbQPN+vBN5kt0Ao90Kf7D208MIw8IOx+tHIoUGuJqgBUsGICWqBB/ZM7UyDVozQshcoAbhkmz6YQFHQRPYwLATs+mFr4P1VNzilDvCQYO6iXxmRWYOOY7/FbyhLM7ty4JuRHNtgA5QZJrDrQOjaQ3MBIJtWvp9TrsmzvDTzlr8YPT6YoIV8zYTt9ttq9eVpzKfL+fX3ZH6Am4sLk2WLeWU2bB5WHiwDAqqCUJ0k8NBslzFJHzY40aM7VXOFZTLnZngNhTRFDCPVL1UkK1LdVT33ke36VEi670BmCysKOZXioELMKPKNoIye0MTQu2urE1gs61TGYtgIIj2mbjlTDUPbGygMHQojh0O1OdknCnOWFqqdKCqoUICmgCndTc1AzgjR02eCluwZP9WpfNXfACtkvYl45sULnauSUDak69SlFPCDzoGDyrsooNBZ1ozzU6hTK9/0lzhnXFN7D08gQe3iFgqwwVCJuu6ZlOr4hTGaqi9Fnv7SAeVlCpByijesvEwgrweSsg5drpvUqnlMHoczk/4dlBFYZzHKiHxXGdGAMKJzCQO9fLZoQabapLQQOC5LlvRV0p49p0CPVBBc4H4dldva+lu3Qr9jl5Ke+7ncdriLB7izmKAcS7bte+YQoWaFz1rDndKF/UMdndakrJVnZnU97iRROHkhkcQipdJJVNf3uO0/L3nklDxwi/5hBuc3g3h0IoQBMxgS9NnMIP4wg1eaQey/kxmMrv6uGYycknOc0/r59BFb6NYn9L/ag9nQwDX17Xe+8OSfPYjdO9/VuU5zHk7uHitMiKpI8ZhMd9X9wLXZlNZTLqiN0L+rcr2wrfQI5/qmnMr6ANb4xRD6j2khQK/Rgp3vhQiN9ee9XgVOZBG7Hh/6A7JAb5eF6rZvao1BtO+76Pon7Vdrb5swFP01SNuHVmBjknzMo2knrdLWaK32aXLBAWsGZ8Zp0v76XYN5BbqmVbdqUpWXfXx9jc85vgEHz9P9uaKb5FJGTDjIjfYOXjgIea43hh+D3JfIaIJLIFY8skENsOIPrJpp0S2PWN4J1FIKzTddMJRZxkLdwahSctcNW0vRXXVDY9YDViEVffSGRzop0TFxG/yC8TipVvZcO5LSKtgCeUIjuWtB+MzBcyWlLlvpfs6EIa/ipZy3fGS0vjDFMn3MhMXS0/R6eeP6i6tLTaJv0fzixGa5o2JrN2wvVt9XDCi5zSJmkngOnu0SrtlqQ0MzugPNAUt0KuzwWmbaioh86OcQybPYdKHXv+RqfaY027cgu4VzJlOm1T2E2FFc8Wv95FdG2TXqeBOLJS1lKoxaQ8R16oYzaFjankEh6lNIZuD6OYHPCD6+QxY9VmG7uksdFTzOoB0COUwBYEjh4MSpHUh5FJnpM8Vy/kBvi1Qu9DeSZ7rYFpmZtSDXVsu8lMGkzrWSP9lcCgl5F5nMTJY1F+IQaqnn2v6SplwYsq/krdQSdnEpM1kFy60qnJBoDQcSETyFL6DTfJmA/DSWMhaMbnh+Gsq0GAjzInS5LlNDs05O0MymfwWveP6BV9CAV/CAV/y/5RX89HFjWTQ1dcs4QdA852HXJs1x7Cl0zVREM9oVEgpwZYCqgOE/0cuiTkHsk9sijwxwV2GKCar5XbeMDhFqV/hiTNzSDh1oFxxokhfWs7PaZe8gUX3wH0ukqYqZ7iUq9K23/XLJ/aHyQN7LwtuVBeKTJ8vC+F9WBfJeFY6sCuTw3/+lVSEYvW1VCHqSp3QPoFt0XDiE5rAj94ODApqamyxh6kLw8b+rGF5wVMWwGxq4q332+UaTg/Pt9883cgdMil/hgG8+fyMiD3/cxN8nKyQuwsnX88G77KAQNOJ3ZkErDKC/tuZ5YGa1q/vQis1v7ZKpeUODzD4BUeafxD2pvRKXU9qj5XJw9cWKgY0Y9tLx9/gePkbcyhwOwnhsXkMmfQXhe88GoyOfDV4gPHSbR7eyKjQPwPjsNw==7VlNc9owEP01nmkPYWzLXxwDhOTQtEkzSTq5dBRb2Gpli5FFgP76SraMbeQ0QClpMnAAa7VeWe+93dUYAwzTxTmD0+SSRogYthktDDAybNsyrUD8SMuytPh9UBpihiPlVBtu8C9U3amsMxyhvOXIKSUcT9vGkGYZCnnLBhmj87bbhJL2qlMYI81wE0KiW+9xxJPSGrhmbb9AOE6qlS1TzaSwclaGPIERnTdM4MwAQ0YpL6/SxRARCV6FS3nf+JnZ1YMxlPFNbph9in98/nb9uDx5uI2If7+IL+GJivIEyUxtWD0sX1YIMDrLIiSDWAYYzBPM0c0UhnJ2LjgXtoSnRE1PaMYVibYjxrnwxFksh2KkP3K1PmIcLRomtYVzRFPE2VK4qFlQCUPpyXHUeF6zY/WVLWkwU9mgEkS8Cl1jJi4UbFtAaGsQfjBsD6YSGSIWH3gfNUzFZnkbOEhwnInrUECDmDBISLDQ4amaSHEUydsHDOX4F3wsQpliPKU448Wm3IHhjmSsGad5SYIMnXNGf6IhJVTEHWU0k1EmmJB1U4M7U43HMMVEQv2VPlJOxS4uaUYrZzpjhQ4SzkU62i44FV8CTPklHfJeTGlMEJzivBfStJgI88J1PClDi8tVcNceqPB7UIoVrCnF1ZXidwjF+VdCAS/nGsqiU1m0pBAIzHMctlVS56JG0B1iEcxgm0dRfSv+q+oF/oQuilrVUMe2gZ3bgV1lY4hAjp/aNbQLULXCldRwgzqwRt168uaF8tRdzZq3FgiYLwTikMWIa4EKflfb3p1yR6Nc5J3Mb9s8Fon/oUi49stFwvIOWSXcY5XYsEpo3O1aJbz+61YJ7/kq8eaKguVvVBTUhjoOsdufCD23zZ6vp3DQoUKwhwweXoSjcfTErq/u774/fJndntvxWzxTtxF0gwOeqTsh1M/UQqLiIYeO1Olby4l30Si9dY0EvcDeKNH20So7VXI8UO/aKj27Zu9vu2VXrP01zE7i3/Wx+hU6qLPGqNVR/51DttCOQ7A7CET9lz3AP/aBV3yr4ra1UvQBq19/dOWAQ/YE/Sx97AnPvGSx+70Gb33f18u6Tuy23cLut1cJtllk5zYihvWL9dK9/nsCnP0G7VjbbuIwEP2aPG6VxAmXRy6l3Uu1q0XaPpvEJFadDHJMgX79jhObkEsXumor7aoSAvvMeByfMzMGHDLL9jeSbtI7iJlwfDfeO2Tu+L7neiP80MihQoZjUgGJ5LFxqoElf2J2pUG3PGZFw1EBCMU3TTCCPGeRamBUStg13dYgmrtuaMI6wDKioove81ilFToK3Rq/ZTxJ7c6eaywZtc4GKFIaw+4EItcOmUkAVY2y/YwJTZ7lpVq3eMZ6fDDJcnXJgsPDwvO+jtX1djlffkmz4eo2/mSiPFKxNQc2D6sOlgEJ2zxmOojnkOku5YotNzTS1h1qjliqMmHMa8iVEdEPcF6gJ88TPcVZ95Ht/kwqtj+BzBFuGGRMyQO6GKs/DKslJp/CwNC7q9XxxgZLT5SxGDUJkRxD15zhwND2Agr9DoVLJfWZ20TiCVWTLSp4kuM4Qj6YREDzwDH5JsaQ8TjWy6eSFfyJrspQLs43wHNVniScOuFcx9oqKCrmdehCSXhgMxCAcec55DrKmgvRhk4Ec818QTMuNL8/YQUK8BR3kIN1hq0sxU+Vwhr0QzLBN2RQv2mH4ioBSASjG15cRZCVhqgoXRfrKjQOj8FDf2rCv0J6hLZv2PQIe9LD7UmP4K3Sg5yvMJbHE92qdCYIWhQ8aqZJXYEdhX4xGdOcNoXEnmsTwPYs8id6WdzogV1yT8gLe7izmGSCKv7Y7Jx9hJodfugkrrUL2qXdLtmiTD2z6rTTtQINgjOBFJUJU51Apb7HY/+95EFHctztG8d6+9dagje8qCWYA/VcSS8uYOK3tBt2C7ivvZO3qt/B82Ii6xP90hfvQGghOQ4SPfj8HVF7D1Q23P1o/g8T4TXu9pb2xA072h+Ffhfxhx/N+8LmTcLWxdvW5NLm3Q4UDN63eY8+JL9U8tEZpS6WfHQmd95Y8nFH8kb7/mjVPdd063t24Pd8z/Zfp1XjtP6VXGle/9dArn8D7VfbjpswEP0aHrsCDCX7mMteKnXVqlHbZy844NZ4qHE2yX59x2DCLW3SbfKw0koRsY/HY3zOzInikHm+vVO0yB4gYcLx3WTrkIXj+57rTfDLILsaia5JDaSKJzaoBZb8mTU7LbrmCSt7gRpAaF70wRikZLHuYVQp2PTDViD6pxY0ZSNgGVMxRr/zRGc1OgndFr9nPM2akz3XruS0CbZAmdEENh2I3DhkrgB0Pcq3cyYMeQ0v9b7bP6zuX0wxqU/ZMF9tI3I/KcjXX75YzNWP6f3snc3yRMXaXti+rN41DChYy4SZJJ5DZpuMa7YsaGxWN6g5YpnOhV1egdRWRD/AeYmRXKZmirPxKzfnM6XZtgPZK9wxyJlWOwyxqySwdNp6mjT0blp1vGuLZR1lGozagkj3qVvOcGBp+wcK/RGFS63MnYdE4g11ny0qeCpxHCMfTCFgeOBYfFO7kPMkMdtnipX8mT5WqVycF8Clrm4SzpxwYXKtNZQ18yZ1qRX8ZHMQgHkXEqTJsuJCDKGOYK6d39KcC8PvF3gEDXiLB5DQBMNaVeJnWmMP+iGZ4gMZNA8TUF6lAKlgtODlVQx5tRCXVejtqk6Nw33y0J/Z9GcojzAK++VBDpSHe6A8gkuVBzneYUwmU2NVphIELUse98uk7cCRQt+YSqikfSHRc5sCaDyL/I1elvQ8cExuh7zwAHcNppigmj/1nfMQofaEz6aIO9oNWzsaaFJWpWd3dZ1ukCjyjiTSVKVMjxJV+u6v/XLJg5HkeNpHjv1mWmxqPr774RM+XqlTeMFJTmEvdOCX6v9tPzzR9sml+jp86+sT+zrwBp7svbCvh4mioWlfuK/fv0l+quTBEaVOljw4UjsXljwaSW58+7XZdXTMrs/g0IE7kNwdO3RwHoPGafu3qVa6/fNJbn4D7VvLcuI4FP2WWVA1s6DLD2zwEkIe0zXp6QpV3VOzE7YwmhiLyOKRfH1LWAJbEuAQYBLiyiL2lXxt6R7do3uiNNyryfKWgOn4HkcwaThWtGy4/Ybj2JbdYb+45Tm3tAM3N8QERaLTxjBAL1A+KawzFMGs1JFinFA0LRtDnKYwpCUbIAQvyt1GOCm/dQpiqBkGIUh0608U0XFu7XjWxn4HUTyWb7Yt0TIBsrMwZGMQ4UXB5F433CuCMc2vJssrmPDJk/OSP3ezpXX9YQSmtMoD377d9zvN/vXIc56/Bv/On+5un5rCyxwkMzHghuMnzF9vhJlbFjdAQZNfNzMSrnr4TzP+yb0xpTwGXf5G54Z3yb7EGMcJBFOUfQnxhJnDjHW5GYEJSjgAHvAQU9xwevc4xRtXPDAghCX3squldvVj/ns6owNK/krl54pPkM1iyumzjCPBszSCfCps1rwYIwoH0/ydC4bc1XgmiWjmngQUnRa7z1hPlMb8du27OPFyFiGhcFkwiUDcQjyBlDyzLqLVlaAQq6LVEfeLDcbsQNjGBXxJGxCwjteuN5FnFyL4rwCCsxUIEZrLWX2A2YzbuoVZLzQbnpCmoTT8/kfh2aHWjagW9Q1KWNl803LsQILilF2HLDqQMAOPCmILuisaJiiK+OM9AjP0AoYrVxaHFEYMw9yv12t4fe5rRnGW44C7zijBj/AKJ5j57ac45V5GKElUUwE+lri/UZdAjmvRGc/ICor5mnI8PsMeX1fetpXl5WuLdZWri12unXtihR0FrJ6ngNX3dLDaBrD6pwKrawCrAgyYRl2e/jkSEpBlKCzDZJMPtAj9gCQCKSgHkvGYBIDkAXfX9MKoxCv65BYmzzPMnbQRmACK5mU2Mk2oeMN3DuJC7JRE47lKTLIV9MRTRfZQHPnBHkcUkBhSzdEqvuthHx5yb3d+suSy3zCFSAAac3w4grP4N/DPsJiB05BjNVkvMOGsJYZkyVExqkpLK0H6zYe4AjN3ZVvTZf425b1//s1aS1k6d6ml5TLdXkS2titla/l8w3HdDv851p7AK6+wVsU9gboSj5Zm/TrNVkyzblCOXat9YJrVHFnnTbPtOuQVQ95y9kSqasg1Ryp2Thzyzm5mLbACCCnC6Zu2/99JPvY1k6GcrUAaARKxSzyjjPk+VY1gt/exzhHYxWmVUeYa2GVtMy2Xo7NLUAV3XRLPJqtRvwF0EmzbMfXBEXQJVabtV6gy3XNWmbKkrclwLxnarSOVma59tjJz+PJf9Pi1Q24H/rz/82H+D/nh1Xpo413oob5fsfY5hh5qBMK59FC93L78Dc8l0JUqivqtM4qiRsTWomhVtlJF0bZ9IFupoqjm6MRs1dqdpCqogLamAr5/JfWgUb03+VX/xFp+teVe49XyawKGMOmB8DFepTDlFXkrJhEkSssxti52ubBuO/rWxbEMyewYsq0xK1QSdOrC+rPsVNTC2rhTOVVhbQSoSfmpdypV6uqDdypqXX3unYpRSyntHvZSpmNg9fdajR9hbPo2ZN14h34r9dy1JTAWjx9YGQgM9HpeZcDerhHVR6U+OdeqqkBg/d+qgG0SsmqyrSILBIf+EV+VBTRHpyZbkxT0Ol3AuUhdwDCqt+gC2wh5oxi8WhbQv7CWBWynCjGc6VRWUPWk9snKe9uk+tUZvcppquDg8klx1GmdOaObjr/WMa9ynEoL1aHnsjTwnDrmpuOXJz+YtZ3U6pNaZzqppdQQbQPjuCa97oCTWux28w9oOXA3/8bnXv8C7Vddb5swFP01SNtDKj4CTR/zuVVatamRNu3RAYd4NZgZk4/++l1jG3BI1rTrtpe+JPj4+mDfe861cIJptv/AUbG5Ywmmju8meyeYOb4fBDfwK4GDAvxoqICUk0RBXgssySPWoKvRiiS4tAIFY1SQwgZjluc4FhaGOGc7O2zNqP3WAqW4ByxjRPvoN5KIjUJHodviHzFJN+bNnqtnMmSCNVBuUMJ2HSiYO8GUMybUU7afYipzZ/Ki1i3OzDYb4zgXlyyYjb9X6V32I8rmxeH69nF+/3AYaJYtopU+sONHFPgmCdnKTYuDzkT0s5I7naxZLgZlXacxBHhRAaWetPMy75SkubUohj1i3g2KUv1PDakUCRJooF7AY4tgI4Qs+Fgez1/IkPIqZSylGBWkvIpZBnBcQshijTJCpdru2YoJ5viTO5Yza4MvOtUaxfYaw+8e86uzFZVYCv4phwBJWvMCQPIUHgYQhbICwnQe3NvP8PPuvcmIPuVxogCuC2NQ3zqNz1mVJ1gW3IPp3YYIvCzUvndgzzqRGdXTkl8bzh/CuIRIuTsYNtxbzAXen1Wc1+gY/I9ZhgU/QIgxf6SlfzDjUI13rZP8ax2z6bjoRmNImzdtqFt9w4OW+DPk7v9e7iqr97isJDY+l/TeCgOtDNAv5qoXy59ZW6iDsAtofKbdFUxktQj0rrGeyEiSyOUTjkHbaFVTuVKbjICDJG84ccKZ5KoEU/qvqUvB2QOeMsqAd5azXLKsCaXHUEdDrh4vjg2oDKKDWcVrPSpH+6FMcyhdHZ7zdaicDaHG2/DYkIfa36+i2Oj6SLGmz3cU63knFBv9LcUGJxR7JAycJ2N500klUFSWJLZl0jaFXoW+Yp6gHNmF9EaNAMyVFzTpxUnvvnwyuZ3khSdyZzCOKRJka9OfSqh+wxcp4k7thnbthl5oU5S19PSq7kV5RDTyniASiKdY9Ijq+jbHfnnJw0ua1JinVVYn/g/alLmOznept57033uS717Qk4J/2ZOit550YU/yRq/Uk5qiv3pPgmH7DaLC2w+5YP4L7VdNc9owFPwtPXimPSTjD2zIEUhI0jbTTJhpjx1hCaOpLBFZBtJfX8mSbGRDgDTppbmAvXpaSW/32c9eNM431xwsF3cMIuKFPtx40aUXhrF/IX8V8KSB6KKvgYxjqKGgAab4NzKgb9ASQ1Q4gYIxIvDSBVNGKUqFgwHO2doNmzPirroEGeoA0xSQLvoDQ7HQ6CD2G/wG4WxhVw58M5IDG2yAYgEgW29B0ZUXjTljQl/lmzEiKnc2L3reZM9ovTGOqDhmwuOMTz+j5Mv4/rY/Ca6jnxP0cGZYVoCU5sBemBDJN4J4pTYtnkwmksdS7XQ0Z1ScFZVOQxkQREsp9agZV3knOKPOpFTuEfHtoCQz/8SSKpMAAc70Ajx1CBZCKMGH6njhRIUU5xljGUFgiYvzlOUSTgsZMpmDHBPltgc2Y4J54eiOUeZs8EWnmoPUnWP5/Ta/PtuyFFPBv1JP6WIHb/AHh1QtV614+03+fPxkE2IO2c6ThCtdLBo6hwk5KylESu9ADq8XWKDpUm97LauzymNOzLDiN/UW9uR9ISMxzdRtzb1CXKDNXsMFtY1l+SOWI8GfZIiZEPZjPcWWfmIqYd0UUnBhsMVWEVkMmNrNaurG3vLCOPwEt4fPu11n9QEVpcKG+5LemWGhmQUcHWedMH6irFIC4WpnK8zUVTRSQmH51BqagRxDqKaPOJKuBrOKyleuZFjWjuKNR158qbhKwbTzK+pCcPYLjRlhkveSMqpY5piQNrRlH9/cT9qlp0vDBLOSV1bUtRzGKsOxqud4X0XHuqZlqK1qeVmTx6ayX8Wssd8yay/umjXYYdbkrcwa7TBryxiIwqF6xyknEFAUOHVt0jwPOgp9RxwCClwhg0FtAPuyi+r0Ith5Ux5M7lby4h25sxhHBAi8cul3JdSscK9M3GjXaz1oekFLk6Kynpm1/YpsESW9A0QC8AyJDlGlb33sl0sev0t+pOSR5Xlq3Z8qeZsoHPxbyZN3yY+VfHBAqaMlHxzwzhtL3j+mC6n6QZAKzOhftSL3XJ99fwuKaWU3QCHgUF6yUsjW9b9qYIL+oQbmFTqMsNVh1B+GW+UTRc/UzwkdhrxtPiy1b5uv8+jqDw==7VdLc9sgEP41nmkPyUgisp2jH0maQ9qO3WlzxQJLTBCrIuRHfn1BQpaRXNvJJL00Fxu+XT6W/ZaHemiSbu4kzpIHIJT3Ao9semjaCwLf84f6zyDbChlcowqIJSPWqQHm7JnWIy1aMEJzx1EBcMUyF4xACBopB8NSwtp1WwJ3Z81wTDvAPMK8i/5iRCUVOgy9Bv9CWZzUM/uetaS4drZAnmAC6z0I3fTQRAKoqpVuJpSb5NV5qcbd/sW6C0xSoc4ZMBP9bPr8mD0+xF/FLEWr+Sy9sCwrzAu7YBus2tYZkFAIQg2J30PjdcIUnWc4Mta11lxjiUq5NS9BKCticKX7ufZkIjZd3euGXM9PpaKbPcgu4Y5CSpXcahdrRcim09ZT/yqs+utGHf/a+iR7ytQYtgUR76ibnOmGTdsLUhgcSGGf61nHhK10M1blyivI5MdJb/93AbXhIi8zN9IO/iDbVMOsvSaa0bzghuJTOeHn0r3i1tFX9O6UGj4QyAFo8ebB/qAbQ6C2ma6fk1F25m9H3ipMZdid6sOcxUK3I11fVGrA1BXTm3lkDSkjxAwfS6rDx4uSytP9DJhQZWWE4144NVyFgmqJJXWuJDzRCXDQvFMBwrAsGedtqNkA/sD2b3HKuKnXGSxAgV7FAwionaGQ5WZKlNJnWhCalIa6Is2PccgvY4CYU5yx/DKCtDREeel6u6yodXNHHgZjS/8G2y08Z7sND2y38L22Gzp9YlFBRuboN5XAcZ6zyC2T5kTz2gr9pJJggVtCDncFUN8B6Fh6KXHulG5y95IXHsmdpBwrtnJvokMJtTN8N0V8RLvr0KXIy9Kzo/ZvjhbRwDtBpLCMqeoQlfrulv16yfsfkp8p+ZUXukqhV0reJgr7/1bywYfk50qOTih1tuToRO28s+TDc95R999MQJFiINyXzwufOPfmgaB5Spm0SoH3RLcLwJL81w+SN3gxoNZlEfrdF8PR/fCCF4PuNt9PVR02X6Ho5g8=7Vdbb9sgGP01lraHVraJc3ls0ssqrbs00ib1ZSI2sdEwn4dxk+zXD2zwNWvdqt3D1JcWDh8H+M7BfHHQKt1fCZwlNxAR5vhutHfQueP7nuvN1T+NHCpktkAVEAsamaAGWNPfxM40aEEjkncCJQCTNOuCIXBOQtnBsBCw64ZtgXVXzXBMBsA6xGyIfqeRTCp0HrgN/oHQOLEre64ZSbENNkCe4Ah2LQhdOGglAGTVSvcrwnTybF6qeZd/Ga03JgiXYyasJn5+/fFu8ePrycHzxJ1Ed/GJYbnHrDAHNpuVB5sBAQWPiCbxHLTcJVSSdYZDPbpTmisskSkzw1vg0ojoT1Q/V5GUx7qresMt2/WJkGTfgswRrgikRIqDCjGjyKbT+Glq+7tGHW9hsKSljMWwMURcUzc5Uw2Ttiek0D+SwilTqy4jeq+asSxPXkE6P530Tn8VYAdO8jJzZyrAm2X7apoZt0S3JC+YpnhXLvi+DK+4N6K/mjpPteAAPrK119htvTULfAKZaD/4bpGTbcFaW9oMJj33PD0DK2fJrksxozFX7VD5kAgFaP9RdenPzEBKo0hPXwqiTok3JZWr+hlQLksHBUsnONdchYQqEyV1LgX8JCtgoHjPOXDNsqWM9aHmongz07/EKWXa17ewAQnqFDfAwQZDIcpLl0ipvn1+oDMfKOfqPzogP40BYkZwRvPTENJyIMzL0MttRa2aNXngLw39C1zLwBtxLedHrmXwWtcSPf5lIzw600+EdgLDeU7Drk2aL5/bV+gbERHmuCfkvDaAfSvQQ+klUeftGSa3lbzggdwJwrCk990X61hCzQpftIlb2vU/qbOeJnlpPTOr/cL0iKaLR4gkFjGRA6JS3/rYz5d88ib5SMnRIugq5T1T8j5RMPm3kgdvko+UfOI/otRYyftEA++8suTTMfXW9We9oVBS4K0KaXThU9ci60QZx3cVi/JUKAjhT65Y/u/S5AVqB4S6z8ZkcaR2mL1M7aC6zS+uypHN71Z08Qc=7VfLctsgFP0azbSLZPSw/Fj6EafpTKadpI/pEktYokVcF6HY6df3IoFlSXbsZJxumk0iDpcDnHOBayeYZptrSVbpLcSUO74bb5xg5vi+53pD/KeRxwoZjIIKSCSLTVAN3LM/1I40aMFimjcCFQBXbNUEIxCCRqqBESlh3QxbAm/OuiIJ7QD3EeFd9DuLVVqhw9Ct8Q+UJamd2XNNT0ZssAHylMSw3oGCKyeYSgBVfWWbKeVaPKtLNW5+oHe7MEmFOmXAV8iG1x9D927Axmz0BaKfP+YXhuWB8MJs2CxWPVoFJBQipprEc4LJOmWK3q9IpHvX6Dliqcq46V6CUMZEv4ftHCOZSHQTW90l2/mpVHSzA5ktXFPIqJKPGGJ6A5sYJp/6Vt517Y43Mli644zFiEmIZEtda4YfRrZnSOjvkbDPcdZJzB7wM1HlzitI69OQt/+7ANtxkZfKjTHAG6w21TDTb4nuaF5wTfGunPB9GV5x4+or+uaUCO9ZSAmdsI7+gXVYnsVelm78jViCo716pg6H5pdUFVJgWh7avLvQWTP+fIM9s/Ppv9233KPzAfkXxyxpnTg8Cqp5rAhnicDvCA8OlQjoA8PwlhqbjozFsR6OuuAmyKKkcrG9AiZUmfLhxAlnmqtQUG20pM6VhF90ChyQdyZAaJYl47wN1SfbG5j2nGSM64N4BwtQ2t9bEGCDoZDlLZEqhZe1H2phQzxq+o8OyC8TgIRTsmL5ZQRZ2RHlZeh8WVHj55Y89CeG/gz3SOgev0f8cM89Er7WPRIcv4qpiMf6TdOZwEmes6iZJvVV7bYd+kZlTARpGTncJoB93IKn5KVx47Hsirsj3lPaScqJYg/NJ3afoGaGzzqJd7xrvwGDlid5mXpm1O6T2CIahEeIFJEJVR2i0t/ttl9uee/N8hMtD4Zh0ynvhZa3icLev7U8fLP8RMt73hGnTrW8TdTJnVe2vH9KgXjzSS8oUgxEs6Q7VLt1yrAKmJJyQUTY4sfFR9x3Z0SRBcnpk+XL/1ennKGQwEekkV290Z5Con+eQgKb9e/FKj3rX93B1V8=7Vdrb5swFP01fFxFcJykH/Nou2mNui1S99nFDlgzvsh2Hu2vnw0mvLIli9pJlSpFBB9fH/A5914gQPNsf6dIni6BMhFEId0HaBFE0SAcTOyfQ55LZHyNSiBRnPqgGljxF1at9OiGU6ZbgQZAGJ63wRikZLFpYUQp2LXD1iDaV81JwnrAKiaij/7k1KQlOsFhjX9mPEmrKw9CP5ORKtgDOiUUdg0I3QRorgBMeZbt50w48SpdynW3f5g93Jhi0pyz4N7kj0vz9fuGytXsPssni7uHT1HJsiVi4zfsb9Y8Vwoo2EjKHMkgQLNdyg1b5SR2szvrucVSkwk/vQZpvInR0I61jeQycUM76t+y38WWKcP2Dchv4Y5Bxox6tiF+FlX6+nwaIT/e1e4Mrj2WNpypMOITIjlQ15rZEy/bP0iIehKSnoZ2c6YtFBE8kfY8tlIwZQEnAbd5N/UTGafULZ8ppvkLeSqoQjvOgUtTbALPArxwXBsDuhTdUWuj4BebgwDLu5AgHcuaC9GFGl6FfnxLMi6ctD/gCQzYXSxBQhUMG1X4nhpjyy/CaGoPVjx3cAH6KgFIBCM511cxZMVErIvQ23VJbU8P5DiaefpXyAw8wu3MGPYzY3gkMYZvlRjD07XFJJ26JuUSQRCtedzOkrr2egY9MkWJJG0fbbet/K+6Ffqbuoy2ul9f24Z2+Ih2FaaYIIZv2z3zmKD+Ct9cDjes6xb1pOOJLjLPr2r2uA7RaHyCyBCVMNMjKvw9bPtyy3HPcu04U9cCXY1N3S8Kvzy4W3p3XWKAz+oSfkNHHlAXdPtOTY+OdPvJkcREb1XUo4+iPrOo0bjjXXRhUXeJMP6/RT3+sPxMy4fhCafOtbxL1MudN7Z80rPctex31q5Pv9S9RofuPHFx5dyJjLvgrcsO66+l0un6mxPd/AY=7VfJbtswEP2WHHxsoNXL0UuWoklTOEF6piVaIkppXIqO7Xx9hxJprY2d1ClQNBebfBw+SvMeh1TPnSbbK0FW8S2ElPccK9z23FnPcWzLHuKfQnYFMhi5BRAJFuqgErhnz9TM1OiahTSrBUoALtmqDgaQpjSQNYwIAZt62BJ4fdUViWgLuA8Ib6PfWSjjAh36VolfUxbFZmXb0iMJMcEayGISwqYCuRc9dyoAZNFKtlPKVfJMXop5l78Z3T+YoKk8ZsL45svQfvRuZvOf118frmbzu/nuk2Z5InytX1g/rNyZDAhYpyFVJHbPnWxiJun9igRqdIOaIxbLhOvhJaRSi+h42M8wkqWR6mKv/chmfSok3VYg/QpXFBIqxQ5DzOhAp1P7yXf9or8p1bFHOiauKGMwog0R7anLnGFDp+0VKXQ6UtjnuOokZE/YjFRzTrO1wsZmDJeqDHfMMNDCANOYiMrsRTOwydiQEfMr61oRzqIU2wGqQQUCSgWG1h/rgYSFoZo+ETRjz2SRU1nYXwFLZZ5Hf9LzZ4prLSErdFfUmRTwg06BA/LOUkgVy5Jx3oQqdrF0/5IkjCt157AACfgWt5CCCYa1yK0XS4kVAOXHjPqon/pRAdl5BBBxSlYsOw8gyQeCLA+9XBbU2NyT+85E05/AnJ7VMKfZ+1Vz2h3m7L+XOd3D+5um4VgVSuUETrKMBXWblPu/pdAjFSFJSV1IrPjGAKZiui+ll4a1CtxObiV5fkfuDCYoJ5I91et2V0L1Ct+UiUvt3GZhGfp1iiy3np5VrbMNIt87QCSJiKhsEeX67l/77ZJ7LclxtaJ4WKoAqR1jfb7DnwL8x+qE3T+qTugX6jglX3/k9P26oP32keNYHc5032tX+x+7+shd7XgN7Zw37uomkef/3V3d/5D8WMkHB5Q6WvLBAe+8s+SDDslb18S8kJNAMkj/6HZ5TZVCO1if4d/DbkUVrfquwjMC6ak4e+nuuRD/3W30BAfLqO4vd9S+LbreaW6L2C2/NAuDlt/r7sUv7Vddb9sgFP0te/DjKtuEpH1M0rSb1GpTs7XaI7GJjYrBwjgf/fUDG2Jje236NWlaXxI4XC5wz7kX7IF5trsUKE+veYypF/rxzgPnXhgGfnCq/jSyr5HJGaiBRJDYGDXAkjxgO9OgJYlx4RhKzqkkuQtGnDEcSQdDQvCta7bm1F01RwnuAcsI0T56R2KZ1ugp9Bv8CyZJalcOfDOSIWtsgCJFMd+2ILDwwFxwLutWtptjqoNn41LPu/jD6GFjAjN5zAS2XIKHYHN7tfh1vy1/wu0omnwOay8bREtzYLNZubcRELxkMdZOAg/MtimReJmjSI9uFecKS2VGzfCaM2lIDEeqXyhLwhLdVb3+ls0pNlhIvGtB5giXmGdYir0yMaPhGaynGD1B3/S3DTvBmQl52mLGYsgIIjm4bmKmGiZszwghGAjhmKpVZzHZqGaimze4KDU2tWNqqdbwwAwLrSywlEJHspm/6pp2fXaIVBGWLluIkoSpdqT4wEIBmgeixD81AxmJYz19JnBBHtCqcuWrfs4Jk1Uk4cyD59pXKXlRM69dF1LwezznlCu/54wz7WVNKO1CLcH4pn+BMkI1vzd8xSVXp7jmjFtjXopKfKmUqgaEUMcUKgb1jzYoThLOE4pRToqTiGfVQFRUphfr2rVqHpzDcGbcv4E8YejKc2Rl15ZnMCDP8XvJc/R0hmMWT3Wp1EqgqChI5MqkqQA9hm6xiBFDLpGq5lsB2JoJHgsvjp0a3A9uK3hwIHYWE5giSTZu5R4KqFnhuxZxw92oW1ogdF0UlfTMrHal7Tgaj59wJJFIsOw5qvg9HPvllMMe5Wq1K6LyTafYtKpC/tdv6sfWlH+sUgTwqEphDjRwU77g2vFdSkH/2gn9AW2C98rr8UdeH5nXYOJyd8jz5+Z1z1H4d/N68kH5saXcf4KpYynvOepq550pPx2gvPdUrEo5iiTh7FUvzB/7XF8Rhd51Wt0LPmKxZojokCx06f/02AN0Jf67J+lb3C2++1wA4/6b8VB6XvlmVN3mi7MWafPdDha/AQ== -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Haskell Course 2 | 3 | > *The easiest way to learn Haskell* - R.M. 4 | 5 | **This course is designed to teach non-engineers (e.g., self-taught/bootcamp coders) Haskell from zero to productive in an interactive, easy-to-follow way.** The course doesn't contain content specific to [Plinth](https://plutus.cardano.intersectmbo.org/docs/), but it will cover all the Haskell you'll needed to start using it. 6 | 7 | For a more detailed explanation, keep reading or watch the introduction video: 8 | [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://youtu.be/H1vbUKMKvnM) 9 | 10 | ## How to read/watch the lessons 11 | 12 | To go through the **interactive lessons**, go to your chosen lesson's outline inside "[What we'll cover](#what-well-cover)" and click on the button that looks like this: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F01-Introduction-to-haskell.ipynb). At the top, you will see a console that displays the progress of preparing your interactive lesson. During this time, you can scroll down and look at the lesson, that is displayed non-interactively. 13 | 14 | > NOTE: If the page loads with a "500: Internal Server Error" just refresh it, and it should be fine. 15 | 16 | And to see the **video lessons**, click on the button that looks like this: [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://youtu.be/H1vbUKMKvnM) 17 | 18 | ## To do the homework 19 | 20 | 1. Clone this repository. 21 | 2. Create a [GitPod](https://www.gitpod.io/) account. 22 | 3. Click this button to create a remote dev environment: [![Visual Studio Code](https://img.shields.io/badge/Visual%20Studio%20Code-0078d7.svg?style=flat&logo=visual-studio-code&logoColor=white)](https://gitpod.io/#https://github.com/input-output-hk/haskell-course) 23 | 4. Select the `Homework/HomeworkXX` folder with the homework you want to complete. 24 | 5. Follow the instructions inside the `Homework.hs` or `Main.hs` file. 25 | 6. **Check the solutions in the `solutions` branch!** 26 | 27 | #### Repository structure 28 | 29 | Haskell-Course 30 | | | 31 | | |---- Homework 32 | | | 33 | | |---- Homework01 (Homework for lesson 01) 34 | | |---- Homework02 (Homework for lesson 02) 35 | | ... 36 | | 37 | |-------- lessons (Lessons in Jupyter notebook format. Access through Binder.) 38 | | 39 | |---- 1-Introduction-to-haskell 40 | |---- 2-Functions-Data-Types-and-Signatures 41 | 42 | Everything else can be safely ignored 43 | 44 | ## To hang out and discuss with other students 45 | 46 | - [IOG's technical community (check out the #ask-haskell channel!)](https://discord.gg/inputoutput) 47 | 48 | ## FAQ 49 | 50 | [FAQ](FAQ.md) 51 | 52 | ## What we'll cover 53 | 54 | **If there are no buttons on a lesson, it means that it's not published yet.** 55 | 56 | --- 57 | 58 | ### GETTING STARTED WITH HASKELL - 🥚⟶🐣 59 | In this section, we get familiar with basic concepts and Haskell syntax. 60 | 61 | --- 62 | 63 | ### 1. Intro and tools [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F01-Introduction-to-haskell.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://youtu.be/pkU8eiNZipQ) 64 | 65 | - Intro to the course and lectures 66 | - What we’ll cover 67 | - Repository structure 68 | - Intro to Haskell 69 | - How to open and use JupyterLab 70 | - Purely functional programming language 71 | - Basic syntax 72 | - Haskell Type system 73 | - Laziness 74 | - GHC (and GHCi) 75 | - GitPod 76 | - How to open and use GitPod 77 | - Example of how to complete a homework assignment. 78 | 79 | ### 2. Data types, Signatures, and Polymorphism [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F02-Functions-Data-Types-and-Signatures.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://youtu.be/RABzYje2d2A) 80 | 81 | - Pragmatic intro to types 82 | - Type signature 83 | - Function’s signatures 84 | - Variables in Haskell 85 | - Parameters in functions 86 | - Names/Definitions 87 | - Infix and prefix functions 88 | - Data Types in depth 89 | - Int, Integer 90 | - Float, Double 91 | - Rational 92 | - Bool 93 | - Char 94 | - Lists 95 | - Strings 96 | - Tuples + Tuples VS Lists 97 | - Polymorphic values and type variables 98 | 99 | ### 3. Conditions and helper constructions [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F03-Conditions-and-helper-constructions.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=G0XPELNZuws) 100 | 101 | - If-then-else 102 | - Guards 103 | - `let` expressions 104 | - `where` 105 | - Should I use `let` or `where`? 106 | - Things to keep in mind 107 | 108 | ### 4. Pattern matching and Case [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F04-Pattern-matching.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=sQPGN4b95DU) 109 | 110 | - What is pattern matching 111 | - Pattern matching on 112 | - Function implementations 113 | - Lists 114 | - Tuples 115 | - Case 116 | 117 | ### 5. Improving and combining functions [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F05-Improving-and-combining-functions.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://youtu.be/E-OEw4FKdf4) 118 | 119 | - Higher-order functions 120 | - The `filter` function 121 | - The `any` function 122 | - Lambda functions 123 | - Precedence and associativity 124 | - Curried functions 125 | - Partial application 126 | - Composing and applying functions 127 | - The `$` operator 128 | - The `.` operator 129 | - Point-free style 130 | 131 | ### 6. Recursion [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F06-Recursion-and-folds.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=wzvbUxSykXM) 132 | 133 | - Why Recursion? 134 | - Thinking Recursively 135 | - `sum` and `product` 136 | - Steps to create your own recursive function 137 | - Examples of recursion 138 | - `and`, `length`, `reverse`, `drop`, `take`, `map`, `filter` 139 | - Extracting the `foldr` pattern 140 | - The `foldl` function 141 | - The `foldl'` function 142 | - When to use `foldr`, `foldl`, and `foldl'` 143 | 144 | ### 7. Intro to Type Classes [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F07-Intro-to-Type-Classes.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=dGNd0GmsYWU) 145 | 146 | - The awesomeness of type classes 147 | - What are type classes 148 | - Common type classes 149 | - `Eq`, `Ord` 150 | - `Num`, `Integral`, `Floating` 151 | - `Read`, `Show` 152 | - The most general valid type 153 | - Multiple constraints 154 | 155 | ### 8. Creating Non-Parameterized Types [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F08-Creating-non-parameterized-types.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=mAZC1w_VfEs) 156 | 157 | - Type synonyms 158 | - How to define type synonyms 159 | - Why use type synonyms 160 | - Defining new types 161 | - Creating new types with `data` 162 | - Using new types 163 | - Value parameters 164 | - Record syntax 165 | 166 | ### 9. Creating Parameterized and Recursive Types [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F09-Creating-parameterized-and-recursive-types.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=wPV94aZIOGQ) 167 | 168 | - Type Parameters 169 | - Prameteryzing `type` synonyms 170 | - Prameteryzing `data` types 171 | - Recursive data types 172 | - `Tweet` me a river 173 | - A `Sequence` of `Node`s 174 | - A `Tree` of `Node`s 175 | - Kinds 176 | - The `newType` keyword 177 | 178 | ### 10. Creating Type Classes and Instances [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F10-Creating-Type-Classes-and-Instances.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=I6tmM3wNGEI) 179 | 180 | - Overloading 181 | - Steps to create Type Classes and Instances 182 | - The `Eq` type class 183 | - Defining the Type Class 184 | - Defining multiple instances 185 | - Improving our `Eq` type class with mutual recursion (and Minimal Complete Definitions) 186 | - Defining an instance for a parameterized type. 187 | - The `WeAccept` Type Class 188 | - The `Container` Type Class 189 | - Exploring `Ord` type class (Subclassing) 190 | - Deriving 191 | - Deriving can go wrong 192 | 193 | ### 11. Basic IO [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F11-Basic-IO.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=0xQ8j6h8bNc) 194 | 195 | - Pure functions 196 | - Introduction to IO actions 197 | - IO actions under the hood 198 | - IO actions in practice 199 | - The `()` type 200 | - Interacting with the user 201 | - `getChar`, `getLine`, and `putStrLn` 202 | - Actions are first-class values 203 | - Composing IO actions (`>>` and `>>=` operators) 204 | - The `do` block 205 | - Using `let`, nesting do-blocks, escaping `IO` and `return` 206 | - The `main` action 207 | - Concepts and syntax recap 208 | 209 | --- 210 | 211 | ### GAINING INDEPENDENCE - 🐣⟶🐥 212 | In this section, we learn about Haskell tooling and the necessary concepts to start working on our own projects. 213 | 214 | --- 215 | 216 | ### 12. Installing Haskell Locally [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F12-Installing-Haskell-and-first-program.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=hSN5mxITv0A&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=13) 217 | 218 | - Installing Haskell 219 | - Installing GHCup 220 | - Installing GHC, Cabal, Stack, and HLS with GHCup 221 | - Installing VSCode Extensions 222 | - Creating our first program 223 | - Writing the simplest Haskell program 224 | - Compiling and running our program 225 | 226 | ### 13. Modules [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F13-Modules.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=R64sCXU0Ru0&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=14) 227 | 228 | - Importing Modules 229 | - Controlling environments 230 | - Controlling namespaces 231 | - Creating our own Modules 232 | - The `Prelude` and Standard Libraries 233 | 234 | ### 14. Cabal and language extensions [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F14-Cabal_and_language_extensions.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=AvpMOMSSZHs&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=15) 235 | 236 | - Cabal 237 | - Introduction to Cabal 238 | - Creating a new Haskell project 239 | - Going over the Cabal file using an external library 240 | - Building and running our executable 241 | - Language extensions and Pragmas 242 | - Introduction 243 | - `NumericUnderscores` 244 | - `TypeApplications` 245 | 246 | ### 15. Handling Errors [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F15-Handling-Errors.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=QkUCHFG1hK8&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=16) 247 | 248 | - There're always `Exception`s to the rule 249 | - Speed-running `Exception`s with a dumb self-driving 🤖 car 🚗 250 | - I'm the `Exception` cause I have `class` 😎 251 | - `throw` all the `Exception`s you want. I'll `catch` them all! 252 | - `Maybe` give me a value? 🙏 253 | - Benefits of optional values 254 | - Ok, you `Either` give me a value or a reason why you didn't! 255 | - From `Exception`s to optional values 256 | - Tradeoffs 257 | - So, what should I use? 258 | 259 | ### 16. Learning on your own and final section project [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F16-Gaining-your-independence-and-project.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=ObZ_1eap5MY&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=17) 260 | 261 | - Small tips and tricks 🤹 262 | - REPL 263 | - Hackage 264 | - Hoogle 265 | - `undefined` 266 | - Type Holes 🕳️ 267 | - Section's Final Project 🥳 268 | 269 | --- 270 | 271 | ### BASIC ABSTRACTIONS & EFFECTS - 🐥⟶🐓 272 | In this section, we learn about a few of the most useful and talked about Abstractions in Haskell and how we deal with effects in general (not only `IO`). 273 | 274 | --- 275 | 276 | ### 17. Semigroup and Monoid [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F17-Semigroup-and-Monoid.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=-O1ZApHPvkg&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=18) 277 | 278 | - What does it mean to abstract a pattern? 279 | - Why abstracting patterns (in general)? 280 | - Teaser: Why abstracting `Semigroup` and `Monoid`? 281 | - The `Semigroup` type class 282 | - The `Monoid` type class 283 | - What can I do with `Semigroup` and `Monoid`? 284 | 285 | ### 18. Functor [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F18-Functor.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=Fb4qD5PWEs8&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=19) 286 | 287 | - Abstracting the `map` function 288 | - The `Functor` type class 289 | - Defining `Functor` instances 290 | - Seemingly unintuitive `Functor` instances 291 | - The `Either a` functor 🤔 292 | - The `(,) a` functor 🤨 293 | - The `(->) r` functor 🤯 294 | - Defining the `<$>` operator and *lifting* 🏋️ a function 295 | - `Functor` nesting dolls 🪆 296 | - Extra functions and `Functor` as defined in `base` 297 | 298 | 299 | ### 19. Applicative and Effects [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F19-Applicative.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=7vxhNfNWP3k&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=20) 300 | 301 | - Why `Applicative` functors? 302 | - Our journey until now 303 | - The limits of Functor 304 | - Function application at the `Functor` level 🥇 305 | - Being `pure` 😇 306 | - The `Applicative` type class 307 | - The `Applicative` laws 308 | - 🎆 Programming with effects 🎆 309 | - Extra functions and `Applicative` as defined in `base` 310 | 311 | ### 20. Monad [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F20-Monad.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=BFgmrO-c0Ec&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=21) 312 | 313 | - Why `Monad`s? 314 | - Extracting the pattern for: 315 | - `Maybe` 316 | - `Either e` 317 | - `Log` 318 | - Abstracting the `Monad` Type Class 319 | - The abstraction `Monad`chy 320 | - The `Monad`ic laws 321 | - `Monad` as defined in `base` 322 | - You have to `do` what you have to `do` 323 | 324 | ### 21. Reader, Writer, and State Monads [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/input-output-hk/haskell-course/HEAD?labpath=%2Flessons%2F21-Reader-Writer-and-State-Monads.ipynb) [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=RN9Fr7YRt5c&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=22) 325 | 326 | - Introduction 327 | - Logs `Writer` (based on `Log`) 328 | - Incentive 329 | - Generalizing `Log` 330 | - Helper fucntions 331 | - Environment `Reader` 332 | - Interacting with a (fake) database 333 | - Extracting the pattern 334 | - Using the `Reader` Monad 335 | - The `State` of things 336 | - Implementing a vending machine 337 | - Extracting the pattern 338 | - Using the `State` Monad 339 | 340 | ### 22. Final project [![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white)](https://www.youtube.com/watch?v=zqaqxEGaqrc&list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb&index=22) 341 | 342 | In this project, we'll build a CLI that converts Markdown files into HTML files. We're going to learn about: 343 | - The `text` library 344 | - How to build a CLI using `optparse-applicative` 345 | - How to parse text using `megaparsec` and combinators 346 | - How to generate HTML from a Markdown AST 347 | 348 | Ideally, you'll see the video without taking notes, and then try to implement the whole thing referencing only the libraries' documentation. Have fun!! 😃 349 | 350 | --- 351 | 352 | #### 🥳 CONGRATULATIONS! 🥳 You can call yourself a (beginner) Haskell developer! 353 | Thank you for going on this journey with me! Please feel free to give us feedback through issues, email, or Twitter. And share this course if you find it valuable. 😄🙌 354 | 355 | --- 356 | 357 | ### Where can you go from now? 358 | 359 | It depends on your preferences. You could: 360 | 361 | - If you're interested in Cardano, explore [Plinth](https://plutus.cardano.intersectmbo.org/docs/), [Atlas](https://atlas-app.io/), and other Cardano-related tools and libraries. 362 | - Start your own project and learn what you need on the way (e.g., a server, a compiler, a full-stack app using [IHP](https://ihp.digitallyinduced.com/)) 363 | - Read Haskell books and sources that go into more advanced subjects. See [recommended resources here](https://www.haskell.org/documentation/). 364 | - Learn about specific subjects, for example (organized roughly by difficulty): 365 | 1. Explore the `base` library and understand all types and type classes. 366 | 1. Learn about Testing (unit vs property testing and QuickCheck). 367 | 1. Explore how to deal with Concurrency and Parallelism. 368 | 1. Learn how Haskell deals with data structures under the Hood. 369 | 1. Monad Transformers and Free monads. 370 | 1. Generic programming. 371 | 1. Meta programming with Template Haskell. 372 | 1. Type-level programming. 373 | 374 | --- 375 | 376 | 377 | -------------------------------------------------------------------------------- /lessons/01-Introduction-to-haskell.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "# Introduction to\n", 12 | "\n", 13 | "\n", 14 | "![](https://www.haskell.org/img/haskell-logo.svg)" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": { 20 | "slideshow": { 21 | "slide_type": "slide" 22 | } 23 | }, 24 | "source": [ 25 | "## Outline\n", 26 | "* What is Haskell?\n", 27 | "* Functional programming languages\n", 28 | "\t* Function composition\n", 29 | "* Explicit effects (Pure)\n", 30 | "* Basic syntax\n", 31 | "\t* Indenting and Commenting\n", 32 | "\t* Defining and using functions\n", 33 | "* Haskell Type system\n", 34 | "* Laziness\n", 35 | "* Tools: GHC (GHCi), Cabal, Stack" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": { 41 | "slideshow": { 42 | "slide_type": "skip" 43 | } 44 | }, 45 | "source": [ 46 | "## How to use JupyterLab\n", 47 | "* Each lesson is a Jupyter notebook.\n", 48 | "* Each Jupyter notebook is a series of cells.\n", 49 | "* To execute a cell, click ⇧⏎ (Shift + Enter).\n", 50 | "* You can play around with the code inside the cells.\n", 51 | "* Once you close the tab, every change will be lost." 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": { 57 | "slideshow": { 58 | "slide_type": "slide" 59 | } 60 | }, 61 | "source": [ 62 | "## What is Haskell?" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": { 68 | "slideshow": { 69 | "slide_type": "notes" 70 | } 71 | }, 72 | "source": [ 73 | "We'll go over each property of Haskell individually and answer this question at the end of the lecture." 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": { 79 | "slideshow": { 80 | "slide_type": "slide" 81 | } 82 | }, 83 | "source": [ 84 | "## Functional programming languages" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": { 90 | "slideshow": { 91 | "slide_type": "notes" 92 | } 93 | }, 94 | "source": [ 95 | "Haskell is a functional programming language." 96 | ] 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "metadata": { 101 | "slideshow": { 102 | "slide_type": "fragment" 103 | } 104 | }, 105 | "source": [ 106 | "In imperative programming languages, function definitions are a sequence of imperative statements." 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": { 112 | "slideshow": { 113 | "slide_type": "skip" 114 | } 115 | }, 116 | "source": [ 117 | "\n", 118 | "In functional programming languages, function definitions are **trees of expressions that map values to other values**." 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": { 124 | "slideshow": { 125 | "slide_type": "fragment" 126 | } 127 | }, 128 | "source": [ 129 | "\n", 130 | "**Programs are constructed by *applying* and *composing* functions**. " 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": { 136 | "slideshow": { 137 | "slide_type": "slide" 138 | } 139 | }, 140 | "source": [ 141 | "### Function composition" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": { 147 | "slideshow": { 148 | "slide_type": "fragment" 149 | } 150 | }, 151 | "source": [ 152 | "**Function composition is the act of *pipelining* the *result* of one function, to the *input* of another, creating an entirely new function**" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": { 158 | "slideshow": { 159 | "slide_type": "fragment" 160 | } 161 | }, 162 | "source": [ 163 | "Like the usual composition of functions in mathematics, the **result of each function is passed as the argument of the next**, and the result of the last one is the result of the whole." 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": { 169 | "slideshow": { 170 | "slide_type": "notes" 171 | } 172 | }, 173 | "source": [ 174 | "For example, suppose we have two functions $f$ and $g$:\n", 175 | "\n", 176 | "$$y = f(x)$$\n", 177 | "$$z = g(y)$$\n", 178 | "\n", 179 | "Composing them means we first compute $f(x)$ to get $y$, and then use $y$ as an argument to compute $g(y)$ to get $z$.\n", 180 | "\n", 181 | "Creating a function that goes from $x$ to $z$:\n", 182 | "\n", 183 | "$$z = g(f(x))$$\n", 184 | "\n", 185 | "And that's how we can create **arbitrarily complex functions by composing simple functions.** " 186 | ] 187 | }, 188 | { 189 | "cell_type": "markdown", 190 | "metadata": { 191 | "slideshow": { 192 | "slide_type": "slide" 193 | } 194 | }, 195 | "source": [ 196 | "For example, if we have:\n", 197 | "\n", 198 | "* A function that takes a spreadsheet and returns the list of players it contains.\n", 199 | "* A function that takes a list of players and returns the same list sorted by scores.\n", 200 | "* And a function that takes a list of players and returns the first 3.\n", 201 | "\n", 202 | "We could create **a single function that takes a spreadsheet and returns the 3 best players** by composing those three functions." 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": { 208 | "slideshow": { 209 | "slide_type": "notes" 210 | } 211 | }, 212 | "source": [ 213 | "Also, Haskell has explicit effects (also called pure 👼)!" 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "metadata": { 219 | "slideshow": { 220 | "slide_type": "slide" 221 | } 222 | }, 223 | "source": [ 224 | "## Explicit effects (purely functional)" 225 | ] 226 | }, 227 | { 228 | "cell_type": "markdown", 229 | "metadata": { 230 | "slideshow": { 231 | "slide_type": "fragment" 232 | } 233 | }, 234 | "source": [ 235 | "Purely functional programming languages treat **all computations as the evaluation of mathematical functions**." 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": { 241 | "slideshow": { 242 | "slide_type": "fragment" 243 | } 244 | }, 245 | "source": [ 246 | "In mathematics, the expression $y = x + 1$ means that the value of $y$ is a function that depends on $x$." 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": { 252 | "slideshow": { 253 | "slide_type": "notes" 254 | } 255 | }, 256 | "source": [ 257 | "For a specific $x$, the value of $y$ will always be the same.\n", 258 | "\n", 259 | "No matter if you're in Italy or Spain, if it's 1994 or 2022, or if you have other equations in the notebook. $y$ will care about the value of $x$ and nothing else. " 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": { 265 | "slideshow": { 266 | "slide_type": "slide" 267 | } 268 | }, 269 | "source": [ 270 | "In purely functional programming languages, pure functions depend **only on their arguments** and **don't interact with any global or local state**. (This is called having \"no *side effects*.\")" 271 | ] 272 | }, 273 | { 274 | "cell_type": "markdown", 275 | "metadata": { 276 | "slideshow": { 277 | "slide_type": "fragment" 278 | } 279 | }, 280 | "source": [ 281 | "This means that, **for a specific input, a function will always return the same value. Every time.**" 282 | ] 283 | }, 284 | { 285 | "cell_type": "markdown", 286 | "metadata": { 287 | "slideshow": { 288 | "slide_type": "notes" 289 | } 290 | }, 291 | "source": [ 292 | "It sounds like a bad idea, but if you think about it, it has some extremely convenient consequences:" 293 | ] 294 | }, 295 | { 296 | "cell_type": "markdown", 297 | "metadata": { 298 | "slideshow": { 299 | "slide_type": "slide" 300 | } 301 | }, 302 | "source": [ 303 | "* It lets you easily deduce and prove that a function is correct.\n", 304 | "* In Haskell, one can always “replace equals by equals”, just like you learned in algebra class.\n", 305 | "* Makes your code significantly less error-prone.\n", 306 | "* It's easier to do parallel/concurrent computing. (If there is no data dependency between two pure expressions, then they can be performed in parallel, and they cannot interfere with one another.)\n", 307 | "* The list goes on..." 308 | ] 309 | }, 310 | { 311 | "cell_type": "markdown", 312 | "metadata": { 313 | "slideshow": { 314 | "slide_type": "slide" 315 | } 316 | }, 317 | "source": [ 318 | "**Haskell works as a pure language, but allows for side effects (network communication, I/O, etc.) by explicitly tagging them in the type system.** We'll see how in future lessons. (This is called having \"*explicit effects*\")." 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": { 324 | "slideshow": { 325 | "slide_type": "notes" 326 | } 327 | }, 328 | "source": [ 329 | "Before continuing with more properties, let's see how Haskell actually looks like." 330 | ] 331 | }, 332 | { 333 | "cell_type": "markdown", 334 | "metadata": { 335 | "slideshow": { 336 | "slide_type": "slide" 337 | } 338 | }, 339 | "source": [ 340 | "## Basic syntax" 341 | ] 342 | }, 343 | { 344 | "cell_type": "markdown", 345 | "metadata": { 346 | "slideshow": { 347 | "slide_type": "slide" 348 | } 349 | }, 350 | "source": [ 351 | "### Commenting the code" 352 | ] 353 | }, 354 | { 355 | "cell_type": "code", 356 | "execution_count": null, 357 | "metadata": { 358 | "slideshow": { 359 | "slide_type": "fragment" 360 | } 361 | }, 362 | "outputs": [], 363 | "source": [ 364 | "-- Use double dash to comment within a single line of code.\n", 365 | "\n", 366 | "{-\n", 367 | "Use curly bracket with a single dash to\n", 368 | " open and close\n", 369 | "multi-line comments.\n", 370 | "-}" 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": { 376 | "slideshow": { 377 | "slide_type": "slide" 378 | } 379 | }, 380 | "source": [ 381 | "### Indentation\n", 382 | "\n", 383 | "**Haskell is indentation sensitive**. Which means that spaces, tabs, and newlines matter.\n", 384 | "\n", 385 | "The golden rule is:\n" 386 | ] 387 | }, 388 | { 389 | "cell_type": "markdown", 390 | "metadata": { 391 | "slideshow": { 392 | "slide_type": "fragment" 393 | } 394 | }, 395 | "source": [ 396 | "

Code which is part of some expression should be indented further in than the beginning of that expression (even if the expression is not the leftmost element of the line).

" 397 | ] 398 | }, 399 | { 400 | "cell_type": "markdown", 401 | "metadata": { 402 | "slideshow": { 403 | "slide_type": "notes" 404 | } 405 | }, 406 | "source": [ 407 | "We'll look at examples in future lessons." 408 | ] 409 | }, 410 | { 411 | "cell_type": "markdown", 412 | "metadata": { 413 | "slideshow": { 414 | "slide_type": "slide" 415 | } 416 | }, 417 | "source": [ 418 | "### Defining functions" 419 | ] 420 | }, 421 | { 422 | "cell_type": "markdown", 423 | "metadata": { 424 | "slideshow": { 425 | "slide_type": "notes" 426 | } 427 | }, 428 | "source": [ 429 | "Haskell being a functional programming language means that you're going to write plenty of functions. So, that's where we'll start." 430 | ] 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "metadata": { 435 | "slideshow": { 436 | "slide_type": "fragment" 437 | } 438 | }, 439 | "source": [ 440 | "This is an expression to define a function that checks if a number is greater than 18:" 441 | ] 442 | }, 443 | { 444 | "cell_type": "code", 445 | "execution_count": null, 446 | "metadata": { 447 | "slideshow": { 448 | "slide_type": "fragment" 449 | } 450 | }, 451 | "outputs": [], 452 | "source": [ 453 | "greaterThan18 x = x > 18" 454 | ] 455 | }, 456 | { 457 | "cell_type": "markdown", 458 | "metadata": { 459 | "slideshow": { 460 | "slide_type": "notes" 461 | } 462 | }, 463 | "source": [ 464 | "* `greaterThan18` is the name of the function. Choose a name that makes it easy to know what it does.\n", 465 | "* `x` is a parameter.\n", 466 | "* The `=` operator assigns the `x > 18` expression to the `greaterThan18` name.\n", 467 | "\n", 468 | "To the left of the `=` sign, we write the function's name and parameters. And to the right, the expression that'll be contained by this function." 469 | ] 470 | }, 471 | { 472 | "cell_type": "markdown", 473 | "metadata": { 474 | "slideshow": { 475 | "slide_type": "slide" 476 | } 477 | }, 478 | "source": [ 479 | "### Using functions" 480 | ] 481 | }, 482 | { 483 | "cell_type": "markdown", 484 | "metadata": { 485 | "slideshow": { 486 | "slide_type": "notes" 487 | } 488 | }, 489 | "source": [ 490 | "To use the `greaterThan18` function, we just have to write the name, a space, and write a number:" 491 | ] 492 | }, 493 | { 494 | "cell_type": "code", 495 | "execution_count": null, 496 | "metadata": { 497 | "slideshow": { 498 | "slide_type": "fragment" 499 | } 500 | }, 501 | "outputs": [], 502 | "source": [ 503 | "greaterThan18 3" 504 | ] 505 | }, 506 | { 507 | "cell_type": "markdown", 508 | "metadata": { 509 | "slideshow": { 510 | "slide_type": "notes" 511 | } 512 | }, 513 | "source": [ 514 | "The function is executed, Haskell replaces all the `x` with `30`, and `greaterThan18 30` becomes `30 > 18`. Then, it evaluates the expression, returning `True`." 515 | ] 516 | }, 517 | { 518 | "cell_type": "markdown", 519 | "metadata": { 520 | "slideshow": { 521 | "slide_type": "slide" 522 | } 523 | }, 524 | "source": [ 525 | "### More examples" 526 | ] 527 | }, 528 | { 529 | "cell_type": "code", 530 | "execution_count": null, 531 | "metadata": { 532 | "slideshow": { 533 | "slide_type": "slide" 534 | } 535 | }, 536 | "outputs": [], 537 | "source": [ 538 | "-- A function that adds 6 numbers:\n", 539 | "add6numbers u v w x y z = u + v + w + x + y + z\n", 540 | "add6numbers 1 2 3 4 5 6 -- 21" 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": null, 546 | "metadata": { 547 | "slideshow": { 548 | "slide_type": "slide" 549 | } 550 | }, 551 | "outputs": [], 552 | "source": [ 553 | "-- A function that calculates the volume of a cylinder\n", 554 | "volumeOfACylinder r h = pi * r^2 * h -- pi represents the number π, and it comes with Haskell\n", 555 | "volumeOfACylinder 3 10" 556 | ] 557 | }, 558 | { 559 | "cell_type": "code", 560 | "execution_count": null, 561 | "metadata": { 562 | "slideshow": { 563 | "slide_type": "slide" 564 | } 565 | }, 566 | "outputs": [], 567 | "source": [ 568 | "-- A function that takes the temperature in Fahrenheit and returns it in Celsius\n", 569 | "fToC x = (x - 32) * 5 / 9\n", 570 | "fToC 212 -- 100\t" 571 | ] 572 | }, 573 | { 574 | "cell_type": "markdown", 575 | "metadata": { 576 | "slideshow": { 577 | "slide_type": "slide" 578 | } 579 | }, 580 | "source": [ 581 | "### Key points\n", 582 | "* Parameters are separated by spaces.\n", 583 | "* Everything after the `=` is the function's body.\n", 584 | "* The first letter of a function's name has to be lowercase.\n", 585 | "* We use parentheses to prioritize calculations, like in math." 586 | ] 587 | }, 588 | { 589 | "cell_type": "markdown", 590 | "metadata": { 591 | "slideshow": { 592 | "slide_type": "slide" 593 | } 594 | }, 595 | "source": [ 596 | "## Haskell type system" 597 | ] 598 | }, 599 | { 600 | "cell_type": "markdown", 601 | "metadata": {}, 602 | "source": [ 603 | "We will cover Haskell type system in depth in lesson 2. Here you will learn some basics." 604 | ] 605 | }, 606 | { 607 | "cell_type": "markdown", 608 | "metadata": { 609 | "slideshow": { 610 | "slide_type": "notes" 611 | } 612 | }, 613 | "source": [ 614 | "Types are **attributes that constraint the values a piece of code can have**. For example, if you indicate that some data is a number, that data could have any of these values:" 615 | ] 616 | }, 617 | { 618 | "cell_type": "markdown", 619 | "metadata": { 620 | "slideshow": { 621 | "slide_type": "fragment" 622 | } 623 | }, 624 | "source": [ 625 | " 32\n", 626 | "\n", 627 | " 9999695939294\n", 628 | "\n", 629 | " 0.5" 630 | ] 631 | }, 632 | { 633 | "cell_type": "markdown", 634 | "metadata": { 635 | "slideshow": { 636 | "slide_type": "skip" 637 | } 638 | }, 639 | "source": [ 640 | "But, if you try to add a character in there, like this: `6A3` (instead of `63`), the compiler/interpreter will yell at you.\n", 641 | "\n", 642 | "What your compiler/interpreter did just there is called \"**type checking**.\" Some languages have more strongly enforced type checking, some less." 643 | ] 644 | }, 645 | { 646 | "cell_type": "markdown", 647 | "metadata": { 648 | "slideshow": { 649 | "slide_type": "fragment" 650 | } 651 | }, 652 | "source": [ 653 | " 6A3" 654 | ] 655 | }, 656 | { 657 | "cell_type": "markdown", 658 | "metadata": { 659 | "slideshow": { 660 | "slide_type": "slide" 661 | } 662 | }, 663 | "source": [ 664 | "### Type checking\n", 665 | "\n", 666 | "**Type checking is the process of verifying and enforcing the constraints of types.**" 667 | ] 668 | }, 669 | { 670 | "cell_type": "markdown", 671 | "metadata": { 672 | "slideshow": { 673 | "slide_type": "fragment" 674 | } 675 | }, 676 | "source": [ 677 | "**What does this mean?** It means that each type has its own constraints (E.g., you can't do math with letters.), and this process checks that those constraints are respected." 678 | ] 679 | }, 680 | { 681 | "cell_type": "markdown", 682 | "metadata": { 683 | "slideshow": { 684 | "slide_type": "fragment" 685 | } 686 | }, 687 | "source": [ 688 | "**Why would you do this?** To avoid preventable mistakes." 689 | ] 690 | }, 691 | { 692 | "cell_type": "markdown", 693 | "metadata": { 694 | "slideshow": { 695 | "slide_type": "skip" 696 | } 697 | }, 698 | "source": [ 699 | "### Dynamically typed languages" 700 | ] 701 | }, 702 | { 703 | "cell_type": "markdown", 704 | "metadata": { 705 | "slideshow": { 706 | "slide_type": "notes" 707 | } 708 | }, 709 | "source": [ 710 | "If further along in your program, you want to add up some numbers and one of them has a letter, the program wouldn't know what to do, and it would crash on you. Those are preventable mistakes (bugs), and the compiler/interpreter helps you avoid them." 711 | ] 712 | }, 713 | { 714 | "cell_type": "markdown", 715 | "metadata": { 716 | "slideshow": { 717 | "slide_type": "notes" 718 | } 719 | }, 720 | "source": [ 721 | "Usually, this is done automatically. But not all languages do this the same way. There are two main distinctions regarding as to WHEN the types are checked: Dynamically typed languages and Statically typed languages." 722 | ] 723 | }, 724 | { 725 | "cell_type": "markdown", 726 | "metadata": { 727 | "slideshow": { 728 | "slide_type": "slide" 729 | } 730 | }, 731 | "source": [ 732 | "**Dynamically typed languages check the types at run-time**." 733 | ] 734 | }, 735 | { 736 | "cell_type": "markdown", 737 | "metadata": { 738 | "slideshow": { 739 | "slide_type": "skip" 740 | } 741 | }, 742 | "source": [ 743 | "Run-time is the very last thing that you do with a program. It's the stage when you run your program to test it or use it.\n", 744 | "\n", 745 | "Common examples of dynamically typed languages include JavaScript, Python, Objective-C, and PHP." 746 | ] 747 | }, 748 | { 749 | "cell_type": "markdown", 750 | "metadata": { 751 | "slideshow": { 752 | "slide_type": "skip" 753 | } 754 | }, 755 | "source": [ 756 | "### Statically typed languages" 757 | ] 758 | }, 759 | { 760 | "cell_type": "markdown", 761 | "metadata": { 762 | "slideshow": { 763 | "slide_type": "fragment" 764 | } 765 | }, 766 | "source": [ 767 | "**Statically typed languages check the types at compile-time**." 768 | ] 769 | }, 770 | { 771 | "cell_type": "markdown", 772 | "metadata": { 773 | "slideshow": { 774 | "slide_type": "skip" 775 | } 776 | }, 777 | "source": [ 778 | "Meaning that you'll know if your types are wrong as soon as you compile your program. Which leads to a safer and more optimized code.\n", 779 | "\n", 780 | "Common examples of statically typed languages include Java, C, and C++." 781 | ] 782 | }, 783 | { 784 | "cell_type": "markdown", 785 | "metadata": { 786 | "slideshow": { 787 | "slide_type": "slide" 788 | } 789 | }, 790 | "source": [ 791 | "### Haskell type system" 792 | ] 793 | }, 794 | { 795 | "cell_type": "markdown", 796 | "metadata": { 797 | "slideshow": { 798 | "slide_type": "fragment" 799 | } 800 | }, 801 | "source": [ 802 | "**Haskell is statically typed**. And, in Haskell, **every expression has a type**." 803 | ] 804 | }, 805 | { 806 | "cell_type": "markdown", 807 | "metadata": { 808 | "slideshow": { 809 | "slide_type": "notes" 810 | } 811 | }, 812 | "source": [ 813 | "But don't worry, you don't have to manually define the types of every expression because Haskell's compiler is very good at **type inference**." 814 | ] 815 | }, 816 | { 817 | "cell_type": "markdown", 818 | "metadata": { 819 | "slideshow": { 820 | "slide_type": "fragment" 821 | } 822 | }, 823 | "source": [ 824 | "**Type inference allows Haskell to infer the types on its own**." 825 | ] 826 | }, 827 | { 828 | "cell_type": "markdown", 829 | "metadata": { 830 | "slideshow": { 831 | "slide_type": "notes" 832 | } 833 | }, 834 | "source": [ 835 | "If you write something like `3 + 4`, Haskell will know that the result of that expression is a number, and it will treat it like it without the need for you to specify the type. (It works with more complicated expressions, too. See previous examples.)" 836 | ] 837 | }, 838 | { 839 | "cell_type": "markdown", 840 | "metadata": { 841 | "slideshow": { 842 | "slide_type": "fragment" 843 | } 844 | }, 845 | "source": [ 846 | "That allows the compiler to **comprehend and reason *quite a lot* about your program**. Providing you with a pretty effective bug-catching assistant." 847 | ] 848 | }, 849 | { 850 | "cell_type": "markdown", 851 | "metadata": { 852 | "slideshow": { 853 | "slide_type": "skip" 854 | } 855 | }, 856 | "source": [ 857 | "Even though it's not needed for the compiler, **it's considered good practice to write out the type signature of top-level functions and expressions**. To improve code readability." 858 | ] 859 | }, 860 | { 861 | "cell_type": "markdown", 862 | "metadata": { 863 | "slideshow": { 864 | "slide_type": "notes" 865 | } 866 | }, 867 | "source": [ 868 | "If the code is too ambiguous for the compiler to infer the type, it'll ask you to specify the type." 869 | ] 870 | }, 871 | { 872 | "cell_type": "markdown", 873 | "metadata": { 874 | "slideshow": { 875 | "slide_type": "slide" 876 | } 877 | }, 878 | "source": [ 879 | "## Laziness" 880 | ] 881 | }, 882 | { 883 | "cell_type": "markdown", 884 | "metadata": { 885 | "slideshow": { 886 | "slide_type": "fragment" 887 | } 888 | }, 889 | "source": [ 890 | "**Haskell is lazy. This means that it won't evaluate expressions until their results are needed**" 891 | ] 892 | }, 893 | { 894 | "cell_type": "markdown", 895 | "metadata": { 896 | "slideshow": { 897 | "slide_type": "notes" 898 | } 899 | }, 900 | "source": [ 901 | "Examples of Haskell laziness in practice:" 902 | ] 903 | }, 904 | { 905 | "cell_type": "markdown", 906 | "metadata": { 907 | "slideshow": { 908 | "slide_type": "slide" 909 | } 910 | }, 911 | "source": [ 912 | "* We can use infinite lists." 913 | ] 914 | }, 915 | { 916 | "cell_type": "code", 917 | "execution_count": null, 918 | "metadata": { 919 | "slideshow": { 920 | "slide_type": "fragment" 921 | } 922 | }, 923 | "outputs": [], 924 | "source": [ 925 | "giveMe x = take x [1..] -- [1..] is an infinite list of natural numbers that starts at 1.\n", 926 | "giveMe 7" 927 | ] 928 | }, 929 | { 930 | "cell_type": "markdown", 931 | "metadata": { 932 | "slideshow": { 933 | "slide_type": "slide" 934 | } 935 | }, 936 | "source": [ 937 | "\n", 938 | "* Haskell won't evaluate expressions if they aren't needed" 939 | ] 940 | }, 941 | { 942 | "cell_type": "code", 943 | "execution_count": null, 944 | "metadata": { 945 | "slideshow": { 946 | "slide_type": "fragment" 947 | } 948 | }, 949 | "outputs": [], 950 | "source": [ 951 | "cheapComputation = 7 \n", 952 | "expensiveComputation = sum [1..10000000] -- sum is a function that takes a list and returns the sum of all the elements. This will crash the kernel.\n", 953 | "if cheapComputation > 5 || expensiveComputation > 5 then \"Done\" else \"This won't ever show because expensiveComputation is always bigger than 5\"\n", 954 | "-- Try running this cell with cheapComputation being bigger and smaller than 5.\n", 955 | "-- When cheapComputation > 5, expensiveComputation isn't evaluated because it is not needed." 956 | ] 957 | }, 958 | { 959 | "cell_type": "markdown", 960 | "metadata": { 961 | "slideshow": { 962 | "slide_type": "slide" 963 | } 964 | }, 965 | "source": [ 966 | "## So, what is Haskell?" 967 | ] 968 | }, 969 | { 970 | "cell_type": "markdown", 971 | "metadata": { 972 | "slideshow": { 973 | "slide_type": "fragment" 974 | } 975 | }, 976 | "source": [ 977 | "#### Haskell is a statically typed, **lazy**, functional programming language with **explicit effects** and functions that look like this:\n", 978 | "\n", 979 | "```haskell\n", 980 | "volumeOfACylinder r h = pi * r^2 * h \n", 981 | "```" 982 | ] 983 | }, 984 | { 985 | "cell_type": "markdown", 986 | "metadata": { 987 | "slideshow": { 988 | "slide_type": "fragment" 989 | } 990 | }, 991 | "source": [ 992 | "

\n", 993 | "Note: Haskell has other important properties (like algebraic data types, type classes, type inference, polymorphism, ...) that we'll cover in future lessons.\n", 994 | "
" 995 | ] 996 | }, 997 | { 998 | "cell_type": "markdown", 999 | "metadata": { 1000 | "slideshow": { 1001 | "slide_type": "notes" 1002 | } 1003 | }, 1004 | "source": [ 1005 | "(*Lazy* and *explicit effects* are two of the more unique properties of Haskell. That's why they're in bold.)" 1006 | ] 1007 | }, 1008 | { 1009 | "cell_type": "markdown", 1010 | "metadata": { 1011 | "slideshow": { 1012 | "slide_type": "slide" 1013 | } 1014 | }, 1015 | "source": [ 1016 | "## Tools" 1017 | ] 1018 | }, 1019 | { 1020 | "cell_type": "markdown", 1021 | "metadata": { 1022 | "slideshow": { 1023 | "slide_type": "slide" 1024 | } 1025 | }, 1026 | "source": [ 1027 | "### A few words on Cabal and Stack" 1028 | ] 1029 | }, 1030 | { 1031 | "cell_type": "markdown", 1032 | "metadata": { 1033 | "slideshow": { 1034 | "slide_type": "fragment" 1035 | } 1036 | }, 1037 | "source": [ 1038 | "While learning about Haskell, you'll often encounter the Cabal and Stack terms.\n", 1039 | "\n", 1040 | "**These are systems for managing libraries and programs**. They make it straightforward to work with libraries.\n", 1041 | "\n", 1042 | "We'll use Cabal in this course, and we'll explain how to use it at a later stage." 1043 | ] 1044 | }, 1045 | { 1046 | "cell_type": "markdown", 1047 | "metadata": { 1048 | "slideshow": { 1049 | "slide_type": "slide" 1050 | } 1051 | }, 1052 | "source": [ 1053 | "### GHC and GHCi" 1054 | ] 1055 | }, 1056 | { 1057 | "cell_type": "markdown", 1058 | "metadata": { 1059 | "slideshow": { 1060 | "slide_type": "fragment" 1061 | } 1062 | }, 1063 | "source": [ 1064 | "**GHC (Glasgow Haskell Compiler) is a compiler and interactive environment for Haskell**. Using GHC we can:\n", 1065 | "* Compile programs and execute them like any other app.\n", 1066 | "* Evaluate Haskell expressions on the fly using the interactive environment provided by the GHC (the GHCi).\n", 1067 | "\n", 1068 | "To use GHCi, open the terminal in the GitPod remote environment that we've prepared and type `ghci`.\n", 1069 | "\n", 1070 | "Use `:l relative/path.hs` inside GHCi to load a file and interactively use its contents and `:q` to quit." 1071 | ] 1072 | }, 1073 | { 1074 | "cell_type": "markdown", 1075 | "metadata": {}, 1076 | "source": [ 1077 | "**NOTE:** If you want to install GHC and GHCi on your computer you can follown the instructions on www.haskell.org/ghcup/. Instructions are provided for Windows, Mac and Linux." 1078 | ] 1079 | } 1080 | ], 1081 | "metadata": { 1082 | "celltoolbar": "Slideshow", 1083 | "kernelspec": { 1084 | "display_name": "Haskell", 1085 | "language": "haskell", 1086 | "name": "haskell" 1087 | }, 1088 | "language_info": { 1089 | "codemirror_mode": "ihaskell", 1090 | "file_extension": ".hs", 1091 | "mimetype": "text/x-haskell", 1092 | "name": "haskell", 1093 | "pygments_lexer": "Haskell", 1094 | "version": "8.10.7" 1095 | }, 1096 | "rise": { 1097 | "enable_chalkboard": true, 1098 | "header": "" 1099 | } 1100 | }, 1101 | "nbformat": 4, 1102 | "nbformat_minor": 4 1103 | } -------------------------------------------------------------------------------- /ES-translation/Lecciones/01-Introducción-a-haskell.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "# Introducción a\n", 12 | "\n", 13 | "\n", 14 | "![](https://www.haskell.org/img/haskell-logo.svg)" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": { 20 | "slideshow": { 21 | "slide_type": "slide" 22 | } 23 | }, 24 | "source": [ 25 | "## Índice\n", 26 | "* ¿Qué es Haskell?\n", 27 | "* Lenguajes de programación funcionales\n", 28 | "\t* Composición de funciones\n", 29 | "* Efectos explícitos (puramente funcionales)\n", 30 | "* Sintaxis básica\n", 31 | "\t* Identación y Comentarios\n", 32 | "\t* Definiendo y usando funciones \n", 33 | "* Sistema de Tipos de Haskell\n", 34 | "* Laziness\n", 35 | "* Herramientas: GHC (GHCi), Cabal, Stack" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": { 41 | "slideshow": { 42 | "slide_type": "skip" 43 | } 44 | }, 45 | "source": [ 46 | "## Cómo utilizar JupyterLab\n", 47 | "* Cada lección es un notebook de Jupyter.\n", 48 | "* Cada notebook de Jupyter es una serie de celdas.\n", 49 | "* Para ejecutar una celda, presiona ⇧⏎ (Shift + Enter).\n", 50 | "* Puedes experimentar con el código dentro de las celdas.\n", 51 | "* Una vez cierres la pestaña, se perderán todos los cambios." 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": { 57 | "slideshow": { 58 | "slide_type": "slide" 59 | } 60 | }, 61 | "source": [ 62 | "## ¿Qué es Haskell?" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": { 68 | "slideshow": { 69 | "slide_type": "notes" 70 | } 71 | }, 72 | "source": [ 73 | "Vamos a repasar cada propiedad de Haskell por separado y responderemos esta pregunta al final de la lección." 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": { 79 | "slideshow": { 80 | "slide_type": "slide" 81 | } 82 | }, 83 | "source": [ 84 | "## Lenguajes de programación funcional" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": { 90 | "slideshow": { 91 | "slide_type": "notes" 92 | } 93 | }, 94 | "source": [ 95 | "Haskell es un lenguaje de programación funcional." 96 | ] 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "metadata": { 101 | "slideshow": { 102 | "slide_type": "fragment" 103 | } 104 | }, 105 | "source": [ 106 | "En lenguajes de programación imperativos, las definiciones de funciones son una secuencia declaraciones imperativas." 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": { 112 | "slideshow": { 113 | "slide_type": "skip" 114 | } 115 | }, 116 | "source": [ 117 | "En un lenguaje de programación funcional, las definiciones de funciones son **árboles de expresiones que relacionan valores con otros valores**. \n", 118 | "\n", 119 | "*Aclaración: un árbol de expresiones es una forma de representar expresiones algebráicas, no te preocupes si el concepto no queda claro del todo.\n", 120 | "Podés pensar una función simplemente como una caja mágica donde entran valores por un lado y por el otro salen valores distintos.*\n", 121 | "\n", 122 | "![Ejemplo Funcion](https://adit.io/imgs/functors/function_with_value.png \"ejemplo funcion\")" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": { 128 | "slideshow": { 129 | "slide_type": "fragment" 130 | } 131 | }, 132 | "source": [ 133 | "\n", 134 | "**Los programas se construyen *aplicando* y *componiendo* funciones**." 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": { 140 | "slideshow": { 141 | "slide_type": "slide" 142 | } 143 | }, 144 | "source": [ 145 | "### Composición de funciones" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": { 151 | "slideshow": { 152 | "slide_type": "fragment" 153 | } 154 | }, 155 | "source": [ 156 | "**La composición es el acto de *dirigir* el *resultado* de una función hacia la *entrada* de otra, creando una función completamente nueva**" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": { 162 | "slideshow": { 163 | "slide_type": "fragment" 164 | } 165 | }, 166 | "source": [ 167 | "Tal como la composición de funciones en matemática, el **resultado de cada función se pasa como argumento de la siguiente**, y el resultado de la última es el resultado total." 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": { 173 | "slideshow": { 174 | "slide_type": "notes" 175 | } 176 | }, 177 | "source": [ 178 | "Por ejemplo, supongamos que tenemos dos funciones $f$ y $g$:\n", 179 | "\n", 180 | "$$y = f(x)$$\n", 181 | "$$z = g(y)$$\n", 182 | "\n", 183 | "Componer ambas funciones implica primero calcular $f(x)$ para obtener $y$, y luego usar $y$ como argumento para calcular $g(y)$ y obtener $z$.\n", 184 | "\n", 185 | "Creando así una función que va de $x$ hasta $z$:\n", 186 | "\n", 187 | "$$z = g(f(x))$$\n", 188 | "\n", 189 | "De esa manera podemos crear **funciones arbitrariamente complejas componiendo funciones sencillas**" 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": { 195 | "slideshow": { 196 | "slide_type": "slide" 197 | } 198 | }, 199 | "source": [ 200 | "Por ejemplo, si tenemos:\n", 201 | "\n", 202 | "* Una función que toma una hoja de cálculo y devielve la lista de jugadores que contiene.\n", 203 | "* Una función que toma la lista de jugadores y devuelve la misma lista ordenada por puntajes.\n", 204 | "* Y una finción que toma la lista de jugadores y devuelve los primeros 3.\n", 205 | "\n", 206 | "Podríamos crear **una única función que tome la hoja de cálculo y devuelva los 3 mejores jugadores** simplemente componiendo esas tres funciones.\n", 207 | "\n", 208 | "Un ejemplo más visual:\n", 209 | "\n", 210 | "![Ejemplo Composición](https://i.imgur.com/S1jPpWN.png \"ejemplo composicion\")\n", 211 | "\n", 212 | "Una función que suma 2 a un número, y una función que suma 3 a un número, compuestas forman una única función suma 5 al valor de entrada." 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": { 218 | "slideshow": { 219 | "slide_type": "notes" 220 | } 221 | }, 222 | "source": [ 223 | "\n", 224 | "\n", 225 | "Además, Haskell tiene efectos explícitos (también llamados puros 👼)!" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": { 231 | "slideshow": { 232 | "slide_type": "slide" 233 | } 234 | }, 235 | "source": [ 236 | "## Efectos explícitos (puramente funcionales)" 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": { 242 | "slideshow": { 243 | "slide_type": "fragment" 244 | } 245 | }, 246 | "source": [ 247 | "Los lenguajes de programación funcionales puros tratan **todos los cálculos como evaluación de funciones matemáticas**." 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "metadata": { 253 | "slideshow": { 254 | "slide_type": "fragment" 255 | } 256 | }, 257 | "source": [ 258 | "En matemática, la expresión $y$ = $x$ + 1 significa que el valor de $y$ es una función que depende de $x$." 259 | ] 260 | }, 261 | { 262 | "cell_type": "markdown", 263 | "metadata": { 264 | "slideshow": { 265 | "slide_type": "notes" 266 | } 267 | }, 268 | "source": [ 269 | "Para una $x$ específica, el valor de $y$ siempre va a ser el mismo.\n", 270 | "\n", 271 | "Sin importar si estás en Italia o en España, si es el año 1994 o el 2022, o si tenés otras ecuaciones en tu cuaderno. $y$ solo se va a fijar en el valor de $x$ y nada más." 272 | ] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "metadata": { 277 | "slideshow": { 278 | "slide_type": "slide" 279 | } 280 | }, 281 | "source": [ 282 | "En los lenguajes de programación funcionales, las funciones puras dependen **solo de sus argumentos** y **no interactuan con ningún estado global ni local** (esto se llama \"no tener \"*efectos secundarios*.\")" 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": { 288 | "slideshow": { 289 | "slide_type": "fragment" 290 | } 291 | }, 292 | "source": [ 293 | "Esto significa que, **para una entrada específica, la función devolverá siempre el mismo valor. Siempre.**" 294 | ] 295 | }, 296 | { 297 | "cell_type": "markdown", 298 | "metadata": { 299 | "slideshow": { 300 | "slide_type": "notes" 301 | } 302 | }, 303 | "source": [ 304 | "Esto puede parecer una mala idea, pero en realidad tiene algunas consecuencias extremadamente convenientes:" 305 | ] 306 | }, 307 | { 308 | "cell_type": "markdown", 309 | "metadata": { 310 | "slideshow": { 311 | "slide_type": "slide" 312 | } 313 | }, 314 | "source": [ 315 | "* Te permite deducir facilmente y probar que una función es correcta.\n", 316 | "* En Haskell, uno puede siempre \"reemplazar iguales por iguales\", tal como aprendiste en tu clase de álgebra.\n", 317 | "* Permite que tu código sea menos propenso a errores.\n", 318 | "* Es más fácil la programación paralela/concurrente. (Si no hay dependencia entre los datos de dos expresiones puras, entonces llevarse a cabo en paralelo y no pueden interferir entre ellas.)\n", 319 | "* La lista continúa..." 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "metadata": { 325 | "slideshow": { 326 | "slide_type": "slide" 327 | } 328 | }, 329 | "source": [ 330 | "**Haskell funciona como un lenguaje funcional puro, pero también admite efectos secundarios (comunicación de red, I/O (Flujos de entrada y salida, etc.) mencionándolos explicitamente en el sistema de tipos**. Veremos cómo hacerlo en futuras lecciones. (Esto se llama tener efectos explícitos)." 331 | ] 332 | }, 333 | { 334 | "cell_type": "markdown", 335 | "metadata": { 336 | "slideshow": { 337 | "slide_type": "notes" 338 | } 339 | }, 340 | "source": [ 341 | "Antes de continuar con más propiedades, veamos cómo se ve Haskell realmente." 342 | ] 343 | }, 344 | { 345 | "cell_type": "markdown", 346 | "metadata": { 347 | "slideshow": { 348 | "slide_type": "slide" 349 | } 350 | }, 351 | "source": [ 352 | "## Sintaxis básica" 353 | ] 354 | }, 355 | { 356 | "cell_type": "markdown", 357 | "metadata": { 358 | "slideshow": { 359 | "slide_type": "slide" 360 | } 361 | }, 362 | "source": [ 363 | "### Comentando el código" 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": null, 369 | "metadata": { 370 | "slideshow": { 371 | "slide_type": "fragment" 372 | } 373 | }, 374 | "outputs": [], 375 | "source": [ 376 | "-- Usa doble guión medio para comentar dentro de una línea de código\n", 377 | "\n", 378 | "{-\n", 379 | "Usa llaves con un guión para abrir y cerrar\n", 380 | " comentarios multi-línea.\n", 381 | "-}" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": { 387 | "slideshow": { 388 | "slide_type": "slide" 389 | } 390 | }, 391 | "source": [ 392 | "### Identación\n", 393 | "\n", 394 | "**Haskell es sensible a la identación**. Lo cual significa que los espacios, tabulaciones y saltos de línea **importan**.\n", 395 | "\n", 396 | "La clave es:" 397 | ] 398 | }, 399 | { 400 | "cell_type": "markdown", 401 | "metadata": { 402 | "slideshow": { 403 | "slide_type": "fragment" 404 | } 405 | }, 406 | "source": [ 407 | "

El código que forma parte de una misma expresión debe ser identitado más lejos que dicha expresión (incluso is la expresión no se encuentra sobre el borde izquierdo de la línea).

\n", 408 | " " 409 | ] 410 | }, 411 | { 412 | "cell_type": "code", 413 | "execution_count": null, 414 | "metadata": {}, 415 | "outputs": [], 416 | "source": [ 417 | "--Ejemplo, esta función suma dos valores (en la próxima sección daremos más detalles de cómo su sintaxis)\n", 418 | "funcionLoca valor1 valor2 = valor1 + valor2\n", 419 | "-- Pero también es correcto escribirlo de esta forma:\n", 420 | "funcionLoca valor1 valor2 = valor1\n", 421 | " +valor2\n", 422 | "{-^^ no importa cuántos espacios haya, mientras la segunda parte esté más a la derecha que la primera,\n", 423 | "seguirá formando parte de la misma expresión-}\n", 424 | " " 425 | ] 426 | }, 427 | { 428 | "cell_type": "code", 429 | "execution_count": null, 430 | "metadata": {}, 431 | "outputs": [], 432 | "source": [ 433 | "--Por más que la primera parte no esté sobre el borde de la línea, la segunda siempre debe estar más a la derecha.\n", 434 | " funcionLoca valor1 valor2 = valor1\n", 435 | " +valor2" 436 | ] 437 | }, 438 | { 439 | "cell_type": "markdown", 440 | "metadata": { 441 | "slideshow": { 442 | "slide_type": "notes" 443 | } 444 | }, 445 | "source": [ 446 | "Veremos más ejemplos en las próximas lecciones." 447 | ] 448 | }, 449 | { 450 | "cell_type": "markdown", 451 | "metadata": { 452 | "slideshow": { 453 | "slide_type": "slide" 454 | } 455 | }, 456 | "source": [ 457 | "### Definiendo funciones" 458 | ] 459 | }, 460 | { 461 | "cell_type": "markdown", 462 | "metadata": { 463 | "slideshow": { 464 | "slide_type": "notes" 465 | } 466 | }, 467 | "source": [ 468 | "Que Haskell sea un lenguaje funcional significa que vas a tener que escribir muchas funciones. Empecemos por eso." 469 | ] 470 | }, 471 | { 472 | "cell_type": "markdown", 473 | "metadata": { 474 | "slideshow": { 475 | "slide_type": "fragment" 476 | } 477 | }, 478 | "source": [ 479 | "Esta es una expresión que define una función que verifica si un número es mayor que 18:" 480 | ] 481 | }, 482 | { 483 | "cell_type": "code", 484 | "execution_count": null, 485 | "metadata": { 486 | "slideshow": { 487 | "slide_type": "fragment" 488 | } 489 | }, 490 | "outputs": [], 491 | "source": [ 492 | "mayorQue18 x = x > 18" 493 | ] 494 | }, 495 | { 496 | "cell_type": "markdown", 497 | "metadata": { 498 | "slideshow": { 499 | "slide_type": "notes" 500 | } 501 | }, 502 | "source": [ 503 | "* `mayorQue18` es el nombre de la función. Es conveniente elegir un nombre que identifique facilmente lo que hace la función.\n", 504 | "* `x` es un parámetro.\n", 505 | "* El operador `=` asigna la expresión `x > 18` al nombre `mayorQue18`.\n", 506 | "\n", 507 | "A la izquierda del signo `=` debemos escribir el nombre de la función y los parámetros. A la derecha, la expresión que será contenida por esta función.\n", 508 | "\n", 509 | "Algo así (puedo recibir todos los parámetros que quiera):" 510 | ] 511 | }, 512 | { 513 | "cell_type": "code", 514 | "execution_count": null, 515 | "metadata": {}, 516 | "outputs": [], 517 | "source": [ 518 | "nombreDeLaFuncion parametro1 parametro2 parametroX = parametro1 + parametro2 + parametroX" 519 | ] 520 | }, 521 | { 522 | "cell_type": "markdown", 523 | "metadata": { 524 | "slideshow": { 525 | "slide_type": "slide" 526 | } 527 | }, 528 | "source": [ 529 | "### Usando funciones" 530 | ] 531 | }, 532 | { 533 | "cell_type": "markdown", 534 | "metadata": { 535 | "slideshow": { 536 | "slide_type": "notes" 537 | } 538 | }, 539 | "source": [ 540 | "Para usar la función `mayorQue18`, solo tenemos que escribir su nombre, seguido de un espacio y un número:" 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": null, 546 | "metadata": { 547 | "slideshow": { 548 | "slide_type": "fragment" 549 | } 550 | }, 551 | "outputs": [], 552 | "source": [ 553 | "mayorQue18 30" 554 | ] 555 | }, 556 | { 557 | "cell_type": "markdown", 558 | "metadata": { 559 | "slideshow": { 560 | "slide_type": "notes" 561 | } 562 | }, 563 | "source": [ 564 | "La función es ejecutada, Haskell reemplaza todos las `x` con `30`, y se convierte en `mayorQue18 30`. Luego, evalúa la expresión, devolviendo `True`." 565 | ] 566 | }, 567 | { 568 | "cell_type": "markdown", 569 | "metadata": { 570 | "slideshow": { 571 | "slide_type": "slide" 572 | } 573 | }, 574 | "source": [ 575 | "### Más ejemplos" 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": null, 581 | "metadata": { 582 | "slideshow": { 583 | "slide_type": "slide" 584 | } 585 | }, 586 | "outputs": [], 587 | "source": [ 588 | "-- Una función que suma 6 números\n", 589 | "sumar6numeros u v w x y z = u + v + w + x + y + z\n", 590 | "sumar6numeros 1 2 3 4 5 6 -- 21" 591 | ] 592 | }, 593 | { 594 | "cell_type": "code", 595 | "execution_count": null, 596 | "metadata": { 597 | "slideshow": { 598 | "slide_type": "slide" 599 | } 600 | }, 601 | "outputs": [], 602 | "source": [ 603 | "-- Función que calcula el volumen de un cilindro\n", 604 | "volumenDeUnCilindro r h = pi * r^2 * h -- pi representa el numero π, ya viene dentro de Haskell\n", 605 | "volumenDeUnCilindro 3 10" 606 | ] 607 | }, 608 | { 609 | "cell_type": "code", 610 | "execution_count": null, 611 | "metadata": { 612 | "slideshow": { 613 | "slide_type": "slide" 614 | } 615 | }, 616 | "outputs": [], 617 | "source": [ 618 | "-- Función que recibe la temperatura en Farenheit y la devuelve en Celsius\n", 619 | "deFaC x = (x - 32) * 5 / 9\n", 620 | "deFaC 212 -- 100\t" 621 | ] 622 | }, 623 | { 624 | "cell_type": "markdown", 625 | "metadata": { 626 | "slideshow": { 627 | "slide_type": "slide" 628 | } 629 | }, 630 | "source": [ 631 | "### Puntos clave\n", 632 | "* Los parámetros van separados por espacios.\n", 633 | "* Todo lo que está después del `=` es el cuerpo de la función.\n", 634 | "* La primera letra del nombre de una función debe estar en minúscula.\n", 635 | "> Se utiliza la convención cammelCase para nombrar identificadores. Si el nombre de mi función abarca más de una palabra,\n", 636 | "la primera será escrita toda en minúscula y luego la primera letra de cada palabra estará en mayúscula,\n", 637 | "tal cual como \"cammelCase\". (Esto es una buena práctica, aunque solo es obligatorio la primera letra en minúscula).\n", 638 | "* Se utiliza paréntesis para dar prioridad a una operación, como en matemática." 639 | ] 640 | }, 641 | { 642 | "cell_type": "markdown", 643 | "metadata": { 644 | "slideshow": { 645 | "slide_type": "slide" 646 | } 647 | }, 648 | "source": [ 649 | "## Sistema de tipos de Haskell" 650 | ] 651 | }, 652 | { 653 | "cell_type": "markdown", 654 | "metadata": {}, 655 | "source": [ 656 | "Cubriremos el sistema de tipos de Haskell en profundidad en la leccion 2. A continuación aprenderas algunos conceptos básicos." 657 | ] 658 | }, 659 | { 660 | "cell_type": "markdown", 661 | "metadata": { 662 | "slideshow": { 663 | "slide_type": "notes" 664 | } 665 | }, 666 | "source": [ 667 | "Los tipos son **atributos que restringen los valores que una pieza de código puede tener**. Por ejemplo, si indicas que un dato es number, ese dato puede tener cualquiera de estos valores:" 668 | ] 669 | }, 670 | { 671 | "cell_type": "markdown", 672 | "metadata": { 673 | "slideshow": { 674 | "slide_type": "fragment" 675 | } 676 | }, 677 | "source": [ 678 | " 32\n", 679 | "\n", 680 | " 9999695939294\n", 681 | "\n", 682 | " 0.5" 683 | ] 684 | }, 685 | { 686 | "cell_type": "markdown", 687 | "metadata": { 688 | "slideshow": { 689 | "slide_type": "skip" 690 | } 691 | }, 692 | "source": [ 693 | "Pero, si intentas añadir un caracter allí, como éste: `6A3` (en vez de `63`), el compilador/intérprete va a quejarse. \n", 694 | "\n", 695 | "Lo que tu compilador/intérprete hizo se llama **type checking**. Algunos lenguajes tienen un type checking más estricto, otros menos." 696 | ] 697 | }, 698 | { 699 | "cell_type": "markdown", 700 | "metadata": { 701 | "slideshow": { 702 | "slide_type": "fragment" 703 | } 704 | }, 705 | "source": [ 706 | " 6A3" 707 | ] 708 | }, 709 | { 710 | "cell_type": "markdown", 711 | "metadata": { 712 | "slideshow": { 713 | "slide_type": "slide" 714 | } 715 | }, 716 | "source": [ 717 | "### Type checking\n", 718 | "\n", 719 | "**Type checking es el proceso de verificar y forzar las restricciones de tipos**." 720 | ] 721 | }, 722 | { 723 | "cell_type": "markdown", 724 | "metadata": { 725 | "slideshow": { 726 | "slide_type": "fragment" 727 | } 728 | }, 729 | "source": [ 730 | "**¿Qué significa?** Significa que cada tipo tiene sus limitaciones (Por ej. no puedes sumar letras), y este proceso verifica que esas limitaciones sean respetadas." 731 | ] 732 | }, 733 | { 734 | "cell_type": "markdown", 735 | "metadata": { 736 | "slideshow": { 737 | "slide_type": "fragment" 738 | } 739 | }, 740 | "source": [ 741 | "**¿Por qué hacer esto?** Para prevenir errores evitables." 742 | ] 743 | }, 744 | { 745 | "cell_type": "markdown", 746 | "metadata": { 747 | "slideshow": { 748 | "slide_type": "skip" 749 | } 750 | }, 751 | "source": [ 752 | "### Lenguajes de tipado dinámico" 753 | ] 754 | }, 755 | { 756 | "cell_type": "markdown", 757 | "metadata": { 758 | "slideshow": { 759 | "slide_type": "notes" 760 | } 761 | }, 762 | "source": [ 763 | "Si más adelante en tu programa querés sumar números y uno de ellos tiene una letra, el programa no sabrá que hacer y el compilador te devolverá un error. Esos errores son prevenibles (bugs), y el compilador ayuda a evitarlos." 764 | ] 765 | }, 766 | { 767 | "cell_type": "markdown", 768 | "metadata": { 769 | "slideshow": { 770 | "slide_type": "notes" 771 | } 772 | }, 773 | "source": [ 774 | "Generalmente, esto se realiza automáticamente. Pero no todos los lenguajes lo hacen de la misma manera. Hay dos principales distinciones respectoa CUANDO se chequean los tipos: lenguajes de **tipado dinámico** y lenguajes de **tipado estático**." 775 | ] 776 | }, 777 | { 778 | "cell_type": "markdown", 779 | "metadata": { 780 | "slideshow": { 781 | "slide_type": "slide" 782 | } 783 | }, 784 | "source": [ 785 | "**Los lenguajes de tipado dinámico chequean los tipos en tiempo de ejecución**." 786 | ] 787 | }, 788 | { 789 | "cell_type": "markdown", 790 | "metadata": { 791 | "slideshow": { 792 | "slide_type": "skip" 793 | } 794 | }, 795 | "source": [ 796 | "Esto es lo últomo que se realiza con un programa. Es el estado donde corre el programa para realizar pruebas o utilizarlo.\n", 797 | "\n", 798 | "Algunos ejemplos comunes de lenguajes de tipado dinámico son JavaScript, Python, Objective-C y PHP." 799 | ] 800 | }, 801 | { 802 | "cell_type": "markdown", 803 | "metadata": { 804 | "slideshow": { 805 | "slide_type": "skip" 806 | } 807 | }, 808 | "source": [ 809 | "### Lenguajes de tipado estático" 810 | ] 811 | }, 812 | { 813 | "cell_type": "markdown", 814 | "metadata": { 815 | "slideshow": { 816 | "slide_type": "fragment" 817 | } 818 | }, 819 | "source": [ 820 | "**Los lenguajes de tipado estático chequean los tipos al momento de compilación**." 821 | ] 822 | }, 823 | { 824 | "cell_type": "markdown", 825 | "metadata": { 826 | "slideshow": { 827 | "slide_type": "skip" 828 | } 829 | }, 830 | "source": [ 831 | "Lo que significa que vas a saber si hay un error de tipos tan pronto como compiles tu programas. Lo que lleva a un código más seguro y optimizado.\n", 832 | "\n", 833 | "Entre los lenguajes más comunes de tipado estático se encuentran Java, C y C++." 834 | ] 835 | }, 836 | { 837 | "cell_type": "markdown", 838 | "metadata": { 839 | "slideshow": { 840 | "slide_type": "slide" 841 | } 842 | }, 843 | "source": [ 844 | "### Sistema de tipos de Haskell" 845 | ] 846 | }, 847 | { 848 | "cell_type": "markdown", 849 | "metadata": { 850 | "slideshow": { 851 | "slide_type": "fragment" 852 | } 853 | }, 854 | "source": [ 855 | "**Haskell es de tipado estático**. Además, en Haskell **toda expresión tiene un tipo**." 856 | ] 857 | }, 858 | { 859 | "cell_type": "markdown", 860 | "metadata": { 861 | "slideshow": { 862 | "slide_type": "notes" 863 | } 864 | }, 865 | "source": [ 866 | "Pero no te preocupes, no es necesario definir manualmente los tipos de cada expresión, ya que el compilador es muy bueno en la **inferencia de tipos**." 867 | ] 868 | }, 869 | { 870 | "cell_type": "markdown", 871 | "metadata": { 872 | "slideshow": { 873 | "slide_type": "fragment" 874 | } 875 | }, 876 | "source": [ 877 | "**La inferencia de tipos le permite a Haskell inferir tipos por su cuenta**." 878 | ] 879 | }, 880 | { 881 | "cell_type": "markdown", 882 | "metadata": { 883 | "slideshow": { 884 | "slide_type": "notes" 885 | } 886 | }, 887 | "source": [ 888 | "Si escribes algo como `3 + 4`, Haskell sabrá que el resultado de esa expresión es un número, y va a tratarlo como tal sin necesidad de especificar el tipo. (Funciona con expresiones más complicadas también. Mira los ejemplos anteriores.)" 889 | ] 890 | }, 891 | { 892 | "cell_type": "markdown", 893 | "metadata": { 894 | "slideshow": { 895 | "slide_type": "fragment" 896 | } 897 | }, 898 | "source": [ 899 | "Esto permite al compilador **comprender y razonar *bastante* sobre tu programa**, brindando así una ayuda bastante efectiva para cazar bugs." 900 | ] 901 | }, 902 | { 903 | "cell_type": "markdown", 904 | "metadata": { 905 | "slideshow": { 906 | "slide_type": "skip" 907 | } 908 | }, 909 | "source": [ 910 | "A pesar de que no es necesario para el compilador, **es considerado una buena práctica escribir la firma de tipos de las funciones y expresiones más importantes**, para mejorar le legibilidad del código." 911 | ] 912 | }, 913 | { 914 | "cell_type": "markdown", 915 | "metadata": { 916 | "slideshow": { 917 | "slide_type": "notes" 918 | } 919 | }, 920 | "source": [ 921 | "Si el código es muy ambiguo para el compilador para inferir el tipo, te pedirá que lo especifiques." 922 | ] 923 | }, 924 | { 925 | "cell_type": "markdown", 926 | "metadata": { 927 | "slideshow": { 928 | "slide_type": "slide" 929 | } 930 | }, 931 | "source": [ 932 | "## Laziness" 933 | ] 934 | }, 935 | { 936 | "cell_type": "markdown", 937 | "metadata": { 938 | "slideshow": { 939 | "slide_type": "fragment" 940 | } 941 | }, 942 | "source": [ 943 | "**Haskell es lazy (perezoso 😪). Esto significa que no evalúa expresiones hasta que se necesiten sus resultados**" 944 | ] 945 | }, 946 | { 947 | "cell_type": "markdown", 948 | "metadata": { 949 | "slideshow": { 950 | "slide_type": "notes" 951 | } 952 | }, 953 | "source": [ 954 | "Ejemplos de lazyness en práctica" 955 | ] 956 | }, 957 | { 958 | "cell_type": "markdown", 959 | "metadata": { 960 | "slideshow": { 961 | "slide_type": "slide" 962 | } 963 | }, 964 | "source": [ 965 | "* Podemos usar listas infinitas." 966 | ] 967 | }, 968 | { 969 | "cell_type": "code", 970 | "execution_count": null, 971 | "metadata": { 972 | "slideshow": { 973 | "slide_type": "fragment" 974 | } 975 | }, 976 | "outputs": [], 977 | "source": [ 978 | "dame x = take x [1..] -- [1..] es una lista infinita de números naturales que empieza en 1.\n", 979 | "dame 7 -- la funcion *take x* toma los primeros x números de una lista " 980 | ] 981 | }, 982 | { 983 | "cell_type": "markdown", 984 | "metadata": { 985 | "slideshow": { 986 | "slide_type": "slide" 987 | } 988 | }, 989 | "source": [ 990 | "\n", 991 | "* Haskell no evalúa expresiones si no son necesarias." 992 | ] 993 | }, 994 | { 995 | "cell_type": "code", 996 | "execution_count": null, 997 | "metadata": { 998 | "slideshow": { 999 | "slide_type": "fragment" 1000 | } 1001 | }, 1002 | "outputs": [], 1003 | "source": [ 1004 | "calculoLiviano = 7 \n", 1005 | "calculoPesado = sum [1..10000000] -- sum es una finción que toma una lista y devuelve la suma de todos sus elementos. Esto va a crashear el kernel.\n", 1006 | "if calculoLiviano > 5 || calculoPesado > 5 then \"Listo\" else \"Esto nunca va a mostrarse porque calculoPesado es siempre mayor que 5\"\n", 1007 | "-- Intentá correr esta celda usando calculoLiviano haciendo que sea mayor y menor que 5.\n", 1008 | "-- Cuando calculoLiviano > 5, calculoPesado no es evaluado porque no es necesario. When cheapComputation > 5, expensiveComputation isn't evaluated because it is not needed." 1009 | ] 1010 | }, 1011 | { 1012 | "cell_type": "markdown", 1013 | "metadata": { 1014 | "slideshow": { 1015 | "slide_type": "slide" 1016 | } 1017 | }, 1018 | "source": [ 1019 | "## ¿Entonces, qué es Haskell?" 1020 | ] 1021 | }, 1022 | { 1023 | "cell_type": "markdown", 1024 | "metadata": { 1025 | "slideshow": { 1026 | "slide_type": "fragment" 1027 | } 1028 | }, 1029 | "source": [ 1030 | "#### Haskell es un lenguaje de programación funcional, **lazy**, de tipado estático con **efectos explícitos** y funciones que se ven como ésta:\n", 1031 | "\n", 1032 | "```haskell\n", 1033 | "volumenDeUnCilindro r h = pi * r^2 * h \n", 1034 | "```" 1035 | ] 1036 | }, 1037 | { 1038 | "cell_type": "markdown", 1039 | "metadata": { 1040 | "slideshow": { 1041 | "slide_type": "fragment" 1042 | } 1043 | }, 1044 | "source": [ 1045 | "

\n", 1046 | "Nota: Haskell tiene otras propiedades importantes (como tipos de datos algebráicos, type classes, inferencia de tipos, polimorfismo, ...) que cubriremos en las próximas lecciones.\n", 1047 | "
" 1048 | ] 1049 | }, 1050 | { 1051 | "cell_type": "markdown", 1052 | "metadata": { 1053 | "slideshow": { 1054 | "slide_type": "notes" 1055 | } 1056 | }, 1057 | "source": [ 1058 | "(*Lazy* y *efectos explícitos* son dos de las propiedades más únicas de Haskell. Es por eso que están en negrita.)" 1059 | ] 1060 | }, 1061 | { 1062 | "cell_type": "markdown", 1063 | "metadata": { 1064 | "slideshow": { 1065 | "slide_type": "slide" 1066 | } 1067 | }, 1068 | "source": [ 1069 | "## Herramientas" 1070 | ] 1071 | }, 1072 | { 1073 | "cell_type": "markdown", 1074 | "metadata": { 1075 | "slideshow": { 1076 | "slide_type": "slide" 1077 | } 1078 | }, 1079 | "source": [ 1080 | "### Acerca de Cabal y Stack" 1081 | ] 1082 | }, 1083 | { 1084 | "cell_type": "markdown", 1085 | "metadata": { 1086 | "slideshow": { 1087 | "slide_type": "fragment" 1088 | } 1089 | }, 1090 | "source": [ 1091 | "Mientras aprendes Haskell seguramente te econtrarás con los términos Stack y Cabal.\n", 1092 | "\n", 1093 | "**Estos son sistemas para manejar bibliotecas y programas**. Te facilitan trabajar con bibliotecas.\n", 1094 | "\n", 1095 | "En este curso utilizaremos Cabal, y explicaremos cómo usarlo más adelante." 1096 | ] 1097 | }, 1098 | { 1099 | "cell_type": "markdown", 1100 | "metadata": { 1101 | "slideshow": { 1102 | "slide_type": "slide" 1103 | } 1104 | }, 1105 | "source": [ 1106 | "### GHC y GHCi" 1107 | ] 1108 | }, 1109 | { 1110 | "cell_type": "markdown", 1111 | "metadata": { 1112 | "slideshow": { 1113 | "slide_type": "fragment" 1114 | } 1115 | }, 1116 | "source": [ 1117 | "**GHC (Glasgow Haskell Compiler) es un compolador y un entorno interactivo para Haskell**. Usando GHC podemos:\n", 1118 | "* Compilar programas y ejecutarlos como cualquier otra app.\n", 1119 | "* Evaluar expresiones de Haskell rápidamente usando el entorno interactivo provisto por GHC (el GHCi).\n", 1120 | "\n", 1121 | "Para usar GHCi, abre la terminal en el entorno remoto de GitPod que preparamos y escribe `ghci`.\n", 1122 | "\n", 1123 | "Usa `:l relative/path.hs` dentro de GHCi para cargar un archivo y usar de manera interactiva sus contenidos. Escribe `:r` si deseas volver a cargar el archivo y `:q` para salir. " 1124 | ] 1125 | }, 1126 | { 1127 | "cell_type": "markdown", 1128 | "metadata": {}, 1129 | "source": [ 1130 | "**NOTA** Si deseas instalar GHC y GHCi en tu computadora, puedes seguir las instrucciones en www.haskell.org/ghcup/.\n", 1131 | "Se explica la forma de instalación para Windows, Linux y Mac." 1132 | ] 1133 | } 1134 | ], 1135 | "metadata": { 1136 | "celltoolbar": "Slideshow", 1137 | "kernelspec": { 1138 | "display_name": "Haskell", 1139 | "language": "haskell", 1140 | "name": "haskell" 1141 | }, 1142 | "language_info": { 1143 | "codemirror_mode": "ihaskell", 1144 | "file_extension": ".hs", 1145 | "mimetype": "text/x-haskell", 1146 | "name": "haskell", 1147 | "pygments_lexer": "Haskell", 1148 | "version": "8.10.7" 1149 | }, 1150 | "rise": { 1151 | "enable_chalkboard": true, 1152 | "header": "" 1153 | } 1154 | }, 1155 | "nbformat": 4, 1156 | "nbformat_minor": 4 1157 | } 1158 | --------------------------------------------------------------------------------