Repository: jamesdbrock/learn-you-a-haskell-notebook Branch: master Commit: e6de38f9ece1 Files: 35 Total size: 1.6 MB Directory structure: gitextract_njh7ebiu/ ├── .devcontainer/ │ ├── Dockerfile │ └── devcontainer.json ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── anchordoc/ │ ├── LICENSE │ ├── README.md │ ├── Setup.hs │ ├── anchordoc.cabal │ ├── notebooks.anchordoc.sh │ ├── src/ │ │ └── Main.hs │ └── stack.yaml ├── docker-compose.yml ├── flake.nix ├── kernels/ │ └── haskell.nix ├── kernels.nix ├── notebook/ │ ├── 00-preface.ipynb │ ├── 01-introduction.ipynb │ ├── 02-starting-out.ipynb │ ├── 03-types-and-typeclasses.ipynb │ ├── 04-syntax-in-functions.ipynb │ ├── 05-recursion.ipynb │ ├── 06-higher-order-functions.ipynb │ ├── 07-modules.ipynb │ ├── 08-making-our-own-types-and-typeclasses.ipynb │ ├── 09-input-and-output.ipynb │ ├── 10-functionally-solving-problems.ipynb │ ├── 11-functors-applicative-functors-and-monoids.ipynb │ ├── 12-a-fistful-of-monads.ipynb │ ├── 13-for-a-few-monads-more.ipynb │ └── 14-zippers.ipynb └── notebook_extra/ ├── WidgetChart.ipynb ├── WidgetDiagram.ipynb └── WidgetRevival.ipynb ================================================ FILE CONTENTS ================================================ ================================================ FILE: .devcontainer/Dockerfile ================================================ FROM ghcr.io/ihaskell/ihaskell-notebook:master ================================================ FILE: .devcontainer/devcontainer.json ================================================ // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.194.0/containers/docker-existing-dockerfile { "name": "IHaskell Community Docker Stack", // Sets the run context to one level up instead of the .devcontainer folder. "context": "..", // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. "dockerFile": "./Dockerfile", // Set *default* container specific settings.json values on container create. "settings": {}, // Add the IDs of extensions you want installed when the container is created. "extensions": [ "ms-toolsai.jupyter", "haskell.haskell", "justusadam.language-haskell", // A popup dialog pesters us to install the Python extension if it's not installed. // I think this is needed to render errors. "ms-python.python" ] // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Uncomment the next line to run commands after the container is created - for example installing curl. // "postCreateCommand": "apt-get update && apt-get install -y curl", // Uncomment when using a ptrace-based debugger like C++, Go, and Rust // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. // "remoteUser": "vscode" } ================================================ FILE: .gitignore ================================================ notebook/*.txt notebook/Geometry.dyn_hi notebook/Geometry.dyn_o notebook/Geometry.hi notebook/Geometry.hs notebook/Geometry.o notebook/Geometry/ */.ipynb_checkpoints .jupyter/ ================================================ FILE: Dockerfile ================================================ # Dockerfile for mybinder.org # # Test this Dockerfile: # # docker build -t learn-you-a-haskell . # docker run --rm -p 8888:8888 --name learn-you-a-haskell learn-you-a-haskell:latest jupyter lab --ServerApp.token='' # FROM ghcr.io/ihaskell/ihaskell-notebook:master@sha256:61ec7f69544bfc59b0f50055c6975509dd0d3c4f40862c771e7407d7272b0274 USER root RUN mkdir /home/$NB_USER/learn_you_a_haskell COPY notebook/*.ipynb /home/$NB_USER/learn_you_a_haskell/ COPY notebook/img /home/$NB_USER/learn_you_a_haskell/img RUN chown --recursive $NB_UID:users /home/$NB_USER/learn_you_a_haskell ARG EXAMPLES_PATH=/home/$NB_USER/ihaskell_examples COPY notebook_extra/WidgetRevival.ipynb $EXAMPLES_PATH/ RUN chown $NB_UID:users $EXAMPLES_PATH/WidgetRevival.ipynb USER $NB_UID ENV JUPYTER_ENABLE_LAB=yes ================================================ FILE: LICENSE ================================================ https://creativecommons.org/licenses/by-nc-sa/3.0/ ================================================ FILE: README.md ================================================ # Learn You a Haskell for Great Good! ## Read the book right now ### Read the book method 1: [mybinder.org](https://mybinder.org/) Read the book right now on __mybinder.org__ with this link: [![launch Learn You a Haskell for Great Good!](https://img.shields.io/badge/launch-Learn%20You%20A%20Haskell%20For%20Great%20Good!-579ACA.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFkAAABZCAMAAABi1XidAAAB8lBMVEX///9XmsrmZYH1olJXmsr1olJXmsrmZYH1olJXmsr1olJXmsrmZYH1olL1olJXmsr1olJXmsrmZYH1olL1olJXmsrmZYH1olJXmsr1olL1olJXmsrmZYH1olL1olJXmsrmZYH1olL1olL0nFf1olJXmsrmZYH1olJXmsq8dZb1olJXmsrmZYH1olJXmspXmspXmsr1olL1olJXmsrmZYH1olJXmsr1olL1olJXmsrmZYH1olL1olLeaIVXmsrmZYH1olL1olL1olJXmsrmZYH1olLna31Xmsr1olJXmsr1olJXmsrmZYH1olLqoVr1olJXmsr1olJXmsrmZYH1olL1olKkfaPobXvviGabgadXmsqThKuofKHmZ4Dobnr1olJXmsr1olJXmspXmsr1olJXmsrfZ4TuhWn1olL1olJXmsqBi7X1olJXmspZmslbmMhbmsdemsVfl8ZgmsNim8Jpk8F0m7R4m7F5nLB6jbh7jbiDirOEibOGnKaMhq+PnaCVg6qWg6qegKaff6WhnpKofKGtnomxeZy3noG6dZi+n3vCcpPDcpPGn3bLb4/Mb47UbIrVa4rYoGjdaIbeaIXhoWHmZYHobXvpcHjqdHXreHLroVrsfG/uhGnuh2bwj2Hxk17yl1vzmljzm1j0nlX1olL3AJXWAAAAbXRSTlMAEBAQHx8gICAuLjAwMDw9PUBAQEpQUFBXV1hgYGBkcHBwcXl8gICAgoiIkJCQlJicnJ2goKCmqK+wsLC4usDAwMjP0NDQ1NbW3Nzg4ODi5+3v8PDw8/T09PX29vb39/f5+fr7+/z8/Pz9/v7+zczCxgAABC5JREFUeAHN1ul3k0UUBvCb1CTVpmpaitAGSLSpSuKCLWpbTKNJFGlcSMAFF63iUmRccNG6gLbuxkXU66JAUef/9LSpmXnyLr3T5AO/rzl5zj137p136BISy44fKJXuGN/d19PUfYeO67Znqtf2KH33Id1psXoFdW30sPZ1sMvs2D060AHqws4FHeJojLZqnw53cmfvg+XR8mC0OEjuxrXEkX5ydeVJLVIlV0e10PXk5k7dYeHu7Cj1j+49uKg7uLU61tGLw1lq27ugQYlclHC4bgv7VQ+TAyj5Zc/UjsPvs1sd5cWryWObtvWT2EPa4rtnWW3JkpjggEpbOsPr7F7EyNewtpBIslA7p43HCsnwooXTEc3UmPmCNn5lrqTJxy6nRmcavGZVt/3Da2pD5NHvsOHJCrdc1G2r3DITpU7yic7w/7Rxnjc0kt5GC4djiv2Sz3Fb2iEZg41/ddsFDoyuYrIkmFehz0HR2thPgQqMyQYb2OtB0WxsZ3BeG3+wpRb1vzl2UYBog8FfGhttFKjtAclnZYrRo9ryG9uG/FZQU4AEg8ZE9LjGMzTmqKXPLnlWVnIlQQTvxJf8ip7VgjZjyVPrjw1te5otM7RmP7xm+sK2Gv9I8Gi++BRbEkR9EBw8zRUcKxwp73xkaLiqQb+kGduJTNHG72zcW9LoJgqQxpP3/Tj//c3yB0tqzaml05/+orHLksVO+95kX7/7qgJvnjlrfr2Ggsyx0eoy9uPzN5SPd86aXggOsEKW2Prz7du3VID3/tzs/sSRs2w7ovVHKtjrX2pd7ZMlTxAYfBAL9jiDwfLkq55Tm7ifhMlTGPyCAs7RFRhn47JnlcB9RM5T97ASuZXIcVNuUDIndpDbdsfrqsOppeXl5Y+XVKdjFCTh+zGaVuj0d9zy05PPK3QzBamxdwtTCrzyg/2Rvf2EstUjordGwa/kx9mSJLr8mLLtCW8HHGJc2R5hS219IiF6PnTusOqcMl57gm0Z8kanKMAQg0qSyuZfn7zItsbGyO9QlnxY0eCuD1XL2ys/MsrQhltE7Ug0uFOzufJFE2PxBo/YAx8XPPdDwWN0MrDRYIZF0mSMKCNHgaIVFoBbNoLJ7tEQDKxGF0kcLQimojCZopv0OkNOyWCCg9XMVAi7ARJzQdM2QUh0gmBozjc3Skg6dSBRqDGYSUOu66Zg+I2fNZs/M3/f/Grl/XnyF1Gw3VKCez0PN5IUfFLqvgUN4C0qNqYs5YhPL+aVZYDE4IpUk57oSFnJm4FyCqqOE0jhY2SMyLFoo56zyo6becOS5UVDdj7Vih0zp+tcMhwRpBeLyqtIjlJKAIZSbI8SGSF3k0pA3mR5tHuwPFoa7N7reoq2bqCsAk1HqCu5uvI1n6JuRXI+S1Mco54YmYTwcn6Aeic+kssXi8XpXC4V3t7/ADuTNKaQJdScAAAAAElFTkSuQmCC)](https://mybinder.org/v2/gh/IHaskell/learn-you-a-haskell-notebook/master?urlpath=lab/tree/learn_you_a_haskell/00-preface.ipynb) (Usually takes a minute to launch) ### Read the book method 2: [Nix run](https://determinate.systems/posts/nix-run) ```console nix run github:IHaskell/learn-you-a-haskell-notebook ``` --- ![Screenshot](README.screenshot.png) This is a Jupyter notebook adaptation of the book [__*Learn You a Haskell for Great Good!*__](http://learnyouahaskell.com/) by Miran Lipovača. I learned Haskell from this book in 2014 by following along in [GHCI](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html), as the book suggested. In 2019, the [Jupyter](https://jupyter.org/) notebook format would be a nice way read this book. This is one of the best cases for Theodore Gray's idea of the [computational essay](https://www.theatlantic.com/science/archive/2018/04/the-scientific-paper-is-obsolete/556676/), and Andrew Gibiansky has made it possible with [IHaskell](https://github.com/IHaskell/IHaskell). Each chapter of the book is one `.ipynb` Jupyter notebook file. See the list of chapter files on the left sidebar in JupyterLab. Each notebook cell depends on cells that come before it, so run the notebooks from top to bottom. I have refactored code to make the examples work in Jupyter, and removed instructions for how to use GHCI. Other than that I have tried to be faithful to the original text. Miran Lipovača wrote this book and released it to the world under a [Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License](http://creativecommons.org/licenses/by-nc-sa/3.0/), which means that the book is free-as-in-speech, and allows us to enjoy the book in notebook format. But that does not mean that the book is free-as-in-beer; it means that you, dear reader, pay for the book under the honor system. If you are honorable and you want to keep living in a world in which honorable artists license their art as Creative Commons, then [buy the book](http://learnyouahaskell.com/). Thanks also to Paul Vorbach for . # How to run on your local computer in Visual Studio Code Visual Studio Code has a [Notebook UI](https://code.visualstudio.com/docs/datascience/jupyter-notebooks) which you can use to run Jupyter kernels instead of the browser-based JupyterLab or classic Jupyter UI. Clone this repository with `git` and `cd` into the cloned directory ```bash git clone https://github.com/IHaskell/learn-you-a-haskell-notebook.git cd learn-you-a-haskell-notebook code . ``` There is a Docker [Visual Studio Code devcontainer](https://github.com/microsoft/vscode-dev-containers) configuration in the [`.devcontainer`](.devcontainer) directory. When you open Visual Studio Code by running `code .`, a pop-up dialog will tell you that “Folder contains a Dev Container configuration file. Reopen folder to develop in a container.” Click the button labelled __Reopen in Container__. You will then be able to open the `.ipynb` chapter files in the `notebook` directory. # How to run on your local computer in JupyterLab in a web browser Clone this repository with `git` and `cd` into the cloned directory ```bash git clone https://github.com/IHaskell/learn-you-a-haskell-notebook.git cd learn-you-a-haskell-notebook ``` Then here are three options for running. 1. `docker` ```bash docker run --rm -p 8888:8888 -v $PWD/notebook:/home/jovyan/work --name learn-you-a-haskell ghcr.io/ihaskell/ihaskell-notebook:master jupyter lab --ServerApp.token='' ``` then open [http://localhost:8888](http://localhost:8888) to read the book. 2. `podman` ```bash podman run --privileged --userns=keep-id --rm -p 8888:8888 -v $PWD/notebook:/home/jovyan/work --name learn-you-a-haskell ghcr.io/ihaskell/ihaskell-notebook:master jupyter lab --ServerApp.token='' ``` then open [http://localhost:8888](http://localhost:8888) to read the book. 3. [NixOS](https://nixos.org/) flake ``` nix run . -- --notebook-dir notebook ``` then the book will open in a browser automatically. If it doesn’t, open the “running at” link. # How to edit notebooks If you make an improvement to a notebook `.ipynb` file and you want to save the file and commit the changes and make a pull request to the repository, 1. Maximize the browser window to full screen width, because cell horizontal scroll bar visibility gets saved in the notebook. 2. Run -> Restart kernel and Run All Cells... 3. Save the notebook. # ihaskell-notebook For more information about the IHaskell Docker image, see ================================================ FILE: anchordoc/LICENSE ================================================ Copyright James D. Brock (c) 2019 All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of James D. Brock nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: anchordoc/README.md ================================================ # anchordoc ================================================ FILE: anchordoc/Setup.hs ================================================ import Distribution.Simple main = defaultMain ================================================ FILE: anchordoc/anchordoc.cabal ================================================ name: anchordoc version: 0.1.0.0 -- synopsis: -- description: homepage: https://github.com/jamesdbrock/anchordoc#readme license: BSD3 license-file: LICENSE author: James D. Brock maintainer: jamesbrock@gmail.com copyright: James D. Brock category: personal build-type: Simple cabal-version: >=1.10 extra-source-files: README.md executable anchordoc hs-source-dirs: src main-is: Main.hs default-language: Haskell2010 build-depends: base >= 4.7 && < 5 , megaparsec , split , wreq , aeson , text , lens , lens-aeson , transformers , replace-megaparsec ================================================ FILE: anchordoc/notebooks.anchordoc.sh ================================================ #! /usr/bin/env bash for n in $(ls ../notebook/*.ipynb); do echo $n " ..." stack exec anchordoc < $n > ../notebook/tmp mv ../notebook/tmp $n echo "done" done ================================================ FILE: anchordoc/src/Main.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ViewPatterns #-} module Main where import Network.Wreq (getWith, responseBody, param, defaults) import Control.Lens ((^..), (&), (.~)) import Data.Aeson.Lens import Data.Text (unpack, pack) import Text.Megaparsec as Mp import Replace.Megaparsec import Data.Void import Data.Maybe import Data.Proxy import Control.Monad type Parser = Parsec Void String main :: IO () main = do getContents >>= streamEditT pattern hoogleReplace >>= putStr where pattern = match $ eitherP doubleBacktickMask backtickSymbol hoogleReplace (orig, Left _) = return orig -- masked by doubleBacktickMask hoogleReplace (orig, Right (_, _, "=", _, _)) = return orig -- not acually a symbol hoogleReplace (orig, Right (tickPrefix, tickOpen, symbol, tickClose, tickSuffix)) = do -- https://github.com/ndmitchell/hoogle/blob/master/docs/API.md#json-api -- -- Query looks like this: -- https://hoogle.haskell.org/?mode=json&hoogle=head&scope=package:base&start=1&count=1 -- -- Query looks like this: -- https://hoogle.haskell.org/?mode=json&hoogle=%3E%3E%3D&scope=package:base&start=1&count=1 hoogleResult <- flip getWith "https://hoogle.haskell.org" $ defaults & param "mode" .~ ["json"] & param "hoogle" .~ [pack symbol] -- will be url encoded & param "scope" .~ ["package:base"] & param "start" .~ ["1"] & param "count" .~ ["1"] -- Result looks like this: -- [ -- { -- "url": "https://hackage.haskell.org/package/base/docs/Prelude.html#v:head", -- "module": { -- "url": "https://hackage.haskell.org/package/base/docs/Prelude.html", -- "name": "Prelude" -- }, -- "package": { -- "url": "https://hackage.haskell.org/package/base", -- "name": "base" -- }, -- "item": "head :: [a] -> a", -- "type": "", -- "docs": "Extract the first element of a list, which must be non-empty.\n" -- } -- ] -- -- Result looks like this: -- [ -- { -- "url": "https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-", -- "module": { -- "url": "https://hackage.haskell.org/package/base/docs/Prelude.html", -- "name": "Prelude" -- }, -- "package": { -- "url": "https://hackage.haskell.org/package/base", -- "name": "base" -- }, -- "item": "(>>=) :: forall a b . Monad m => m a -> (a -> m b) -> m b", -- "type": "", -- "docs": "Sequentially compose two actions, passing any value produced by the\nfirst as an argument to the second.\n" -- } -- ] let hoogleReturnItem = fmap unpack $ listToMaybe $ hoogleResult ^.. responseBody . nth 0 . key "item" . _String -- If hoogle returns a documentation URL, and the same symbol -- that was queried, then substitute a markdown link case ( maybe False (==symbol) $ parseMaybe (hoogleReturnItemSymbol :: Parser String) =<< hoogleReturnItem , listToMaybe $ hoogleResult ^.. responseBody . nth 0 . key "url" . _String ) of (True, Just docUrl) -> -- Construct a Markdown link with the documentation URL return $ tickPrefix ++ "[" ++ tickOpen ++ symbol ++ tickClose ++ "](" ++ unpack docUrl ++ ")" ++ tickSuffix _ -> return orig hoogleReturnItemSymbol :: Parser String hoogleReturnItemSymbol = do void $ manyTill anySingle $ chunk "" s <- fmap (tokensToChunk (Proxy::Proxy String)) $ someTill anySingle $ chunk "" void $ takeRest -- must consume all input for parseMaybe success return $ streamEdit (chunk "<") (const "<") $ streamEdit (chunk ">") (const ">") $ streamEdit (chunk "&") (const "&") s -- exclude special Markdown backtick escape like ``92 `div` 10``. doubleBacktickMask = do void $ chunk "``" void $ manyTill anySingle $ chunk "``" -- we have to succeed and return something or 'sepCap' will backtrack -- Parse something that looks like a symbol from Prelude in backticks. backtickSymbol = do tickPrefix <- noneOf ("[" :: String) tickOpen <- chunk "`" symbol <- Mp.some $ Mp.oneOf $ ['a'..'z'] ++ ['A'..'Z'] ++ "|&?%^*#~.<>+=-$/:'" tickClose <- chunk "`" tickSuffix <- noneOf ("]" :: String) return ([tickPrefix], tickOpen, symbol, tickClose, [tickSuffix]) -- TODO: -- -- cases that don't work: -- infix backtick functions like `elem` in code blocks. we're going to -- need to only search in "cell-type":"markdown". -- -- We need to parse only markdown cells, like: -- { -- "cell_type": "markdown", -- "metadata": {}, -- "source": [ -- "__`repeat`__ takes an element and produces an infinite list of just that\n", -- "element. It's like cycling a list with only one element." -- ] -- }, -- markdownCell :: Parser () -- markdownCell = do -- chunk "{" -- space -- chunk "\"cell_type\": \"markdown\"," -- space -- chunk "\"metadata\": {}," -- space -- chunk "\"source\": [" -- space -- chunk "]" >> newline -- return () ================================================ FILE: anchordoc/stack.yaml ================================================ resolver: nightly-2019-09-13 ================================================ FILE: docker-compose.yml ================================================ version: '3.3' services: ihaskell-notebook: build: . ports: - "8888:8888" volumes: - ./notebook:/home/jovyan/work restart: always environment: JUPYTER_ENABLE_LAB: "yes" JUPYTER_TOKEN: x ================================================ FILE: flake.nix ================================================ { description = "Learn You a Haskell for Great Good! Jupyter adaptation"; inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.IHaskell.url = "github:IHaskell/IHaskell"; nixConfig = { extra-substituters = [ "https://ihaskell.cachix.org" ]; extra-trusted-public-keys = [ "ihaskell.cachix.org-1:WoIvex/Ft/++sjYW3ntqPUL3jDGXIKDpX60pC8d5VLM="]; }; outputs = { self, flake-utils, IHaskell, ... } @ inputs: flake-utils.lib.eachDefaultSystem (system: let pkgs = IHaskell.inputs.nixpkgs24_11.legacyPackages.${system}; ihaskell-env = IHaskell.packages.${system}.ihaskell-env-display-ghc98; notebook-lyah = pkgs.stdenv.mkDerivation { name = "notebook-lyah"; src = ./notebook; phases = [ "unpackPhase" "installPhase" ]; installPhase = '' mkdir -p $out cp -r $src/* $out/ ''; }; in rec { packages = { inherit ihaskell-env notebook-lyah; }; apps = { default = let script = pkgs.writeShellApplication { name = "jupyter-lab-lyah"; runtimeInputs = [ ihaskell-env notebook-lyah ]; text = "jupyter lab --notebook-dir=${notebook-lyah} ${notebook-lyah}/00-preface.ipynb"; }; in { type = "app"; program = "${script}/bin/jupyter-lab-lyah"; }; }; } ); } ================================================ FILE: kernels/haskell.nix ================================================ { name, availableKernels, extraArgs, }: availableKernels.haskell { inherit name; inherit (extraArgs) pkgs; displayName = "Haskell"; } ================================================ FILE: kernels.nix ================================================ {pkgs, ...}: { # kernel.python.minimal = { # enable = true; # }; kernel.haskell."1" = { enable = true; displayName = "Haskell"; }; } ================================================ FILE: notebook/00-preface.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Preface to the Jupyter notebook\n", "===============================\n", "\n", "This is a Jupyter notebook adaptation of the book [__*Learn You a Haskell for Great Good!*__](http://learnyouahaskell.com/) by Miran Lipovača.\n", "\n", "I learned Haskell from this book in 2014 by following along in [GHCI](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html), as the book suggested.\n", "\n", "In 2019, the Jupyter notebook format would be a nice way read this book. This is one of the best cases for Theodore Gray's idea of the [computational essay](https://www.theatlantic.com/science/archive/2018/04/the-scientific-paper-is-obsolete/556676/), and Andrew Gibiansky has made it possible with [IHaskell](https://github.com/IHaskell/IHaskell).\n", "\n", "Each chapter of the book is one `.ipynb` Jupyter notebook file. See the list of chapter files on the left sidebar in JupyterLab.\n", "\n", "Each notebook cell depends on cells that come before it, so run the notebooks from top to bottom. I have refactored code to make the examples work in Jupyter, and removed instructions for how to use GHCI. Other than that I have tried to be faithful to the original text.\n", " \n", "Miran Lipovača wrote this book and released it to the world under a [Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License](http://creativecommons.org/licenses/by-nc-sa/3.0/), which means that the book is free-as-in-speech, and allows us to enjoy the book in notebook format. But that does not mean that the book is free-as-in-beer; it means that you, dear reader, pay for the book under the honor system. If you are honorable and you want to keep living in a world in which honorable artists license their art as Creative Commons, then [buy the book](http://learnyouahaskell.com/).\n", "\n", "Please fork or contribute or ★ or report bugs on GitHub: \n", "\n", "
\n", "James Brock, Tokyo 2019\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.8.4" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/01-introduction.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Introduction\n", "============\n", "\n", "About this tutorial\n", "-------------------\n", "\n", "Welcome to __Learn You a Haskell for Great Good__! If you're reading this,\n", "chances are you want to learn Haskell. Well, you've come to the right\n", "place, but let's talk about this tutorial a bit first.\n", "\n", "I decided to write this because I wanted to solidify my own knowledge of\n", "Haskell and because I thought I could help people new to Haskell learn\n", "it from my perspective. There are quite a few tutorials on Haskell\n", "floating around on the internet. When I was starting out in Haskell, I\n", "didn't learn from just one resource. The way I learned it was by reading\n", "several different tutorials and articles because each explained\n", "something in a different way than the other did. By going through\n", "several resources, I was able put together the pieces and it all just\n", "came falling into place. So this is an attempt at adding another useful\n", "resource for learning Haskell so you have a bigger chance of finding one\n", "you like.\n", "\n", "\n", "\n", "This tutorial is aimed at people who have experience in imperative\n", "programming languages (C, C++, Java, Python …) but haven't programmed in\n", "a functional language before (Haskell, ML, OCaml …). Although I bet that\n", "even if you don't have any significant programming experience, a smart\n", "person such as yourself will be able to follow along and learn Haskell.\n", "\n", "The channel \\#haskell on the freenode network is a great place to ask\n", "questions if you're feeling stuck. People there are extremely nice,\n", "patient and understanding to newbies.\n", "\n", "I failed to learn Haskell approximately 2 times before finally grasping\n", "it because it all just seemed too weird to me and I didn't get it. But\n", "then once it just \"clicked\" and after getting over that initial hurdle,\n", "it was pretty much smooth sailing. I guess what I'm trying to say is:\n", "Haskell is great and if you're interested in programming you should\n", "really learn it even if it seems weird at first. Learning Haskell is\n", "much like learning to program for the first time — it's fun! It forces\n", "you to think differently, which brings us to the next section …\n", "\n", "So what's Haskell?\n", "------------------\n", "\n", " Haskell is a __purely\n", "functional programming language__. In imperative languages you get things\n", "done by giving the computer a sequence of tasks and then it executes\n", "them. While executing them, it can change state. For instance, you set\n", "variable `a` to 5 and then do some stuff and then set it to something\n", "else. You have control flow structures for doing some action several\n", "times. In purely functional programming you don't tell the computer what\n", "to do as such but rather you tell it what stuff *is*. The factorial of a\n", "number is the product of all the numbers from 1 to that number, the sum\n", "of a list of numbers is the first number plus the sum of all the other\n", "numbers, and so on. You express that in the form of functions. You also\n", "can't set a variable to something and then set it to something else\n", "later. If you say that a is 5, you can't say it's something else later\n", "because you just said it was 5. What are you, some kind of liar? So in\n", "purely functional languages, a function has no side-effects. The only\n", "thing a function can do is calculate something and return it as a\n", "result. At first, this seems kind of limiting but it actually has some\n", "very nice consequences: if a function is called twice with the same\n", "parameters, it's guaranteed to return the same result. That's called\n", "referential transparency and not only does it allow the compiler to\n", "reason about the program's behavior, but it also allows you to easily\n", "deduce (and even prove) that a function is correct and then build more\n", "complex functions by gluing simple functions together.\n", "\n", " Haskell is __lazy__. That\n", "means that unless specifically told otherwise, Haskell won't execute\n", "functions and calculate things until it's really forced to show you a\n", "result. That goes well with referential transparency and it allows you\n", "to think of programs as a series of __transformations on data__. It also\n", "allows cool things such as infinite data structures. Say you have an\n", "immutable list of numbers `xs = [1,2,3,4,5,6,7,8]` and a function `doubleMe`\n", "which multiplies every element by 2 and then returns a new list. If we\n", "wanted to multiply our list by 8 in an imperative language and did\n", "`doubleMe(doubleMe(doubleMe(xs)))`, it would probably pass through the\n", "list once and make a copy and then return it. Then it would pass through\n", "the list another two times and return the result. In a lazy language,\n", "calling `doubleMe` on a list without forcing it to show you the result\n", "ends up in the program sort of telling you \"Yeah yeah, I'll do it\n", "later!\". But once you want to see the result, the first `doubleMe` tells\n", "the second one it wants the result, now! The second one says that to the\n", "third one and the third one reluctantly gives back a doubled 1, which is\n", "a 2. The second one receives that and gives back 4 to the first one. The\n", "first one sees that and tells you the first element is 8. So it only\n", "does one pass through the list and only when you really need it. That\n", "way when you want something from a lazy language you can just take some\n", "initial data and efficiently transform and mend it so it resembles what\n", "you want at the end.\n", "\n", " Haskell is __statically\n", "typed__. When you compile your program, the compiler knows which piece of\n", "code is a number, which is a string and so on. That means that a lot of\n", "possible errors are caught at compile time. If you try to add together a\n", "number and a string, the compiler will whine at you. Haskell uses a very\n", "good type system that has __type inference__. That means that you don't\n", "have to explicitly label every piece of code with a type because the\n", "type system can intelligently figure out a lot about it. If you say `a =\n", "5 + 4`, you don't have to tell Haskell that `a` is a number, it can figure\n", "that out by itself. Type inference also allows your code to be more\n", "general. If a function you make takes two parameters and adds them\n", "together and you don't explicitly state their type, the function will\n", "work on any two parameters that act like numbers.\n", "\n", "Haskell is __elegant and concise__. Because it uses a lot of high level\n", "concepts, Haskell programs are usually shorter than their imperative\n", "equivalents. And shorter programs are easier to maintain than longer\n", "ones and have less bugs.\n", "\n", "Haskell was made by some __really smart guys__ (with PhDs). Work on\n", "Haskell began in 1987 when a committee of researchers got together to\n", "design a kick-ass language. In 2003 the Haskell Report was published,\n", "which defines a stable version of the language." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/02-starting-out.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ " \n", "\n", "Starting Out\n", "============\n", "\n", "Ready, set, go!\n", "---------------\n", "\n", "Alright, let's get started!\n", "\n", "Here are some Jupyter keyboard commands for reading this book. The basic way to read the book is start at the top of each chapter and run each cell by pressing Shift+Enter. When you run a Code cell, the result will be printed below the cell. For more information about how to use JuptyerLab, see the top pulldown menus.\n", "\n", "| Keyboard Command | Action |\n", "|-------------:|---------------|\n", "|Shift+Enter | Run the selected Jupyter cell and advance to the next cell |\n", "|Ctrl+Enter | Run the selected Jupyter cell and don't advance |\n", "|Alt+Enter | Run the selected Jupyter cell and insert a new cell after |\n", "|Enter | Edit the selected Jupyter cell |\n", "|Ctrl+Shift+- | Split the Jupyter cell at the cursor position |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's some simple arithmetic." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "17" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "2 + 15" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "4900" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "49 * 100" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "420" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "1892 - 1472" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "2.5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5 / 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is pretty self-explanatory. We can also use several operators on\n", "one line and all the usual precedence rules are obeyed. We can use\n", "parentheses to make the precedence explicit or to change it." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(50 * 100) - 4999" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "50 * 100 - 4999" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "-244950" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "50 * (100 - 4999)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty cool, huh? Yeah, I know it's not but bear with me. A little\n", "pitfall to watch out for here is negating numbers. If we want to have a\n", "negative number, it's always best to surround it with parentheses. Doing\n", "`5 * -3` will make GHC yell at you but doing `5 * (-3)` will work just\n", "fine.\n", "\n", "Boolean algebra is also pretty straightforward. As you probably know, [`&&`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-38--38-)\n", "means a boolean *and*, [`||`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-124--124-) means a boolean *or*. [`not`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:not) negates a [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) or a\n", "[`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False)." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "True && False" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "True && True" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "False || True" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "not False" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "not (True && True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Testing for equality is done like so." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5 == 5" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "1 == 0" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5 /= 5" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5 /= 4" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"hello\" == \"hello\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What about doing `5 + \"llama\"` or `5 == True`? Well, if we try the first\n", "snippet, we get a big scary error message!" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ ":1:3: error:\n • No instance for (Num String) arising from a use of ‘+’\n • In the expression: 5 + \"llama\"\n In an equation for ‘it’: it = 5 + \"llama\"" ] } ], "source": [ "5 + \"llama\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yikes! What GHC is telling us here is that `\"llama\"` is not a number and\n", "so it doesn't know how to add it to 5. Even if it wasn't `\"llama\"` but\n", "`\"four\"` or `\"4\"`, Haskell still wouldn't consider it to be a number. [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-)\n", "expects its left and right side to be numbers. If we tried to do `True == 5`,\n", "GHC would tell us that the types don't match. Whereas [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-) works only\n", "on things that are considered numbers, [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) works on any two things that\n", "can be compared. But the catch is that they both have to be the same\n", "type of thing. You can't compare apples and oranges. We'll take a closer\n", "look at types a bit later. Note: you can do `5 + 4.0` because `5` is sneaky\n", "and can act like an integer or a floating-point number. `4.0` can't act\n", "like an integer, so `5` is the one that has to adapt.\n", "\n", " \n", "You may not have known it but we've been using functions now all along.\n", "\n", "For instance, [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) is a function that takes two numbers and multiplies\n", "them. As you've seen, we call it by sandwiching it between them. This is\n", "what we call an *infix* function. Most functions that aren't used with\n", "numbers are *prefix* functions. Let's take a look at them.\n", "\n", "Functions are\n", "usually prefix so from now on we won't explicitly state that a function\n", "is of the prefix form, we'll just assume it. In most imperative\n", "languages functions are called by writing the function name and then\n", "writing its parameters in parentheses, usually separated by commas. In\n", "Haskell, functions are called by writing the function name, a space and\n", "then the parameters, separated by spaces. For a start, we'll try calling\n", "one of the most boring functions in Haskell." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "9" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "succ 8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The [`succ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:succ) function takes anything that has a defined successor and\n", "returns that successor. As you can see, we just separate the function\n", "name from the parameter with a space. Calling a function with several\n", "parameters is also simple. The functions [`min`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:min) and [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max) take two things\n", "that can be put in an order (like numbers!). [`min`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:min) returns the one that's\n", "lesser and [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max) returns the one that's greater. See for yourself:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "9" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "min 9 10" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3.2" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "min 3.4 3.2" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "101" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "max 100 101" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Function application (calling a function by putting a space after it and\n", "then typing out the parameters) has the highest precedence of them all.\n", "What that means for us is that these two statements are equivalent." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "16" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "succ 9 + max 5 4 + 1" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "16" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(succ 9) + (max 5 4) + 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, if we wanted to get the successor of the product of numbers 9\n", "and 10, we couldn't write `succ 9 * 10` because that would get the\n", "successor of 9, which would then be multiplied by 10. So 100. We'd have\n", "to write `succ (9 * 10)` to get 91.\n", "\n", "If a function takes two parameters, we can also call it as an infix\n", "function by surrounding it with backticks. For instance, the [`div`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:div)\n", "function takes two integers and does integral division between them.\n", "Doing `div 92 10` results in a 9. But when we call it like that, there may\n", "be some confusion as to which number is doing the division and which one\n", "is being divided. So we can call it as an infix function by doing\n", "``92 `div` 10`` and suddenly it's much clearer.\n", "\n", "Lots of people who come from imperative languages tend to stick to the\n", "notion that parentheses should denote function application. For example,\n", "in C, you use parentheses to call functions like `foo()`, `bar(1)` or\n", "`baz(3, \"haha\")`. Like we said, spaces are used for function application in\n", "Haskell. So those functions in Haskell would be `foo`, `bar 1` and `baz 3\n", "\"haha\"`. So if you see something like `bar (bar 3)`, it doesn't mean that\n", "`bar` is called with `bar` and `3` as parameters. It means that we first call\n", "the function `bar` with `3` as the parameter to get some number and then we\n", "call `bar` again with that number. In C, that would be something like\n", "`bar(bar(3))`.\n", "\n", "Baby's first functions\n", "----------------------\n", "\n", "In the previous section we got a basic feel for calling functions. Now\n", "let's try making our own!" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "doubleMe x = x + x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Functions are defined in a similar way that they are called. The\n", "function name is followed by parameters separated by spaces. But when\n", "defining functions, there's a `=` and after that we define what the\n", "function does." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "18" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "doubleMe 9" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "16.6" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "doubleMe 8.3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-) works on integers as well as on floating-point numbers\n", "(anything that can be considered a number, really), our function also\n", "works on any number. Let's make a function that takes two numbers and\n", "multiplies each by two and then adds them together." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "doubleUs x y = x*2 + y*2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Simple. We could have also defined it as `doubleUs x y = x + x + y + y`.\n", "Testing it out produces pretty predictable results." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "26" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "doubleUs 4 9" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "73.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "doubleUs 2.3 34.2" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "478" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "doubleUs 28 88 + doubleMe 123" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As expected, you can call your own functions from other functions that\n", "you made. With that in mind, we could redefine `doubleUs` like this:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "doubleUs x y = doubleMe x + doubleMe y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is a very simple example of a common pattern you will see\n", "throughout Haskell. Making basic functions that are obviously correct\n", "and then combining them into more complex functions. This way you also\n", "avoid repetition. What if some mathematicians figured out that 2 is\n", "actually 3 and you had to change your program? You could just redefine\n", "`doubleMe` to be `x + x + x` and since `doubleUs` calls `doubleMe`, it would\n", "automatically work in this strange new world where 2 is 3.\n", "\n", "Functions in Haskell don't have to be in any particular order, so it\n", "doesn't matter if you define `doubleMe` first and then `doubleUs` or if you\n", "do it the other way around.\n", "\n", "Now we're going to make a function that multiplies a number by 2 but\n", "only if that number is smaller than or equal to 100 because numbers\n", "bigger than 100 are big enough as it is!" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "doubleSmallNumber x = if x > 100\n", " then x\n", " else x*2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Right here we introduced Haskell's if statement. You're probably\n", "familiar with if statements from other languages. The difference between\n", "Haskell's if statement and if statements in imperative languages is that\n", "the else part is mandatory in Haskell. In imperative languages you can\n", "just skip a couple of steps if the condition isn't satisfied but in\n", "Haskell every expression and function must return something. We could\n", "have also written that if statement in one line but I find this way more\n", "readable. Another thing about the if statement in Haskell is that it is\n", "an *expression*. An expression is basically a piece of code that returns\n", "a value. `5` is an expression because it returns 5, `4 + 8` is an\n", "expression, `x + y` is an expression because it returns the sum of `x` and\n", "`y`. Because the else is mandatory, an if statement will always return\n", "something and that's why it's an expression. If we wanted to add one to\n", "every number that's produced in our previous function, we could have\n", "written its body like this." ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "doubleSmallNumber' x = (if x > 100 then x else x*2) + 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Had we omitted the parentheses, it would have added one only if `x` wasn't\n", "greater than 100. Note the `'` at the end of the function name. That\n", "apostrophe doesn't have any special meaning in Haskell's syntax. It's a\n", "valid character to use in a function name. We usually use `'` to either\n", "denote a strict version of a function (one that isn't lazy) or a\n", "slightly modified version of a function or a variable. Because `'` is a\n", "valid character in functions, we can make a function like this." ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "conanO'Brien = \"It's a-me, Conan O'Brien!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are two noteworthy things here. The first is that in the function\n", "name we didn't capitalize Conan's name. That's because functions can't\n", "begin with uppercase letters. We'll see why a bit later. The second\n", "thing is that this function doesn't take any parameters. When a function\n", "doesn't take any parameters, we usually say it's a *definition* (or a\n", "*name*). Because we can't change what names (and functions) mean once\n", "we've defined them, `conanO'Brien` and the string `\"It's a-me, Conan\n", "O'Brien!\"` can be used interchangeably.\n", "\n", "An intro to lists\n", "-----------------\n", "\n", " Much like shopping\n", "lists in the real world, lists in Haskell are very useful. It's the most\n", "used data structure and it can be used in a multitude of different ways\n", "to model and solve a whole bunch of problems. Lists are SO awesome. In\n", "this section we'll look at the basics of lists, strings (which are\n", "lists) and list comprehensions.\n", "\n", "In Haskell, lists are a *homogenous* data structure. It stores several\n", "elements of the same type. That means that we can have a list of\n", "integers or a list of characters but we can't have a list that has a few\n", "integers and then a few characters. And now, a list!\n", "\n", "> __Note:__ We can use the `let` keyword to define a name right in GHCI. Doing\n", "> `let a = 1` inside GHCI is the equivalent of writing `a = 1` in a script and\n", "> then loading it." ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let lostNumbers = [4,8,15,16,23,42]" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[4,8,15,16,23,42]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lostNumbers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, lists are denoted by square brackets and the values in\n", "the lists are separated by commas. If we tried a list like\n", "`[1,2,'a',3,'b','c',4]`, Haskell would complain that characters (which\n", "are, by the way, denoted as a character between single quotes) are not\n", "numbers. Speaking of characters, strings are just lists of characters.\n", "`\"hello\"` is just syntactic sugar for `['h','e','l','l','o']`. Because\n", "strings are lists, we can use list functions on them, which is really\n", "handy.\n", "\n", "A common task is putting two lists together. This is done by using the\n", "[`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) operator." ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,9,10,11,12]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1,2,3,4] ++ [9,10,11,12]" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"hello world\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"hello\" ++ \" \" ++ \"world\"" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"woot\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "['w','o'] ++ ['o','t']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Watch out when repeatedly using the [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) operator on long strings. When\n", "you put together two lists (even if you append a singleton list to a\n", "list, for instance: `[1,2,3] ++ [4]`), internally, Haskell has to walk\n", "through the whole list on the left side of [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-). That's not a problem when\n", "dealing with lists that aren't too big. But putting something at the end\n", "of a list that's fifty million entries long is going to take a while.\n", "However, putting something at the beginning of a list using the `:`\n", "operator (also called the cons operator) is instantaneous." ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"A SMALL CAT\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "'A':\" SMALL CAT\"" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[5,1,2,3,4,5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5:[1,2,3,4,5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice how `:` takes a number and a list of numbers or a character and a\n", "list of characters, whereas [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) takes two lists. Even if you're adding an\n", "element to the end of a list with [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-), you have to surround it with\n", "square brackets so it becomes a list.\n", "\n", "`[1,2,3]` is actually just syntactic sugar for `1:2:3:[]`. `[]` is an empty\n", "list. If we prepend `3` to it, it becomes `[3]`. If we prepend `2` to that, it\n", "becomes `[2,3]`, and so on.\n", "\n", "> __Note:__ `[]`, `[[]]` and `[[],[],[]]` are all different things. The first one\n", "> is an empty list, the second one is a list that contains one empty list, the\n", "> third one is a list that contains three empty lists.\n", "\n", "If you want to get an element out of a list by index, use `!!`. The\n", "indices start at 0." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "'B'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"Steve Buscemi\" !! 6" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "33.2" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[9.4,33.2,96.2,11.2,23.25] !! 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But if you try to get the sixth element from a list that only has four\n", "elements, you'll get an error so be careful!\n", "\n", "Lists can also contain lists. They can also contain lists that contain\n", "lists that contain lists …" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "b" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3],[1,1,1,1]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "b ++ [[1,1,1,1]]" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[6,6,6],[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[6,6,6]:b" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,2,3,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "b !! 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The lists within a list can be of different lengths but they can't be of\n", "different types. Just like you can't have a list that has some\n", "characters and some numbers, you can't have a list that has some lists\n", "of characters and some lists of numbers.\n", "\n", "Lists can be compared if the stuff they contain can be compared. When\n", "using [`<`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60-), [`<=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--61-), [`>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62-) and [`>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--61-) to compare lists, they are compared in\n", "lexicographical order. First the heads are compared. If they are equal\n", "then the second elements are compared, etc." ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[3,2,1] > [2,1,0]" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[3,2,1] > [2,10,100]" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[3,4,2] > [3,4]" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[3,4,2] > [2,4]" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[3,4,2] == [3,4,2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What else can you do with lists? Here are some basic functions that\n", "operate on lists.\n", "\n", "__[`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head)__ takes a list and returns its head. The head of a list is basically\n", "its first element." ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "head [5,4,3,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`tail`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:tail)__ takes a list and returns its tail. In other words, it chops off a\n", "list's head." ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[4,3,2,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "tail [5,4,3,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`last`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:last)__ takes a list and returns its last element." ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "last [5,4,3,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`init`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:init)__ takes a list and returns everything except its last element." ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[5,4,3,2]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "init [5,4,3,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we think of a list as a monster, here's what's what.\n", "\n", "\n", "\n", "But what happens if we try to get the head of an empty list?" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Prelude.head: empty list" ] } ], "source": [ "head []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oh my! It all blows up in our face! If there's no monster, it doesn't\n", "have a head. When using [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head), [`tail`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:tail), [`last`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:last) and [`init`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:init), be careful not to use\n", "them on empty lists. This error cannot be caught at compile time so it's\n", "always good practice to take precautions against accidentally telling\n", "Haskell to give you some elements from an empty list.\n", "\n", "__[`length`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:length)__ takes a list and returns its length, obviously." ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "length [5,4,3,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`null`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:null)__ checks if a list is empty. If it is, it returns [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), otherwise it\n", "returns [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False). Use this function instead of `xs == []` (if you have a list\n", "called `xs`)" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "null [1,2,3]" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "null []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse)__ reverses a list." ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "reverse [5,4,3,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`take`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:take)__ takes number and a list. It extracts that many elements from the\n", "beginning of the list. Watch." ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[5,4,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 3 [5,4,3,2,1]" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 1 [3,9,3]" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 5 [1,2]" ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 0 [6,6,6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See how if we try to take more elements than there are in the list, it\n", "just returns the list. If we try to take 0 elements, we get an empty\n", "list.\n", "\n", "__[`drop`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:drop)__ works in a similar way, only it drops the number of elements from\n", "the beginning of a list." ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,5,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "drop 3 [8,4,2,1,5,6]" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "drop 0 [1,2,3,4]" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "drop 100 [1,2,3,4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`maximum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:maximum)__ takes a list of stuff that can be put in some kind of order and\n", "returns the biggest element.\n", "\n", "__[`minimum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:minimum)__ returns the smallest." ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "minimum [8,4,2,1,5,6]" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "9" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "maximum [1,9,2,3,4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`sum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sum)__ takes a list of numbers and returns their sum.\n", "\n", "__[`product`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:product)__ takes a list of numbers and returns their product." ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "31" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sum [5,2,1,6,3,2,5,7]" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "24" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "product [6,2,1,2]" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "product [1,2,5,6,7,9,2,0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`elem`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem)__ takes a thing and a list of things and tells us if that thing is an\n", "element of the list. It's usually called as an infix function because\n", "it's easier to read that way." ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "4 `elem` [3,4,5,6]" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "10 `elem` [3,4,5,6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Those were a few basic functions that operate on lists. We'll take a\n", "look at more list functions [later](http://learnyouahaskell.com/modules#data-list)\n", "\n", "Texas ranges\n", "------------\n", "\n", " What if we want a list\n", "of all numbers between 1 and 20? Sure, we could just type them all out\n", "but obviously that's not a solution for gentlemen who demand excellence\n", "from their programming languages. Instead, we'll use ranges. Ranges are\n", "a way of making lists that are arithmetic sequences of elements that can\n", "be enumerated. Numbers can be enumerated. One, two, three, four, etc.\n", "Characters can also be enumerated. The alphabet is an enumeration of\n", "characters from A to Z. Names can't be enumerated. What comes after\n", "\"John\"? I don't know.\n", "\n", "To make a list containing all the natural numbers from 1 to 20, you just\n", "write `[1..20]`. That is the equivalent of writing\n", "`[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]` and there's no\n", "difference between writing one or the other except that writing out long\n", "enumeration sequences manually is stupid." ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1..20]" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"abcdefghijklmnopqrstuvwxyz\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "['a'..'z']" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"KLMNOPQRSTUVWXYZ\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "['K'..'Z']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ranges are cool because you can also specify a step. What if we want all\n", "even numbers between 1 and 20? Or every third number between 1 and 20?" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[2,4,6,8,10,12,14,16,18,20]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[2,4..20]" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[3,6,9,12,15,18]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[3,6..20]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's simply a matter of separating the first two elements with a comma\n", "and then specifying what the upper limit is. While pretty smart, ranges\n", "with steps aren't as smart as some people expect them to be. You can't\n", "do `[1,2,4,8,16..100]` and expect to get all the powers of 2. Firstly\n", "because you can only specify one step. And secondly because some\n", "sequences that aren't arithmetic are ambiguous if given only by a few of\n", "their first terms.\n", "\n", "To make a list with all the numbers from 20 to 1, you can't just do\n", "`[20..1]`, you have to do `[20,19..1]`.\n", "\n", "Watch out when using floating point numbers in ranges! Because they are\n", "not completely precise (by definition), their use in ranges can yield\n", "some pretty funky results." ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[0.1, 0.3 .. 1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "My advice is not to use them in list ranges.\n", "\n", "You can also use ranges to make infinite lists by just not specifying an\n", "upper limit. Later we'll go into more detail on infinite lists. For now,\n", "let's examine how you would get the first 24 multiples of 13. Sure, you\n", "could do `[13,26..24*13]`. But there's a better way: `take 24 [13,26..]`.\n", "Because Haskell is lazy, it won't try to evaluate the infinite list\n", "immediately because it would never finish. It'll wait to see what you\n", "want to get out of that infinite lists. And here it sees you just want\n", "the first 24 elements and it gladly obliges.\n", "\n", "A handful of functions that produce infinite lists:\n", "\n", "__[`cycle`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:cycle)__ takes a list and cycles it into an infinite list. If you just try\n", "to display the result, it will go on forever so you have to slice it off\n", "somewhere." ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,1,2,3,1,2,3,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 10 (cycle [1,2,3])" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"LOL LOL LOL \"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 12 (cycle \"LOL \")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`repeat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:repeat)__ takes an element and produces an infinite list of just that\n", "element. It's like cycling a list with only one element." ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[5,5,5,5,5,5,5,5,5,5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 10 (repeat 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Although it's simpler to just use the [`replicate`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:replicate) function if you want\n", "some number of the same element in a list. `replicate 3 10` returns\n", "`[10,10,10]`.\n", "\n", "\n", "\n", "I'm a list comprehension\n", "------------------------\n", "\n", " If you've ever taken a\n", "course in mathematics, you've probably run into *set comprehensions*.\n", "They're normally used for building more specific sets out of general\n", "sets. A basic comprehension for a set that contains the first ten even\n", "natural numbers is $ S=\\{2 \\centerdot x | x \\in \\mathbb{N}, x \\le 10 \\}$. The part before\n", "the pipe is called the output function, `x` is the variable, `N` is the\n", "input set and `x <= 10` is the predicate. That means that the set\n", "contains the doubles of all natural numbers that satisfy the predicate.\n", "\n", "If we wanted to write that in Haskell, we could do something like `take\n", "10 [2,4..]`. But what if we didn't want doubles of the first 10 natural\n", "numbers but some kind of more complex function applied on them? We could\n", "use a list comprehension for that. List comprehensions are very similar\n", "to set comprehensions. We'll stick to getting the first 10 even numbers\n", "for now. The list comprehension we could use is `[x*2 | x <- [1..10]]`.\n", "`x` is drawn from `[1..10]` and for every element in `[1..10]` (which we have\n", "bound to `x`), we get that element, only doubled. Here's that\n", "comprehension in action." ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[2,4,6,8,10,12,14,16,18,20]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[x*2 | x <- [1..10]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, we get the desired results. Now let's add a condition\n", "(or a predicate) to that comprehension. Predicates go after the binding\n", "parts and are separated from them by a comma. Let's say we want only the\n", "elements which, doubled, are greater than or equal to 12." ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[12,14,16,18,20]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[x*2 | x <- [1..10], x*2 >= 12]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool, it works. How about if we wanted all numbers from 50 to 100 whose\n", "remainder when divided with the number 7 is 3? Easy." ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[52,59,66,73,80,87,94]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[ x | x <- [50..100], x `mod` 7 == 3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Success! Note that weeding out lists by predicates is also called\n", "*filtering*. We took a list of numbers and we filtered them by the\n", "predicate. Now for another example. Let's say we want a comprehension\n", "that replaces each odd number greater than 10 with `\"BANG!\"` and each odd\n", "number that's less than 10 with `\"BOOM!\"`. If a number isn't odd, we throw\n", "it out of our list. For convenience, we'll put that comprehension inside\n", "a function so we can easily reuse it." ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "boomBangs xs = [ if x < 10 then \"BOOM!\" else \"BANG!\" | x <- xs, odd x]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The last part of the comprehension is the predicate. The function [`odd`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:odd)\n", "returns [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) on an odd number and [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) on an even one. The element is\n", "included in the list only if all the predicates evaluate to [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True)." ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"BOOM!\",\"BOOM!\",\"BANG!\",\"BANG!\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "boomBangs [7..13]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can include several predicates. If we wanted all numbers from 10 to\n", "20 that are not 13, 15 or 19, we'd do:" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[10,11,12,14,16,17,18,20]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[ x | x <- [10..20], x /= 13, x /= 15, x /= 19]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Not only can we have multiple predicates in list comprehensions (an\n", "element must satisfy all the predicates to be included in the resulting\n", "list), we can also draw from several lists. When drawing from several\n", "lists, comprehensions produce all combinations of the given lists and\n", "then join them by the output function we supply. A list produced by a\n", "comprehension that draws from two lists of length 4 will have a length\n", "of 16, provided we don't filter them. If we have two lists, `[2,5,10]` and\n", "`[8,10,11]` and we want to get the products of all the possible\n", "combinations between numbers in those lists, here's what we'd do." ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[16,20,22,40,50,55,80,100,110]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[ x*y | x <- [2,5,10], y <- [8,10,11]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As expected, the length of the new list is 9. What if we wanted all\n", "possible products that are more than 50?" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[55,80,100,110]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[ x*y | x <- [2,5,10], y <- [8,10,11], x*y > 50]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How about a list comprehension that combines a list of adjectives and a\n", "list of nouns … for epic hilarity." ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"lazy hobo\",\"lazy frog\",\"lazy pope\",\"grouchy hobo\",\"grouchy frog\",\"grouchy pope\",\"scheming hobo\",\"scheming frog\",\"scheming pope\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let nouns = [\"hobo\",\"frog\",\"pope\"]\n", "let adjectives = [\"lazy\",\"grouchy\",\"scheming\"]\n", "[adjective ++ \" \" ++ noun | adjective <- adjectives, noun <- nouns]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I know! Let's write our own version of [`length`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:length)! We'll call it `length'`." ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "length' xs = sum [1 | _ <- xs]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`_` means that we don't care what we'll draw from the list anyway so\n", "instead of writing a variable name that we'll never use, we just write\n", "`_`. This function replaces every element of a list with `1` and then sums\n", "that up. This means that the resulting sum will be the length of our\n", "list.\n", "\n", "Just a friendly reminder: because strings are lists, we can use list\n", "comprehensions to process and produce strings. Here's a function that\n", "takes a string and removes everything except uppercase letters from it." ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Testing it out:" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"HA\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "removeNonUppercase \"Hahaha! Ahahaha!\"" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"ILIKEFROGS\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "removeNonUppercase \"IdontLIKEFROGS\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The predicate here does all the work. It says that the character will be\n", "included in the new list only if it's an element of the list `['A'..'Z']`.\n", "Nested list comprehensions are also possible if you're operating on\n", "lists that contain lists. A list contains several lists of numbers.\n", "Let's remove all odd numbers without flattening the list." ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]\n", "[ [ x | x <- xs, even x ] | xs <- xxs]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can write list comprehensions across several lines. It's better to split longer list comprehensions across multiple\n", "lines, especially if they're nested.\n", "\n", "Tuples\n", "------\n", "\n", "\n", "\n", "In some ways, tuples are like lists — they are a way to store several\n", "values into a single value. However, there are a few fundamental\n", "differences. A list of numbers is a list of numbers. That's its type and\n", "it doesn't matter if it has only one number in it or an infinite amount\n", "of numbers. Tuples, however, are used when you know exactly how many\n", "values you want to combine and its type depends on how many components\n", "it has and the types of the components. They are denoted with\n", "parentheses and their components are separated by commas.\n", "\n", "Another key difference is that they don't have to be homogenous. Unlike\n", "a list, a tuple can contain a combination of several types.\n", "\n", "Think about how we'd represent a two-dimensional vector in Haskell. One\n", "way would be to use a list. That would kind of work. So what if we\n", "wanted to put a couple of vectors in a list to represent points of a\n", "shape on a two-dimensional plane? We could do something like\n", "`[[1,2],[8,11],[4,5]]`. The problem with that method is that we could also\n", "do stuff like `[[1,2],[8,11,5],[4,5]]`, which Haskell has no problem with\n", "since it's still a list of lists with numbers but it kind of doesn't\n", "make sense. But a tuple of size two (also called a pair) is its own\n", "type, which means that a list can't have a couple of pairs in it and\n", "then a triple (a tuple of size three), so let's use that instead.\n", "Instead of surrounding the vectors with square brackets, we use\n", "parentheses: `[(1,2),(8,11),(4,5)]`. What if we tried to make a shape like\n", "`[(1,2),(8,11,5),(4,5)]`? Well, we'd get this error:" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ ":1:8: error:\n • Couldn't match expected type: (a, b)\n with actual type: (a0, b0, c0)\n • In the expression: (8, 11, 5)\n In the expression: [(1, 2), (8, 11, 5), (4, 5)]\n In an equation for ‘it’: it = [(1, 2), (8, 11, 5), (4, 5)]\n • Relevant bindings include it :: [(a, b)] (bound at :1:1)" ] } ], "source": [ "[(1,2),(8,11,5),(4,5)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's telling us that we tried to use a pair and a triple in the same\n", "list, which is not supposed to happen. You also couldn't make a list\n", "like `[(1,2),(\"One\",2)]` because the first element of the list is a pair\n", "of numbers and the second element is a pair consisting of a string and a\n", "number. Tuples can also be used to represent a wide variety of data. For\n", "instance, if we wanted to represent someone's name and age in Haskell,\n", "we could use a triple: `(\"Christopher\", \"Walken\", 55)`. As seen in this\n", "example, tuples can also contain lists.\n", "\n", "Use tuples when you know in advance how many components some piece of\n", "data should have. Tuples are much more rigid because each different size\n", "of tuple is its own type, so you can't write a general function to\n", "append an element to a tuple — you'd have to write a function for\n", "appending to a pair, one function for appending to a triple, one\n", "function for appending to a 4-tuple, etc.\n", "\n", "While there are singleton lists, there's no such thing as a singleton\n", "tuple. It doesn't really make much sense when you think about it. A\n", "singleton tuple would just be the value it contains and as such would\n", "have no benefit to us.\n", "\n", "Like lists, tuples can be compared with each other if their components\n", "can be compared. Only you can't compare two tuples of different sizes,\n", "whereas you can compare two lists of different sizes. Two useful\n", "functions that operate on pairs:\n", "\n", "__[`fst`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fst)__ takes a pair and returns its first component." ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "8" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fst (8,11)" ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Wow\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fst (\"Wow\", False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`snd`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:snd)__ takes a pair and returns its second component. Surprise!" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "11" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "snd (8,11)" ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "snd (\"Wow\", False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Note:__ these functions operate only on pairs. They won't work on\n", "> triples, 4-tuples, 5-tuples, etc. We'll go over extracting data from\n", "> tuples in different ways a bit later.\n", "\n", "A cool function that produces a list of pairs: __[`zip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zip)__. It takes two lists\n", "and then zips them together into one list by joining the matching\n", "elements into pairs. It's a really simple function but it has loads of\n", "uses. It's especially useful for when you want to combine two lists in a\n", "way or traverse two lists simultaneously. Here's a demonstration." ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(1,5),(2,5),(3,5),(4,5),(5,5)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zip [1,2,3,4,5] [5,5,5,5,5]" ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(1,\"one\"),(2,\"two\"),(3,\"three\"),(4,\"four\"),(5,\"five\")]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zip [1 .. 5] [\"one\", \"two\", \"three\", \"four\", \"five\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It pairs up the elements and produces a new list. The first element goes\n", "with the first, the second with the second, etc. Notice that because\n", "pairs can have different types in them, [`zip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zip) can take two lists that\n", "contain different types and zip them up. What happens if the lengths of\n", "the lists don't match?" ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(5,\"im\"),(3,\"a\"),(2,\"turtle\")]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zip [5,3,2,6,2,7,2,5,4,6,6] [\"im\",\"a\",\"turtle\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The longer list simply gets cut off to match the length of the shorter\n", "one. Because Haskell is lazy, we can zip finite lists with infinite\n", "lists:" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(1,\"apple\"),(2,\"orange\"),(3,\"cherry\"),(4,\"mango\")]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zip [1..] [\"apple\", \"orange\", \"cherry\", \"mango\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Here's a problem that combines tuples and list comprehensions: which\n", "right triangle that has integers for all sides and all sides equal to or\n", "smaller than 10 has a perimeter of 24? First, let's try generating all\n", "triangles with sides equal to or smaller than 10:" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We're just drawing from three lists and our output function is combining\n", "them into a triple. If you evaluate that by typing out `triangles` in\n", "GHC, you'll get a list of all possible triangles with sides under or\n", "equal to 10. Next, we'll add a condition that they all have to be right\n", "triangles. We'll also modify this function by taking into consideration\n", "that side b isn't larger than the hypotenuse and that side a isn't\n", "larger than side b." ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let rightTriangles = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We're almost done. Now, we just modify the function by saying that we\n", "want the ones where the perimeter is 24." ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(6,8,10)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]\n", "rightTriangles'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And there's our answer! This is a common pattern in functional\n", "programming. You take a starting set of solutions and then you apply\n", "transformations to those solutions and filter them until you get the\n", "right ones." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/03-types-and-typeclasses.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Types and Typeclasses\n", "=====================\n", "\n", "Believe the type\n", "----------------\n", "\n", "\n", "\n", "Previously we mentioned that Haskell has a static type system. The type\n", "of every expression is known at compile time, which leads to safer code.\n", "If you write a program where you try to divide a boolean type with some\n", "number, it won't even compile. That's good because it's better to catch\n", "such errors at compile time instead of having your program crash.\n", "Everything in Haskell has a type, so the compiler can reason quite a lot\n", "about your program before compiling it.\n", "\n", "Unlike Java or Pascal, Haskell has type inference. If we write a number,\n", "we don't have to tell Haskell it's a number. It can *infer* that on its\n", "own, so we don't have to explicitly write out the types of our functions\n", "and expressions to get things done. We covered some of the basics of\n", "Haskell with only a very superficial glance at types. However,\n", "understanding the type system is a very important part of learning\n", "Haskell.\n", "\n", "A type is a kind of label that every expression has. It tells us in\n", "which category of things that expression fits. The expression [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) is a\n", "boolean, `\"hello\"` is a string, etc." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we'll use GHCI to examine the types of some expressions. We'll do\n", "that by using the `:t` command which, followed by any valid expression,\n", "tells us its type. Let's give it a whirl." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "'a' :: Char" ], "text/plain": [ "'a' :: Char" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t 'a'" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "True :: Bool" ], "text/plain": [ "True :: Bool" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t True" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "\"HELLO!\" :: String" ], "text/plain": [ "\"HELLO!\" :: String" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t \"HELLO!\"" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "(True, 'a') :: (Bool, Char)" ], "text/plain": [ "(True, 'a') :: (Bool, Char)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t (True, 'a')" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "4 == 5 :: Bool" ], "text/plain": [ "4 == 5 :: Bool" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t 4 == 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Here we see that doing `:t`\n", "on an expression prints out the expression followed by `::` and its type.\n", "`::` is read as \"has type of\". Explicit types are always denoted with the\n", "first letter in capital case. `'a'`, as it would seem, has a type of [`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char).\n", "It's not hard to conclude that it stands for *character*. [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) is of a\n", "[`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) type. That makes sense. But what's this? Examining the type of\n", "`\"HELLO!\"` yields a `[Char]`. The square brackets denote a list. So we read\n", "that as it being *a list of characters*. Unlike lists, each tuple length\n", "has its own type. So the expression of `(True, 'a')` has a type of\n", "`(Bool, Char)`, whereas an expression such as `('a','b','c')` would have the\n", "type of `(Char, Char, Char)`. `4 == 5` will always return [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False), so its type\n", "is [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool).\n", "\n", "Functions also have types. When writing our own functions, we can choose\n", "to give them an explicit type declaration. This is generally considered\n", "to be good practice except when writing very short functions. From here\n", "on, we'll give all the functions that we make type declarations.\n", "Remember the list comprehension we made previously that filters a string\n", "so that only caps remain? Here's how it looks like with a type\n", "declaration." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "removeNonUppercase :: [Char] -> [Char]\n", "removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`removeNonUppercase` has a type of `[Char] -> [Char]`, meaning that it maps\n", "from a string to a string. That's because it takes one string as a\n", "parameter and returns another as a result. The `[Char]` type is synonymous\n", "with [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String) so it's clearer if we write\n", "`removeNonUppercase :: String -> String`.\n", "We didn't have to give this function a type declaration because\n", "the compiler can infer by itself that it's a function from a string to a\n", "string but we did anyway. But how do we write out the type of a function\n", "that takes several parameters? Here's a simple function that takes three\n", "integers and adds them together:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "addThree :: Int -> Int -> Int -> Int\n", "addThree x y z = x + y + z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The parameters are separated with `->` and there's no special distinction\n", "between the parameters and the return type. The return type is the last\n", "item in the declaration and the parameters are the first three. Later on\n", "we'll see why they're all just separated with `->` instead of having some\n", "more explicit distinction between the return types and the parameters\n", "like `Int, Int, Int -> Int` or something.\n", "\n", "If you want to give your function a type declaration but are unsure as\n", "to what it should be, you can always just write the function without it\n", "and then check it with `:t`. Functions are expressions too, so `:t` works\n", "on them without a problem.\n", "\n", "Here's an overview of some common types.\n", "\n", "__[`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int)__ stands for integer. It's used for whole numbers. `7` can be an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) but\n", "`7.2` cannot. [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) is bounded, which means that it has a minimum and a\n", "maximum value. Usually on 32-bit machines the maximum possible [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) is\n", "2147483647 and the minimum is -2147483648.\n", "\n", "__[`Integer`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Integer)__ stands for, er … also integer. The main difference is that it's\n", "not bounded so it can be used to represent really really big numbers. I\n", "mean like really big. [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), however, is more efficient." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "factorial :: Integer -> Integer\n", "factorial n = product [1..n]" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "30414093201713378043612608166064768844377641568960512000000000000" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "factorial 50" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`Float`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Float)__ is a real floating point with single precision." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "circumference :: Float -> Float\n", "circumference r = 2 * pi * r" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "25.132742" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "circumference 4.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`Double`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Double)__ is a real floating point with double the precision!" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "circumference' :: Double -> Double\n", "circumference' r = 2 * pi * r" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "25.132741228718345" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "circumference' 4.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool)__ is a boolean type. It can have only two values: [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) and [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False).\n", "\n", "__[`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char)__ represents a character. It's denoted by single quotes. A list of\n", "characters is a string.\n", "\n", "Tuples are types but they are dependent on their length as well as the\n", "types of their components, so there is theoretically an infinite number\n", "of tuple types, which is too many to cover in this tutorial. Note that\n", "the empty tuple `()` is also a type which can only have a single value: `()`\n", "\n", "Type variables\n", "--------------\n", "\n", "What do you think is the type of the [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head) function? Because [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head) takes a\n", "list of any type and returns the first element, so what could it be?\n", "Let's check!" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "head :: forall a. [a] -> a" ], "text/plain": [ "head :: forall a. [a] -> a" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t head" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Hmmm! What is this `a`? Is it\n", "a type? Remember that we previously stated that types are written in\n", "capital case, so it can't exactly be a type. Because it's not in capital\n", "case it's actually a *type variable*. That means that `a` can be of any\n", "type. This is much like generics in other languages, only in Haskell\n", "it's much more powerful because it allows us to easily write very\n", "general functions if they don't use any specific behavior of the types\n", "in them. Functions that have type variables are called *polymorphic\n", "functions*. The type declaration of [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head) states that it takes a list of\n", "any type and returns one element of that type.\n", "\n", "Although type variables can have names longer than one character, we\n", "usually give them names of a, b, c, d …\n", "\n", "Remember [`fst`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fst)? It returns the first component of a pair. Let's examine\n", "its type." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "fst :: forall a b. (a, b) -> a" ], "text/plain": [ "fst :: forall a b. (a, b) -> a" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t fst" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that [`fst`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fst) takes a tuple which contains two types and returns an\n", "element which is of the same type as the pair's first component. That's\n", "why we can use [`fst`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fst) on a pair that contains any two types. Note that just\n", "because `a` and `b` are different type variables, they don't have to be\n", "different types. It just states that the first component's type and the\n", "return value's type are the same.\n", "\n", "\n", "\n", "Typeclasses 101\n", "---------------\n", "\n", "\n", "\n", "A typeclass is a sort of interface that defines some behavior. If a type\n", "is a part of a typeclass, that means that it supports and implements the\n", "behavior the typeclass describes. A lot of people coming from OOP get\n", "confused by typeclasses because they think they are like classes in\n", "object oriented languages. Well, they're not. You can think of them kind\n", "of as Java interfaces, only better.\n", "\n", "What's the type signature of the [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) function?" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "(==) :: forall a. Eq a => a -> a -> Bool" ], "text/plain": [ "(==) :: forall a. Eq a => a -> a -> Bool" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t (==)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Note__: the equality operator, [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) is a function. So are [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-), [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-), [`-`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-45-), [`/`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47-) and\n", "> pretty much all operators. If a function is comprised only of special\n", "> characters, it's considered an infix function by default. If we want to\n", "> examine its type, pass it to another function or call it as a prefix\n", "> function, we have to surround it in parentheses.\n", "\n", "Interesting. We see a new thing here, the `=>` symbol. Everything before\n", "the `=>` symbol is called a *class constraint*. We can read the previous\n", "type declaration like this: the equality function takes any two values\n", "that are of the same type and returns a [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool). The type of those two\n", "values must be a member of the `Eq` class (this was the class constraint).\n", "\n", "The `Eq` typeclass provides an interface for testing for equality. Any\n", "type where it makes sense to test for equality between two values of\n", "that type should be a member of the `Eq` class. All standard Haskell types\n", "except for [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) (the type for dealing with input and output) and functions\n", "are a part of the `Eq` typeclass.\n", "\n", "The [`elem`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem) function has a type of `(Eq a) => a -> [a] -> Bool` because it\n", "uses [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) over a list to check whether some value we're looking for is in\n", "it.\n", "\n", "Some basic typeclasses:\n", "\n", "__`Eq`__ is used for types that support equality testing. The functions its\n", "members implement are [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) and [`/=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47--61-). So if there's an `Eq` class constraint\n", "for a type variable in a function, it uses [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) or [`/=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47--61-) somewhere inside its\n", "definition. All the types we mentioned previously except for functions\n", "are part of `Eq`, so they can be tested for equality." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5 == 5" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5 /= 5" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "'a' == 'a'" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"Ho Ho\" == \"Ho Ho\"" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "3.432 == 3.432" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord)__ is for types that have an ordering." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "(>) :: forall a. Ord a => a -> a -> Bool" ], "text/plain": [ "(>) :: forall a. Ord a => a -> a -> Bool" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t (>)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All the types we covered so far except for functions are part of [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord).\n", "[`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) covers all the standard comparing functions such as [`>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62-), [`<`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60-), [`>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--61-) and\n", "[`<=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--61-). The [`compare`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:compare) function takes two [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) members of the same type and\n", "returns an ordering. [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering) is a type that can be [`GT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:GT), [`LT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:LT) or [`EQ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:EQ),\n", "meaning *greater than*, *lesser than* and *equal*, respectively.\n", "\n", "To be a member of [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord), a type must first have membership in the\n", "prestigious and exclusive `Eq` club." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"Abrakadabra\" < \"Zebra\"" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"Abrakadabra\" `compare` \"Zebra\"" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5 >= 2" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5 `compare` 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Members of __[`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show)__ can be presented as strings. All types covered so far\n", "except for functions are a part of [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show). The most used function that\n", "deals with the [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) typeclass is show. It takes a value whose type is a\n", "member of [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) and presents it to us as a string." ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"3\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show 3" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"5.334\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show 5.334" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"True\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read)__ is sort of the opposite typeclass of [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show). The [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) function takes\n", "a string and returns a type which is a member of [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read)." ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"True\" || False" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "12.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"8.2\" + 3.8" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"5\" - 2" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"[1,2,3,4]\" ++ [3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far so good. Again, all types covered so far are in this typeclass.\n", "But what happens if we try to do just `read \"4\"`?" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "reads \"4\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What GHC is telling us here is that it doesn't know what we want in\n", "return. Notice that in the previous uses of [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) we did something with\n", "the result afterwards. That way, GHC could infer what kind of result we\n", "wanted out of our [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read). If we used it as a boolean, it knew it had to\n", "return a [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool). But now, it knows we want some type that is part of the\n", "[`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read) class, it just doesn't know which one. Let's take a look at the\n", "type signature of [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read)." ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "read :: forall a. Read a => String -> a" ], "text/plain": [ "read :: forall a. Read a => String -> a" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t read" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See? It returns a type that's part of [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read) but if we don't try to use it\n", "in some way later, it has no way of knowing which type. That's why we\n", "can use explicit __type annotations__. Type annotations are a way of\n", "explicitly saying what the type of an expression should be. We do that\n", "by adding `::` at the end of the expression and then specifying a type.\n", "Observe:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"5\" :: Int" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "5.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"5\" :: Float" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "20.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(read \"5\" :: Float) * 4" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"[1,2,3,4]\" :: [Int]" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(3,'a')" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"(3, 'a')\" :: (Int, Char)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Most expressions are such that the compiler can infer what their type is\n", "by itself. But sometimes, the compiler doesn't know whether to return a\n", "value of type [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) or [`Float`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Float) for an expression like `read \"5\"`. To see what\n", "the type is, Haskell would have to actually evaluate `read \"5\"`. But since\n", "Haskell is a statically typed language, it has to know all the types\n", "before the code is compiled (or in the case of GHCI, evaluated). So we\n", "have to tell Haskell: \"Hey, this expression should have this type, in\n", "case you don't know!\".\n", "\n", "__[`Enum`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Enum)__ members are sequentially ordered types — they can be enumerated.\n", "The main advantage of the [`Enum`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Enum) typeclass is that we can use its types in\n", "list ranges. They also have defined successors and predecessors, which\n", "you can get with the [`succ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:succ) and [`pred`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pred) functions. Types in this class: `()`,\n", "[`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool), [`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char), [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering), [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), [`Integer`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Integer), [`Float`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Float) and [`Double`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Double)." ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"abcde\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "['a'..'e']" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[LT,EQ,GT]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[LT .. GT]" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[3,4,5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[3 .. 5]" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "'C'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "succ 'B'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`Bounded`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bounded)__ members have an upper and a lower bound." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Jupyter Note:__ Parentheses resolve ambiguity in the following expressions, see [IHaskell Issue #509 The type signature for ‘minBound’ lacks an accompanying binding](https://github.com/IHaskell/IHaskell/issues/509)" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "-9223372036854775808" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(minBound :: Int)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "'\\1114111'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(maxBound :: Char)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(maxBound :: Bool)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(minBound :: Bool)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[`minBound`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:minBound) and [`maxBound`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:maxBound) are interesting because they have a type of\n", "`(Bounded a) => a`. In a sense they are polymorphic constants.\n", "\n", "All tuples are also part of [`Bounded`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bounded) if the components are also in it." ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(True,9223372036854775807,'\\1114111')" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(maxBound :: (Bool, Int, Char))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num)__ is a numeric typeclass. Its members have the property of being able\n", "to act like numbers. Let's examine the type of a number." ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "20 :: forall {a}. Num a => a" ], "text/plain": [ "20 :: forall {a}. Num a => a" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t 20" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It appears that whole numbers are also polymorphic constants. They can\n", "act like any type that's a member of the [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) typeclass." ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "20" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "20 :: Int" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "20" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "20 :: Integer" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "20.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "20 :: Float" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "20.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "20 :: Double" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Those are types that are in the [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) typeclass. If we examine the type of\n", "[`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-), we'll see that it accepts all numbers." ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "(*) :: forall a. Num a => a -> a -> a" ], "text/plain": [ "(*) :: forall a. Num a => a -> a -> a" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t (*)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It takes two numbers of the same type and returns a number of that type.\n", "That's why `(5 :: Int) * (6 :: Integer)` will result in a type error\n", "whereas `5 * (6 :: Integer)` will work just fine and produce an [`Integer`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Integer)\n", "because `5` can act like an [`Integer`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Integer) or an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int).\n", "\n", "To join [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num), a type must already be friends with [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) and `Eq`.\n", "\n", "__[`Integral`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Integral)__ is also a numeric typeclass. [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) includes all numbers,\n", "including real numbers and integral numbers, [`Integral`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Integral) includes only\n", "integral (whole) numbers. In this typeclass are [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) and [`Integer`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Integer).\n", "\n", "__[`Floating`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Floating)__ includes only floating point numbers, so [`Float`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Float) and [`Double`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Double).\n", "\n", "A very useful function for dealing with numbers is __[`fromIntegral`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fromIntegral)__. It has\n", "a type declaration of `fromIntegral :: (Num b, Integral a) => a -> b`.\n", "From its type signature we see that it takes an integral number and\n", "turns it into a more general number. That's useful when you want\n", "integral and floating point types to work together nicely. For instance,\n", "the [`length`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:length) function has a type declaration of `length :: [a] -> Int`\n", "instead of having a more general type of\n", "`(Num b) => length :: [a] -> b`.\n", "I think that's there for historical reasons or something, although in\n", "my opinion, it's pretty stupid. Anyway, if we try to get a length of a\n", "list and then add it to `3.2`, we'll get an error because we tried to add\n", "together an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) and a floating point number. So to get around this, we\n", "do `fromIntegral (length [1,2,3,4]) + 3.2` and it all works out.\n", "\n", "Notice that [`fromIntegral`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fromIntegral) has several class constraints in its type\n", "signature. That's completely valid and as you can see, the class\n", "constraints are separated by commas inside the parentheses." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/04-syntax-in-functions.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "Syntax in Functions\n", "===================\n", "\n", "Pattern matching\n", "----------------\n", "\n", "\n", "\n", "This chapter will cover some of Haskell's cool syntactic constructs and\n", "we'll start with pattern matching. Pattern matching consists of\n", "specifying patterns to which some data should conform and then checking\n", "to see if it does and deconstructing the data according to those\n", "patterns.\n", "\n", "When defining functions, you can define separate function bodies for\n", "different patterns. This leads to really neat code that's simple and\n", "readable. You can pattern match on any data type — numbers, characters,\n", "lists, tuples, etc. Let's make a really trivial function that checks if\n", "the number we supplied to it is a seven or not." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "lucky :: (Integral a) => a -> String\n", "lucky 7 = \"LUCKY NUMBER SEVEN!\"\n", "lucky x = \"Sorry, you're out of luck, pal!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When you call `lucky`, the patterns will be checked from top to bottom and\n", "when it conforms to a pattern, the corresponding function body will be\n", "used. The only way a number can conform to the first pattern here is if\n", "it is 7. If it's not, it falls through to the second pattern, which\n", "matches anything and binds it to `x`. This function could have also been\n", "implemented by using an if statement. But what if we wanted a function\n", "that says the numbers from 1 to 5 and says `\"Not between 1 and 5\"` for any\n", "other number? Without pattern matching, we'd have to make a pretty\n", "convoluted if then else tree. However, with it:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "sayMe :: (Integral a) => a -> String\n", "sayMe 1 = \"One!\"\n", "sayMe 2 = \"Two!\"\n", "sayMe 3 = \"Three!\"\n", "sayMe 4 = \"Four!\"\n", "sayMe 5 = \"Five!\"\n", "sayMe x = \"Not between 1 and 5\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that if we moved the last pattern (the catch-all one) to the top,\n", "it would always say `\"Not between 1 and 5\"`, because it would catch all\n", "the numbers and they wouldn't have a chance to fall through and be\n", "checked for any other patterns.\n", "\n", "Remember the factorial function we implemented previously? We defined\n", "the factorial of a number `n` as `product [1..n]`. We can also define a\n", "factorial function *recursively*, the way it is usually defined in\n", "mathematics. We start by saying that the factorial of 0 is 1. Then we\n", "state that the factorial of any positive integer is that integer\n", "multiplied by the factorial of its predecessor. Here's how that looks\n", "like translated in Haskell terms." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "factorial :: (Integral a) => a -> a\n", "factorial 0 = 1\n", "factorial n = n * factorial (n - 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the first time we've defined a function recursively. Recursion\n", "is important in Haskell and we'll take a closer look at it later. But in\n", "a nutshell, this is what happens if we try to get the factorial of, say, 3. \n", "It tries to compute `3 * factorial 2`. The factorial of 2 is\n", "`2 * factorial 1`, so for now we have `3 * (2 * factorial 1)`.\n", "`factorial 1` is `1 * factorial 0`, so we have\n", "`3 * (2 * (1 * factorial 0))`. Now here comes the trick — we've defined\n", "the factorial of 0 to be just 1 and\n", "because it encounters that pattern before the catch-all one, it just\n", "returns 1. So the final result is equivalent to `3 * (2 * (1 * 1))`.\n", "Had we written the second pattern on top of the first one, it would\n", "catch all numbers, including 0 and our calculation would never\n", "terminate. That's why order is important when specifying patterns and\n", "it's always best to specify the most specific ones first and then the\n", "more general ones later.\n", "\n", "Pattern matching can also fail. If we define a function like this:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "charName :: Char -> String\n", "charName 'a' = \"Albert\"\n", "charName 'b' = \"Broseph\"\n", "charName 'c' = \"Cecil\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and then try to call it with an input that we didn't expect, this is\n", "what happens:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Albert\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "charName 'a'" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Broseph\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "charName 'b'" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ ":(2,1)-(4,22): Non-exhaustive patterns in function charName" ] } ], "source": [ "charName 'h'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It complains that we have non-exhaustive patterns, and rightfully so.\n", "When making patterns, we should always include a catch-all pattern so\n", "that our program doesn't crash if we get some unexpected input.\n", "\n", "Pattern matching can also be used on tuples. What if we wanted to make a\n", "function that takes two vectors in a 2D space (that are in the form of\n", "pairs) and adds them together? To add together two vectors, we add their\n", "x components separately and then their y components separately. Here's\n", "how we would have done it if we didn't know about pattern matching:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)\n", "addVectors a b = (fst a + fst b, snd a + snd b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well, that works, but there's a better way to do it. Let's modify the\n", "function so that it uses pattern matching." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)\n", "addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There we go! Much better. Note that this is already a catch-all pattern.\n", "The type of `addVectors` (in both cases) is\n", "`addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)`,\n", "so we are guaranteed to get two pairs as\n", "parameters.\n", "\n", "[`fst`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fst) and [`snd`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:snd) extract the components of pairs. But what about triples?\n", "Well, there are no provided functions that do that but we can make our\n", "own." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "first :: (a, b, c) -> a\n", "first (x, _, _) = x\n", "\n", "second :: (a, b, c) -> b\n", "second (_, y, _) = y\n", "\n", "third :: (a, b, c) -> c\n", "third (_, _, z) = z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `_` means the same thing as it does in list comprehensions. It means\n", "that we really don't care what that part is, so we just write a `_`.\n", "\n", "Which reminds me, you can also pattern match in list comprehensions.\n", "Check this out:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[4,7,6,8,11,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)]\n", "[a+b | (a,b) <- xs]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Should a pattern match fail, it will just move on to the next element.\n", "\n", "Lists themselves can also be used in pattern matching. You can match\n", "with the empty list `[]` or any pattern that involves `:` and the empty\n", "list. But since `[1,2,3]` is just syntactic sugar for `1:2:3:[]`, you can\n", "also use the former pattern. A pattern like `x:xs` will bind the head of\n", "the list to `x` and the rest of it to `xs`, even if there's only one element\n", "so `xs` ends up being an empty list.\n", "\n", "> __Note:__ The `x:xs` pattern is used a lot, especially with recursive\n", "> functions. But patterns that have `:` in them only match against lists of\n", "> length 1 or more.\n", "\n", "If you want to bind, say, the first three elements to variables and the\n", "rest of the list to another variable, you can use something like\n", "`x:y:z:zs`. It will only match against lists that have three elements or\n", "more.\n", "\n", "Now that we know how to pattern match against list, let's make our own\n", "implementation of the [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head) function." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "head' :: [a] -> a\n", "head' [] = error \"Can't call head on an empty list, dummy!\"\n", "head' (x:_) = x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Checking if it works:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "head' [4,5,6]" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:", "ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "'H'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "head' \"Hello\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nice! Notice that if you want to bind to several variables (even if one\n", "of them is just `_` and doesn't actually bind at all), we have to\n", "surround them in parentheses. Also notice the [`error`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:error) function that we\n", "used. It takes a string and generates a runtime error, using that string\n", "as information about what kind of error occurred. It causes the program\n", "to crash, so it's not good to use it too much. But calling [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head) on an\n", "empty list doesn't make sense.\n", "\n", "Let's make a trivial function that tells us some of the first elements\n", "of the list in (in)convenient English form." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "tell :: (Show a) => [a] -> String\n", "tell [] = \"The list is empty\"\n", "tell (x:[]) = \"The list has one element: \" ++ show x\n", "tell (x:y:[]) = \"The list has two elements: \" ++ show x ++ \" and \" ++ show y\n", "tell (x:y:_) = \"This list is long. The first two elements are: \" ++ show x ++ \" and \" ++ show y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This function is safe because it takes care of the empty list, a\n", "singleton list, a list with two elements and a list with more than two\n", "elements. Note that `(x:[])` and `(x:y:[])` could be rewritten as `[x]` and\n", "`[x,y]` (because its syntactic sugar, we don't need the parentheses). We\n", "can't rewrite `(x:y:_)` with square brackets because it matches any list\n", "of length 2 or more.\n", "\n", "We already implemented our own [`length`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:length) function using list comprehension.\n", "Now we'll do it by using pattern matching and a little recursion:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:", "hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "length' :: (Num b) => [a] -> b\n", "length' [] = 0\n", "length' (_:xs) = 1 + length' xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is similar to the factorial function we wrote earlier. First we\n", "defined the result of a known input — the empty list. This is also known\n", "as the edge condition. Then in the second pattern we take the list apart\n", "by splitting it into a head and a tail. We say that the length is equal\n", "to 1 plus the length of the tail. We use `_` to match the head because we\n", "don't actually care what it is. Also note that we've taken care of all\n", "possible patterns of a list. The first pattern matches an empty list and\n", "the second one matches anything that isn't an empty list.\n", "\n", "Let's see what happens if we call `length'` on `\"ham\"`. First, it will check\n", "if it's an empty list. Because it isn't, it falls through to the second\n", "pattern. It matches on the second pattern and there it says that the\n", "length is `1 + length' \"am\"`, because we broke it into a head and a tail\n", "and discarded the head. O-kay. The `length'` of `\"am\"` is, similarly,\n", "`1 + length' \"m\"`.\n", "So right now we have `1 + (1 + length' \"m\")`. `length' \"m\"` is\n", "`1 + length' \"\"` (could also be written as `1 + length' []`). And we've\n", "defined `length' []` to be `0`. So in the end we have `1 + (1 + (1 + 0))`.\n", "\n", "Let's implement [`sum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sum). We know that the sum of an empty list is 0. We\n", "write that down as a pattern. And we also know that the sum of a list is\n", "the head plus the sum of the rest of the list. So if we write that down,\n", "we get:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:nogutter:nocontrols:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "sum' :: (Num a) => [a] -> a\n", "sum' [] = 0\n", "sum' (x:xs) = x + sum' xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There's also a thing called *as patterns*. Those are a handy way of\n", "breaking something up according to a pattern and binding it to names\n", "whilst still keeping a reference to the whole thing. You do that by\n", "putting a name and an `@` in front of a pattern. For instance, the pattern\n", "`xs@(x:y:ys)`. This pattern will match exactly the same thing as `x:y:ys`\n", "but you can easily get the whole list via `xs` instead of repeating\n", "yourself by typing out `x:y:ys` in the function body again. Here's a quick\n", "and dirty example:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:nogutter:nocontrols:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "capital :: String -> String\n", "capital \"\" = \"Empty string, whoops!\"\n", "capital all@(x:xs) = \"The first letter of \" ++ all ++ \" is \" ++ [x]" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"The first letter of Dracula is D\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "capital \"Dracula\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Normally we use as patterns to avoid repeating ourselves when matching\n", "against a bigger pattern when we have to use the whole thing again in\n", "the function body.\n", "\n", "One more thing — you can't use [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) in pattern matches. If you tried to\n", "pattern match against `(xs ++ ys)`, what would be in the first and what\n", "would be in the second list? It doesn't make much sense. It would make\n", "sense to match stuff against `(xs ++ [x,y,z])` or just `(xs ++ [x])`, but\n", "because of the nature of lists, you can't do that.\n", "\n", "\n", "\n", "Guards, guards!\n", "---------------\n", "\n", "\n", "\n", "Whereas patterns are a way of making sure a value conforms to some form\n", "and deconstructing it, guards are a way of testing whether some property\n", "of a value (or several of them) are true or false. That sounds a lot\n", "like an if statement and it's very similar. The thing is that guards are\n", "a lot more readable when you have several conditions and they play\n", "really nicely with patterns.\n", "\n", "Instead of explaining their syntax, let's just dive in and make a\n", "function using guards. We're going to make a simple function that\n", "berates you differently depending on your\n", "[BMI](http://en.wikipedia.org/wiki/Body_mass_index) (body mass index).\n", "Your BMI equals your weight divided by your height squared. If your BMI\n", "is less than 18.5, you're considered underweight. If it's anywhere from\n", "18.5 to 25 then you're considered normal. 25 to 30 is overweight and\n", "more than 30 is obese. So here's the function (we won't be calculating\n", "it right now, this function just gets a BMI and tells you off)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "bmiTell :: (RealFloat a) => a -> String\n", "bmiTell bmi\n", " | bmi <= 18.5 = \"You're underweight, you emo, you!\"\n", " | bmi <= 25.0 = \"You're supposedly normal. Pffft, I bet you're ugly!\"\n", " | bmi <= 30.0 = \"You're fat! Lose some weight, fatty!\"\n", " | otherwise = \"You're a whale, congratulations!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Guards are indicated by pipes that follow a function's name and its\n", "parameters. Usually, they're indented a bit to the right and lined up. A\n", "guard is basically a boolean expression. If it evaluates to [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), then\n", "the corresponding function body is used. If it evaluates to [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False),\n", "checking drops through to the next guard and so on. If we call this\n", "function with `24.3`, it will first check if that's smaller than or equal\n", "to `18.5`. Because it isn't, it falls through to the next guard. The check\n", "is carried out with the second guard and because 24.3 is less than 25.0,\n", "the second string is returned.\n", "\n", "This is very reminiscent of a big if else tree in imperative languages,\n", "only this is far better and more readable. While big if else trees are\n", "usually frowned upon, sometimes a problem is defined in such a discrete\n", "way that you can't get around them. Guards are a very nice alternative\n", "for this.\n", "\n", "Many times, the last guard is [`otherwise`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:otherwise). [`otherwise`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:otherwise) is defined simply as\n", "`otherwise = True` and catches everything. This is very similar to\n", "patterns, only they check if the input satisfies a pattern but guards\n", "check for boolean conditions. If all the guards of a function evaluate\n", "to [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) (and we haven't provided an [`otherwise`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:otherwise) catch-all guard),\n", "evaluation falls through to the next *pattern*. That's how patterns and\n", "guards play nicely together. If no suitable guards or patterns are\n", "found, an error is thrown.\n", "\n", "Of course we can use guards with functions that take as many parameters\n", "as we want. Instead of having the user calculate his own BMI before\n", "calling the function, let's modify this function so that it takes a\n", "height and weight and calculates it for us." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "bmiTell :: (RealFloat a) => a -> a -> String\n", "bmiTell weight height\n", " | weight / height ^ 2 <= 18.5 = \"You're underweight, you emo, you!\"\n", " | weight / height ^ 2 <= 25.0 = \"You're supposedly normal. Pffft, I bet you're ugly!\"\n", " | weight / height ^ 2 <= 30.0 = \"You're fat! Lose some weight, fatty!\"\n", " | otherwise = \"You're a whale, congratulations!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's see if I'm fat ..." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"You're supposedly normal. Pffft, I bet you're ugly!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "bmiTell 85 1.90" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yay! I'm not fat! But Haskell just called me ugly. Whatever!\n", "\n", "Note that there's no `=` right after the function name and its parameters,\n", "before the first guard. Many newbies get syntax errors because they\n", "sometimes put it there.\n", "\n", "Another very simple example: let's implement our own [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max) function. If\n", "you remember, it takes two things that can be compared and returns the\n", "larger of them." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "max' :: (Ord a) => a -> a -> a\n", "max' a b\n", " | a > b = a\n", " | otherwise = b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Guards can also be written inline, although I'd advise against that\n", "because it's less readable, even for very short functions. But to\n", "demonstrate, we could write `max'` like this:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "max' :: (Ord a) => a -> a -> a\n", "max' a b | a > b = a | otherwise = b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ugh! Not very readable at all! Moving on: let's implement our own\n", "[`compare`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:compare) by using guards." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "myCompare :: (Ord a) => a -> a -> Ordering\n", "a `myCompare` b\n", " | a > b = GT\n", " | a == b = EQ\n", " | otherwise = LT" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "3 `myCompare` 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Note:__ Not only can we call functions as infix with backticks, we can\n", "> also define them using backticks. Sometimes it's easier to read that\n", "> way.\n", "\n", "Where!?\n", "-------\n", "\n", "In the previous section, we defined a BMI calculator function and\n", "berator like this:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "bmiTell :: (RealFloat a) => a -> a -> String\n", "bmiTell weight height\n", " | weight / height ^ 2 <= 18.5 = \"You're underweight, you emo, you!\"\n", " | weight / height ^ 2 <= 25.0 = \"You're supposedly normal. Pffft, I bet you're ugly!\"\n", " | weight / height ^ 2 <= 30.0 = \"You're fat! Lose some weight, fatty!\"\n", " | otherwise = \"You're a whale, congratulations!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that we repeat ourselves here three times. We repeat ourselves\n", "three times. Repeating yourself (three times) while programming is about\n", "as desirable as getting kicked inna head. Since we repeat the same\n", "expression three times, it would be ideal if we could calculate it once,\n", "bind it to a name and then use that name instead of the expression.\n", "Well, we can modify our function like this:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "bmiTell :: (RealFloat a) => a -> a -> String\n", "bmiTell weight height\n", " | bmi <= 18.5 = \"You're underweight, you emo, you!\"\n", " | bmi <= 25.0 = \"You're supposedly normal. Pffft, I bet you're ugly!\"\n", " | bmi <= 30.0 = \"You're fat! Lose some weight, fatty!\"\n", " | otherwise = \"You're a whale, congratulations!\"\n", " where bmi = weight / height ^ 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We put the keyword `where` after the guards (usually it's best to indent\n", "it as much as the pipes are indented) and then we define several names\n", "or functions. These names are visible across the guards and give us the\n", "advantage of not having to repeat ourselves. If we decide that we want\n", "to calculate BMI a bit differently, we only have to change it once. It\n", "also improves readability by giving names to things and can make our\n", "programs faster since stuff like our `bmi` variable here is calculated\n", "only once. We could go a bit overboard and present our function like\n", "this:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "bmiTell :: (RealFloat a) => a -> a -> String\n", "bmiTell weight height\n", " | bmi <= skinny = \"You're underweight, you emo, you!\"\n", " | bmi <= normal = \"You're supposedly normal. Pffft, I bet you're ugly!\"\n", " | bmi <= fat = \"You're fat! Lose some weight, fatty!\"\n", " | otherwise = \"You're a whale, congratulations!\"\n", " where bmi = weight / height ^ 2\n", " skinny = 18.5\n", " normal = 25.0\n", " fat = 30.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The names we define in the where section of a function are only visible\n", "to that function, so we don't have to worry about them polluting the\n", "namespace of other functions. Notice that all the names are aligned at a\n", "single column. If we don't align them nice and proper, Haskell gets\n", "confused because then it doesn't know they're all part of the same\n", "block.\n", "\n", "*where* bindings aren't shared across function bodies of different\n", "patterns. If you want several patterns of one function to access some\n", "shared name, you have to define it globally.\n", "\n", "You can also use where bindings to *pattern match*! We could have\n", "rewritten the where section of our previous function as:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
...\n",
    "    where bmi = weight / height ^ 2\n",
    "          (skinny, normal, fat) = (18.5, 25.0, 30.0)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make another fairly trivial function where we get a first and a\n", "last name and give someone back their initials." ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "initials :: String -> String -> String\n", "initials firstname lastname = [f] ++ \". \" ++ [l] ++ \".\"\n", " where (f:_) = firstname\n", " (l:_) = lastname" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could have done this pattern matching directly in the function's\n", "parameters (it would have been shorter and clearer actually) but this\n", "just goes to show that it's possible to do it in where bindings as well.\n", "\n", "Just like we've defined constants in where blocks, you can also define\n", "functions. Staying true to our healthy programming theme, let's make a\n", "function that takes a list of weight-height pairs and returns a list of\n", "BMIs." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "calcBmis :: (RealFloat a) => [(a, a)] -> [a]\n", "calcBmis xs = [bmi w h | (w, h) <- xs]\n", " where bmi weight height = weight / height ^ 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And that's all there is to it! The reason we had to introduce `bmi` as a\n", "function in this example is because we can't just calculate one BMI from\n", "the function's parameters. We have to examine the list passed to the\n", "function and there's a different BMI for every pair in there.\n", "\n", "*where* bindings can also be nested. It's a common idiom to make a\n", "function and define some helper function in its *where* clause and then\n", "to give those functions helper functions as well, each with its own\n", "*where* clause.\n", "\n", "\n", "\n", "Let it be\n", "---------\n", "\n", "Very similar to where bindings are let bindings. Where bindings are a\n", "syntactic construct that let you bind to variables at the end of a\n", "function and the whole function can see them, including all the guards.\n", "Let bindings let you bind to variables anywhere and are expressions\n", "themselves, but are very local, so they don't span across guards. Just\n", "like any construct in Haskell that is used to bind values to names, let\n", "bindings can be used for pattern matching. Let's see them in action!\n", "This is how we could define a function that gives us a cylinder's\n", "surface area based on its height and radius:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "cylinder :: (RealFloat a) => a -> a -> a\n", "cylinder r h =\n", " let sideArea = 2 * pi * r * h\n", " topArea = pi * r ^2\n", " in sideArea + 2 * topArea" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "The form is `let in `. The names that you\n", "define in the *let* part are accessible to the expression after the *in*\n", "part. As you can see, we could have also defined this with a *where*\n", "binding. Notice that the names are also aligned in a single column. So\n", "what's the difference between the two? For now it just seems that *let*\n", "puts the bindings first and the expression that uses them later whereas\n", "*where* is the other way around.\n", "\n", "The difference is that *let* bindings are expressions themselves.\n", "*where* bindings are just syntactic constructs. Remember when we did the\n", "if statement and it was explained that an if else statement is an\n", "expression and you can cram it in almost anywhere?" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"Woo\",\"Bar\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[if 5 > 3 then \"Woo\" else \"Boo\", if 'a' > 'b' then \"Foo\" else \"Bar\"]" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "42" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "4 * (if 10 > 5 then 10 else 0) + 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also do that with let bindings." ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "42" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "4 * (let a = 9 in a + 1) + 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "They can also be used to introduce functions in a local scope:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(25,9,4)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[let square x = x * x in (square 5, square 3, square 2)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to bind to several variables inline, we obviously can't align\n", "them at columns. That's why we can separate them with semicolons." ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(6000000,\"Hey there!\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(let a = 100; b = 200; c = 300 in a*b*c, let foo=\"Hey \"; bar = \"there!\" in foo ++ bar)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You don't have to put a semicolon after the last binding but you can if\n", "you want. Like we said before, you can pattern match with *let*\n", "bindings. They're very useful for quickly dismantling a tuple into\n", "components and binding them to names and such." ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "600" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(let (a,b,c) = (1,2,3) in a+b+c) * 100" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also put *let* bindings inside list comprehensions. Let's\n", "rewrite our previous example of calculating lists of weight-height pairs\n", "to use a *let* inside a list comprehension instead of defining an\n", "auxiliary function with a *where*." ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "calcBmis :: (RealFloat a) => [(a, a)] -> [a]\n", "calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We include a *let* inside a list comprehension much like we would a\n", "predicate, only it doesn't filter the list, it only binds to names. The\n", "names defined in a *let* inside a list comprehension are visible to the\n", "output function (the part before the |) and all predicates and sections\n", "that come after of the binding. So we could make our function return\n", "only the BMIs of fat people:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "calcBmis :: (RealFloat a) => [(a, a)] -> [a]\n", "calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can't use the `bmi` name in the `(w, h) <- xs` part because it's defined\n", "prior to the *let* binding.\n", "\n", "We omitted the *in* part of the *let* binding when we used them in list\n", "comprehensions because the visibility of the names is already predefined\n", "there. However, we could use a *let in* binding in a predicate and the\n", "names defined would only be visible to that predicate. The *in* part can\n", "also be omitted when defining functions and constants directly in GHCi.\n", "If we do that, then the names will be visible throughout the entire\n", "interactive session." ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let zoot x y z = x * y + z" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "29" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zoot 3 9 2" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "14" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let boot x y z = x * y + z in boot 3 4 2" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ ":1:1: error:\n • Variable not in scope: boot\n • Perhaps you meant ‘zoot’ (line 1)" ] } ], "source": [ "boot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If *let* bindings are so cool, why not use them all the time instead of\n", "*where* bindings, you ask? Well, since *let* bindings are expressions\n", "and are fairly local in their scope, they can't be used across guards.\n", "Some people prefer *where* bindings because the names come after the\n", "function they're being used in. That way, the function body is closer to\n", "its name and type declaration and to some that's more readable.\n", "\n", "Case expressions\n", "----------------\n", "\n", "\n", "\n", "Many imperative languages (C, C++, Java, etc.) have case syntax and if\n", "you've ever programmed in them, you probably know what it's about. It's\n", "about taking a variable and then executing blocks of code for specific\n", "values of that variable and then maybe including a catch-all block of\n", "code in case the variable has some value for which we didn't set up a\n", "case.\n", "\n", "Haskell takes that concept and one-ups it. Like the name implies, case\n", "expressions are, well, expressions, much like if else expressions and\n", "*let* bindings. Not only can we evaluate expressions based on the\n", "possible cases of the value of a variable, we can also do pattern\n", "matching. Hmmm, taking a variable, pattern matching it, evaluating\n", "pieces of code based on its value, where have we heard this before? Oh\n", "yeah, pattern matching on parameters in function definitions! Well,\n", "that's actually just syntactic sugar for case expressions. These two\n", "pieces of code do the same thing and are interchangeable:" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "head' :: [a] -> a\n", "head' [] = error \"No head for empty lists!\"\n", "head' (x:_) = x" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "head' :: [a] -> a\n", "head' xs = case xs of [] -> error \"No head for empty lists!\"\n", " (x:_) -> x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, the syntax for case expressions is pretty simple:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
case expression of pattern -> result\n",
    "                   pattern -> result\n",
    "                   pattern -> result\n",
    "                   ...
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`expression` is matched against the patterns. The pattern matching action\n", "is the same as expected: the first pattern that matches the expression\n", "is used. If it falls through the whole case expression and no suitable\n", "pattern is found, a runtime error occurs.\n", "\n", "Whereas pattern matching on function parameters can only be done when\n", "defining functions, case expressions can be used pretty much anywhere.\n", "For instance:" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "describeList :: [a] -> String\n", "describeList xs = \"The list is \" ++ case xs of [] -> \"empty.\"\n", " [x] -> \"a singleton list.\"\n", " xs -> \"a longer list.\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "They are useful for pattern matching against something in the middle of\n", "an expression. Because pattern matching in function definitions is\n", "syntactic sugar for case expressions, we could have also defined this\n", "like so:" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "describeList :: [a] -> String\n", "describeList xs = \"The list is \" ++ what xs\n", " where what [] = \"empty.\"\n", " what [x] = \"a singleton list.\"\n", " what xs = \"a longer list.\"" ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/05-recursion.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Recursion\n", "=========\n", "\n", "\n", "\n", "Hello recursion!\n", "----------------\n", "\n", "\n", "\n", "We mention recursion briefly in the previous chapter. In this chapter,\n", "we'll take a closer look at recursion, why it's important to Haskell and\n", "how we can work out very concise and elegant solutions to problems by\n", "thinking recursively.\n", "\n", "If you still don't know what recursion is, read this sentence. Haha!\n", "Just kidding! Recursion is actually a way of defining functions in which\n", "the function is applied inside its own definition. Definitions in\n", "mathematics are often given recursively. For instance, the Fibonacci\n", "sequence is defined recursively. First, we define the first two\n", "Fibonacci numbers non-recursively. We say that $F(0) = 0$ and\n", "$F(1) = 1$, meaning that the 0th and 1st Fibonacci numbers are 0 and 1,\n", "respectively. Then we say that for any other natural number, that\n", "Fibonacci number is the sum of the previous two Fibonacci numbers. So\n", "$F(n) = F(n-1) + F(n-2)$. That way, $F(3)$ is $F(2) + F(1)$, which is\n", "$(F(1) + F(0)) + F(1)$. Because we've now come down to only\n", "non-recursively defined Fibonacci numbers, we can safely say that $F(3)$\n", "is 2. Having an element or two in a recursion definition defined\n", "non-recursively (like $F(0)$ and $F(1)$ here) is also called the __edge\n", "condition__ and is important if you want your recursive function to\n", "terminate. If we hadn't defined $F(0)$ and $F(1)$ non recursively, you'd\n", "never get a solution any number because you'd reach 0 and then you'd go\n", "into negative numbers. All of a sudden, you'd be saying that $F(-2000)$\n", "is $F(-2001) + F(-2002)$ and there still wouldn't be an end in sight!\n", "\n", "Recursion is important to Haskell because unlike imperative languages,\n", "you do computations in Haskell by declaring what something *is* instead\n", "of declaring *how* you get it. That's why there are no while loops or\n", "for loops in Haskell and instead we many times have to use recursion to\n", "declare what something is.\n", "\n", "Maximum awesome\n", "---------------\n", "\n", "The [`maximum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:maximum) function takes a list of things that can be ordered (e.g.\n", "instances of the [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) typeclass) and returns the biggest of them. Think\n", "about how you'd implement that in an imperative fashion. You'd probably\n", "set up a variable to hold the maximum value so far and then you'd loop\n", "through the elements of a list and if an element is bigger than then the\n", "current maximum value, you'd replace it with that element. The maximum\n", "value that remains at the end is the result. Whew! That's quite a lot of\n", "words to describe such a simple algorithm!\n", "\n", "Now let's see how we'd define it recursively. We could first set up an\n", "edge condition and say that the maximum of a singleton list is equal to\n", "the only element in it. Then we can say that the maximum of a longer\n", "list is the head if the head is bigger than the maximum of the tail. If\n", "the maximum of the tail is bigger, well, then it's the maximum of the\n", "tail. That's it! Now let's implement that in Haskell." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "maximum' :: (Ord a) => [a] -> a\n", "maximum' [] = error \"maximum of empty list\"\n", "maximum' [x] = x\n", "maximum' (x:xs)\n", " | x > maxTail = x\n", " | otherwise = maxTail\n", " where maxTail = maximum' xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, pattern matching goes great with recursion! Most\n", "imperative languages don't have pattern matching so you have to make a\n", "lot of if else statements to test for edge conditions. Here, we simply\n", "put them out as patterns. So the first edge condition says that if the\n", "list is empty, crash! Makes sense because what's the maximum of an empty\n", "list? I don't know. The second pattern also lays out an edge condition.\n", "It says that if it's the singleton list, just give back the only\n", "element.\n", "\n", "Now the third pattern is where the action happens. We use pattern\n", "matching to split a list into a head and a tail. This is a very common\n", "idiom when doing recursion with lists, so get used to it. We use a\n", "*where* binding to define `maxTail` as the maximum of the rest of the\n", "list. Then we check if the head is greater than the maximum of the rest\n", "of the list. If it is, we return the head. Otherwise, we return the\n", "maximum of the rest of the list.\n", "\n", "Let's take an example list of numbers and check out how this would work\n", "on them: `[2,5,1]`. If we call `maximum'` on that, the first two patterns\n", "won't match. The third one will and the list is split into `2` and `[5,1]`.\n", "The *where* clause wants to know the maximum of `[5,1]`, so we follow that\n", "route. It matches the third pattern again and `[5,1]` is split into `5` and\n", "`[1]`. Again, the `where` clause wants to know the maximum of `[1]`. Because\n", "that's the edge condition, it returns `1`. Finally! So going up one step,\n", "comparing `5` to the maximum of `[1]` (which is `1`), we obviously get back `5`.\n", "So now we know that the maximum of `[5,1]` is `5`. We go up one step again\n", "where we had `2` and `[5,1]`. Comparing `2` with the maximum of `[5,1]`, which\n", "is `5`, we choose `5`.\n", "\n", "An even clearer way to write this function is to use [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max). If you\n", "remember, [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max) is a function that takes two numbers and returns the\n", "bigger of them. Here's how we could rewrite `maximum'` by using [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max):" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "maximum' :: (Ord a) => [a] -> a\n", "maximum' [] = error \"maximum of empty list\"\n", "maximum' [x] = x\n", "maximum' (x:xs) = max x (maximum' xs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How's that for elegant! In essence, the maximum of a list is the max of\n", "the first element and the maximum of the tail.\n", "\n", "\n", "\n", "A few more recursive functions\n", "------------------------------\n", "\n", "Now that we know how to generally think recursively, let's implement a\n", "few functions using recursion. First off, we'll implement [`replicate`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:replicate).\n", "[`replicate`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:replicate) takes an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) and some element and returns a list that has\n", "several repetitions of the same element. For instance, `replicate 3 5`\n", "returns `[5,5,5]`. Let's think about the edge condition. My guess is that\n", "the edge condition is 0 or less. If we try to replicate something zero\n", "times, it should return an empty list. Also for negative numbers,\n", "because it doesn't really make sense." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "replicate' :: (Num i, Ord i) => i -> a -> [a]\n", "replicate' n x\n", " | n <= 0 = []\n", " | otherwise = x:replicate' (n-1) x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We used guards here instead of patterns because we're testing for a\n", "boolean condition. If `n` is less than or equal to 0, return an empty\n", "list. Otherwise return a list that has `x` as the first element and then `x`\n", "replicated n-1 times as the tail. Eventually, the `(n-1)` part will cause\n", "our function to reach the edge condition.\n", "\n", "> __Note:__ [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) is not a subclass of [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord). That means that what constitutes\n", "> for a number doesn't really have to adhere to an ordering. So that's why\n", "> we have to specify both the [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) and [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) class constraints when doing\n", "> addition or subtraction and also comparison.\n", "\n", "Next up, we'll implement [`take`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:take). It takes a certain number of elements\n", "from a list. For instance, `take 3 [5,4,3,2,1]` will return `[5,4,3]`. If we\n", "try to take 0 or less elements from a list, we get an empty list. Also\n", "if we try to take anything from an empty list, we get an empty list.\n", "Notice that those are two edge conditions right there. So let's write\n", "that out:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "take' :: (Num i, Ord i) => i -> [a] -> [a]\n", "take' n _\n", " | n <= 0 = []\n", "take' _ [] = []\n", "take' n (x:xs) = x : take' (n-1) xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "The first pattern specifies that if we try to take a 0 or negative\n", "number of elements, we get an empty list. Notice that we're using `_` to\n", "match the list because we don't really care what it is in this case.\n", "Also notice that we use a guard, but without an [`otherwise`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:otherwise) part. That\n", "means that if `n` turns out to be more than 0, the matching will fall\n", "through to the next pattern. The second pattern indicates that if we try\n", "to take anything from an empty list, we get an empty list. The third\n", "pattern breaks the list into a head and a tail. And then we state that\n", "taking `n` elements from a list equals a list that has `x` as the head and\n", "then a list that takes `n-1` elements from the tail as a tail. Try using a\n", "piece of paper to write down how the evaluation would look like if we\n", "try to take, say, 3 from `[4,3,2,1]`.\n", "\n", "[`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse) simply reverses a list. Think about the edge condition. What is\n", "it? Come on ... it's the empty list! An empty list reversed equals the\n", "empty list itself. O-kay. What about the rest of it? Well, you could say\n", "that if we split a list to a head and a tail, the reversed list is equal\n", "to the reversed tail and then the head at the end." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "reverse' :: [a] -> [a]\n", "reverse' [] = []\n", "reverse' (x:xs) = reverse' xs ++ [x]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There we go!\n", "\n", "Because Haskell supports infinite lists, our recursion doesn't really\n", "have to have an edge condition. But if it doesn't have it, it will\n", "either keep churning at something infinitely or produce an infinite data\n", "structure, like an infinite list. The good thing about infinite lists\n", "though is that we can cut them where we want. [`repeat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:repeat) takes an element\n", "and returns an infinite list that just has that element. A recursive\n", "implementation of that is really easy, watch." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "repeat' :: a -> [a]\n", "repeat' x = x:repeat' x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Calling `repeat 3` will give us a list that starts with `3` and then has an\n", "infinite amount of 3's as a tail. So calling `repeat 3` would evaluate\n", "like `3:repeat 3`, which is `3:(3:repeat 3)`, which is `3:(3:(3:repeat 3))`,\n", "etc. `repeat 3` will never finish evaluating, whereas `take 5 (repeat 3)`\n", "will give us a list of five 3's. So essentially it's like doing\n", "`replicate 5 3`.\n", "\n", "[`zip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zip) takes two lists and zips them together. `zip [1,2,3] [2,3]` returns\n", "`[(1,2),(2,3)]`, because it truncates the longer list to match the length\n", "of the shorter one. How about if we zip something with an empty list?\n", "Well, we get an empty list back then. So there's our edge condition.\n", "However, [`zip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zip) takes two lists as parameters, so there are actually two\n", "edge conditions." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "zip' :: [a] -> [b] -> [(a,b)]\n", "zip' _ [] = []\n", "zip' [] _ = []\n", "zip' (x:xs) (y:ys) = (x,y):zip' xs ys" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First two patterns say that if the first list or second list is empty,\n", "we get an empty list. The third one says that two lists zipped are equal\n", "to pairing up their heads and then tacking on the zipped tails. Zipping\n", "`[1,2,3]` and `['a','b']` will eventually try to zip `[3]` with `[]`. The edge\n", "condition patterns kick in and so the result is `(1,'a'):(2,'b'):[]`,\n", "which is exactly the same as `[(1,'a'),(2,'b')]`.\n", "\n", "Let's implement one more standard library function — [`elem`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem). It takes an\n", "element and a list and sees if that element is in the list. The edge\n", "condition, as is most of the times with lists, is the empty list. We\n", "know that an empty list contains no elements, so it certainly doesn't\n", "have the droids we're looking for." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "elem' :: (Eq a) => a -> [a] -> Bool\n", "elem' a [] = False\n", "elem' a (x:xs)\n", " | a == x = True\n", " | otherwise = a `elem'` xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty simple and expected. If the head isn't the element then we check\n", "the tail. If we reach an empty list, the result is [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False).\n", "\n", "Quick, sort!\n", "------------\n", "\n", "We have a list of items that can be sorted. Their type is an instance of\n", "the [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) typeclass. And now, we want to sort them! There's a very cool\n", "algorithm for sorting called quicksort. It's a very clever way of sorting\n", "items. While it takes upwards of 10 lines to implement quicksort in\n", "imperative languages, the implementation is much shorter and elegant in\n", "Haskell. Quicksort has become a sort of poster child for Haskell.\n", "Therefore, let's implement it here, even though implementing quicksort\n", "in Haskell is considered really cheesy because everyone does it to\n", "showcase how elegant Haskell is.\n", "\n", "\n", "\n", "So, the type signature is going to be\n", "`quicksort :: (Ord a) => [a] -> [a]`.\n", "No surprises there. The edge condition? Empty list, as is expected.\n", "A sorted empty list is an empty list. Now here comes the main algorithm:\n", "__a sorted list is a list that has all the values smaller than (or equal\n", "to) the head of the list in front (and those values are sorted), then\n", "comes the head of the list in the middle and then come all the values\n", "that are bigger than the head (they're also sorted).__ Notice that we\n", "said *sorted* two times in this definition, so we'll probably have to\n", "make the recursive call twice! Also notice that we defined it using the\n", "verb *is* to define the algorithm instead of saying *do this, do that,\n", "then do that ...*. That's the beauty of functional programming! How are\n", "we going to filter the list so that we get only the elements smaller\n", "than the head of our list and only elements that are bigger? List\n", "comprehensions. So, let's dive in and define this function." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "quicksort :: (Ord a) => [a] -> [a]\n", "quicksort [] = []\n", "quicksort (x:xs) =\n", " let smallerSorted = quicksort [a | a <- xs, a <= x]\n", " biggerSorted = quicksort [a | a <- xs, a > x]\n", " in smallerSorted ++ [x] ++ biggerSorted" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's give it a small test run to see if it appears to behave correctly." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,2,3,3,4,4,5,6,7,8,9,10]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "quicksort [10,2,5,3,1,6,7,4,2,3,4,8,9]" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\" abcdeeefghhijklmnoooopqrrsttuuvwxyz\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "quicksort \"the quick brown fox jumps over the lazy dog\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Booyah! That's what I'm talking about! So if we have, say\n", "`[5,1,9,4,6,7,3]` and we want to sort it, this algorithm will first take\n", "the head, which is `5` and then put it in the middle of two lists that are\n", "smaller and bigger than it. So at one point, you'll have\n", "`[1,4,3] ++ [5] ++ [9,6,7]`. We know that once the list is sorted completely,\n", "the number `5` will stay in the fourth place since there are 3 numbers lower\n", "than it and 3 numbers higher than it. Now, if we sort `[1,4,3]` and `[9,6,7]`, we\n", "have a sorted list! We sort the two lists using the same function.\n", "Eventually, we'll break it up so much that we reach empty lists and an\n", "empty list is already sorted in a way, by virtue of being empty. Here's\n", "an illustration:\n", "\n", "\n", "\n", "An element that is in place and won't move anymore is represented in\n", "orange. If you read them from left to right, you'll see the sorted list.\n", "Although we chose to compare all the elements to the heads, we could\n", "have used any element to compare against. In quicksort, an element that\n", "you compare against is called a pivot. They're in green here. We chose\n", "the head because it's easy to get by pattern matching. The elements that\n", "are smaller than the pivot are light green and elements larger than the\n", "pivot are dark green. The yellowish gradient thing represents an\n", "application of quicksort.\n", "\n", "Thinking recursively\n", "--------------------\n", "\n", "We did quite a bit of recursion so far and as you've probably noticed,\n", "there's a pattern here. Usually you define an edge case and then you\n", "define a function that does something between some element and the\n", "function applied to the rest. It doesn't matter if it's a list, a tree\n", "or any other data structure. A sum is the first element of a list plus\n", "the sum of the rest of the list. A product of a list is the first\n", "element of the list times the product of the rest of the list. The\n", "length of a list is one plus the length of the tail of the list.\n", "Ekcetera, ekcetera ...\n", "\n", "\n", "\n", "Of course, these also have edge cases. Usually the edge case is some\n", "scenario where a recursive application doesn't make sense. When dealing\n", "with lists, the edge case is most often the empty list. If you're\n", "dealing with trees, the edge case is usually a node that doesn't have\n", "any children.\n", "\n", "It's similar when you're dealing with numbers recursively. Usually it\n", "has to do with some number and the function applied to that number\n", "modified. We did the factorial function earlier and it's the product of\n", "a number and the factorial of that number minus one. Such a recursive\n", "application doesn't make sense with zero, because factorials are defined\n", "only for positive integers. Often the edge case value turns out to be an\n", "identity. The identity for multiplication is 1 because if you multiply\n", "something by 1, you get that something back. Also when doing sums of\n", "lists, we define the sum of an empty list as 0 and 0 is the identity for\n", "addition. In quicksort, the edge case is the empty list and the identity\n", "is also the empty list, because if you add an empty list to a list, you\n", "just get the original list back.\n", "\n", "So when trying to think of a recursive way to solve a problem, try to\n", "think of when a recursive solution doesn't apply and see if you can use\n", "that as an edge case, think about identities and think about whether\n", "you'll break apart the parameters of the function (for instance, lists\n", "are usually broken into a head and a tail via pattern matching) and on\n", "which part you'll use the recursive call." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/06-higher-order-functions.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Higher order functions\n", "======================\n", "\n", "\n", "\n", "Haskell functions can take functions as parameters and return functions\n", "as return values. A function that does either of those is called a\n", "higher order function. Higher order functions aren't just a part of the\n", "Haskell experience, they pretty much are the Haskell experience. It\n", "turns out that if you want to define computations by defining what stuff\n", "*is* instead of defining steps that change some state and maybe looping\n", "them, higher order functions are indispensable. They're a really\n", "powerful way of solving problems and thinking about programs." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Curried functions\n", "-----------------\n", "\n", "Every function in Haskell officially only takes one parameter. So how is\n", "it possible that we defined and used several functions that take more\n", "than one parameter so far? Well, it's a clever trick! All the functions\n", "that accepted *several parameters* so far have been __curried functions__.\n", "What does that mean? You'll understand it best on an example. Let's take\n", "our good friend, the [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max) function. It looks like it takes two parameters\n", "and returns the one that's bigger. Doing `max 4 5` first creates a\n", "function that takes a parameter and returns either `4` or that parameter,\n", "depending on which is bigger. Then, `5` is applied to that function and\n", "that function produces our desired result. That sounds like a mouthful\n", "but it's actually a really cool concept. The following two calls are\n", "equivalent:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "max 4 5" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(max 4) 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Putting a space between two things is simply **function application**.\n", "The space is sort of like an operator and it has the highest precedence.\n", "Let's examine the type of [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max). It's `max :: (Ord a) => a -> a -> a`.\n", "That can also be written as `max :: (Ord a) => a -> (a -> a)`. That\n", "could be read as: [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max) takes an `a` and returns (that's the `->`)\n", "a function that takes an `a` and returns an `a`.\n", "That's why the return type and the\n", "parameters of functions are all simply separated with arrows.\n", "\n", "So how is that beneficial to us? Simply speaking, if we call a function\n", "with too few parameters, we get back a __partially applied__ function,\n", "meaning a function that takes as many parameters as we left out. Using\n", "partial application (calling functions with too few parameters, if you\n", "will) is a neat way to create functions on the fly so we can pass them\n", "to another function or to seed them with some data.\n", "\n", "Take a look at this offensively simple function:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "multThree :: (Num a) => a -> a -> a -> a\n", "multThree x y z = x * y * z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What really happens when we do `multThree 3 5 9` or `((multThree 3) 5) 9`?\n", "First, `3` is applied to `multThree`, because they're separated by a space.\n", "That creates a function that takes one parameter and returns a function.\n", "So then `5` is applied to that, which creates a function that will take a\n", "parameter and multiply it by 15. `9` is applied to that function and the\n", "result is 135 or something. Remember that this function's type could\n", "also be written as `multThree :: (Num a) => a -> (a -> (a -> a))`. The\n", "thing before the `->` is the parameter that a function takes and the\n", "thing after it is what it returns. So our function takes an `a` and\n", "returns a function of type `(Num a) => a -> (a -> a)`. Similarly, this\n", "function takes an `a` and returns a function of type `(Num a) => a -> a`.\n", "And this function, finally, just takes an `a` and returns an `a`. Take a\n", "look at this:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "54" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let multTwoWithNine = multThree 9\n", "multTwoWithNine 2 3" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "180" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let multWithEighteen = multTwoWithNine 2\n", "multWithEighteen 10" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By calling functions with too few parameters, so to speak, we're\n", "creating new functions on the fly. What if we wanted to create a\n", "function that takes a number and compares it to `100`? We could do\n", "something like this:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "compareWithHundred :: (Num a, Ord a) => a -> Ordering\n", "compareWithHundred x = compare 100 x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we call it with `99`, it returns a [`GT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:GT). Simple stuff. Notice that the `x`\n", "is on the right hand side on both sides of the equation. Now let's think\n", "about what `compare 100` returns. It returns a function that takes a\n", "number and compares it with `100`. Wow! Isn't that the function we wanted?\n", "We can rewrite this as:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "compareWithHundred :: (Num a, Ord a) => a -> Ordering\n", "compareWithHundred = compare 100" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The type declaration stays the same, because `compare 100` returns a\n", "function. Compare has a type of `(Ord a) => a -> (a -> Ordering)` and\n", "calling it with `100` returns a `(Num a, Ord a) => a -> Ordering`. The\n", "additional class constraint sneaks up there because `100` is also part of\n", "the [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) typeclass.\n", "\n", "> __Yo!__ Make sure you really understand how curried functions and partial\n", "> application work because they're really important!\n", "\n", "Infix functions can also be partially applied by using sections. To\n", "section an infix function, simply surround it with parentheses and only\n", "supply a parameter on one side. That creates a function that takes one\n", "parameter and then applies it to the side that's missing an operand. An\n", "insultingly trivial function:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "divideByTen :: (Floating a) => a -> a\n", "divideByTen = (/10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Calling, say, `divideByTen 200` is equivalent to doing `200 / 10`, as is\n", "doing `(/10) 200`. A function that checks if a character supplied to it is\n", "an uppercase letter:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "isUpperAlphanum :: Char -> Bool\n", "isUpperAlphanum = (`elem` ['A'..'Z'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The only special thing about sections is using [`-`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-45-). From the definition of\n", "sections, `(-4)` would result in a function that takes a number and\n", "subtracts 4 from it. However, for convenience, `(-4)` means minus four. So\n", "if you want to make a function that subtracts 4 from the number it gets\n", "as a parameter, partially apply the [`subtract`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:subtract) function like so:\n", "`(subtract 4)`.\n", "\n", "What happens if we try to just do `multThree 3 4` instead of\n", "binding it to a name with a *let* or passing it to another function?" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ ":1:1: error:\n • No instance for (Show (Integer -> Integer)) arising from a use of ‘print’\n (maybe you haven't applied a function to enough arguments?)\n • In a stmt of an interactive GHCi command: print it" ] } ], "source": [ "multThree 3 4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "GHC is telling us that the expression produced a function of type\n", "`a -> a`\n", "but it doesn't know how to print it to the screen. Functions aren't\n", "instances of the [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) typeclass, so we can't get a neat string\n", "representation of a function. When we do, say, `1 + 1` at the GHC prompt,\n", "it first calculates that to `2` and then calls [`show`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:show) on `2` to get a textual\n", "representation of that number. And the textual representation of `2` is\n", "just the string `\"2\"`, which then gets printed to our screen.\n", "\n", "Some higher-orderism is in order\n", "--------------------------------\n", "\n", "Functions can take functions as parameters and also return functions. To\n", "illustrate this, we're going to make a function that takes a function\n", "and then applies it twice to something!" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "applyTwice :: (a -> a) -> a -> a\n", "applyTwice f x = f (f x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "First of all, notice the type declaration. Before, we didn't need\n", "parentheses because `->` is naturally right-associative. However, here,\n", "they're mandatory. They indicate that the first parameter is a function\n", "that takes something and returns that same thing. The second parameter\n", "is something of that type also and the return value is also of the same\n", "type. We could read this type declaration in the curried way, but to\n", "save ourselves a headache, we'll just say that this function takes two\n", "parameters and returns one thing. The first parameter is a function (of\n", "type `a -> a`) and the second is that same `a`. The function can also be\n", "`Int -> Int` or `String -> String` or whatever. But then, the second\n", "parameter to also has to be of that type.\n", "\n", "> __Note:__ From now on, we'll say that functions take several parameters\n", "> despite each function actually taking only one parameter and returning\n", "> partially applied functions until we reach a function that returns a\n", "> solid value. So for simplicity's sake, we'll say that `a -> a -> a`\n", "> takes two parameters, even though we know what's really going on under\n", "> the hood.\n", "\n", "The body of the function is pretty simple. We just use the parameter `f`\n", "as a function, applying `x` to it by separating them with a space and then\n", "applying the result to `f` again. Anyway, playing around with the\n", "function:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "16" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "applyTwice (+3) 10" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"HEY HAHA HAHA\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "applyTwice (++ \" HAHA\") \"HEY\"" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"HAHA HAHA HEY\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "applyTwice (\"HAHA \" ++) \"HEY\"" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "144" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "applyTwice (multThree 2 2) 9" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[3,3,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "applyTwice (3:) [1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The awesomeness and usefulness of partial application is evident. If our\n", "function requires us to pass it a function that takes only one\n", "parameter, we can just partially apply a function to the point where it\n", "takes only one parameter and then pass it.\n", "\n", "Now we're going to use higher order programming to implement a really\n", "useful function that's in the standard library. It's called [`zipWith`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zipWith). It\n", "takes a function and two lists as parameters and then joins the two\n", "lists by applying the function between corresponding elements. Here's\n", "how we'll implement it:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]\n", "zipWith' _ [] _ = []\n", "zipWith' _ _ [] = []\n", "zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Look at the type declaration. The first parameter is a function that\n", "takes two things and produces a third thing. They don't have to be of\n", "the same type, but they can. The second and third parameter are lists.\n", "The result is also a list. The first has to be a list of `a`'s, because\n", "the joining function takes `a`'s as its first argument. The second has to\n", "be a list of `b`'s, because the second parameter of the joining function\n", "is of type `b`. The result is a list of `c`'s. If the type declaration of a\n", "function says it accepts an `a -> b -> c` function as a parameter, it\n", "will also accept an `a -> a -> a` function, but not the other way\n", "around! Remember that when you're making functions, especially higher\n", "order ones, and you're unsure of the type, you can just try omitting the\n", "type declaration and then checking what Haskell infers it to be by using\n", "`:t`.\n", "\n", "The action in the function is pretty similar to the normal [`zip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zip). The edge\n", "conditions are the same, only there's an extra argument, the joining\n", "function, but that argument doesn't matter in the edge conditions, so we\n", "just use a `_` for it. And function body at the last pattern is also\n", "similar to [`zip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zip), only it doesn't do `(x,y)`, but `f x y`. A single higher\n", "order function can be used for a multitude of different tasks if it's\n", "general enough. Here's a little demonstration of all the different\n", "things our `zipWith'` function can do:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[6,8,7,9]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zipWith' (+) [4,2,5,6] [2,6,2,3]" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[7,3,2,5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zipWith' max [6,3,2,1] [7,3,1,5]" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"foo fighters\",\"bar hoppers\",\"baz aldrin\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zipWith' (++) [\"foo \", \"bar \", \"baz \"] [\"fighters\", \"hoppers\", \"aldrin\"]" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[2,4,6,8,10]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zipWith' (*) (replicate 5 2) [1..]" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[3,4,6],[9,20,30],[10,12,12]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zipWith' (zipWith' (*)) [[1,2,3],[3,5,6],[2,3,4]] [[3,2,2],[3,4,5],[5,4,3]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, a single higher order function can be used in very\n", "versatile ways. Imperative programming usually uses stuff like for\n", "loops, while loops, setting something to a variable, checking its state,\n", "etc. to achieve some behavior and then wrap it around an interface, like\n", "a function. Functional programming uses higher order functions to\n", "abstract away common patterns, like examining two lists in pairs and\n", "doing something with those pairs or getting a set of solutions and\n", "eliminating the ones you don't need.\n", "\n", "We'll implement another function that's already in the standard library,\n", "called [`flip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:flip). Flip simply takes a function and returns a function that is\n", "like our original function, only the first two arguments are flipped. We\n", "can implement it like so:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "flip' :: (a -> b -> c) -> (b -> a -> c)\n", "flip' f = g\n", " where g x y = f y x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Reading the type declaration, we say that it takes a function that takes\n", "an `a` and a `b` and returns a function that takes a `b` and an `a`. But because\n", "functions are curried by default, the second pair of parentheses is\n", "really unnecessary, because `->` is right associative by default.\n", "`(a -> b -> c) -> (b -> a -> c)` is the same as\n", "`(a -> b -> c) -> (b -> (a -> c))`,\n", "which is the same as `(a -> b -> c) -> b -> a -> c`. We\n", "wrote that `g x y = f y x`. If that's true, then `f y x = g x y` must also\n", "hold, right? Keeping that in mind, we can define this function in an\n", "even simpler manner." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "flip' :: (a -> b -> c) -> b -> a -> c\n", "flip' f y x = f x y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we take advantage of the fact that functions are curried. When we\n", "call `flip' f` without the parameters `y` and `x`, it will return an `f` that\n", "takes those two parameters but calls them flipped. Even though flipped\n", "functions are usually passed to other functions, we can take advantage\n", "of currying when making higher-order functions by thinking ahead and\n", "writing what their end result would be if they were called fully\n", "applied." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[('h',1),('e',2),('l',3),('l',4),('o',5)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "flip' zip [1,2,3,4,5] \"hello\"" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[5,4,3,2,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zipWith (flip' div) [2,2..] [10,8,6,4,2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Maps and filters\n", "----------------\n", "\n", "__[`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map)__ takes a function and a list and applies that function to every\n", "element in the list, producing a new list. Let's see what its type\n", "signature is and how it's defined." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
map :: (a -> b) -> [a] -> [b]\n",
    "map _ [] = []\n",
    "map f (x:xs) = f x : map f xs
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The type signature says that it takes a function that takes an `a` and\n", "returns a `b`, a list of `a`'s and returns a list of `b`'s. It's interesting\n", "that just by looking at a function's type signature, you can sometimes\n", "tell what it does. [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map) is one of those really versatile higher-order\n", "functions that can be used in millions of different ways. Here it is in\n", "action:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[4,8,6,4,9]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (+3) [1,5,3,1,6]" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"BIFF!\",\"BANG!\",\"POW!\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (++ \"!\") [\"BIFF\", \"BANG\", \"POW\"]" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[3,3,3],[4,4,4],[5,5,5],[6,6,6]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (replicate 3) [3..6]" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,4],[9,16,25,36],[49,64]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (map (^2)) [[1,2],[3,4,5,6],[7,8]]" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,3,6,2,2]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map fst [(1,2),(3,5),(6,3),(2,6),(2,5)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You've probably noticed that each of these could be achieved with a list\n", "comprehension. `map (+3) [1,5,3,1,6]` is the same as writing\n", "`[x+3 | x <- [1,5,3,1,6]]`.\n", "However, using [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map) is much more readable for cases where\n", "you only apply some function to the elements of a list, especially once\n", "you're dealing with maps of maps and then the whole thing with a lot of\n", "brackets can get a bit messy.\n", "\n", "__[`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter)__ is a function that takes a predicate (a predicate is a function\n", "that tells whether something is true or not, so in our case, a function\n", "that returns a boolean value) and a list and then returns the list of\n", "elements that satisfy the predicate. The type signature and\n", "implementation go like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
filter :: (a -> Bool) -> [a] -> [a]\n",
    "filter _ [] = []\n",
    "filter p (x:xs)\n",
    "    | p x       = x : filter p xs\n",
    "    | otherwise = filter p xs
"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pretty simple stuff. If `p x` evaluates to [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), the element gets included\n",
    "in the new list. If it doesn't, it stays out. Some usage examples:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[5,6,4]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "filter (>3) [1,5,3,2,1,6,4,3,2,1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[3]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "filter (==3) [1,2,3,4,5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2,4,6,8,10]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "filter even [1..10]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[1,2,3],[3,4,5],[2,2]]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "let notNull x = not (null x) in filter notNull [[1,2,3],[],[3,4,5],[2,2],[],[],[]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"uagameasadifeent\""
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "filter (`elem` ['a'..'z']) \"u LaUgH aT mE BeCaUsE I aM diFfeRent\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"GAYBALLS\""
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "filter (`elem` ['A'..'Z']) \"i lauGh At You BecAuse u r aLL the Same\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All of this could also be achieved with list comprehensions by the use of\n",
    "predicates. There's no set rule for when to use [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map) and [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) versus\n",
    "using list comprehension, you just have to decide what's more readable\n",
    "depending on the code and the context. The [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) equivalent of applying\n",
    "several predicates in a list comprehension is either filtering something\n",
    "several times or joining the predicates with the logical [`&&`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-38--38-) function.\n",
    "\n",
    "Remember our quicksort function from the [previous chapter](http://learnyouahaskell.com/recursion)?\n",
    "We used list comprehensions to filter out the list elements that are\n",
    "smaller than (or equal to) and larger than the pivot. We can achieve the\n",
    "same functionality in a more readable way by using [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "quicksort :: (Ord a) => [a] -> [a]\n",
    "quicksort [] = []\n",
    "quicksort (x:xs) =\n",
    "    let smallerSorted = quicksort (filter (<=x) xs)\n",
    "        biggerSorted = quicksort (filter (>x) xs)\n",
    "    in  smallerSorted ++ [x] ++ biggerSorted"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "Mapping and filtering is the bread and butter of every functional\n",
    "programmer's toolbox. Uh. It doesn't matter if you do it with the [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map)\n",
    "and [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) functions or list comprehensions. Recall how we solved the\n",
    "problem of finding right triangles with a certain circumference. With\n",
    "imperative programming, we would have solved it by nesting three loops\n",
    "and then testing if the current combination satisfies a right triangle\n",
    "and if it has the right perimeter. If that's the case, we would have\n",
    "printed it out to the screen or something. In functional programming,\n",
    "that pattern is achieved with mapping and filtering. You make a function\n",
    "that takes a value and produces some result. We map that function over a\n",
    "list of values and then we filter the resulting list out for the results\n",
    "that satisfy our search. Thanks to Haskell's laziness, even if you map\n",
    "something over a list several times and filter it several times, it will\n",
    "only pass over the list once.\n",
    "\n",
    "Let's __find the largest number under 100,000 that's divisible by 3829__.\n",
    "To do that, we'll just filter a set of possibilities in which we know\n",
    "the solution lies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "largestDivisible :: (Integral a) => a\n",
    "largestDivisible = head (filter p [100000,99999..])\n",
    "    where p x = x `mod` 3829 == 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We first make a list of all numbers lower than 100,000, descending. Then\n",
    "we filter it by our predicate and because the numbers are sorted in a\n",
    "descending manner, the largest number that satisfies our predicate is\n",
    "the first element of the filtered list. We didn't even need to use a\n",
    "finite list for our starting set. That's laziness in action again.\n",
    "Because we only end up using the head of the filtered list, it doesn't\n",
    "matter if the filtered list is finite or infinite. The evaluation stops\n",
    "when the first adequate solution is found.\n",
    "\n",
    "Next up, we're going to __find the sum of all odd squares that are\n",
    "smaller than 10,000__. But first, because we'll be using it in our\n",
    "solution, we're going to introduce the __[`takeWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:takeWhile)__ function. It takes a\n",
    "predicate and a list and then goes from the beginning of the list and\n",
    "returns its elements while the predicate holds true. Once an element is\n",
    "found for which the predicate doesn't hold, it stops. If we wanted to\n",
    "get the first word of the string `\"elephants know how to party\"`, we could\n",
    "do `takeWhile (/=' ') \"elephants know how to party\"` and it would return\n",
    "`\"elephants\"`. Okay. The sum of all odd squares that are smaller than\n",
    "10,000. First, we'll begin by mapping the `(^2)` function to the infinite\n",
    "list `[1..]`. Then we filter them so we only get the odd ones. And then,\n",
    "we'll take elements from that list while they are smaller than 10,000.\n",
    "Finally, we'll get the sum of that list. We don't even have to define a\n",
    "function for that, we can do it in one line in GHCI:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "166650"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sum (takeWhile (<10000) (filter odd (map (^2) [1..])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Awesome! We start with some initial data (the infinite list of all\n",
    "natural numbers) and then we map over it, filter it and cut it until it\n",
    "suits our needs and then we just sum it up. We could have also written\n",
    "this using list comprehensions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "166650"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sum (takeWhile (<10000) [n^2 | n <- [1..], odd (n^2)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's a matter of taste as to which one you find prettier. Again,\n",
    "Haskell's property of laziness is what makes this possible. We can map\n",
    "over and filter an infinite list, because it won't actually map and\n",
    "filter it right away, it'll delay those actions. Only when we force\n",
    "Haskell to show us the [`sum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sum) does the sum function say to the [`takeWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:takeWhile)\n",
    "that it needs those numbers. [`takeWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:takeWhile) forces the filtering and mapping\n",
    "to occur, but only until a number greater than or equal to 10,000 is\n",
    "encountered.\n",
    "\n",
    "For our next problem, we'll be dealing with Collatz sequences. We take a\n",
    "natural number. If that number is even, we divide it by two. If it's\n",
    "odd, we multiply it by 3 and then add 1 to that. We take the resulting\n",
    "number and apply the same thing to it, which produces a new number and\n",
    "so on. In essence, we get a chain of numbers. It is thought that for all\n",
    "starting numbers, the chains finish at the number 1. So if we take the\n",
    "starting number 13, we get this sequence: *13, 40, 20, 10, 5, 16, 8, 4,\n",
    "2, 1*. 13\\*3 + 1 equals 40. 40 divided by 2 is 20, etc. We see that the\n",
    "chain has 10 terms.\n",
    "\n",
    "Now what we want to know is this: __for all starting numbers between 1\n",
    "and 100, how many chains have a length greater than 15?__ First off,\n",
    "we'll write a function that produces a chain:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "chain :: (Integral a) => a -> [a]\n",
    "chain 1 = [1]\n",
    "chain n\n",
    "    | even n =  n:chain (n `div` 2)\n",
    "    | odd n  =  n:chain (n*3 + 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because the chains end at 1, that's the edge case. This is a pretty\n",
    "standard recursive function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[10,5,16,8,4,2,1]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "chain 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "chain 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[30,15,46,23,70,35,106,53,160,80,40,20,10,5,16,8,4,2,1]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "chain 30"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Yay! It seems to be working correctly. And now, the function that tells\n",
    "us the answer to our question:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "numLongChains :: Int\n",
    "numLongChains = length (filter isLong (map chain [1..100]))\n",
    "    where isLong xs = length xs > 15"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "66"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "numLongChains"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We map the `chain` function to `[1..100]` to get a list of chains, which are\n",
    "themselves represented as lists. Then, we filter them by a predicate\n",
    "that just checks whether a list's length is longer than 15. Once we've\n",
    "done the filtering, we see how many chains are left in the resulting\n",
    "list.\n",
    "\n",
    "> __Note:__ This function has a type of `numLongChains :: Int` because [`length`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:length)\n",
    "> returns an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) instead of a `Num a` for historical reasons. If we wanted\n",
    "> to return a more general `Num a`, we could have used [`fromIntegral`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fromIntegral) on the\n",
    "> resulting length.\n",
    "\n",
    "Using [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map), we can also do stuff like `map (*) [0..]`, if not for any\n",
    "other reason than to illustrate how currying works and how (partially\n",
    "applied) functions are real values that you can pass around to other\n",
    "functions or put into lists (you just can't turn them to strings). So\n",
    "far, we've only mapped functions that take one parameter over lists,\n",
    "like `map (*2) [0..]` to get a list of type `(Num a) => [a]`, but we can\n",
    "also do `map (*) [0..]` without a problem. What happens here is that the\n",
    "number in the list is applied to the function [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-), which has a type of\n",
    "`(Num a) => a -> a -> a`. Applying only one parameter to a function\n",
    "that takes two parameters returns a function that takes one parameter.\n",
    "If we map [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) over the list `[0..]`, we get back a list of functions that\n",
    "only take one parameter, so `(Num a) => [a -> a]`. `map (*) [0..]`\n",
    "produces a list like the one we'd get by writing\n",
    "`[(0*),(1*),(2*),(3*),(4*),(5*)...`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "20"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "let listOfFuns = map (*) [0..]\n",
    "(listOfFuns !! 4) 5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Getting the element with the index `4` from our list returns a function\n",
    "that's equivalent to `(4*)`. And then, we just apply `5` to that function.\n",
    "So that's like writing `(4*) 5` or just `4 * 5`.\n",
    "\n",
    "Lambdas\n",
    "-------\n",
    "\n",
    "\n",
    "\n",
    "Lambdas are basically anonymous functions that are used because we need\n",
    "some functions only once. Normally, we make a lambda with the sole\n",
    "purpose of passing it to a higher-order function. To make a lambda, we\n",
    "write a `\\` (because it kind of looks like the Greek letter lambda if you\n",
    "squint hard enough) and then we write the parameters, separated by\n",
    "spaces. After that comes a `->` and then the function body. We usually\n",
    "surround them by parentheses, because otherwise they extend all the way\n",
    "to the right.\n",
    "\n",
    "If you look about 5 inches up, you'll see that we used a *where* binding\n",
    "in our `numLongChains` function to make the `isLong` function for the sole\n",
    "purpose of passing it to [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter). Well, instead of doing that, we can use\n",
    "a lambda:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "numLongChains :: Int\n",
    "numLongChains = length (filter (\\xs -> length xs > 15) (map chain [1..100]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lambdas are expressions, that's why we can just pass them like that. The\n",
    "expression `(\\xs -> length xs > 15)` returns a function that tells us\n",
    "whether the length of the list passed to it is greater than 15.\n",
    "\n",
    "\n",
    "\n",
    "People who are not well acquainted with how currying and partial\n",
    "application works often use lambdas where they don't need to. For\n",
    "instance, the expressions `map (+3) [1,6,3,2]` and\n",
    "`map (\\x -> x + 3) [1,6,3,2]`\n",
    "are equivalent since both `(+3)` and `(\\x -> x + 3)` are\n",
    "functions that take a number and add 3 to it. Needless to say, making a\n",
    "lambda in this case is stupid since using partial application is much\n",
    "more readable.\n",
    "\n",
    "Like normal functions, lambdas can take any number of parameters:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[153.0,61.5,31.0,15.75,6.6]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "zipWith (\\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And like normal functions, you can pattern match in lambdas. The only\n",
    "difference is that you can't define several patterns for one parameter,\n",
    "like making a `[]` and a `(x:xs)` pattern for the same parameter and then\n",
    "having values fall through. If a pattern matching fails in a lambda, a\n",
    "runtime error occurs, so be careful when pattern matching in lambdas!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[3,8,9,8,7]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "map (\\(a,b) -> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lambdas are normally surrounded by parentheses unless we mean for them\n",
    "to extend all the way to the right. Here's something interesting: due to\n",
    "the way functions are curried by default, these two are equivalent:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "addThree :: (Num a) => a -> a -> a -> a\n",
    "addThree x y z = x + y + z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "addThree :: (Num a) => a -> a -> a -> a\n",
    "addThree = \\x -> \\y -> \\z -> x + y + z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we define a function like this, it's obvious why the type declaration\n",
    "is what it is. There are three `->`'s in both the type declaration and\n",
    "the equation. But of course, the first way to write functions is far\n",
    "more readable, the second one is pretty much a gimmick to illustrate\n",
    "currying.\n",
    "\n",
    "However, there are times when using this notation is cool. I think that\n",
    "the [`flip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:flip) function is the most readable when defined like so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "flip' :: (a -> b -> c) -> b -> a -> c\n",
    "flip' f = \\x y -> f y x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Even though that's the same as writing `flip' f x y = f y x`, we make it\n",
    "obvious that this will be used for producing a new function most of the\n",
    "time. The most common use case with [`flip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:flip) is calling it with just the\n",
    "function parameter and then passing the resulting function on to a map\n",
    "or a filter. So use lambdas in this way when you want to make it\n",
    "explicit that your function is mainly meant to be partially applied and\n",
    "passed on to a function as a parameter.\n",
    "\n",
    "\n",
    "\n",
    "Only folds and horses\n",
    "---------------------\n",
    "\n",
    "\n",
    "\n",
    "Back when we were dealing with recursion, we noticed a theme throughout\n",
    "many of the recursive functions that operated on lists. Usually, we'd\n",
    "have an edge case for the empty list. We'd introduce the `x:xs` pattern\n",
    "and then we'd do some action that involves a single element and the rest\n",
    "of the list. It turns out this is a very common pattern, so a couple of\n",
    "very useful functions were introduced to encapsulate it. These functions\n",
    "are called folds. They're sort of like the [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map) function, only they\n",
    "reduce the list to some single value.\n",
    "\n",
    "A fold takes a binary function, a starting value (I like to call it the\n",
    "accumulator) and a list to fold up. The binary function itself takes two\n",
    "parameters. The binary function is called with the accumulator and the\n",
    "first (or last) element and produces a new accumulator. Then, the binary\n",
    "function is called again with the new accumulator and the now new first\n",
    "(or last) element, and so on. Once we've walked over the whole list,\n",
    "only the accumulator remains, which is what we've reduced the list to.\n",
    "\n",
    "First let's take a look at the __[`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl)__ function, also called the left\n",
    "fold. It folds the list up from the left side. The binary function is\n",
    "applied between the starting value and the head of the list. That\n",
    "produces a new accumulator value and the binary function is called with\n",
    "that value and the next element, etc.\n",
    "\n",
    "Let's implement [`sum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sum) again, only this time, we'll use a fold instead of\n",
    "explicit recursion."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "sum' :: (Num a) => [a] -> a\n",
    "sum' xs = foldl (\\acc x -> acc + x) 0 xs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Testing, one two three:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "11"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sum' [3,5,2,1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "Let's take an in-depth look into how this fold happens.\n",
    "`\\acc x -> acc + x` is the binary function.\n",
    "`0` is the starting value and `xs` is the list\n",
    "to be folded up. Now first, `0` is used as the `acc` parameter to the binary\n",
    "function and `3` is used as the `x` (or the current element) parameter.\n",
    "`0 + 3` produces a `3` and it becomes the new accumulator value, so to speak.\n",
    "Next up, `3` is used as the accumulator value and `5` as the current element\n",
    "and `8` becomes the new accumulator value. Moving forward, `8` is the\n",
    "accumulator value, `2` is the current element, the new accumulator value\n",
    "is `10`. Finally, that `10` is used as the accumulator value and `1` as the\n",
    "current element, producing an `11`. Congratulations, you've done a fold!\n",
    "\n",
    "This professional diagram on the left illustrates how a fold happens,\n",
    "step by step (day by day!). The greenish brown number is the accumulator\n",
    "value. You can see how the list is sort of consumed up from the left\n",
    "side by the accumulator. Om nom nom nom! If we take into account that\n",
    "functions are curried, we can write this implementation ever more\n",
    "succinctly, like so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "sum' :: (Num a) => [a] -> a\n",
    "sum' = foldl (+) 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The lambda function `(\\acc x -> acc + x)` is the same as `(+)`. We can\n",
    "omit the `xs` as the parameter because calling `foldl (+) 0` will return a\n",
    "function that takes a list. Generally, if you have a function like\n",
    "`foo a = bar b a`, you can rewrite it as `foo = bar b`, because of currying.\n",
    "\n",
    "Anyhoo, let's implement another function with a left fold before moving\n",
    "on to right folds. I'm sure you all know that [`elem`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem) checks whether a\n",
    "value is part of a list so I won't go into that again (whoops, just\n",
    "did!). Let's implement it with a left fold."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "elem' :: (Eq a) => a -> [a] -> Bool\n",
    "elem' y ys = foldl (\\acc x -> if x == y then True else acc) False ys"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Well, well, well, what do we have here? The starting value and\n",
    "accumulator here is a boolean value. The type of the accumulator value\n",
    "and the end result is always the same when dealing with folds. Remember\n",
    "that if you ever don't know what to use as a starting value, it'll give\n",
    "you some idea. We start off with [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False). It makes sense to use [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) as a\n",
    "starting value. We assume it isn't there. Also, if we call a fold on an\n",
    "empty list, the result will just be the starting value. Then we check\n",
    "the current element is the element we're looking for. If it is, we set\n",
    "the accumulator to [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True). If it's not, we just leave the accumulator\n",
    "unchanged. If it was [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) before, it stays that way because this\n",
    "current element is not it. If it was [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), we leave it at that.\n",
    "\n",
    "The right fold, __[`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr)__ works in a similar way to the left fold, only the\n",
    "accumulator eats up the values from the right. Also, the left fold's\n",
    "binary function has the accumulator as the first parameter and the\n",
    "current value as the second one (so `\\acc x -> ...`), the right fold's\n",
    "binary function has the current value as the first parameter and the\n",
    "accumulator as the second one (so `\\x acc -> ...`). It kind of makes\n",
    "sense that the right fold has the accumulator on the right, because it\n",
    "folds from the right side.\n",
    "\n",
    "The accumulator value (and hence, the result) of a fold can be of any\n",
    "type. It can be a number, a boolean or even a new list. We'll be\n",
    "implementing the map function with a right fold. The accumulator will be\n",
    "a list, we'll be accumulating the mapped list element by element. From\n",
    "that, it's obvious that the starting element will be an empty list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "map' :: (a -> b) -> [a] -> [b]\n",
    "map' f xs = foldr (\\x acc -> f x : acc) [] xs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we're mapping `(+3)` to `[1,2,3]`, we approach the list from the right\n",
    "side. We take the last element, which is `3` and apply the function to it,\n",
    "which ends up being `6`. Then, we prepend it to the accumulator, which is\n",
    "was `[]`. `6:[]` is `[6]` and that's now the accumulator. We apply `(+3)` to `2`,\n",
    "that's `5` and we prepend (`:`) it to the accumulator, so the accumulator is\n",
    "now `[5,6]`. We apply `(+3)` to `1` and prepend that to the accumulator and so\n",
    "the end value is `[4,5,6]`.\n",
    "\n",
    "Of course, we could have implemented this function with a left fold too.\n",
    "It would be `map' f xs = foldl (\\acc x -> acc ++ [f x]) [] xs`, but the\n",
    "thing is that the [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) function is much more expensive than `:`, so we\n",
    "usually use right folds when we're building up new lists from a list.\n",
    "\n",
    "\n",
    "\n",
    "If you reverse a list, you can do a right fold on it just like you would\n",
    "have done a left fold and vice versa. Sometimes you don't even have to\n",
    "do that. The [`sum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sum) function can be implemented pretty much the same with a\n",
    "left and right fold. One big difference is that right folds work on\n",
    "infinite lists, whereas left ones don't! To put it plainly, if you take\n",
    "an infinite list at some point and you fold it up from the right, you'll\n",
    "eventually reach the beginning of the list. However, if you take an\n",
    "infinite list at a point and you try to fold it up from the left, you'll\n",
    "never reach an end!\n",
    "\n",
    "__Folds can be used to implement any function where you traverse a list\n",
    "once, element by element, and then return something based on that.\n",
    "Whenever you want to traverse a list to return something, chances are\n",
    "you want a fold.__ That's why folds are, along with maps and filters, one\n",
    "of the most useful types of functions in functional programming.\n",
    "\n",
    "The __`foldl1`__ and __`foldr1`__ functions work much like [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl) and [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr), only you\n",
    "don't need to provide them with an explicit starting value. They assume\n",
    "the first (or last) element of the list to be the starting value and\n",
    "then start the fold with the element next to it. With that in mind, the\n",
    "[`sum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sum) function can be implemented like so: `sum = foldl1 (+)`. Because they\n",
    "depend on the lists they fold up having at least one element, they cause\n",
    "runtime errors if called with empty lists. [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl) and [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr), on the other\n",
    "hand, work fine with empty lists. When making a fold, think about how it\n",
    "acts on an empty list. If the function doesn't make sense when given an\n",
    "empty list, you can probably use a `foldl1` or `foldr1` to implement it.\n",
    "\n",
    "Just to show you how powerful folds are, we're going to implement a\n",
    "bunch of standard library functions by using folds:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "maximum' :: (Ord a) => [a] -> a\n",
    "maximum' = foldr1 (\\x acc -> if x > acc then x else acc)\n",
    "\n",
    "reverse' :: [a] -> [a]\n",
    "reverse' = foldl (\\acc x -> x : acc) []\n",
    "\n",
    "product' :: (Num a) => [a] -> a\n",
    "product' = foldr1 (*)\n",
    "\n",
    "filter' :: (a -> Bool) -> [a] -> [a]\n",
    "filter' p = foldr (\\x acc -> if p x then x : acc else acc) []\n",
    "\n",
    "head' :: [a] -> a\n",
    "head' = foldr1 (\\x _ -> x)\n",
    "\n",
    "last' :: [a] -> a\n",
    "last' = foldl1 (\\_ x -> x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head) is better implemented by pattern matching, but this just goes to\n",
    "show, you can still achieve it by using folds. Our `reverse'` definition\n",
    "is pretty clever, I think. We take a starting value of an empty list and\n",
    "then approach our list from the left and just prepend to our\n",
    "accumulator. In the end, we build up a reversed list.\n",
    "`\\acc x -> x : acc`\n",
    "kind of looks like the : function, only the parameters are flipped.\n",
    "That's why we could have also written our reverse as\n",
    "`foldl (flip (:)) []`.\n",
    "\n",
    "Another way to picture right and left folds is like this: say we have a\n",
    "right fold and the binary function is `f` and the starting value is `z`.\n",
    "If we're right folding over the list `[3,4,5,6]`,\n",
    "we're essentially doing this:\n",
    "`f 3 (f 4 (f 5 (f 6 z)))`.\n",
    "`f` is called with the last element in the\n",
    "list and the accumulator, that value is given as the accumulator to the\n",
    "next to last value and so on. If we take `f` to be [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-) and the starting\n",
    "accumulator value to be `0`, that's `3 + (4 + (5 + (6 + 0)))`. Or if we\n",
    "write [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-) as a prefix function, that's `(+) 3 ((+) 4 ((+) 5 ((+) 6 0)))`.\n",
    "Similarly, doing a left fold over that list with `g` as the binary\n",
    "function and `z` as the accumulator is the equivalent of\n",
    "`g (g (g (g z 3) 4) 5) 6`.\n",
    "If we use `flip (:)` as the binary function and `[]` as the\n",
    "accumulator (so we're reversing the list), then that's the equivalent of\n",
    "`flip (:) (flip (:) (flip (:) (flip (:) [] 3) 4) 5) 6`. And sure enough,\n",
    "if you evaluate that expression, you get `[6,5,4,3]`.\n",
    "\n",
    "__[`scanl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:scanl)__ and __[`scanr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:scanr)__ are like [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl) and [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr), only they report all the\n",
    "intermediate accumulator states in the form of a list. There are also\n",
    "`scanl1` and `scanr1`, which are analogous to `foldl1` and `foldr1`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0,3,8,10,11]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scanl (+) 0 [3,5,2,1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[11,8,3,1,0]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scanr (+) 0 [3,5,2,1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[3,4,5,5,7,9,9,9]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scanl1 (\\acc x -> if x > acc then x else acc) [3,4,5,3,7,9,2,1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[],[3],[2,3],[1,2,3]]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scanl (flip (:)) [] [3,2,1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When using a [`scanl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:scanl), the final result will be in the last element of the\n",
    "resulting list while a [`scanr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:scanr) will place the result in the head.\n",
    "\n",
    "Scans are used to monitor the progression of a function that can be\n",
    "implemented as a fold. Let's answer us this question: *How many elements\n",
    "does it take for the sum of the roots of all natural numbers to exceed\n",
    "1000?* To get the squares of all natural numbers, we just do\n",
    "`map sqrt [1..]`.\n",
    "Now, to get the sum, we could do a fold, but because we're\n",
    "interested in how the sum progresses, we're going to do a scan. Once\n",
    "we've done the scan, we just see how many sums are under 1000. The first\n",
    "sum in the scanlist will be 1, normally. The second will be 1 plus the\n",
    "square root of 2. The third will be that plus the square root of 3. If\n",
    "there are X sums under 1000, then it takes X+1 elements for the sum to\n",
    "exceed 1000."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [],
   "source": [
    "sqrtSums :: Int\n",
    "sqrtSums = length (takeWhile (<1000) (scanl1 (+) (map sqrt [1..]))) + 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "131"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sqrtSums"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1005.0942035344083"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sum (map sqrt [1..131])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:ghci"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "993.6486803921487"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sum (map sqrt [1..130])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We use [`takeWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:takeWhile) here instead of [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) because [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) doesn't work on\n",
    "infinite lists. Even though we know the list is ascending, [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter)\n",
    "doesn't, so we use [`takeWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:takeWhile) to cut the scanlist off at the first\n",
    "occurence of a sum greater than 1000.\n",
    "\n",
    "Function application with \\$\n",
    "----------------------------\n",
    "\n",
    "Alright, next up, we'll take a look at the [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-) function, also called\n",
    "*function application*. First of all, let's check out how it's defined:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "attributes": {
     "classes": [
      "haskell:hs"
     ],
     "id": "",
     "name": "\"code\""
    }
   },
   "source": [
    "
($) :: (a -> b) -> a -> b\n",
    "f $ x = f x
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "What the heck? What is this useless operator? It's just function\n", "application! Well, almost, but not quite! Whereas normal function\n", "application (putting a space between two things) has a really high\n", "precedence, the [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-) function has the lowest precedence. Function\n", "application with a space is left-associative (so `f a b c` is the same as\n", "`((f a) b) c))`, function application with [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-) is right-associative.\n", "\n", "That's all very well, but how does this help us? Most of the time, it's\n", "a convenience function so that we don't have to write so many\n", "parentheses. Consider the expression `sum (map sqrt [1..130])`. Because [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-)\n", "has such a low precedence, we can rewrite that expression as\n", "`sum $ map sqrt [1..130]`,\n", "saving ourselves precious keystrokes! When a [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-) is\n", "encountered, the expression on its right is applied as the parameter to\n", "the function on its left. How about `sqrt 3 + 4 + 9`? This adds together\n", "9, 4 and the square root of 3. If we want get the square root of 3 + 4 + 9, \n", "we'd have to write `sqrt (3 + 4 + 9)` or if we use [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-) we can write\n", "it as `sqrt $ 3 + 4 + 9` because [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-) has the lowest precedence of any\n", "operator. That's why you can imagine a [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-) being sort of the equivalent\n", "of writing an opening parentheses and then writing a closing one on the\n", "far right side of the expression.\n", "\n", "How about\n", "`sum (filter (> 10) (map (*2) [2..10]))`?\n", "Well, because [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-) is right-associative,\n", "`f (g (z x))` is equal to `f $ g $ z x`.\n", "And so, we can rewrite\n", "`sum (filter (> 10) (map (*2) [2..10]))`\n", "as\n", "`sum $ filter (> 10) $ map (*2) [2..10]`.\n", "\n", "But apart from getting rid of parentheses, [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-) means that function\n", "application can be treated just like another function. That way, we can,\n", "for instance, map function application over a list of functions." ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[7.0,30.0,9.0,1.7320508075688772]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map ($ 3) [(4+), (10*), (^2), sqrt]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Function composition\n", "--------------------\n", "\n", "In mathematics, function composition is defined like this:\n", "$(f \\circ g)(x) = f(g(x))$, meaning that\n", "composing two functions produces a new function that, when called with a\n", "parameter, say, $x$ is the equivalent of calling $g$ with the parameter\n", "$x$ and then calling the $f$ with that result.\n", "\n", "In Haskell, function composition is pretty much the same thing. We do\n", "function composition with the [`.`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:.) function, which is defined like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(.) :: (b -> c) -> (a -> b) -> a -> c\n",
    "f . g = \\x -> f (g x)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Mind the type declaration. `f` must take as its parameter a value that has\n", "the same type as `g`'s return value. So the resulting function takes a\n", "parameter of the same type that g takes and returns a value of the same\n", "type that `f` returns. The expression `negate . (* 3)` returns a function\n", "that takes a number, multiplies it by 3 and then negates it.\n", "\n", "One of the uses for function composition is making functions on the fly\n", "to pass to other functions. Sure, can use lambdas for that, but many\n", "times, function composition is clearer and more concise. Say we have a\n", "list of numbers and we want to turn them all into negative numbers. One\n", "way to do that would be to get each number's absolute value and then\n", "negate it, like so:" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[-5,-3,-6,-7,-3,-2,-19,-24]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (\\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice the lambda and how it looks like the result function composition.\n", "Using function composition, we can rewrite that as:" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[-5,-3,-6,-7,-3,-2,-19,-24]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (negate . abs) [5,-3,-6,7,-3,2,-19,24]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fabulous! Function composition is right-associative, so we can compose\n", "many functions at a time. The expression `f (g (z x))` is equivalent to\n", "`(f . g . z) x`. With that in mind, we can turn" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[-14,-15,-27]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (\\xs -> negate (sum (tail xs))) [[1..5],[3..6],[1..7]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "into" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[-14,-15,-27]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (negate . sum . tail) [[1..5],[3..6],[1..7]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But what about functions that take several parameters? Well, if we want\n", "to use them in function composition, we usually have to partially apply\n", "them just so much that each function takes just one parameter.\n", "`sum (replicate 5 (max 6.7 8.9))` can be rewritten as\n", "`(sum . replicate 5 . max 6.7) 8.9` or as\n", "`sum . replicate 5 . max 6.7 $ 8.9`. What goes on in here\n", "is this: a function that takes what `max 6.7` takes and applies\n", "`replicate 5` to it is created.\n", "Then, a function that takes the result of that and\n", "does a sum of it is created. Finally, that function is called with `8.9`.\n", "But normally, you just read that as: apply `8.9` to `max 6.7`, then apply\n", "`replicate 5` to that and then apply [`sum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sum) to that. If you want to rewrite\n", "an expression with a lot of parentheses by using function composition,\n", "you can start by putting the last parameter of the innermost function\n", "after a [`$`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-36-) and then just composing all the other function calls, writing\n", "them without their last parameter and putting dots between them. If you\n", "have\n", "`replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] [4,5,6,7,8])))`,\n", "you can write it as\n", "`replicate 100 . product . map (*3) . zipWith max [1,2,3,4,5] $ [4,5,6,7,8]`.\n", "If the expression ends with\n", "three parentheses, chances are that if you translate it into function\n", "composition, it'll have three composition operators.\n", "\n", "Another common use of function composition is defining functions in the\n", "so-called point free style (also called the point*less* style). Take for\n", "example this function that we wrote earlier:" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "sum' :: (Num a) => [a] -> a\n", "sum' xs = foldl (+) 0 xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `xs` is exposed on both right sides. Because of currying, we can omit\n", "the `xs` on both sides, because calling `foldl (+) 0` creates a function\n", "that takes a list. Writing the function as `sum' = foldl (+) 0` is called\n", "writing it in point free style. How would we write this in point free\n", "style?" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "fn x = ceiling (negate (tan (cos (max 50 x))))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can't just get rid of the `x` on both right right sides. The `x` in the\n", "function body has parentheses after it. `cos (max 50)` wouldn't make\n", "sense. You can't get the cosine of a function. What we can do is express\n", "`fn` as a composition of functions." ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "fn = ceiling . negate . tan . cos . max 50" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Excellent! Many times, a point free style is more readable and concise,\n", "because it makes you think about functions and what kind of functions\n", "composing them results in instead of thinking about data and how it's\n", "shuffled around. You can take simple functions and use composition as\n", "glue to form more complex functions. However, many times, writing a\n", "function in point free style can be less readable if a function is too\n", "complex. That's why making long chains of function composition is\n", "discouraged, although I plead guilty of sometimes being too\n", "composition-happy. The preferred style is to use *let* bindings to give\n", "labels to intermediary results or split the problem into sub-problems\n", "and then put it together so that the function makes sense to someone\n", "reading it instead of just making a huge composition chain.\n", "\n", "In the section about maps and filters, we solved a problem of finding\n", "the sum of all odd squares that are smaller than 10,000. Here's what the\n", "solution looks like when put into a function." ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "oddSquareSum :: Integer\n", "oddSquareSum = sum (takeWhile (<10000) (filter odd (map (^2) [1..])))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Being such a fan of function composition, I would have probably written\n", "that like this:" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "oddSquareSum :: Integer\n", "oddSquareSum = sum . takeWhile (<10000) . filter odd . map (^2) $ [1..]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, if there was a chance of someone else reading that code, I\n", "would have written it like this:" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "oddSquareSum :: Integer\n", "oddSquareSum =\n", " let oddSquares = filter odd $ map (^2) [1..]\n", " belowLimit = takeWhile (<10000) oddSquares\n", " in sum belowLimit" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It wouldn't win any code golf competition, but someone reading the\n", "function will probably find it easier to read than a composition chain." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/07-modules.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Modules\n", "=======\n", "\n", "Loading modules\n", "---------------\n", "\n", "\n", "\n", "A Haskell module is a collection of related functions, types and\n", "typeclasses. A Haskell program is a collection of modules where the main\n", "module loads up the other modules and then uses the functions defined in\n", "them to do something. Having code split up into several modules has\n", "quite a lot of advantages. If a module is generic enough, the functions\n", "it exports can be used in a multitude of different programs. If your own\n", "code is separated into self-contained modules which don't rely on each\n", "other too much (we also say they are loosely coupled), you can reuse\n", "them later on. It makes the whole deal of writing code more manageable\n", "by having it split into several parts, each of which has some sort of\n", "purpose.\n", "\n", "The Haskell standard library is split into modules, each of them\n", "contains functions and types that are somehow related and serve some\n", "common purpose. There's a module for manipulating lists, a module for\n", "concurrent programming, a module for dealing with complex numbers, etc.\n", "All the functions, types and typeclasses that we've dealt with so far\n", "were part of the [`Prelude`](https://hackage.haskell.org/package/base/docs/Prelude.html) module, which is imported by default. In this\n", "chapter, we're going to examine a few useful modules and the functions\n", "that they have. But first, we're going to see how to import modules." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The syntax for importing modules in a Haskell script is\n", "`import `.\n", "This must be done before defining any functions, so imports are\n", "usually done at the top of the file. One script can, of course, import\n", "several modules. Just put each import statement into a separate line.\n", "Let's import the `Data.List` module, which has a bunch of useful functions\n", "for working with lists and use a function that it exports to create a\n", "function that tells us how many unique elements a list has." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.List\n", "\n", "numUniques :: (Eq a) => [a] -> Int\n", "numUniques = length . nub" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When you do `import Data.List`, all the functions that `Data.List` exports\n", "become available in the global namespace, meaning that you can call them\n", "from wherever in the script. [`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub) is a function defined in `Data.List` that\n", "takes a list and weeds out duplicate elements. Composing [`length`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:length) and [`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub)\n", "by doing `length . nub` produces a function that's the equivalent of \n", "`\\xs -> length (nub xs)`.\n", "\n", "You can also put the functions of modules into the global namespace when\n", "using IHaskell or GHCI. If you're in IHaskell or GHCI and you want to be able to call the\n", "functions exported by `Data.List`, do this:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":m + Data.List" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to load up the names from several modules, we\n", "don't have to do `:m +` several times, we can just load up several modules\n", "at once." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "source": [ "
:m + Data.List Data.Map Data.Set
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, if you've loaded a script that already imports a module, you\n", "don't need to use :m + to get access to it.\n", "\n", "If you just need a couple of functions from a module, you can\n", "selectively import just those functions. If we wanted to import only the\n", "[`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub) and [`sort`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:sort) functions from `Data.List`, we'd do this:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.List (nub, sort)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also choose to import all of the functions of a module except a\n", "few select ones. That's often useful when several modules export\n", "functions with the same name and you want to get rid of the offending\n", "ones. Say we already have our own function that's called [`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub) and we want\n", "to import all the functions from `Data.List` except the [`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub) function:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
import Data.List hiding (nub)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another way of dealing with name clashes is to do qualified imports. The\n", "`Data.Map` module, which offers a data structure for looking up values by\n", "key, exports a bunch of functions with the same name as [`Prelude`](https://hackage.haskell.org/package/base/docs/Prelude.html)\n", "functions, like [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) or [`null`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:null). So when we import `Data.Map` and then call\n", "[`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter), Haskell won't know which function to use. Here's how we solve\n", "this:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import qualified Data.Map" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This makes it so that if we want to reference `Data.Map`'s [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter)\n", "function, we have to do `Data.Map.filter`, whereas just [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) still\n", "refers to the normal [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) we all know and love. But typing out\n", "`Data.Map` in front of every function from that module is kind of tedious.\n", "That's why we can rename the qualified import to something shorter:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import qualified Data.Map as M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, to reference `Data.Map`'s [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) function, we just use `M.filter`.\n", "\n", "Use [this handy\n", "reference](http://www.haskell.org/ghc/docs/latest/html/libraries/) to\n", "see which modules are in the standard library. A great way to pick up\n", "new Haskell knowledge is to just click through the standard library\n", "reference and explore the modules and their functions. You can also view\n", "the Haskell source code for each module. Reading the source code of some\n", "modules is a really good way to learn Haskell and get a solid feel for\n", "it.\n", "\n", "To search for functions or to find out where they're located, use\n", "[Hoogle](http://haskell.org/hoogle). It's a really awesome Haskell\n", "search engine, you can search by name, module name or even type\n", "signature.\n", "\n", "\n", "\n", "Data.List\n", "---------\n", "\n", "The `Data.List` module is all about lists, obviously. It provides some\n", "very useful functions for dealing with them. We've already met some of\n", "its functions (like [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map) and [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter)) because the [`Prelude`](https://hackage.haskell.org/package/base/docs/Prelude.html) module exports\n", "some functions from `Data.List` for convenience. You don't have to import\n", "`Data.List` via a qualified import because it doesn't clash with any\n", "[`Prelude`](https://hackage.haskell.org/package/base/docs/Prelude.html) names except for those that [`Prelude`](https://hackage.haskell.org/package/base/docs/Prelude.html) already steals from\n", "`Data.List`. Let's take a look at some of the functions that we haven't\n", "met before.\n", "\n", "__[`intersperse`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:intersperse)__ takes an element and a list and then puts that element in\n", "between each pair of elements in the list. Here's a demonstration:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"M.O.N.K.E.Y\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "intersperse '.' \"MONKEY\"" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,0,2,0,3,0,4,0,5,0,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "intersperse 0 [1,2,3,4,5,6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`intercalate`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:intercalate)__ takes a list of lists and a list. It then inserts that list\n", "in between all those lists and then flattens the result." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"hey there guys\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "intercalate \" \" [\"hey\",\"there\",\"guys\"]" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,0,0,0,4,5,6,0,0,0,7,8,9]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "intercalate [0,0,0] [[1,2,3],[4,5,6],[7,8,9]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`transpose`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:transpose)__ transposes a list of lists. If you look at a list of lists as\n", "a 2D matrix, the columns become the rows and vice versa." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,4,7],[2,5,8],[3,6,9]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "transpose [[1,2,3],[4,5,6],[7,8,9]]" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"htg\",\"ehu\",\"yey\",\"rs\",\"e\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "transpose [\"hey\",\"there\",\"guys\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Say we have the polynomials $3x^2 + 5x + 9$, $10x^3 + 9$ and $8x^3 +\n", "5x^2 + x - 1$ and we want to add them together. We can use the lists\n", "`[0,3,5,9]`, `[10,0,0,9]` and `[8,5,1,-1]` to represent them in Haskell. Now,\n", "to add them, all we have to do is this:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[18,8,6,17]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map sum $ transpose [[0,3,5,9],[10,0,0,9],[8,5,1,-1]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we transpose these three lists, the third powers are then in the\n", "first row, the second powers in the second one and so on. Mapping [`sum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sum) to\n", "that produces our desired result.\n", "\n", "\n", "\n", "__`foldl'`__ and __`foldl1'`__ are stricter versions of their respective lazy\n", "incarnations. When using lazy folds on really big lists, you might often\n", "get a stack overflow error. The culprit for that is that due to the lazy\n", "nature of the folds, the accumulator value isn't actually updated as the\n", "folding happens. What actually happens is that the accumulator kind of\n", "makes a promise that it will compute its value when asked to actually\n", "produce the result (also called a thunk). That happens for every\n", "intermediate accumulator and all those thunks overflow your stack. The\n", "strict folds aren't lazy buggers and actually compute the intermediate\n", "values as they go along instead of filling up your stack with thunks. So\n", "if you ever get stack overflow errors when doing lazy folds, try\n", "switching to their strict versions.\n", "\n", "__[`concat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:concat)__ flattens a list of lists into just a list of elements." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"foobarcar\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "concat [\"foo\",\"bar\",\"car\"]" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[3,4,5,2,3,4,2,1,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "concat [[3,4,5],[2,3,4],[2,1,1]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It will just remove one level of nesting. So if you want to completely\n", "flatten `[[[2,3],[3,4,5],[2]],[[2,3],[3,4]]]`, which is a list of lists of\n", "lists, you have to concatenate it twice.\n", "\n", "Doing __[`concatMap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:concatMap)__ is the same as first mapping a function to a list and\n", "then concatenating the list with [`concat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:concat)." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,1,1,1,2,2,2,2,3,3,3,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "concatMap (replicate 4) [1..3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`and`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:and)__ takes a list of boolean values and returns [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) only if all the\n", "values in the list are [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True)." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "and $ map (>4) [5,6,7,8]" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "and $ map (==4) [4,4,4,3,4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`or`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:or)__ is like [`and`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:and), only it returns [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) if any of the boolean values in a\n", "list is [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True)." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "or $ map (==4) [2,3,4,5,6,1]" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "or $ map (>4) [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`any`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:any)__ and __[`all`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:all)__ take a predicate and then check if any or all the elements\n", "in a list satisfy the predicate, respectively. Usually we use these two\n", "functions instead of mapping over a list and then doing [`and`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:and) or [`or`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:or)." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "any (==4) [2,3,5,6,1,4]" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "all (>4) [6,9,10]" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "all (`elem` ['A'..'Z']) \"HEYGUYSwhatsup\"" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "any (`elem` ['A'..'Z']) \"HEYGUYSwhatsup\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`iterate`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:iterate)__ takes a function and a starting value. It applies the function\n", "to the starting value, then it applies that function to the result, then\n", "it applies the function to that result again, etc. It returns all the\n", "results in the form of an infinite list." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,4,8,16,32,64,128,256,512]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 10 $ iterate (*2) 1" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"haha\",\"hahahaha\",\"hahahahahaha\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 3 $ iterate (++ \"haha\") \"haha\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`splitAt`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:splitAt)__ takes a number and a list. It then splits the list at that many\n", "elements, returning the resulting two lists in a tuple." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(\"hey\",\"man\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "splitAt 3 \"heyman\"" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(\"heyman\",\"\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "splitAt 100 \"heyman\"" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(\"\",\"heyman\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "splitAt (-3) \"heyman\"" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"barfoo\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let (a,b) = splitAt 3 \"foobar\" in b ++ a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`takeWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:takeWhile)__ is a really useful little function. It takes elements from a\n", "list while the predicate holds and then when an element is encountered\n", "that doesn't satisfy the predicate, it's cut off. It turns out this is\n", "very useful." ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[6,5,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "takeWhile (>3) [6,5,4,3,2,1,2,3,4,5,4,3,2,1]" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"This\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "takeWhile (/=' ') \"This is a sentence\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Say we wanted to know the sum of all third powers that are under 10,000.\n", "We can't map `(^3)` to `[1..]`, apply a filter and then try to sum that up\n", "because filtering an infinite list never finishes. You may know that all\n", "the elements here are ascending but Haskell doesn't. That's why we can\n", "do this:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "53361" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sum $ takeWhile (<10000) $ map (^3) [1..]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We apply `(^3)` to an infinite list and then once an element that's over\n", "10,000 is encountered, the list is cut off. Now we can sum it up easily.\n", "\n", "__[`dropWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:dropWhile)__ is similar, only it drops all the elements while the predicate\n", "is true. Once predicate equates to [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False), it returns the rest of the\n", "list. An extremely useful and lovely function!" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\" is a sentence\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "dropWhile (/=' ') \"This is a sentence\"" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[3,4,5,4,3,2,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "dropWhile (<3) [1,2,2,2,3,4,5,4,3,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We're given a list that represents the value of a stock by date. The\n", "list is made of tuples whose first component is the stock value, the\n", "second is the year, the third is the month and the fourth is the date.\n", "We want to know when the stock value first exceeded one thousand\n", "dollars!" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(1001.4,2008,9,4)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let stock = [(994.4,2008,9,1),(995.2,2008,9,2),(999.2,2008,9,3),(1001.4,2008,9,4),(998.3,2008,9,5)]\n", "head (dropWhile (\\(val,y,m,d) -> val < 1000) stock)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`span`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:span)__ is kind of like [`takeWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:takeWhile), only it returns a pair of lists. The\n", "first list contains everything the resulting list from [`takeWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:takeWhile) would\n", "contain if it were called with the same predicate and the same list. The\n", "second list contains the part of the list that would have been dropped." ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"First word:This, the rest: is a sentence\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let (fw, rest) = span (/=' ') \"This is a sentence\" in \"First word:\" ++ fw ++ \", the rest:\" ++ rest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whereas [`span`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:span) spans the list while the predicate is true, __[`break`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:break)__ breaks it\n", "when the predicate is first true. Doing `break p` is the equivalent of\n", "doing `span (not . p)`." ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "([1,2,3],[4,5,6,7])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "break (==4) [1,2,3,4,5,6,7]" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "([1,2,3],[4,5,6,7])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "span (/=4) [1,2,3,4,5,6,7]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When using [`break`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:break), the second list in the result will start with the\n", "first element that satisfies the predicate.\n", "\n", "__[`sort`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:sort)__ simply sorts a list. The type of the elements in the list has to be\n", "part of the [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) typeclass, because if the elements of a list can't be\n", "put in some kind of order, then the list can't be sorted." ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,2,3,4,5,6,8]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sort [8,5,3,2,1,6,4,2]" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\" Tbdeehiillnooorssstw\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sort \"This will be sorted soon\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`group`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:group)__ takes a list and groups adjacent elements into sublists if they\n", "are equal." ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,1,1,1],[2,2,2,2],[3,3],[2,2,2],[5],[6],[7]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "group [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we sort a list before grouping it, we can find out how many times\n", "each element appears in the list." ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(1,4),(2,7),(3,2),(5,1),(6,1),(7,1)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (\\l@(x:xs) -> (x,length l)) . group . sort $ [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`inits`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:inits)__ and __[`tails`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:tails)__ are like [`init`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:init) and [`tail`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:tail), only they recursively apply that\n", "to a list until there's nothing left. Observe." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"\",\"w\",\"w0\",\"w00\",\"w00t\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "inits \"w00t\"" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"w00t\",\"00t\",\"0t\",\"t\",\"\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "tails \"w00t\"" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(\"\",\"w00t\"),(\"w\",\"00t\"),(\"w0\",\"0t\"),(\"w00\",\"t\"),(\"w00t\",\"\")]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let w = \"w00t\" in zip (inits w) (tails w)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's use a fold to implement searching a list for a sublist." ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "search :: (Eq a) => [a] -> [a] -> Bool\n", "search needle haystack =\n", " let nlen = length needle\n", " in foldl (\\acc x -> if take nlen x == needle then True else acc) False (tails haystack)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we call [`tails`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:tails) with the list in which we're searching. Then we go\n", "over each tail and see if it starts with what we're looking for.\n", "\n", "With that, we actually just made a function that behaves like __[`isInfixOf`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:isInfixOf)__.\n", "[`isInfixOf`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:isInfixOf) searches for a sublist within a list and returns [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) if the\n", "sublist we're looking for is somewhere inside the target list." ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"cat\" `isInfixOf` \"im a cat burglar\"" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"Cat\" `isInfixOf` \"im a cat burglar\"" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"cats\" `isInfixOf` \"im a cat burglar\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`isPrefixOf`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:isPrefixOf)__ and __[`isSuffixOf`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:isSuffixOf)__ search for a sublist at the beginning and at\n", "the end of a list, respectively." ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"hey\" `isPrefixOf` \"hey there!\"" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"hey\" `isPrefixOf` \"oh hey there!\"" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"there!\" `isSuffixOf` \"oh hey there!\"" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"there!\" `isSuffixOf` \"oh hey there\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`elem`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem)__ and __[`notElem`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:notElem)__ check if an element is or isn't inside a list.\n", "\n", "__[`partition`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:partition)__ takes a list and a predicate and returns a pair of lists. The\n", "first list in the result contains all the elements that satisfy the\n", "predicate, the second contains all the ones that don't." ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(\"BOBMORGAN\",\"sidneyeddy\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "partition (`elem` ['A'..'Z']) \"BOBsidneyMORGANeddy\"" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "([5,6,7],[1,3,3,2,1,0,3])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "partition (>3) [1,3,5,6,3,2,1,0,3,7]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's important to understand how this is different from [`span`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:span) and [`break`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:break):" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(\"BOB\",\"sidneyMORGANeddy\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "span (`elem` ['A'..'Z']) \"BOBsidneyMORGANeddy\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While [`span`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:span) and [`break`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:break) are done once they encounter the first element that\n", "doesn't and does satisfy the predicate, [`partition`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:partition) goes through the whole\n", "list and splits it up according to the predicate.\n", "\n", "__[`find`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:find)__ takes a list and a predicate and returns the first element that\n", "satisfies the predicate. But it returns that element wrapped in a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)\n", "value. We'll be covering algebraic data types more in depth in the next\n", "chapter but for now, this is what you need to know: a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value can\n", "either be `Just something` or [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). Much like a list can be either an\n", "empty list or a list with some elements, a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value can be either no\n", "elements or a single element. And like the type of a list of, say,\n", "integers is `[Int]`, the type of maybe having an integer is `Maybe Int`.\n", "Anyway, let's take our [`find`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:find) function for a spin." ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "find (>4) [1,2,3,4,5,6]" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "find (>9) [1,2,3,4,5,6]" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "find :: forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a" ], "text/plain": [ "find :: forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t find" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice the type of [`find`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:find). Its result is `Maybe a`. That's kind of like\n", "having the type of `[a]`, only a value of the type [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) can contain\n", "either no elements or one element, whereas a list can contain no\n", "elements, one element or several elements.\n", "\n", "Remember when we were searching for the first time our stock went over\n", "\\$1000. We did `head (dropWhile (\\(val,y,m,d) -> val < 1000) stock)`.\n", "Remember that [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head) is not really safe. What would happen if our stock\n", "never went over \\$1000? Our application of [`dropWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:dropWhile) would return an\n", "empty list and getting the head of an empty list would result in an\n", "error. However, if we rewrote that as\n", "`find (\\(val,y,m,d) -> val > 1000)`\n", "stock, we'd be much safer. If our stock never went over \\$1000 (so\n", "if no element satisfied the predicate), we'd get back a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). But\n", "there was a valid answer in that list, we'd get, say,\n", "`Just (1001.4,2008,9,4)`.\n", "\n", "__[`elemIndex`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:elemIndex)__ is kind of like [`elem`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem), only it doesn't return a boolean value.\n", "It maybe returns the index of the element we're looking for. If that\n", "element isn't in our list, it returns a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing)." ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "elemIndex :: forall a. Eq a => a -> [a] -> Maybe Int" ], "text/plain": [ "elemIndex :: forall a. Eq a => a -> [a] -> Maybe Int" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t elemIndex" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "4 `elemIndex` [1,2,3,4,5,6]" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "10 `elemIndex` [1,2,3,4,5,6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`elemIndices`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:elemIndices)__ is like [`elemIndex`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:elemIndex), only it returns a list of indices, in\n", "case the element we're looking for crops up in our list several times.\n", "Because we're using a list to represent the indices, we don't need a\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) type, because failure can be represented as the empty list, which\n", "is very much synonymous to [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing)." ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[5,9,13]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "' ' `elemIndices` \"Where are the spaces?\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`findIndex`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:findIndex)__ is like find, but it maybe returns the index of the first\n", "element that satisfies the predicate. [`findIndices`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:findIndices) returns the indices of\n", "all elements that satisfy the predicate in the form of a list." ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "findIndex (==4) [5,3,2,1,6,4]" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "findIndex (==7) [5,3,2,1,6,4]" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[0,6,10,14]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "findIndices (`elem` ['A'..'Z']) \"Where Are The Caps?\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We already covered [`zip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zip) and [`zipWith`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zipWith). We noted that they zip together two\n", "lists, either in a tuple or with a binary function (meaning such a\n", "function that takes two parameters). But what if we want to zip together\n", "three lists? Or zip three lists with a function that takes three\n", "parameters? Well, for that, we have __`zip3`__, __`zip4`__, etc. and __`zipWith3`__,\n", "__`zipWith4`__, etc. These variants go up to 7. While this may look like a\n", "hack, it works out pretty fine, because there aren't many times when you\n", "want to zip 8 lists together. There's also a very clever way for zipping\n", "infinite numbers of lists, but we're not advanced enough to cover that\n", "just yet." ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[7,9,8]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zipWith3 (\\x y z -> x + y + z) [1,2,3] [4,5,2,2] [2,2,3]" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(2,2,5,2),(3,2,5,2),(3,2,3,2)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "zip4 [2,3,3] [2,2,2] [5,5,3] [2,2,2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just like with normal zipping, lists that are longer than the shortest\n", "list that's being zipped are cut down to size.\n", "\n", "__[`lines`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:lines)__ is a useful function when dealing with files or input from\n", "somewhere. It takes a string and returns every line of that string in a\n", "separate list." ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"first line\",\"second line\",\"third line\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lines \"first line\\nsecond line\\nthird line\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`'\\n'` is the character for a unix newline. Backslashes have special\n", "meaning in Haskell strings and characters.\n", "\n", "__[`unlines`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:unlines)__ is the inverse function of [`lines`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:lines). It takes a list of strings and\n", "joins them together using a `'\\n'`." ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"first line\\nsecond line\\nthird line\\n\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "unlines [\"first line\", \"second line\", \"third line\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`words`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:words)__ and __[`unwords`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:unwords)__ are for splitting a line of text into words or joining\n", "a list of words into a text. Very useful." ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"hey\",\"these\",\"are\",\"the\",\"words\",\"in\",\"this\",\"sentence\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "words \"hey these are the words in this sentence\"" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"hey\",\"these\",\"are\",\"the\",\"words\",\"in\",\"this\",\"sentence\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "words \"hey these are the words in this\\nsentence\"" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"hey there mate\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "unwords [\"hey\",\"there\",\"mate\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We've already mentioned __[`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub)__. It takes a list and weeds out the duplicate\n", "elements, returning a list whose every element is a unique snowflake!\n", "The function does have a kind of strange name. It turns out that \"nub\"\n", "means a small lump or essential part of something. In my opinion, they\n", "should use real words for function names instead of old-people words." ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nub [1,2,3,4,3,2,1,2,3,4,3,2,1]" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Lots fwrdanu\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nub \"Lots of words and stuff\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`delete`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:delete)__ takes an element and a list and deletes the first occurrence of\n", "that element in the list." ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"ey there ghang!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "delete 'h' \"hey there ghang!\"" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"ey tere ghang!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "delete 'h' . delete 'h' $ \"hey there ghang!\"" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"ey tere gang!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "delete 'h' . delete 'h' . delete 'h' $ \"hey there ghang!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__`\\\\`__ is the list difference function. It acts like a set difference,\n", "basically. For every element in the right-hand list, it removes a\n", "matching element in the left one." ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,3,4,6,7,8,10]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1..10] \\\\ [2,5,9]" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Im a baby\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"Im a big baby\" \\\\ \"big\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Doing `[1..10] \\\\ [2,5,9]` is like doing\n", "`delete 2 . delete 5 . delete 9 $ [1..10]`.\n", "\n", "__[`union`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:union)__ also acts like a function on sets. It returns the union of two\n", "lists. It pretty much goes over every element in the second list and\n", "appends it to the first one if it isn't already in yet. Watch out\n", "though, duplicates are removed from the second list!" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"hey manwt'sup\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"hey man\" `union` \"man what's up\"" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5,6,7,8,9,10]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1..7] `union` [5..10]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`intersect`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:intersect)__ works like set intersection. It returns only the elements that\n", "are found in both lists." ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[5,6,7]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1..7] `intersect` [5..10]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`insert`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:insert)__ takes an element and a list of elements that can be sorted and\n", "inserts it into the last position where it's still less than or equal to\n", "the next element. In other words, [`insert`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:insert) will start at the beginning of\n", "the list and then keep going until it finds an element that's equal to\n", "or greater than the element that we're inserting and it will insert it\n", "just before the element." ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[3,4,5,1,2,8,2]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "insert 4 [3,5,1,2,8,2]" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,3,4,4,4,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "insert 4 [1,3,4,4,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `4` is inserted right after the `3` and before the `5` in the first\n", "example and in between the `3` and `4` in the second example.\n", "\n", "If we use [`insert`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:insert) to insert into a sorted list, the resulting list will\n", "be kept sorted." ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5,6,7]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "insert 4 [1,2,3,5,6,7]" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"abcdefghijklmnopqrstuvwxyz\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "insert 'g' $ ['a'..'f'] ++ ['h'..'z']" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,3,2,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "insert 3 [1,2,4,3,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What [`length`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:length), [`take`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:take), [`drop`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:drop), [`splitAt`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:splitAt), `!!` and [`replicate`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:replicate) have in common is\n", "that they take an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) as one of their parameters (or return an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int)),\n", "even though they could be more generic and usable if they just took any\n", "type that's part of the [`Integral`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Integral) or [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) typeclasses (depending on the\n", "functions). They do that for historical reasons. However, fixing that\n", "would probably break a lot of existing code. That's why `Data.List` has\n", "their more generic equivalents, named __[`genericLength`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:genericLength)__, __[`genericTake`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:genericTake)__,\n", "__[`genericDrop`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:genericDrop)__, __[`genericSplitAt`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:genericSplitAt)__, __[`genericIndex`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:genericIndex)__ and __[`genericReplicate`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:genericReplicate)__. For\n", "instance, [`length`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:length) has a type signature of `length :: [a] -> Int`. If we\n", "try to get the average of a list of numbers by doing\n", "`let xs = [1..6] in sum xs / length xs`,\n", "we get a type error, because you can't use [`/`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47-) with an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int).\n", "[`genericLength`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:genericLength), on the other hand, has a type signature of\n", "`genericLength :: (Num a) => [b] -> a`. Because a [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) can act like a\n", "floating point number, getting the average by doing\n", "`let xs = [1..6] in sum xs / genericLength xs` works out just fine.\n", "\n", "The [`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub), [`delete`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:delete), [`union`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:union), [`intersect`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:intersect) and [`group`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:group) functions all have their\n", "more general counterparts called __[`nubBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nubBy)__, __[`deleteBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:deleteBy)__, __[`unionBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:unionBy)__, __[`intersectBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:intersectBy)__\n", "and __[`groupBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:groupBy)__. The difference between them is that the first set of\n", "functions use [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) to test for equality, whereas the *By* ones also take\n", "an equality function and then compare them by using that equality\n", "function. [`group`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:group) is the same as `groupBy (==)`.\n", "\n", "For instance, say we have a list that describes the value of a function\n", "for every second. We want to segment it into sublists based on when the\n", "value was below zero and when it went above. If we just did a normal\n", "[`group`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:group), it would just group the equal adjacent values together. But what\n", "we want is to group them by whether they are negative or not. That's\n", "where [`groupBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:groupBy) comes in! The equality function supplied to the *By*\n", "functions should take two elements of the same type and return [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) if\n", "it considers them equal by its standards." ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let values = [-4.3, -2.4, -1.2, 0.4, 2.3, 5.9, 10.5, 29.1, 5.3, -2.4, -14.5, 2.9, 2.3]\n", "groupBy (\\x y -> (x > 0) == (y > 0)) values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From this, we clearly see which sections are positive and which are\n", "negative. The equality function supplied takes two elements and then\n", "returns [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) only if they're both negative or if they're both positive.\n", "This equality function can also be written as\n", "`\\x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0)`, although I think the first way is more\n", "readable. An even clearer way to write equality functions for the *By*\n", "functions is if you import the __[`on`](https://hackage.haskell.org/package/base/docs/Data-Function.html#v:on)__ function from Data.Function. [`on`](https://hackage.haskell.org/package/base/docs/Data-Function.html#v:on) is\n", "defined like this:" ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Function" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "source": [ "
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c\n",
    "f `on` g = \\x y -> f (g x) (g y)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So doing ``(==) `on` (> 0)`` returns an equality function that looks like\n", "`\\x y -> (x > 0) == (y > 0)`. [`on`](https://hackage.haskell.org/package/base/docs/Data-Function.html#v:on) is used a lot with the *By* functions\n", "because with it, we can do:" ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "groupBy ((==) `on` (> 0)) values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Very readable indeed! You can read it out loud: Group this by equality\n", "on whether the elements are greater than zero.\n", "\n", "Similarly, the [`sort`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:sort), [`insert`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:insert), [`maximum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:maximum) and [`minimum`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:minimum) also have their more\n", "general equivalents. Functions like [`groupBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:groupBy) take a function that\n", "determines when two elements are equal. __[`sortBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:sortBy)__, __[`insertBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:insertBy)__, __[`maximumBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:maximumBy)__ and\n", "__[`minimumBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:minimumBy)__ take a function that determine if one element is greater,\n", "smaller or equal to the other. The type signature of [`sortBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:sortBy) is\n", "`sortBy :: (a -> a -> Ordering) -> [a] -> [a]`.\n", "If you remember from before, the\n", "[`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering) type can have a value of [`LT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:LT), [`EQ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:EQ) or [`GT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:GT). [`sort`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:sort) is the equivalent\n", "of `sortBy compare`, because compare just takes two elements whose type is\n", "in the [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) typeclass and returns their ordering relationship.\n", "\n", "Lists can be compared, but when they are, they are compared\n", "lexicographically. What if we have a list of lists and we want to sort\n", "it not based on the inner lists' contents but on their lengths? Well, as\n", "you've probably guessed, we'll use the [`sortBy`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:sortBy) function." ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[],[2],[2,2],[1,2,3],[3,5,4,3],[5,4,5,4,4]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let xs = [[5,4,5,4,4],[1,2,3],[3,5,4,3],[],[2],[2,2]]\n", "sortBy (compare `on` length) xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Awesome! ``compare `on` length`` ... man, that reads almost like real\n", "English! If you're not sure how exactly the [`on`](https://hackage.haskell.org/package/base/docs/Data-Function.html#v:on) works here,\n", "``compare `on` length`` is the equivalent of\n", "``\\x y -> length x `compare` length y``.\n", "When you're dealing with *By* functions that take an equality\n", "function, you usually do ``(==) `on` something`` and when you're dealing\n", "with *By* functions that take an ordering function, you usually do\n", "``compare `on` something``.\n", "\n", "Data.Char\n", "---------\n", "\n", "\n", "\n", "The `Data.Char` module does what its name suggests. It exports functions\n", "that deal with characters. It's also helpful when filtering and mapping\n", "over strings because they're just lists of characters.\n", "\n", "`Data.Char` exports a bunch of predicates over characters. That is,\n", "functions that take a character and tell us whether some assumption\n", "about it is true or false. Here's what they are:\n", "\n", "__[`isControl`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isControl)__ checks whether a character is a control character.\n", "\n", "__[`isSpace`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isSpace)__ checks whether a character is a white-space characters. That\n", "includes spaces, tab characters, newlines, etc.\n", "\n", "__[`isLower`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isLower)__ checks whether a character is lower-cased.\n", "\n", "__[`isUpper`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isUpper)__ checks whether a character is upper-cased.\n", "\n", "__[`isAlpha`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isAlpha)__ checks whether a character is a letter.\n", "\n", "__[`isAlphaNum`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isAlphaNum)__ checks whether a character is a letter or a number.\n", "\n", "__[`isPrint`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isPrint)__ checks whether a character is printable. Control characters, for\n", "instance, are not printable.\n", "\n", "__[`isDigit`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isDigit)__ checks whether a character is a digit.\n", "\n", "__[`isOctDigit`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isOctDigit)__ checks whether a character is an octal digit.\n", "\n", "__[`isHexDigit`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isHexDigit)__ checks whether a character is a hex digit.\n", "\n", "__[`isLetter`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isLetter)__ checks whether a character is a letter.\n", "\n", "__[`isMark`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isMark)__ checks for Unicode mark characters. Those are characters that\n", "combine with preceding letters to form letters with accents. Use this if\n", "you are French.\n", "\n", "__[`isNumber`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isNumber)__ checks whether a character is numeric.\n", "\n", "__[`isPunctuation`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isPunctuation)__ checks whether a character is punctuation.\n", "\n", "__[`isSymbol`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isSymbol)__ checks whether a character is a fancy mathematical or currency\n", "symbol.\n", "\n", "__[`isSeparator`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isSeparator)__ checks for Unicode spaces and separators.\n", "\n", "__[`isAscii`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isAscii)__ checks whether a character falls into the first 128 characters\n", "of the Unicode character set.\n", "\n", "__`isLatin1`__ checks whether a character falls into the first 256 characters\n", "of Unicode.\n", "\n", "__[`isAsciiUpper`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isAsciiUpper)__ checks whether a character is ASCII and upper-case.\n", "\n", "__[`isAsciiLower`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isAsciiLower)__ checks whether a character is ASCII and lower-case.\n", "\n", "All these predicates have a type signature of `Char -> Bool`. Most of the\n", "time you'll use this to filter out strings or something like that. For\n", "instance, let's say we're making a program that takes a username and the\n", "username can only be comprised of alphanumeric characters. We can use\n", "the `Data.List` function [`all`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:all) in combination with the `Data.Char` predicates\n", "to determine if the username is alright." ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Char" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "all isAlphaNum \"bobby283\"" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "all isAlphaNum \"eddy the fish!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Kewl. In case you don't remember, [`all`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:all) takes a predicate and a list and\n", "returns [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) only if that predicate holds for every element in the list.\n", "\n", "We can also use [`isSpace`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isSpace) to simulate the `Data.List` function [`words`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:words)." ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"hey\",\"guys\",\"its\",\"me\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "words \"hey guys its me\"" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"hey\",\" \",\"guys\",\" \",\"its\",\" \",\"me\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "groupBy ((==) `on` isSpace) \"hey guys its me\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hmmm, well, it kind of does what [`words`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:words) does but we're left with elements\n", "of only spaces. Hmm, whatever shall we do? I know, let's filter that\n", "sucker." ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Function (on)" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"hey\",\"guys\",\"its\",\"me\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "filter (not . any isSpace) . groupBy ((==) `on` isSpace) $ \"hey guys its me\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah.\n", "\n", "The `Data.Char` also exports a datatype that's kind of like [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering). The\n", "[`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering) type can have a value of [`LT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:LT), [`EQ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:EQ) or [`GT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:GT). It's a sort of\n", "enumeration. It describes a few possible results that can arise from\n", "comparing two elements. The [`GeneralCategory`](https://hackage.haskell.org/package/base/docs/Data-Char.html#t:GeneralCategory) type is also an enumeration.\n", "It presents us with a few possible categories that a character can fall\n", "into. The main function for getting the general category of a character\n", "is [`generalCategory`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:generalCategory). It has a type of\n", "`generalCategory :: Char -> GeneralCategory`.\n", "There are about 31 categories so we won't list them all\n", "here, but let's play around with the function." ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Space" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "generalCategory ' '" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "UppercaseLetter" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "generalCategory 'A'" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "LowercaseLetter" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "generalCategory 'a'" ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "OtherPunctuation" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "generalCategory '.'" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "DecimalNumber" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "generalCategory '9'" ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[Space,Control,Control,UppercaseLetter,DecimalNumber,OtherPunctuation,MathSymbol]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map generalCategory \" \\t\\nA9?|\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since the [`GeneralCategory`](https://hackage.haskell.org/package/base/docs/Data-Char.html#t:GeneralCategory) type is part of the `Eq` typeclass, we can also\n", "test for stuff like `generalCategory c == Space`.\n", "\n", "__[`toUpper`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:toUpper)__ converts a character to upper-case. Spaces, numbers, and the\n", "like remain unchanged.\n", "\n", "__[`toLower`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:toLower)__ converts a character to lower-case.\n", "\n", "__[`toTitle`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:toTitle)__ converts a character to title-case. For most characters,\n", "title-case is the same as upper-case.\n", "\n", "__[`digitToInt`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:digitToInt)__ converts a character to an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int).\n", "To succeed, the character must be in the ranges '0'..'9', 'a'..'f' or 'A'..'F'." ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[3,4,5,3,8]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map digitToInt \"34538\"" ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[15,15,8,5,10,11]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map digitToInt \"FF85AB\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`intToDigit`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:intToDigit)__ is the inverse function of [`digitToInt`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:digitToInt). It takes an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) in the\n", "range of `0..15` and converts it to a lower-case character." ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "'f'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "intToDigit 15" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "'5'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "intToDigit 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The __[`ord`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:ord)__ and [`chr`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:chr) functions convert characters to their corresponding\n", "numbers and vice versa:" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "97" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ord 'a'" ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "'a'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "chr 97" ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[97,98,99,100,101,102,103,104]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map ord \"abcdefgh\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The difference between the [`ord`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:ord) values of two characters is equal to how\n", "far apart they are in the Unicode table.\n", "\n", "The Caesar cipher is a primitive method of encoding messages by shifting\n", "each character in them by a fixed number of positions in the alphabet.\n", "We can easily create a sort of Caesar cipher of our own, only we won't\n", "constrict ourselves to the alphabet." ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "encode :: Int -> String -> String\n", "encode shift msg =\n", " let ords = map ord msg\n", " shifted = map (+ shift) ords\n", " in map chr shifted" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we first convert the string to a list of numbers. Then we add the\n", "shift amount to each number before converting the list of numbers back\n", "to characters. If you're a composition cowboy, you could write the body\n", "of this function as `map (chr . (+ shift) . ord) msg`. Let's try encoding\n", "a few messages." ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Khhhhh|\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "encode 3 \"Heeeeey\"" ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Liiiii}\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "encode 4 \"Heeeeey\"" ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"bcde\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "encode 1 \"abcd\"" ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Rfww~%Hmwnxyrfx&%Mt%mt%mt&\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "encode 5 \"Marry Christmas! Ho ho ho!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's encoded alright. Decoding a message is basically just shifting it\n", "back by the number of places it was shifted by in the first place." ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "decode :: Int -> String -> String\n", "decode shift msg = encode (negate shift) msg" ] }, { "cell_type": "code", "execution_count": 120, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Lp#d#olwwoh#whdsrw\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "encode 3 \"Im a little teapot\"" ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Im a little teapot\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "decode 3 \"Lp#d#olwwoh#whdsrw\"" ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"This is a sentence\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "decode 5 . encode 5 $ \"This is a sentence\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Data.Map\n", "--------\n", "\n", "Association lists (also called dictionaries) are lists that are used to\n", "store key-value pairs where ordering doesn't matter. For instance, we\n", "might use an association list to store phone numbers, where phone\n", "numbers would be the values and people's names would be the keys. We\n", "don't care in which order they're stored, we just want to get the right\n", "phone number for the right person.\n", "\n", "The most obvious way to represent association lists in Haskell would be\n", "by having a list of pairs. The first component in the pair would be the\n", "key, the second component the value. Here's an example of an association\n", "list with phone numbers:" ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "phoneBook =\n", " [(\"betty\",\"555-2938\")\n", " ,(\"bonnie\",\"452-2928\")\n", " ,(\"patsy\",\"493-2928\")\n", " ,(\"lucille\",\"205-2928\")\n", " ,(\"wendy\",\"939-8282\")\n", " ,(\"penny\",\"853-2492\")\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Despite this seemingly odd indentation, this is just a list of pairs of\n", "strings. The most common task when dealing with association lists is\n", "looking up some value by key. Let's make a function that looks up some\n", "value given a key." ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "findKey :: (Eq k) => k -> [(k,v)] -> v\n", "findKey key xs = snd . head . filter (\\(k,v) -> key == k) $ xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty simple. The function that takes a key and a list, filters the\n", "list so that only matching keys remain, gets the first key-value that\n", "matches and returns the value. But what happens if the key we're looking\n", "for isn't in the association list? Hmm. Here, if a key isn't in the\n", "association list, we'll end up trying to get the head of an empty list,\n", "which throws a runtime error. However, we should avoid making our\n", "programs so easy to crash, so let's use the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) data type. If we don't\n", "find the key, we'll return a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). If we find it, we'll return\n", "`Just something`, where something is the value corresponding to that key." ] }, { "cell_type": "code", "execution_count": 125, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "findKey :: (Eq k) => k -> [(k,v)] -> Maybe v\n", "findKey key [] = Nothing\n", "findKey key ((k,v):xs) = if key == k\n", " then Just v\n", " else findKey key xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Look at the type declaration. It takes a key that can be equated, an\n", "association list and then it maybe produces a value. Sounds about right.\n", "\n", "This is a textbook recursive function that operates on a list. Edge\n", "case, splitting a list into a head and a tail, recursive calls, they're\n", "all there. This is the classic fold pattern, so let's see how this would\n", "be implemented as a fold." ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "findKey :: (Eq k) => k -> [(k,v)] -> Maybe v\n", "findKey key = foldr (\\(k,v) acc -> if key == k then Just v else acc) Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Note:__ It's usually better to use folds for this standard list\n", "> recursion pattern instead of explicitly writing the recursion because\n", "> they're easier to read and identify. Everyone knows it's a fold when\n", "> they see the [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr) call, but it takes some more thinking to read\n", "> explicit recursion." ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"853-2492\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "findKey \"penny\" phoneBook" ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"555-2938\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "findKey \"betty\" phoneBook" ] }, { "cell_type": "code", "execution_count": 129, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "findKey \"wilma\" phoneBook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Works like a charm! If we have the girl's phone number, we [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) get the\n", "number, otherwise we get [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "\n", "We just implemented the [`lookup`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:lookup) function from `Data.List`. If we want to\n", "find the corresponding value to a key, we have to traverse all the\n", "elements of the list until we find it. The `Data.Map` module offers\n", "association lists that are much faster (because they're internally\n", "implemented with trees) and also it provides a lot of utility functions.\n", "From now on, we'll say we're working with maps instead of association\n", "lists.\n", "\n", "Because `Data.Map` exports functions that clash with the [`Prelude`](https://hackage.haskell.org/package/base/docs/Prelude.html) and\n", "`Data.List` ones, we'll do a qualified import." ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import qualified Data.Map as Map" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Put this import statement into a script and then load the script via\n", "GHCI.\n", "\n", "Let's go ahead and see what `Data.Map` has in store for us! Here's the\n", "basic rundown of its functions.\n", "\n", "The __[`fromList`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:fromList)__ function takes an association list (in the form of a list)\n", "and returns a map with the same associations." ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(\"betty\",\"555-2938\"),(\"bonnie\",\"452-2928\"),(\"lucille\",\"205-2928\")]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.fromList [(\"betty\",\"555-2938\"),(\"bonnie\",\"452-2928\"),(\"lucille\",\"205-2928\")]" ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(1,2),(3,2),(5,5)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.fromList [(1,2),(3,4),(3,2),(5,5)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If there are duplicate keys in the original association list, the\n", "duplicates are just discarded. This is the type signature of [`fromList`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:fromList)" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
Map.fromList :: (Ord k) => [(k, v)] -> Map.Map k v
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It says that it takes a list of pairs of type `k` and `v` and returns a map\n", "that maps from keys of type `k` to type `v`. Notice that when we were doing\n", "association lists with normal lists, the keys only had to be equatable\n", "(their type belonging to the `Eq` typeclass) but now they have to be\n", "orderable. That's an essential constraint in the `Data.Map` module. It\n", "needs the keys to be orderable so it can arrange them in a tree.\n", "\n", "You should always use `Data.Map` for key-value associations unless you\n", "have keys that aren't part of the [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) typeclass.\n", "\n", "__[`empty`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#v:empty)__ represents an empty map. It takes no arguments, it just returns an\n", "empty map." ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList []" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.empty" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`insert`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:insert)__ takes a key, a value and a map and returns a new map that's just\n", "like the old one, only with the key and value inserted." ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList []" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.empty" ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(3,100)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.insert 3 100 Map.empty" ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(3,100),(4,200),(5,600)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.insert 5 600 (Map.insert 4 200 ( Map.insert 3 100 Map.empty))" ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(3,100),(4,200),(5,600)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.insert 5 600 . Map.insert 4 200 . Map.insert 3 100 $ Map.empty" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can implement our own [`fromList`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:fromList) by using the empty map, [`insert`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:insert) and a\n", "fold. Watch:" ] }, { "cell_type": "code", "execution_count": 138, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "fromList' :: (Ord k) => [(k,v)] -> Map.Map k v\n", "fromList' = foldr (\\(k,v) acc -> Map.insert k v acc) Map.empty" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's a pretty straightforward fold. We start of with an empty map and we\n", "fold it up from the right, inserting the key value pairs into the\n", "accumulator as we go along.\n", "\n", "__[`null`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:null)__ checks if a map is empty." ] }, { "cell_type": "code", "execution_count": 139, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.null Map.empty" ] }, { "cell_type": "code", "execution_count": 140, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.null $ Map.fromList [(2,3),(5,5)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__`size`__ reports the size of a map." ] }, { "cell_type": "code", "execution_count": 141, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.size Map.empty" ] }, { "cell_type": "code", "execution_count": 142, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.size $ Map.fromList [(2,4),(3,3),(4,2),(5,4),(6,4)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__`singleton`__ takes a key and a value and creates a map that has exactly one\n", "mapping." ] }, { "cell_type": "code", "execution_count": 143, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(3,9)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.singleton 3 9" ] }, { "cell_type": "code", "execution_count": 144, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(3,9),(5,9)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.insert 5 9 $ Map.singleton 3 9" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`lookup`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:lookup)__ works like the `Data.List` [`lookup`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:lookup), only it operates on maps. It\n", "returns `Just something` if it finds something for the key and [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) if\n", "it doesn't.\n", "\n", "__`member`__ is a predicate takes a key and a map and reports whether the key\n", "is in the map or not." ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.member 3 $ Map.fromList [(3,6),(4,3),(6,9)]" ] }, { "cell_type": "code", "execution_count": 146, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.member 3 $ Map.fromList [(2,5),(4,5)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map)__ and __[`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter)__ work much like their list equivalents." ] }, { "cell_type": "code", "execution_count": 147, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(1,100),(2,400),(3,900)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.map (*100) $ Map.fromList [(1,1),(2,4),(3,9)]" ] }, { "cell_type": "code", "execution_count": 148, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(2,'A'),(4,'B')]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.filter isUpper $ Map.fromList [(1,'a'),(2,'A'),(3,'b'),(4,'B')]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`toList`](https://hackage.haskell.org/package/base/docs/Data-Foldable.html#v:toList)__ is the inverse of [`fromList`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:fromList)." ] }, { "cell_type": "code", "execution_count": 149, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(4,3),(9,2)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.toList . Map.insert 9 2 $ Map.singleton 4 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__`keys`__ and __`elems`__ return lists of keys and values respectively. `keys` is the\n", "equivalent of map fst . Map.toList and elems is the equivalent of map\n", "snd . Map.toList.\n", "\n", "__`fromListWith`__ is a cool little function. It acts like [`fromList`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:fromList), only it\n", "doesn't discard duplicate keys but it uses a function supplied to it to\n", "decide what to do with them. Let's say that a girl can have several\n", "numbers and we have an association list set up like this." ] }, { "cell_type": "code", "execution_count": 150, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "phoneBook =\n", " [(\"betty\",\"555-2938\")\n", " ,(\"betty\",\"342-2492\")\n", " ,(\"bonnie\",\"452-2928\")\n", " ,(\"patsy\",\"493-2928\")\n", " ,(\"patsy\",\"943-2929\")\n", " ,(\"patsy\",\"827-9162\")\n", " ,(\"lucille\",\"205-2928\")\n", " ,(\"wendy\",\"939-8282\")\n", " ,(\"penny\",\"853-2492\")\n", " ,(\"penny\",\"555-2111\")\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now if we just use [`fromList`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:fromList) to put that into a map, we'll lose a few\n", "numbers! So here's what we'll do:" ] }, { "cell_type": "code", "execution_count": 151, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "phoneBookToMap :: (Ord k) => [(k, String)] -> Map.Map k String\n", "phoneBookToMap xs = Map.fromListWith (\\number1 number2 -> number1 ++ \", \" ++ number2) xs" ] }, { "cell_type": "code", "execution_count": 152, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"827-9162, 943-2929, 493-2928\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.lookup \"patsy\" $ phoneBookToMap phoneBook" ] }, { "cell_type": "code", "execution_count": 153, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"939-8282\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.lookup \"wendy\" $ phoneBookToMap phoneBook" ] }, { "cell_type": "code", "execution_count": 154, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"342-2492, 555-2938\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.lookup \"betty\" $ phoneBookToMap phoneBook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If a duplicate key is found, the function we pass is used to combine the\n", "values of those keys into some other value. We could also first make all\n", "the values in the association list singleton lists and then we can use\n", "[`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) to combine the numbers." ] }, { "cell_type": "code", "execution_count": 155, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "phoneBookToMap :: (Ord k) => [(k, a)] -> Map.Map k [a]\n", "phoneBookToMap xs = Map.fromListWith (++) $ map (\\(k,v) -> (k,[v])) xs" ] }, { "cell_type": "code", "execution_count": 156, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just [\"827-9162\",\"943-2929\",\"493-2928\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.lookup \"patsy\" $ phoneBookToMap phoneBook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty neat! Another use case is if we're making a map from an\n", "association list of numbers and when a duplicate key is found, we want\n", "the biggest value for the key to be kept." ] }, { "cell_type": "code", "execution_count": 157, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(2,100),(3,29),(4,22)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.fromListWith max [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or we could choose to add together values on the same keys." ] }, { "cell_type": "code", "execution_count": 158, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(2,108),(3,62),(4,37)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.fromListWith (+) [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__`insertWith`__ is to [`insert`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:insert) what `fromListWith` is to [`fromList`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:fromList). It inserts a\n", "key-value pair into a map, but if that map already contains the key, it\n", "uses the function passed to it to determine what to do." ] }, { "cell_type": "code", "execution_count": 159, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [(3,104),(5,103),(6,339)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Map.insertWith (+) 3 100 $ Map.fromList [(3,4),(5,103),(6,339)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These were just a few functions from `Data.Map`. You can see a complete\n", "list of functions in the\n", "[documentation](http://www.haskell.org/ghc/docs/latest/html/libraries/containers/Data-Map.html#v%3Aassocs).\n", "\n", "Data.Set\n", "--------\n", "\n", "\n", "\n", "The `Data.Set` module offers us, well, sets. Like sets from mathematics.\n", "Sets are kind of like a cross between lists and maps. All the elements\n", "in a set are unique. And because they're internally implemented with\n", "trees (much like maps in `Data.Map`), they're ordered. Checking for\n", "membership, inserting, deleting, etc. is much faster than doing the same\n", "thing with lists. The most common operation when dealing with sets are\n", "inserting into a set, checking for membership and converting a set to a\n", "list.\n", "\n", "Because the names in `Data.Set` clash with a lot of [`Prelude`](https://hackage.haskell.org/package/base/docs/Prelude.html) and `Data.List`\n", "names, we do a qualified import.\n", "\n", "Put this import statement in a script:" ] }, { "cell_type": "code", "execution_count": 160, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import qualified Data.Set as Set" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then load the script via GHCI.\n", "\n", "Let's say we have two pieces of text. We want to find out which\n", "characters were used in both of them." ] }, { "cell_type": "code", "execution_count": 161, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "text1 = \"I just had an anime dream. Anime... Reality... Are they so different?\"\n", "text2 = \"The old man left his garbage can out and now his trash is all over my lawn!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The __[`fromList`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:fromList)__ function works much like you would expect. It takes a list\n", "and converts it into a set." ] }, { "cell_type": "code", "execution_count": 162, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let set1 = Set.fromList text1\n", "let set2 = Set.fromList text2" ] }, { "cell_type": "code", "execution_count": 163, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList \" .?AIRadefhijlmnorstuy\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "set1" ] }, { "cell_type": "code", "execution_count": 164, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList \" !Tabcdefghilmnorstuvwy\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "set2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, the items are ordered and each element is unique. Now\n", "let's use the __`intersection`__ function to see which elements they both\n", "share." ] }, { "cell_type": "code", "execution_count": 165, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList \" adefhilmnorstuy\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.intersection set1 set2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use the __`difference`__ function to see which letters are in the first\n", "set but aren't in the second one and vice versa." ] }, { "cell_type": "code", "execution_count": 166, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList \".?AIRj\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.difference set1 set2" ] }, { "cell_type": "code", "execution_count": 167, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList \"!Tbcgvw\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.difference set2 set1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or we can see all the unique letters used in both sentences by using\n", "__[`union`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:union)__." ] }, { "cell_type": "code", "execution_count": 168, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList \" !.?AIRTabcdefghijlmnorstuvwy\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.union set1 set2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The __[`null`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:null)__, __`size`__, __`member`__, __[`empty`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#v:empty)__, __`singleton`__, __[`insert`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:insert)__ and __[`delete`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:delete)__ functions\n", "all work like you'd expect them to." ] }, { "cell_type": "code", "execution_count": 169, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.null Set.empty" ] }, { "cell_type": "code", "execution_count": 170, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.null $ Set.fromList [3,4,5,5,4,3]" ] }, { "cell_type": "code", "execution_count": 171, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.size $ Set.fromList [3,4,5,3,4,5]" ] }, { "cell_type": "code", "execution_count": 172, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [9]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.singleton 9" ] }, { "cell_type": "code", "execution_count": 173, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [1,3,4,8,9]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.insert 4 $ Set.fromList [9,3,8,1]" ] }, { "cell_type": "code", "execution_count": 174, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [5,6,7,8,9,10]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.insert 8 $ Set.fromList [5..10]" ] }, { "cell_type": "code", "execution_count": 175, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [3,5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.delete 4 $ Set.fromList [3,4,5,4,3,4,5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also check for subsets or proper subset. Set A is a subset of set\n", "B if B contains all the elements that A does. Set A is a proper subset\n", "of set B if B contains all the elements that A does but has more\n", "elements." ] }, { "cell_type": "code", "execution_count": 176, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.fromList [2,3,4] `Set.isSubsetOf` Set.fromList [1,2,3,4,5]" ] }, { "cell_type": "code", "execution_count": 177, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.fromList [1,2,3,4,5] `Set.isSubsetOf` Set.fromList [1,2,3,4,5]" ] }, { "cell_type": "code", "execution_count": 178, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.fromList [1,2,3,4,5] `Set.isProperSubsetOf` Set.fromList [1,2,3,4,5]" ] }, { "cell_type": "code", "execution_count": 179, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.fromList [2,3,4,8] `Set.isSubsetOf` Set.fromList [1,2,3,4,5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also __[`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map)__ over sets and __[`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter)__ them." ] }, { "cell_type": "code", "execution_count": 180, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [3,5,7]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.filter odd $ Set.fromList [3,4,5,6,7,2,3,4]" ] }, { "cell_type": "code", "execution_count": 181, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "fromList [3,4,5,6,7,8]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Set.map (+1) $ Set.fromList [3,4,5,6,7,2,3,4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sets are often used to weed a list of duplicates from a list by first\n", "making it into a set with [`fromList`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:fromList) and then converting it back to a list\n", "with __[`toList`](https://hackage.haskell.org/package/base/docs/Data-Foldable.html#v:toList)__. The `Data.List` function [`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub) already does that, but weeding\n", "out duplicates for large lists is much faster if you cram them into a\n", "set and then convert them back to a list than using [`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub). But using [`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub)\n", "only requires the type of the list's elements to be part of the `Eq`\n", "typeclass, whereas if you want to cram elements into a set, the type of\n", "the list has to be in [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord)." ] }, { "cell_type": "code", "execution_count": 182, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\" ACEHIKLNRSTWY\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let setNub xs = Set.toList $ Set.fromList xs\n", "setNub \"HEY WHATS CRACKALACKIN\"" ] }, { "cell_type": "code", "execution_count": 183, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"HEY WATSCRKLIN\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nub \"HEY WHATS CRACKALACKIN\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`setNub` is generally faster than [`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub) on big lists but as you can see, [`nub`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:nub)\n", "preserves the ordering of the list's elements, while `setNub` does not.\n", "\n", "Making our own modules\n", "----------------------\n", "\n", "\n", "\n", "We've looked at some cool modules so far, but how do we make our own\n", "module? Almost every programming language enables you to split your code\n", "up into several files and Haskell is no different. When making programs,\n", "it's good practice to take functions and types that work towards a\n", "similar purpose and put them in a module. That way, you can easily reuse\n", "those functions in other programs by just importing your module.\n", "\n", "Let's see how we can make our own modules by making a little module that\n", "provides some functions for calculating the volume and area of a few\n", "geometrical objects. We'll start by creating a file called `Geometry.hs`.\n", "\n", "We say that a module *exports* functions. What that means is that when I\n", "import a module, I can use the functions that it exports. It can define\n", "functions that its functions call internally, but we can only see and\n", "use the ones that it exports.\n", "\n", "At the beginning of a module, we specify the module name. If we have a\n", "file called `Geometry.hs`, then we should name our module `Geometry`. Then,\n", "we specify the functions that it exports and after that, we can start\n", "writing the functions. So we'll start with this." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
module Geometry\n",
    "( sphereVolume\n",
    ", sphereArea\n",
    ", cubeVolume\n",
    ", cubeArea\n",
    ", cuboidArea\n",
    ", cuboidVolume\n",
    ") where
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, we'll be doing areas and volumes for spheres, cubes and\n", "cuboids. Let's go ahead and define our functions then:\n", "\n", "### `Geometry.hs`" ] }, { "cell_type": "code", "execution_count": 184, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "module Geometry\n", "( sphereVolume\n", ", sphereArea\n", ", cubeVolume\n", ", cubeArea\n", ", cuboidArea\n", ", cuboidVolume\n", ") where\n", "\n", "sphereVolume :: Float -> Float\n", "sphereVolume radius = (4.0 / 3.0) * pi * (radius ^ 3)\n", "\n", "sphereArea :: Float -> Float\n", "sphereArea radius = 4 * pi * (radius ^ 2)\n", "\n", "cubeVolume :: Float -> Float\n", "cubeVolume side = cuboidVolume side side side\n", "\n", "cubeArea :: Float -> Float\n", "cubeArea side = cuboidArea side side side\n", "\n", "cuboidVolume :: Float -> Float -> Float -> Float\n", "cuboidVolume a b c = rectangleArea a b * c\n", "\n", "cuboidArea :: Float -> Float -> Float -> Float\n", "cuboidArea a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2\n", "\n", "rectangleArea :: Float -> Float -> Float\n", "rectangleArea a b = a * b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty standard geometry right here. There are a few things to take note\n", "of though. Because a cube is only a special case of a cuboid, we defined\n", "its area and volume by treating it as a cuboid whose sides are all of\n", "the same length. We also defined a helper function called `rectangleArea`,\n", "which calculates a rectangle's area based on the lengths of its sides.\n", "It's rather trivial because it's just multiplication. Notice that we\n", "used it in our functions in the module (namely `cuboidArea` and\n", "`cuboidVolume`) but we didn't export it! Because we want our module to\n", "just present functions for dealing with three dimensional objects, we\n", "used `rectangleArea` but we didn't export it.\n", "\n", "When making a module, we usually export only those functions that act as\n", "a sort of interface to our module so that the implementation is hidden.\n", "If someone is using our `Geometry` module, they don't have to concern\n", "themselves with functions that we don't export. We can decide to change\n", "those functions completely or delete them in a newer version (we could\n", "delete `rectangleArea` and just use [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) instead) and no one will mind\n", "because we weren't exporting them in the first place.\n", "\n", "To use our module, we just do:" ] }, { "cell_type": "code", "execution_count": 185, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Geometry" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Geometry.hs` has to be in the same folder that the program that's\n", "importing it is in, though.\n", "\n", "Modules can also be given a hierarchical structures. Each module can\n", "have a number of sub-modules and they can have sub-modules of their own.\n", "Let's section these functions off so that `Geometry` is a module that has\n", "three sub-modules, one for each type of object.\n", "\n", "First, we'll make a folder called `Geometry`. Mind the capital G. In it,\n", "we'll place three files: `Sphere.hs`, `Cuboid.hs`, and `Cube.hs`. Here's what\n", "the files will contain:\n", "\n", "### `Sphere.hs`" ] }, { "cell_type": "code", "execution_count": 186, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "module Geometry.Sphere\n", "( volume\n", ", area\n", ") where\n", "\n", "volume :: Float -> Float\n", "volume radius = (4.0 / 3.0) * pi * (radius ^ 3)\n", "\n", "area :: Float -> Float\n", "area radius = 4 * pi * (radius ^ 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `Cuboid.hs`" ] }, { "cell_type": "code", "execution_count": 187, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "module Geometry.Cuboid\n", "( volume\n", ", area\n", ") where\n", "\n", "volume :: Float -> Float -> Float -> Float\n", "volume a b c = rectangleArea a b * c\n", "\n", "area :: Float -> Float -> Float -> Float\n", "area a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2\n", "\n", "rectangleArea :: Float -> Float -> Float\n", "rectangleArea a b = a * b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `Cube.hs`" ] }, { "cell_type": "code", "execution_count": 188, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "module Geometry.Cube\n", "( volume\n", ", area\n", ") where\n", "\n", "import qualified Geometry.Cuboid as Cuboid\n", "\n", "volume :: Float -> Float\n", "volume side = Cuboid.volume side side side\n", "\n", "area :: Float -> Float\n", "area side = Cuboid.area side side side" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alright! So first is `Geometry.Sphere`. Notice how we placed it in a\n", "folder called `Geometry` and then defined the module name as\n", "`Geometry.Sphere`. We did the same for the cuboid. Also notice how in all\n", "three sub-modules, we defined functions with the same names. We can do\n", "this because they're separate modules. We want to use functions from\n", "`Geometry.Cuboid` in `Geometry.Cube` but we can't just straight up do\n", "`import Geometry.Cuboid` because it exports functions with the same names as\n", "`Geometry.Cube`. That's why we do a qualified import and all is well.\n", "\n", "So now if we're in a file that's on the same level as the `Geometry`\n", "folder, we can do, say:" ] }, { "cell_type": "code", "execution_count": 189, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Geometry.Sphere" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then we can call `area` and `volume` and they'll give us the area and\n", "volume for a sphere. And if we want to juggle two or more of these\n", "modules, we have to do qualified imports because they export functions\n", "with the same names. So we just do something like:" ] }, { "cell_type": "code", "execution_count": 190, "metadata": { "attributes": { "classes": [ "haskell:ghci" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import qualified Geometry.Sphere as Sphere\n", "import qualified Geometry.Cuboid as Cuboid\n", "import qualified Geometry.Cube as Cube" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then we can call `Sphere.area`, `Sphere.volume`, `Cuboid.area`, etc. and\n", "each will calculate the area or volume for their corresponding object.\n", "\n", "The next time you find yourself writing a file that's really big and has\n", "a lot of functions, try to see which functions serve some common purpose\n", "and then see if you can put them in their own module. You'll be able to\n", "just import your module the next time you're writing a program that\n", "requires some of the same functionality." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/08-making-our-own-types-and-typeclasses.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Making Our Own Types and Typeclasses\n", "====================================\n", "\n", "In the previous chapters, we covered some existing Haskell types and\n", "typeclasses. In this chapter, we'll learn how to make our own and how to\n", "put them to work!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Algebraic data types intro\n", "--------------------------\n", "\n", "So far, we've run into a lot of data types. [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool), [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), [`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char), [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), etc.\n", "But how do we make our own? Well, one way is to use the *data* keyword\n", "to define a type. Let's see how the [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) type is defined in the standard\n", "library." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data Bool = False | True
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`\n", "data` means that we're defining a new data type. The part before the `=`\n", "denotes the type, which is [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool). The parts after the `=` are *value\n", "constructors*. They specify the different values that this type can\n", "have. The `|` is read as *or*. So we can read this as: the [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) type can\n", "have a value of [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) or [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False). Both the type name and the value\n", "constructors have to be capital cased.\n", "\n", "In a similar fashion, we can think of the [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) type as being defined like\n", "this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "The first and last value constructors are the minimum and maximum\n", "possible values of [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int). It's not actually defined like this, the\n", "ellipses are here because we omitted a heapload of numbers, so this is\n", "just for illustrative purposes.\n", "\n", "Now, let's think about how we would represent a shape in Haskell. One\n", "way would be to use tuples. A circle could be denoted as\n", "`(43.1, 55.0, 10.4)`\n", "where the first and second fields are the coordinates of the\n", "circle's center and the third field is the radius. Sounds OK, but those\n", "could also represent a 3D vector or anything else. A better solution\n", "would be to make our own type to represent a shape. Let's say that a\n", "shape can be a circle or a rectangle. Here it is:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Shape = Circle Float Float Float | Rectangle Float Float Float Float" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now what's this? Think of it like this. The `Circle` value constructor has\n", "three fields, which take floats. So when we write a value constructor,\n", "we can optionally add some types after it and those types define the\n", "values it will contain. Here, the first two fields are the coordinates\n", "of its center, the third one its radius. The `Rectangle` value constructor\n", "has four fields which accept floats. The first two are the coordinates\n", "to its upper left corner and the second two are coordinates to its lower\n", "right one.\n", "\n", "Now when I say fields, I actually mean parameters. Value constructors\n", "are actually functions that ultimately return a value of a data type.\n", "Let's take a look at the type signatures for these two value\n", "constructors." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Circle :: Float -> Float -> Float -> Shape" ], "text/plain": [ "Circle :: Float -> Float -> Float -> Shape" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Circle" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Rectangle :: Float -> Float -> Float -> Float -> Shape" ], "text/plain": [ "Rectangle :: Float -> Float -> Float -> Float -> Shape" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Rectangle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool, so value constructors are functions like everything else. Who\n", "would have thought? Let's make a function that takes a shape and returns\n", "its surface." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "surface :: Shape -> Float\n", "surface (Circle _ _ r) = pi * r ^ 2\n", "surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first notable thing here is the type declaration. It says that the\n", "function takes a shape and returns a float. We couldn't write a type\n", "declaration of `Circle -> Float` because `Circle` is not a type, `Shape` is.\n", "Just like we can't write a function with a type declaration of\n", "`True -> Int`.\n", "The next thing we notice here is that we can pattern match against\n", "constructors. We pattern matched against constructors before (all the\n", "time actually) when we pattern matched against values like `[]` or [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False)\n", "or `5`, only those values didn't have any fields. We just write a\n", "constructor and then bind its fields to names. Because we're interested\n", "in the radius, we don't actually care about the first two fields, which\n", "tell us where the circle is." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "314.15927" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "surface $ Circle 10 20 10" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "10000.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "surface $ Rectangle 0 0 100 100" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yay, it works! But if we try to just print out `Circle 10 20 5` in the\n", "prompt, we'll get an error. That's because Haskell doesn't know how to\n", "display our data type as a string (yet). Remember, when we try to print\n", "a value out in the prompt, Haskell first runs the [`show`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:show) function to get\n", "the string representation of our value and then it prints that out to\n", "the terminal. To make our `Shape` type part of the [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) typeclass, we\n", "modify it like this:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We won't concern ourselves with deriving too much for now. Let's just\n", "say that if we add `deriving (Show)` at the end of a *data* declaration,\n", "Haskell automagically makes that type part of the [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) typeclass. So\n", "now, we can do this:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Circle 10.0 20.0 5.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Circle 10 20 5" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Rectangle 50.0 230.0 60.0 90.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Rectangle 50 230 60 90" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Value constructors are functions, so we can map them and partially apply\n", "them and everything. If we want a list of concentric circles with\n", "different radii, we can do this." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[Circle 10.0 20.0 4.0,Circle 10.0 20.0 5.0,Circle 10.0 20.0 6.0,Circle 10.0 20.0 6.0]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (Circle 10 20) [4,5,6,6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our data type is good, although it could be better. Let's make an\n", "intermediate data type that defines a point in two-dimensional space.\n", "Then we can use that to make our shapes more understandable." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Point = Point Float Float deriving (Show)\n", "data Shape = Circle Point Float | Rectangle Point Point deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that when defining a point, we used the same name for the data\n", "type and the value constructor. This has no special meaning, although\n", "it's common to use the same name as the type if there's only one value\n", "constructor. So now the `Circle` has two fields, one is of type `Point` and\n", "the other of type [`Float`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Float). This makes it easier to understand what's what.\n", "Same goes for the rectangle. We have to adjust our `surface` function to\n", "reflect these changes." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "surface :: Shape -> Float\n", "surface (Circle _ r) = pi * r ^ 2\n", "surface (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The only thing we had to change were the patterns. We disregarded the\n", "whole point in the circle pattern. In the rectangle pattern, we just\n", "used a nested pattern matching to get the fields of the points. If we\n", "wanted to reference the points themselves for some reason, we could have\n", "used as-patterns." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "10000.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "surface (Rectangle (Point 0 0) (Point 100 100))" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1809.5574" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "surface (Circle (Point 0 0) 24)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How about a function that nudges a shape? It takes a shape, the amount\n", "to move it on the x axis and the amount to move it on the y axis and\n", "then returns a new shape that has the same dimensions, only it's located\n", "somewhere else." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "nudge :: Shape -> Float -> Float -> Shape\n", "nudge (Circle (Point x y) r) a b = Circle (Point (x+a) (y+b)) r\n", "nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b = Rectangle (Point (x1+a) (y1+b)) (Point (x2+a) (y2+b))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty straightforward. We add the nudge amounts to the points that\n", "denote the position of the shape." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Circle (Point 39.0 44.0) 10.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nudge (Circle (Point 34 34) 10) 5 10" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we don't want to deal directly with points, we can make some\n", "auxilliary functions that create shapes of some size at the zero\n", "coordinates and then nudge those." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "baseCircle :: Float -> Shape\n", "baseCircle r = Circle (Point 0 0) r\n", "\n", "baseRect :: Float -> Float -> Shape\n", "baseRect width height = Rectangle (Point 0 0) (Point width height)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Rectangle (Point 60.0 23.0) (Point 100.0 123.0)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nudge (baseRect 40 100) 60 23" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can, of course, export your data types in your modules. To do that,\n", "just write your type along with the functions you are exporting and then\n", "add some parentheses and in them specify the value constructors that you\n", "want to export for it, separated by commas. If you want to export all\n", "the value constructors for a given type, just write `..`.\n", "\n", "If we wanted to export the functions and types that we defined here in a\n", "module, we could start it off like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
module Shapes\n",
    "( Point(..)\n",
    ", Shape(..)\n",
    ", surface\n",
    ", nudge\n",
    ", baseCircle\n",
    ", baseRect\n",
    ") where
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By doing `Shape(..)`, we exported all the value constructors for `Shape`, so\n", "that means that whoever imports our module can make shapes by using the\n", "`Rectangle` and `Circle` value constructors. It's the same as writing\n", "`Shape (Rectangle, Circle)`.\n", "\n", "We could also opt not to export any value constructors for `Shape` by just\n", "writing `Shape` in the export statement. That way, someone importing our\n", "module could only make shapes by using the auxiliary functions\n", "`baseCircle` and `baseRect`. `Data.Map` uses that approach. You can't create a\n", "map by doing `Map.Map [(1,2),(3,4)]` because it doesn't export that value\n", "constructor. However, you can make a mapping by using one of the\n", "auxilliary functions like `Map.fromList`. Remember, value constructors are\n", "just functions that take the fields as parameters and return a value of\n", "some type (like `Shape`) as a result. So when we choose not to export\n", "them, we just prevent the person importing our module from using those\n", "functions, but if some other functions that are exported return a type,\n", "we can use them to make values of our custom data types.\n", "\n", "Not exporting the value constructors of a data types makes them more\n", "abstract in such a way that we hide their implementation. Also, whoever\n", "uses our module can't pattern match against the value constructors.\n", "\n", "Record syntax\n", "-------------\n", "\n", "\n", "\n", "OK, we've been tasked with creating a data type that describes a person.\n", "The info that we want to store about that person is: first name, last\n", "name, age, height, phone number, and favorite ice-cream flavor. I don't\n", "know about you, but that's all I ever want to know about a person. Let's\n", "give it a go!" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Person = Person String String Int Float String String deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O-kay. The first field is the first name, the second is the last name,\n", "the third is the age and so on. Let's make a person." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Person \"Buddy\" \"Finklestein\" 43 184.2 \"526-2928\" \"Chocolate\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let guy = Person \"Buddy\" \"Finklestein\" 43 184.2 \"526-2928\" \"Chocolate\"\n", "guy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's kind of cool, although slightly unreadable. What if we want to\n", "create a function to get separate info from a person? A function that\n", "gets some person's first name, a function that gets some person's last\n", "name, etc. Well, we'd have to define them kind of like this." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "firstName :: Person -> String\n", "firstName (Person firstname _ _ _ _ _) = firstname\n", "\n", "lastName :: Person -> String\n", "lastName (Person _ lastname _ _ _ _) = lastname\n", "\n", "age :: Person -> Int\n", "age (Person _ _ age _ _ _) = age\n", "\n", "height :: Person -> Float\n", "height (Person _ _ _ height _ _) = height\n", "\n", "phoneNumber :: Person -> String\n", "phoneNumber (Person _ _ _ _ number _) = number\n", "\n", "flavor :: Person -> String\n", "flavor (Person _ _ _ _ _ flavor) = flavor" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whew! I certainly did not enjoy writing that! Despite being very\n", "cumbersome and BORING to write, this method works." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let guy = Person \"Buddy\" \"Finklestein\" 43 184.2 \"526-2928\" \"Chocolate\"" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Buddy\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "firstName guy" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "184.2" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "height guy" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Chocolate\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "flavor guy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There must be a better way, you say! Well no, there isn't, sorry.\n", "\n", "Just kidding, there is. Hahaha! The makers of Haskell were very smart\n", "and anticipated this scenario. They included an alternative way to write\n", "data types. Here's how we could achieve the above functionality with\n", "record syntax." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Person = Person { firstName :: String\n", " , lastName :: String\n", " , age :: Int\n", " , height :: Float\n", " , phoneNumber :: String\n", " , flavor :: String\n", " } deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So instead of just naming the field types one after another and\n", "separating them with spaces, we use curly brackets. First we write the\n", "name of the field, for instance, `firstName` and then we write a double\n", "colon `::` (also called Paamayim Nekudotayim, haha) and then we specify\n", "the type. The resulting data type is exactly the same. The main benefit\n", "of this is that it creates functions that lookup fields in the data\n", "type. By using record syntax to create this data type, Haskell\n", "automatically made these functions: `firstName`, `lastName`, `age`, `height`,\n", "`phoneNumber` and `flavor`." ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "flavor :: Person -> String" ], "text/plain": [ "flavor :: Person -> String" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t flavor" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "firstName :: Person -> String" ], "text/plain": [ "firstName :: Person -> String" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t firstName" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There's another benefit to using record syntax. When we derive [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) for\n", "the type, it displays it differently if we use record syntax to define\n", "and instantiate the type. Say we have a type that represents a car. We\n", "want to keep track of the company that made it, the model name and its\n", "year of production. Watch." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Car = Car String String Int deriving (Show)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Car \"Ford\" \"Mustang\" 1967" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Car \"Ford\" \"Mustang\" 1967" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we define it using record syntax, we can make a new car like this." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Car = Car {company :: String, model :: String, year :: Int} deriving (Show)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Car {company = \"Ford\", model = \"Mustang\", year = 1967}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Car {company=\"Ford\", model=\"Mustang\", year=1967}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When making a new car, we don't have to necessarily put the fields in\n", "the proper order, as long as we list all of them. But if we don't use\n", "record syntax, we have to specify them in order.\n", "\n", "Use record syntax when a constructor has several fields and it's not\n", "obvious which field is which. If we make a 3D vector data type by doing\n", "`data Vector = Vector Int Int Int`, it's pretty obvious that the fields\n", "are the components of a vector. However, in our `Person` and `Car` types, it\n", "wasn't so obvious and we greatly benefited from using record syntax.\n", "\n", "Type parameters\n", "---------------\n", "\n", "A value constructor can take some values parameters and then produce a\n", "new value. For instance, the `Car` constructor takes three values and\n", "produces a car value. In a similar manner, *type constructors* can take\n", "types as parameters to produce new types. This might sound a bit too\n", "meta at first, but it's not that complicated. If you're familiar with\n", "templates in C++, you'll see some parallels. To get a clear picture of\n", "what type parameters work like in action, let's take a look at how a\n", "type we've already met is implemented." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data Maybe a = Nothing | Just a deriving (Show)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "The `a` here is the type parameter. And because there's a type parameter\n", "involved, we call [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) a type constructor. Depending on what we want\n", "this data type to hold when it's not [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), this type constructor can\n", "end up producing a type of `Maybe Int`, `Maybe Car`, `Maybe String`, etc. No\n", "value can have a type of just [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), because that's not a type per se,\n", "it's a type constructor. In order for this to be a real type that a\n", "value can be part of, it has to have all its type parameters filled up.\n", "\n", "So if we pass [`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char) as the type parameter to [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), we get a type of\n", "`Maybe Char`. The value `Just 'a'` has a type of `Maybe Char`, for example.\n", "\n", "You might not know it, but we used a type that has a type parameter\n", "before we used [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe). That type is the list type. Although there's some\n", "syntactic sugar in play, the list type takes a parameter to produce a\n", "concrete type. Values can have an `[Int]` type, a `[Char]` type, a\n", "`[[String]]` type, but you can't have a value that just has a type of `[]`.\n", "\n", "Let's play around with the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) type." ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"Haha\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just \"Haha\"" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 84" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 84" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Just \"Haha\" :: Maybe String" ], "text/plain": [ "Just \"Haha\" :: Maybe String" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Just \"Haha\"" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Just 84 :: forall {a}. Num a => Maybe a" ], "text/plain": [ "Just 84 :: forall {a}. Num a => Maybe a" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Just 84" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Nothing :: forall a. Maybe a" ], "text/plain": [ "Nothing :: forall a. Maybe a" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Nothing" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 10.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 10 :: Maybe Double" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Type parameters are useful because we can make different types with them\n", "depending on what kind of types we want contained in our data type. When\n", "we do `:t Just \"Haha\"`, the type inference engine figures it out to be of\n", "the type `Maybe [Char]`, because if the `a` in the `Just a` is a string, then\n", "the `a` in `Maybe a` must also be a string.\n", "\n", "Notice that the type of [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) is `Maybe a`. Its type is polymorphic. If\n", "some function requires a `Maybe Int` as a parameter, we can give it a\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), because a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) doesn't contain a value anyway and so it\n", "doesn't matter. The `Maybe a` type can act like a `Maybe Int` if it has to,\n", "just like `5` can act like an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) or a [`Double`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Double). Similarly, the type of the\n", "empty list is `[a]`. An empty list can act like a list of anything. That's\n", "why we can do `[1,2,3] ++ []` and `[\"ha\",\"ha\",\"ha\"] ++ []`.\n", "\n", "Using type parameters is very beneficial, but only when using them makes\n", "sense. Usually we use them when our data type would work regardless of\n", "the type of the value it then holds inside it, like with our `Maybe a`\n", "type. If our type acts as some kind of box, it's good to use them. " ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "> __Jupyter Note:__ `DuplicateRecordsFields` to allow Car1 and Car2 to have the same record field names." ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":extension DuplicateRecordFields" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could change our `Car` data type from this:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Car1 = Car1 { company :: String\n", " , model :: String\n", " , year :: Int\n", " } deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To this:" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Car2 a b c = Car2 { company :: a\n", " , model :: b\n", " , year :: c\n", " } deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But would we really benefit? The answer is: probably no, because we'd\n", "just end up defining functions that only work on the `Car String String Int` type.\n", "For instance, given our first definition of `Car1`, we could make\n", "a function that displays the car's properties in a nice little text." ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "tellCar1 :: Car1 -> String\n", "tellCar1 (Car1 {company = c, model = m, year = y}) = \"This \" ++ c ++ \" \" ++ m ++ \" was made in \" ++ show y" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"This Ford Mustang was made in 1967\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let stang = Car1 {company=\"Ford\", model=\"Mustang\", year=1967}\n", "tellCar1 stang" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A cute little function! The type declaration is cute and it works\n", "nicely. Now what if Car was Car2 a b c?" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "tellCar2 :: (Show a) => Car2 String String a -> String\n", "tellCar2 (Car2 {company = c, model = m, year = y}) = \"This \" ++ c ++ \" \" ++ m ++ \" was made in \" ++ show y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'd have to force this function to take a `Car2` type of\n", "`(Show a) => Car2 String String a`.\n", "You can see that the type signature is more complicated\n", "and the only benefit we'd actually get would be that we can use any type\n", "that's an instance of the [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) typeclass as the type for `c`." ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"This Ford Mustang was made in 1967\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "tellCar2 (Car2 \"Ford\" \"Mustang\" 1967)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"This Ford Mustang was made in \\\"nineteen sixty seven\\\"\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "tellCar2 (Car2 \"Ford\" \"Mustang\" \"nineteen sixty seven\")" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Car2 \"Ford\" \"Mustang\" 1967 :: forall {c}. Num c => Car2 String String c" ], "text/plain": [ "Car2 \"Ford\" \"Mustang\" 1967 :: forall {c}. Num c => Car2 String String c" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Car2 \"Ford\" \"Mustang\" 1967" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Car2 \"Ford\" \"Mustang\" \"nineteen sixty seven\" :: Car2 String String String" ], "text/plain": [ "Car2 \"Ford\" \"Mustang\" \"nineteen sixty seven\" :: Car2 String String String" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Car2 \"Ford\" \"Mustang\" \"nineteen sixty seven\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "In real life though, we'd end up using `Car String String Int` most of the\n", "time and so it would seem that parameterizing the `Car` type isn't really\n", "worth it. We usually use type parameters when the type that's contained\n", "inside the data type's various value constructors isn't really that\n", "important for the type to work. A list of stuff is a list of stuff and\n", "it doesn't matter what the type of that stuff is, it can still work. If\n", "we want to sum a list of numbers, we can specify later in the summing\n", "function that we specifically want a list of numbers. Same goes for\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe). [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) represents an option of either having nothing or having one\n", "of something. It doesn't matter what the type of that something is.\n", "\n", "Another example of a parameterized type that we've already met is\n", "`Map k v` from `Data.Map`. The `k` is the type of the keys in a map and the `v` is the\n", "type of the values. This is a good example of where type parameters are\n", "very useful. Having maps parameterized enables us to have mappings from\n", "any type to any other type, as long as the type of the key is part of\n", "the [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) typeclass. If we were defining a mapping type, we could add a\n", "typeclass constraint in the *data* declaration:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data (Ord k) => Map k v = ...
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, it's a very strong convention in Haskell to *never add\n", "typeclass constraints in data declarations.* Why? Well, because we don't\n", "benefit a lot, but we end up writing more class constraints, even when\n", "we don't need them. If we put or don't put the `Ord k` constraint in the\n", "*data* declaration for `Map k v`, we're going to have to put the\n", "constraint into functions that assume the keys in a map can be ordered.\n", "But if we don't put the constraint in the data declaration, we don't\n", "have to put `(Ord k) =>` in the type declarations of functions that don't\n", "care whether the keys can be ordered or not. An example of such a\n", "function is [`toList`](https://hackage.haskell.org/package/base/docs/Data-Foldable.html#v:toList), that just takes a mapping and converts it to an\n", "associative list. Its type signature is `toList :: Map k a -> [(k, a)]`.\n", "If `Map k v` had a type constraint in its *data* declaration, the type for\n", "[`toList`](https://hackage.haskell.org/package/base/docs/Data-Foldable.html#v:toList) would have to be `toList :: (Ord k) => Map k a -> [(k, a)]`, even\n", "though the function doesn't do any comparing of keys by order.\n", "\n", "So don't put type constraints into *data* declarations even if it seems\n", "to make sense, because you'll have to put them into the function type\n", "declarations either way.\n", "\n", "Let's implement a 3D vector type and add some operations for it. We'll\n", "be using a parameterized type because even though it will usually\n", "contain numeric types, it will still support several of them." ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Vector a = Vector a a a deriving (Show)\n", "\n", "vplus :: (Num t) => Vector t -> Vector t -> Vector t\n", "(Vector i j k) `vplus` (Vector l m n) = Vector (i+l) (j+m) (k+n)\n", "\n", "vectMult :: (Num t) => Vector t -> t -> Vector t\n", "(Vector i j k) `vectMult` m = Vector (i*m) (j*m) (k*m)\n", "\n", "scalarMult :: (Num t) => Vector t -> Vector t -> t\n", "(Vector i j k) `scalarMult` (Vector l m n) = i*l + j*m + k*n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`vplus` is for adding two vectors together. Two vectors are added just by\n", "adding their corresponding components. `scalarMult` is for the scalar\n", "product of two vectors and `vectMult` is for multiplying a vector with a\n", "scalar. These functions can operate on types of `Vector Int`,\n", "`Vector Integer`, `Vector Float`,\n", "whatever, as long as the `a` from `Vector a` is from\n", "the [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) typeclass. Also, if you examine the type declaration for these\n", "functions, you'll see that they can operate only on vectors of the same\n", "type and the numbers involved must also be of the type that is contained\n", "in the vectors. Notice that we didn't put a [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) class constraint in the\n", "*data* declaration, because we'd have to repeat it in the functions\n", "anyway.\n", "\n", "Once again, it's very important to distinguish between the type\n", "constructor and the value constructor. When declaring a data type, the\n", "part before the `=` is the type constructor and the constructors after it\n", "(possibly separated by `|`'s) are value constructors. Giving a function a\n", "type of `Vector t t t -> Vector t t t -> t` would be wrong, because we\n", "have to put types in type declaration and the vector *type* constructor\n", "takes only one parameter, whereas the value constructor takes three.\n", "Let's play around with our vectors." ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Vector 12 7 16" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Vector 3 5 8 `vplus` Vector 9 2 8" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Vector 12 9 19" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Vector 3 5 8 `vplus` Vector 9 2 8 `vplus` Vector 0 2 3" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Vector 30 90 70" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Vector 3 9 7 `vectMult` 10" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "74.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Vector 4 9 5 `scalarMult` Vector 9.0 2.0 4.0" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Vector 148 666 222" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Vector 2 9 3 `vectMult` (Vector 4 9 5 `scalarMult` Vector 9 2 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Derived instances\n", "-----------------\n", "\n", "\n", "\n", "In the [Typeclasses 101](http://learnyouahaskell.com/types-and-typeclasses#typeclasses-101) section,\n", "we explained the basics of typeclasses. We explained that a typeclass is\n", "a sort of an interface that defines some behavior. A type can be made an\n", "*instance* of a typeclass if it supports that behavior. Example: the [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int)\n", "type is an instance of the `Eq` typeclass because the `Eq` typeclass defines\n", "behavior for stuff that can be equated. And because integers can be\n", "equated, [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) is a part of the `Eq` typeclass. The real usefulness comes\n", "with the functions that act as the interface for `Eq`, namely [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) and [`/=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47--61-).\n", "If a type is a part of the `Eq` typeclass, we can use the [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) functions\n", "with values of that type. That's why expressions like `4 == 4` and\n", "`\"foo\" /= \"bar\"` typecheck.\n", "\n", "We also mentioned that they're often confused with classes in languages\n", "like Java, Python, C++ and the like, which then baffles a lot of people.\n", "In those languages, classes are a blueprint from which we then create\n", "objects that contain state and can do some actions. Typeclasses are more\n", "like interfaces. We don't make data from typeclasses. Instead, we first\n", "make our data type and then we think about what it can act like. If it\n", "can act like something that can be equated, we make it an instance of\n", "the `Eq` typeclass. If it can act like something that can be ordered, we\n", "make it an instance of the [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) typeclass.\n", "\n", "In the next section, we'll take a look at how we can manually make our\n", "types instances of typeclasses by implementing the functions defined by\n", "the typeclasses. But right now, let's see how Haskell can automatically\n", "make our type an instance of any of the following typeclasses: `Eq`, [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord),\n", "[`Enum`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Enum), [`Bounded`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bounded), [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show), [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read). Haskell can derive the behavior of our types\n", "in these contexts if we use the *deriving* keyword when making our data\n", "type.\n", "\n", "Consider this data type:" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Person = Person { firstName :: String\n", " , lastName :: String\n", " , age :: Int\n", " }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It describes a person. Let's assume that no two people have the same\n", "combination of first name, last name and age. Now, if we have records\n", "for two people, does it make sense to see if they represent the same\n", "person? Sure it does. We can try to equate them and see if they're equal\n", "or not. That's why it would make sense for this type to be part of the\n", "`Eq` typeclass. We'll derive the instance." ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Person = Person { firstName :: String\n", " , lastName :: String\n", " , age :: Int\n", " } deriving (Eq)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we derive the `Eq` instance for a type and then try to compare two\n", "values of that type with [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) or [`/=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47--61-), Haskell will see if the value\n", "constructors match (there's only one value constructor here though) and\n", "then it will check if all the data contained inside matches by testing\n", "each pair of fields with [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-). There's only one catch though, the types of\n", "all the fields also have to be part of the `Eq` typeclass. But since both\n", "[`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String) and [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) are, we're OK. Let's test our `Eq` instance." ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let mikeD = Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}\n", "let adRock = Person {firstName = \"Adam\", lastName = \"Horovitz\", age = 41}\n", "let mca = Person {firstName = \"Adam\", lastName = \"Yauch\", age = 44}" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mca == adRock" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mikeD == adRock" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mikeD == mikeD" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mikeD == Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Of course, since `Person` is now in `Eq`, we can use it as the `a` for all\n", "functions that have a class constraint of `Eq a` in their type signature,\n", "such as [`elem`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem)." ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let beastieBoys = [mca, adRock, mikeD]\n", "mikeD `elem` beastieBoys" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) and [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read) typeclasses are for things that can be converted to or\n", "from strings, respectively. Like with `Eq`, if a type's constructors have\n", "fields, their type has to be a part of [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) or [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read) if we want to make\n", "our type an instance of them. Let's make our `Person` data type a part of\n", "[`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) and [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read) as well." ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Person = Person { firstName :: String\n", " , lastName :: String\n", " , age :: Int\n", " } deriving (Eq, Show, Read)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can print a person out to the terminal." ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let mikeD = Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}\n", "mikeD" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"mikeD is: Person {firstName = \\\"Michael\\\", lastName = \\\"Diamond\\\", age = 43}\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"mikeD is: \" ++ show mikeD" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Had we tried to print a person on the terminal before making the `Person`\n", "data type part of [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show), Haskell would have complained at us, claiming it\n", "doesn't know how to represent a person as a string. But now that we've\n", "derived a [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) instance for it, it does know.\n", "\n", "[`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read) is pretty much the inverse typeclass of [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show). [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) is for\n", "converting values of our a type to a string, [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read) is for converting\n", "strings to values of our type. Remember though, when we use the [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read)\n", "function, we have to use an explicit type annotation to tell Haskell\n", "which type we want to get as a result. If we don't make the type we want\n", "as a result explicit, Haskell doesn't know which type we want." ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"Person {firstName =\\\"Michael\\\", lastName =\\\"Diamond\\\", age = 43}\" :: Person" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we use the result of our [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) later on in a way that Haskell can\n", "infer that it should read it as a person, we don't have to use type\n", "annotation." ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"Person {firstName =\\\"Michael\\\", lastName =\\\"Diamond\\\", age = 43}\" == mikeD" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also read parameterized types, but we have to fill in the type\n", "parameters. So we can't do `read \"Just 't'\" :: Maybe a`, but we can do\n", "`read \"Just 't'\" :: Maybe Char`.\n", "\n", "We can derive instances for the [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) type class, which is for types that\n", "have values that can be ordered. If we compare two values of the same\n", "type that were made using different constructors, the value which was\n", "made with a constructor that's defined first is considered smaller. For\n", "instance, consider the [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) type, which can have a value of either [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False)\n", "or [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True). For the purpose of seeing how it behaves when compared, we can\n", "think of it as being implemented like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data Bool = False | True deriving (Ord)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because the [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) value constructor is specified first and the [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True)\n", "value constructor is specified after it, we can consider [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) as greater\n", "than [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False)." ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "True `compare` False" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "True > False" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "True < False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the `Maybe a` data type, the [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) value constructor is specified\n", "before the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value constructor, so a value of [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) is always\n", "smaller than a value of `Just something`, even if that something is minus\n", "one billion trillion. But if we compare two [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) values, then it goes to\n", "compare what's inside them." ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing < Just 100" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing > Just (-49999)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 3 `compare` Just 2" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 100 > Just 50" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But we can't do something like `Just (*3) > Just (*2)`, because `(*3)`\n", "and `(*2)` are functions, which aren't instances of Ord.\n", "\n", "We can easily use algebraic data types to make enumerations and the [`Enum`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Enum)\n", "and [`Bounded`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bounded) typeclasses help us with that. Consider the following data\n", "type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because all the value constructors are nullary (take no parameters, i.e.\n", "fields), we can make it part of the [`Enum`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Enum) typeclass. The [`Enum`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Enum) typeclass\n", "is for things that have predecessors and successors. We can also make it\n", "part of the [`Bounded`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bounded) typeclass, which is for things that have a lowest\n", "possible value and highest possible value. And while we're at it, let's\n", "also make it an instance of all the other derivable typeclasses and see\n", "what we can do with it." ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday\n", " deriving (Eq, Ord, Show, Read, Bounded, Enum)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because it's part of the [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) and [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read) typeclasses, we can convert\n", "values of this type to and from strings." ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Wednesday" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Wednesday" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"Wednesday\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show Wednesday" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Saturday" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "read \"Saturday\" :: Day" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because it's part of the `Eq` and [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) typeclasses, we can compare or\n", "equate days." ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Saturday == Sunday" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Saturday == Saturday" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Saturday > Friday" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Monday `compare` Wednesday" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's also part of [`Bounded`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bounded), so we can get the lowest and highest day." ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Monday" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(minBound :: Day)" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Sunday" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(maxBound :: Day)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's also an instance of [`Enum`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Enum). We can get predecessors and successors of\n", "days and we can make list ranges from them!" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Tuesday" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "succ Monday" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Friday" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pred Saturday" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[Thursday,Friday,Saturday,Sunday]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[Thursday .. Sunday]" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[minBound .. maxBound] :: [Day]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's pretty awesome.\n", "\n", "\n", "\n", "Type synonyms\n", "-------------\n", "\n", "Previously, we mentioned that when writing types, the `[Char]` and [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String)\n", "types are equivalent and interchangeable. That's implemented with *type\n", "synonyms*. Type synonyms don't really do anything per se, they're just\n", "about giving some types different names so that they make more sense to\n", "someone reading our code and documentation. Here's how the standard\n", "library defines [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String) as a synonym for `[Char]`." ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ " type String = [Char]\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "We've introduced the *type* keyword. The keyword might be misleading to\n", "some, because we're not actually making anything new (we did that with\n", "the *data* keyword), but we're just making a synonym for an already\n", "existing type.\n", "\n", "If we make a function that converts a string to uppercase and call it\n", "`toUpperString` or something, we can give it a type declaration of\n", "`toUpperString :: [Char] -> [Char]` or\n", "`toUpperString :: String -> String`.\n", "Both of these are essentially the same, only the latter is nicer\n", "to read.\n", "\n", "When we were dealing with the `Data.Map` module, we first represented a\n", "phonebook with an association list before converting it into a map. As\n", "we've already found out, an association list is a list of key-value\n", "pairs. Let's look at a phonebook that we had." ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "phoneBook :: [(String,String)]\n", "phoneBook =\n", " [(\"betty\",\"555-2938\")\n", " ,(\"bonnie\",\"452-2928\")\n", " ,(\"patsy\",\"493-2928\")\n", " ,(\"lucille\",\"205-2928\")\n", " ,(\"wendy\",\"939-8282\")\n", " ,(\"penny\",\"853-2492\")\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that the type of `phoneBook` is `[(String,String)]`. That tells us\n", "that it's an association list that maps from strings to strings, but not\n", "much else. Let's make a type synonym to convey some more information in\n", "the type declaration." ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type PhoneBook = [(String,String)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now the type declaration for our phonebook can be\n", "`phoneBook :: PhoneBook`. Let's make a type synonym for [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String) as well." ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type PhoneNumber = String\n", "type Name = String\n", "type PhoneBook = [(Name,PhoneNumber)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Giving the [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String) type synonyms is something that Haskell programmers do\n", "when they want to convey more information about what strings in their\n", "functions should be used as and what they represent.\n", "\n", "So now, when we implement a function that takes a name and a number and\n", "sees if that name and number combination is in our phonebook, we can\n", "give it a very pretty and descriptive type declaration." ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "inPhoneBook :: Name -> PhoneNumber -> PhoneBook -> Bool\n", "inPhoneBook name pnumber pbook = (name,pnumber) `elem` pbook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we decided not to use type synonyms, our function would have a type\n", "of `String -> String -> [(String,String)] -> Bool`. In this case, the\n", "type declaration that took advantage of type synonyms is easier to\n", "understand. However, you shouldn't go overboard with them. We introduce\n", "type synonyms either to describe what some existing type represents in\n", "our functions (and thus our type declarations become better\n", "documentation) or when something has a long-ish type that's repeated a\n", "lot (like `[(String,String)]`) but represents something more specific in\n", "the context of our functions.\n", "\n", "Type synonyms can also be parameterized. If we want a type that\n", "represents an association list type but still want it to be general so\n", "it can use any type as the keys and values, we can do this:" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type AssocList k v = [(k,v)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, a function that gets the value by a key in an association list can\n", "have a type of `(Eq k) => k -> AssocList k v -> Maybe v`. `AssocList` is\n", "a type constructor that takes two types and produces a concrete type,\n", "like `AssocList Int String`, for instance.\n", "\n", "> __Fonzie says:__ Aaay! When I talk about *concrete types* I mean like\n", "> fully applied types like `Map Int String` or if we're dealin' with one of\n", "> them polymorphic functions, `[a]` or `(Ord a) => Maybe a` and stuff. And\n", "> like, sometimes me and the boys say that [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) is a type, but we don't\n", "> mean that, cause every idiot knows [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) is a type constructor. When I\n", "> apply an extra type to [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), like `Maybe String`, then I have a concrete\n", "> type. You know, values can only have types that are concrete types! So\n", "> in conclusion, live fast, love hard and don't let anybody else use your\n", "> comb!\n", "\n", "Just like we can partially apply functions to get new functions, we can\n", "partially apply type parameters and get new type constructors from them.\n", "Just like we call a function with too few parameters to get back a new\n", "function, we can specify a type constructor with too few type parameters\n", "and get back a partially applied type constructor. If we wanted a type\n", "that represents a map (from `Data.Map`) from integers to something, we\n", "could either do this:" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import qualified Data.Map as Map" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type IntMap v = Map.Map Int v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or we could do it like this:" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type IntMap = Map.Map Int" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Either way, the `IntMap` type constructor takes one parameter and that is\n", "the type of what the integers will point to.\n", "\n", "> __Oh yeah__. If you're going to try and implement this, you'll probably\n", "> going to do a qualified import of `Data.Map`. When you do a qualified\n", "> import, type constructors also have to be preceeded with a module name.\n", "> So you'd write `type IntMap = Map.Map Int`.\n", "\n", "Make sure that you really understand the distinction between type\n", "constructors and value constructors. Just because we made a type synonym\n", "called `IntMap` or `AssocList` doesn't mean that we can do stuff like\n", "`AssocList [(1,2),(4,5),(7,9)]`. All it means is that we can refer to its\n", "type by using different names. We can do\n", "`[(1,2),(3,5),(8,9)] :: AssocList Int Int`,\n", "which will make the numbers inside assume a type of\n", "[`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), but we can still use that list as we would any normal list that has\n", "pairs of integers inside. Type synonyms (and types generally) can only\n", "be used in the type portion of Haskell. We're in Haskell's type portion\n", "whenever we're defining new types (so in *data* and *type* declarations)\n", "or when we're located after a `::`. The `::` is in type declarations or in\n", "type annotations.\n", "\n", "Another cool data type that takes two types as its parameters is the\n", "`Either a b` type. This is roughly how it's defined:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It has two value constructors. If the [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) is used, then its contents\n", "are of type `a` and if [`Right`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Right) is used, then its contents are of type b. So\n", "we can use this type to encapsulate a value of one type or another and\n", "then when we get a value of type `Either a b`, we usually pattern match on\n", "both [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) and [`Right`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Right) and we different stuff based on which one of them it\n", "was." ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Right 20" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Right 20" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Left \"w00t\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Left \"w00t\"" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Right 'a' :: forall {a}. Either a Char" ], "text/plain": [ "Right 'a' :: forall {a}. Either a Char" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Right 'a'" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Left True :: forall {b}. Either Bool b" ], "text/plain": [ "Left True :: forall {b}. Either Bool b" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Left True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far, we've seen that `Maybe a` was mostly used to represent the results\n", "of computations that could have either failed or not. But sometimes,\n", "`Maybe a` isn't good enough because [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) doesn't really convey much\n", "information other than that something has failed. That's cool for\n", "functions that can fail in only one way or if we're just not interested\n", "in how and why they failed. A `Data.Map` lookup fails only if the key we\n", "were looking for wasn't in the map, so we know exactly what happened.\n", "However, when we're interested in how some function failed or why, we\n", "usually use the result type of `Either a b`, where `a` is some sort of type\n", "that can tell us something about the possible failure and `b` is the type\n", "of a successful computation. Hence, errors use the [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) value\n", "constructor while results use [`Right`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Right).\n", "\n", "An example: a high-school has lockers so that students have some place\n", "to put their Guns'n'Roses posters. Each locker has a code combination.\n", "When a student wants a new locker, they tell the locker supervisor which\n", "locker number they want and he gives them the code. However, if someone\n", "is already using that locker, he can't tell them the code for the locker\n", "and they have to pick a different one. We'll use a map from `Data.Map` to\n", "represent the lockers. It'll map from locker numbers to a pair of\n", "whether the locker is in use or not and the locker code." ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import qualified Data.Map as Map\n", "\n", "data LockerState = Taken | Free deriving (Show, Eq)\n", "\n", "type Code = String\n", "\n", "type LockerMap = Map.Map Int (LockerState, Code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Simple stuff. We introduce a new data type to represent whether a locker\n", "is taken or free and we make a type synonym for the locker code. We also\n", "make a type synonym for the type that maps from integers to pairs of\n", "locker state and code. And now, we're going to make a function that\n", "searches for the code in a locker map. We're going to use an\n", "`Either String Code` type to represent our result, because our lookup can fail in\n", "two ways — the locker can be taken, in which case we can't tell the code\n", "or the locker number might not exist at all. If the lookup fails, we're\n", "just going to use a [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String) to tell what's happened." ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "lockerLookup :: Int -> LockerMap -> Either String Code\n", "lockerLookup lockerNumber map =\n", " case Map.lookup lockerNumber map of\n", " Nothing -> Left $ \"Locker number \" ++ show lockerNumber ++ \" doesn't exist!\"\n", " Just (state, code) -> if state /= Taken\n", " then Right code\n", " else Left $ \"Locker \" ++ show lockerNumber ++ \" is already taken!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We do a normal lookup in the map. If we get a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), we return a value\n", "of type `Left String`, saying that the locker doesn't exist at all. If we\n", "do find it, then we do an additional check to see if the locker is\n", "taken. If it is, return a [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) saying that it's already taken. If it\n", "isn't, then return a value of type `Right Code`, in which we give the\n", "student the correct code for the locker. It's actually a `Right String`,\n", "but we introduced that type synonym to introduce some additional\n", "documentation into the type declaration. Here's an example map:" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "lockers :: LockerMap\n", "lockers = Map.fromList\n", " [(100,(Taken,\"ZD39I\"))\n", " ,(101,(Free,\"JAH3I\"))\n", " ,(103,(Free,\"IQSA9\"))\n", " ,(105,(Free,\"QOTSA\"))\n", " ,(109,(Taken,\"893JJ\"))\n", " ,(110,(Taken,\"99292\"))\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's try looking up some locker codes." ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Right \"JAH3I\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lockerLookup 101 lockers" ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Left \"Locker 100 is already taken!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lockerLookup 100 lockers" ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Left \"Locker number 102 doesn't exist!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lockerLookup 102 lockers" ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Left \"Locker 110 is already taken!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lockerLookup 110 lockers" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Right \"QOTSA\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lockerLookup 105 lockers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could have used a `Maybe a` to represent the result but then we\n", "wouldn't know why we couldn't get the code. But now, we have information\n", "about the failure in our result type.\n", "\n", "\n", "\n", "Recursive data structures\n", "-------------------------\n", "\n", "\n", "\n", "As we've seen, a constructor in an algebraic data type can have several\n", "(or none at all) fields and each field must be of some concrete type.\n", "With that in mind, we can make types whose constructors have fields that\n", "are of the same type! Using that, we can create recursive data types,\n", "where one value of some type contains values of that type, which in turn\n", "contain more values of the same type and so on.\n", "\n", "Think about this list: `[5]`. That's just syntactic sugar for `5:[]`. On the\n", "left side of the `:`, there's a value and on the right side, there's a\n", "list. And in this case, it's an empty list. Now how about the list\n", "`[4,5]`? Well, that desugars to `4:(5:[])`. Looking at the first `:`, we see\n", "that it also has an element on its left side and a list `(5:[])` on its\n", "right side. Same goes for a list like `3:(4:(5:6:[]))`, which could be\n", "written either like that or like `3:4:5:6:[]` (because `:` is\n", "right-associative) or `[3,4,5,6]`.\n", "\n", "We could say that a list can be an empty list or it can be an element\n", "joined together with a `:` with another list (that can be either the empty\n", "list or not).\n", "\n", "Let's use algebraic data types to implement our own list then!" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This reads just like our definition of lists from one of the previous\n", "paragraphs. It's either an empty list or a combination of a head with\n", "some value and a list. If you're confused about this, you might find it\n", "easier to understand in record syntax." ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data List a = Empty | Cons { listHead :: a, listTail :: List a} deriving (Show, Read, Eq, Ord)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You might also be confused about the `Cons` constructor here. *cons* is\n", "another word for `:`. You see, in lists, `:` is actually a constructor that\n", "takes a value and another list and returns a list. We can already use\n", "our new list type! In other words, it has two fields. One field is of\n", "the type of `a` and the other is of the type `[a]`." ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Empty" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Empty" ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Cons {listHead = 5, listTail = Empty}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5 `Cons` Empty" ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Cons {listHead = 4, listTail = Cons {listHead = 5, listTail = Empty}}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "4 `Cons` (5 `Cons` Empty)" ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Cons {listHead = 3, listTail = Cons {listHead = 4, listTail = Cons {listHead = 5, listTail = Empty}}}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "3 `Cons` (4 `Cons` (5 `Cons` Empty))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We called our `Cons` constructor in an infix manner so you can see how\n", "it's just like `:`. `Empty` is like `[]` and ``4 `Cons` (5 `Cons` Empty)`` is\n", "like `4:(5:[])`.\n", "\n", "We can define functions to be automatically infix by making them\n", "comprised of only special characters. We can also do the same with\n", "constructors, since they're just functions that return a data type. So\n", "check this out." ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "infixr 5 :-:\n", "data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First off, we notice a new syntactic construct, the fixity declarations.\n", "When we define functions as operators, we can use that to give them a\n", "fixity (but we don't have to). A fixity states how tightly the operator\n", "binds and whether it's left-associative or right-associative. For\n", "instance, [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-)'s fixity is `infixl 7 *` and [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-)'s fixity is `infixl 6`. That\n", "means that they're both left-associative (`4 * 3 * 2` is `(4 * 3) * 2`)\n", "but [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) binds tighter than [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-), because it has a greater fixity, so\n", "`5 * 4 + 3` is `(5 * 4) + 3`.\n", "\n", "Otherwise, we just wrote `a :-: (List a)` instead of `Cons a (List a)`. Now,\n", "we can write out lists in our list type like so:" ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3 :-: (4 :-: (5 :-: Empty))" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "3 :-: 4 :-: 5 :-: Empty" ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "100 :-: (3 :-: (4 :-: (5 :-: Empty)))" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let a = 3 :-: 4 :-: 5 :-: Empty\n", "100 :-: a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When deriving [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) for our type, Haskell will still display it as if the\n", "constructor was a prefix function, hence the parentheses around the\n", "operator (remember, `4 + 3` is `(+) 4 3`).\n", "\n", "Let's make a function that adds two of our lists together. This is how\n", "[`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) is defined for normal lists:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
infixr 5  ++\n",
    "(++) :: [a] -> [a] -> [a]\n",
    "[]     ++ ys = ys\n",
    "(x:xs) ++ ys = x : (xs ++ ys)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we'll just steal that for our own list. We'll name the function `.++`." ] }, { "cell_type": "code", "execution_count": 120, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "infixr 5 .++\n", "(.++) :: List a -> List a -> List a\n", "Empty .++ ys = ys\n", "(x :-: xs) .++ ys = x :-: (xs .++ ys)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And let's see if it works ..." ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3 :-: (4 :-: (5 :-: (6 :-: (7 :-: Empty))))" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let a = 3 :-: 4 :-: 5 :-: Empty\n", "let b = 6 :-: 7 :-: Empty\n", "a .++ b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nice. Is nice. If we wanted, we could implement all of the functions\n", "that operate on lists on our own list type.\n", "\n", "Notice how we pattern matched on `(x :-: xs)`. That works because pattern\n", "matching is actually about matching constructors. We can match on `:-:`\n", "because it is a constructor for our own list type and we can also match\n", "on `:` because it is a constructor for the built-in list type. Same goes\n", "for `[]`. Because pattern matching works (only) on constructors, we can\n", "match for stuff like that, normal prefix constructors or stuff like `8` or\n", "`'a'`, which are basically constructors for the numeric and character\n", "types, respectively.\n", "\n", "\n", "\n", "Now, we're going to implement a *binary search tree*. If you're not\n", "familiar with binary search trees from languages like C, here's what\n", "they are: an element points to two elements, one on its left and one on\n", "its right. The element to the left is smaller, the element to the right\n", "is bigger. Each of those elements can also point to two elements (or\n", "one, or none). In effect, each element has up to two sub-trees. And a\n", "cool thing about binary search trees is that we know that all the\n", "elements at the left sub-tree of, say, 5 are going to be smaller than 5.\n", "Elements in its right sub-tree are going to be bigger. So if we need to\n", "find if 8 is in our tree, we'd start at 5 and then because 8 is greater\n", "than 5, we'd go right. We're now at 7 and because 8 is greater than 7,\n", "we go right again. And we've found our element in three hops! Now if\n", "this were a normal list (or a tree, but really unbalanced), it would\n", "take us seven hops instead of three to see if 8 is in there.\n", "\n", "Sets and maps from `Data.Set` and `Data.Map` are implemented using trees,\n", "only instead of normal binary search trees, they use balanced binary\n", "search trees, which are always balanced. But right now, we'll just be\n", "implementing normal binary search trees.\n", "\n", "Here's what we're going to say: a tree is either an empty tree or it's\n", "an element that contains some value and two trees. Sounds like a perfect\n", "fit for an algebraic data type!" ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Okay, good, this is good. Instead of manually building a tree, we're\n", "going to make a function that takes a tree and an element and inserts an\n", "element. We do this by comparing the value we want to insert to the root\n", "node and then if it's smaller, we go left, if it's larger, we go right.\n", "We do the same for every subsequent node until we reach an empty tree.\n", "Once we've reached an empty tree, we just insert a node with that value\n", "instead of the empty tree.\n", "\n", "In languages like C, we'd do this by modifying the pointers and values\n", "inside the tree. In Haskell, we can't really modify our tree, so we have\n", "to make a new sub-tree each time we decide to go left or right and in\n", "the end the insertion function returns a completely new tree, because\n", "Haskell doesn't really have a concept of pointer, just values. Hence,\n", "the type for our insertion function is going to be something like\n", "`a -> Tree a -> Tree a`. It takes an element and a tree and returns a new\n", "tree that has that element inside. This might seem like it's inefficient\n", "but laziness takes care of that problem.\n", "\n", "So, here are two functions. One is a utility function for making a\n", "`singleton` tree (a tree with just one node) and a function to insert an\n", "element into a tree." ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "singleton :: a -> Tree a\n", "singleton x = Node x EmptyTree EmptyTree\n", "\n", "treeInsert :: (Ord a) => a -> Tree a -> Tree a\n", "treeInsert x EmptyTree = singleton x\n", "treeInsert x (Node a left right)\n", " | x == a = Node x left right\n", " | x < a = Node a (treeInsert x left) right\n", " | x > a = Node a left (treeInsert x right)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The singleton function is just a shortcut for making a node that has\n", "something and then two empty sub-trees. In the insertion function, we\n", "first have the edge condition as a pattern. If we've reached an empty\n", "sub-tree, that means we're where we want and instead of the empty tree,\n", "we put a singleton tree with our element. If we're not inserting into an\n", "empty tree, then we have to check some things. First off, if the element\n", "we're inserting is equal to the root element, just return a tree that's\n", "the same. If it's smaller, return a tree that has the same root value,\n", "the same right sub-tree but instead of its left sub-tree, put a tree\n", "that has our value inserted into it. Same (but the other way around)\n", "goes if our value is bigger than the root element.\n", "\n", "Next up, we're going to make a function that checks if some element is\n", "in the tree. First, let's define the edge condition. If we're looking\n", "for an element in an empty tree, then it's certainly not there. Okay.\n", "Notice how this is the same as the edge condition when searching for\n", "elements in lists. If we're looking for an element in an empty list,\n", "it's not there. Anyway, if we're not looking for an element in an empty\n", "tree, then we check some things. If the element in the root node is what\n", "we're looking for, great! If it's not, what then? Well, we can take\n", "advantage of knowing that all the left elements are smaller than the\n", "root node. So if the element we're looking for is smaller than the root\n", "node, check to see if it's in the left sub-tree. If it's bigger, check\n", "to see if it's in the right sub-tree." ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "treeElem :: (Ord a) => a -> Tree a -> Bool\n", "treeElem x EmptyTree = False\n", "treeElem x (Node a left right)\n", " | x == a = True\n", " | x < a = treeElem x left\n", " | x > a = treeElem x right" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All we had to do was write up the previous paragraph in code. Let's have\n", "some fun with our trees! Instead of manually building one (although we\n", "could), we'll use a fold to build up a tree from a list. Remember,\n", "pretty much everything that traverses a list one by one and then returns\n", "some sort of value can be implemented with a fold! We're going to start\n", "with the empty tree and then approach a list from the right and just\n", "insert element after element into our accumulator tree." ] }, { "cell_type": "code", "execution_count": 125, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree)) (Node 7 (Node 6 EmptyTree EmptyTree) (Node 8 EmptyTree EmptyTree))" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let nums = [8,6,4,1,7,3,5]\n", "let numsTree = foldr treeInsert EmptyTree nums\n", "numsTree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In that [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr), `treeInsert` was the folding function (it takes a tree and\n", "a list element and produces a new tree) and `EmptyTree` was the starting\n", "accumulator. `nums`, of course, was the list we were folding over.\n", "\n", "When we print our tree to the console, it's not very readable, but if we\n", "try, we can make out its structure. We see that the root node is 5 and\n", "then it has two sub-trees, one of which has the root node of 3 and the\n", "other a 7, etc." ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "8 `treeElem` numsTree" ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "100 `treeElem` numsTree" ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "1 `treeElem` numsTree" ] }, { "cell_type": "code", "execution_count": 129, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "10 `treeElem` numsTree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Checking for membership also works nicely. Cool.\n", "\n", "So as you can see, algebraic data structures are a really cool and\n", "powerful concept in Haskell. We can use them to make anything from\n", "boolean values and weekday enumerations to binary search trees and more!\n", "\n", "Typeclasses 102\n", "---------------\n", "\n", "\n", "\n", "So far, we've learned about some of the standard Haskell typeclasses and\n", "we've seen which types are in them. We've also learned how to\n", "automatically make our own types instances of the standard typeclasses\n", "by asking Haskell to derive the instances for us. In this section, we're\n", "going to learn how to make our own typeclasses and how to make types\n", "instances of them by hand.\n", "\n", "A quick recap on typeclasses: typeclasses are like interfaces. A\n", "typeclass defines some behavior (like comparing for equality, comparing\n", "for ordering, enumeration) and then types that can behave in that way\n", "are made instances of that typeclass. The behavior of typeclasses is\n", "achieved by defining functions or just type declarations that we then\n", "implement. So when we say that a type is an instance of a typeclass, we\n", "mean that we can use the functions that the typeclass defines with that\n", "type.\n", "\n", "Typeclasses have pretty much nothing to do with classes in languages\n", "like Java or Python. This confuses many people, so I want you to forget\n", "everything you know about classes in imperative languages right now.\n", "\n", "For example, the `Eq` typeclass is for stuff that can be equated. It\n", "defines the functions [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) and [`/=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47--61-). If we have a type (say, `Car`) and\n", "comparing two cars with the equality function [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) makes sense, then it\n", "makes sense for `Car` to be an instance of `Eq`.\n", "\n", "This is how the `Eq` class is defined in the standard prelude:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class Eq a where\n",
    "    (==) :: a -> a -> Bool\n",
    "    (/=) :: a -> a -> Bool\n",
    "    x == y = not (x /= y)\n",
    "    x /= y = not (x == y)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Woah, woah, woah! Some new strange syntax and keywords there! Don't\n", "worry, this will all be clear in a second. First off, when we write\n", "`class Eq a where`, this means that we're defining a new typeclass and\n", "that's called `Eq`. The `a` is the type variable and it means that a will\n", "play the role of the type that we will soon be making an instance of `Eq`.\n", "It doesn't have to be called `a`, it doesn't even have to be one letter,\n", "it just has to be a lowercase word. Then, we define several functions.\n", "It's not mandatory to implement the function bodies themselves, we just\n", "have to specify the type declarations for the functions.\n", "\n", "> Some people might understand this better if we wrote\n", "> `class Eq equatable where`\n", "> and then specified the type declarations like\n", "> `(==) :: equatable -> equatable -> Bool`.\n", "\n", "Anyway, we *did* implement the function bodies for the functions that `Eq`\n", "defines, only we defined them in terms of mutual recursion. We said that\n", "two instances of `Eq` are equal if they are not different and they are\n", "different if they are not equal. We didn't have to do this, really, but\n", "we did and we'll see how this helps us soon.\n", "\n", "> If we have say `class Eq a where` and then define a type declaration\n", "> within that class like `(==) :: a -> -a -> Bool`, then when we examine\n", "> the type of that function later on, it will have the type of\n", "> `(Eq a) => a -> a -> Bool`.\n", "\n", "So once we have a class, what can we do with it? Well, not much, really.\n", "But once we start making types instances of that class, we start getting\n", "some nice functionality. So check out this type:" ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data TrafficLight = Red | Yellow | Green" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It defines the states of a traffic light. Notice how we didn't derive\n", "any class instances for it. That's because we're going to write up some\n", "instances by hand, even though we could derive them for types like `Eq`\n", "and [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show). Here's how we make it an instance of `Eq`." ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Eq TrafficLight where\n", " Red == Red = True\n", " Green == Green = True\n", " Yellow == Yellow = True\n", " _ == _ = False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We did it by using the *instance* keyword. So *class* is for defining\n", "new typeclasses and *instance* is for making our types instances of\n", "typeclasses. When we were defining `Eq`, we wrote `class Eq a where` and we\n", "said that `a` plays the role of whichever type will be made an instance\n", "later on. We can see that clearly here, because when we're making an\n", "instance, we write `instance Eq TrafficLight where`. We replace the `a` with\n", "the actual type.\n", "\n", "Because [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) was defined in terms of [`/=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47--61-) and vice versa in the *class*\n", "declaration, we only had to overwrite one of them in the instance\n", "declaration. That's called the minimal complete definition for the\n", "typeclass — the minimum of functions that we have to implement so that\n", "our type can behave like the class advertises. To fulfill the minimal\n", "complete definition for `Eq`, we have to overwrite either one of [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) or [`/=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47--61-).\n", "If `Eq` was defined simply like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class Eq a where\n",
    "    (==) :: a -> a -> Bool\n",
    "    (/=) :: a -> a -> Bool
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "we'd have to implement both of these functions when making a type an\n", "instance of it, because Haskell wouldn't know how these two functions\n", "are related. The minimal complete definition would then be: both [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) and\n", "[`/=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47--61-).\n", "\n", "You can see that we implemented [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) simply by doing pattern matching.\n", "Since there are many more cases where two lights aren't equal, we\n", "specified the ones that are equal and then just did a catch-all pattern\n", "saying that if it's none of the previous combinations, then two lights\n", "aren't equal.\n", "\n", "Let's make this an instance of [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) by hand, too. To satisfy the minimal\n", "complete definition for [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show), we just have to implement its [`show`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:show)\n", "function, which takes a value and turns it into a string." ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Show TrafficLight where\n", " show Red = \"Red light\"\n", " show Yellow = \"Yellow light\"\n", " show Green = \"Green light\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once again, we used pattern matching to achieve our goals. Let's see how\n", "it works in action:" ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Red == Red" ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Red == Yellow" ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Red `elem` [Red, Yellow, Green]" ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[Red light,Yellow light,Green light]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[Red, Yellow, Green]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nice. We could have just derived `Eq` and it would have had the same\n", "effect (but we didn't for educational purposes). However, deriving [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show)\n", "would have just directly translated the value constructors to strings.\n", "But if we want lights to appear like `\"Red light\"`, then we have to make\n", "the instance declaration by hand.\n", "\n", "You can also make typeclasses that are subclasses of other typeclasses.\n", "The *class* declaration for [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) is a bit long, but here's the first\n", "part:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class (Eq a) => Num a where\n",
    "   ...
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we mentioned previously, there are a lot of places where we can cram\n", "in class constraints. So this is just like writing `class Num a where`,\n", "only we state that our type `a` must be an instance of `Eq`. We're\n", "essentially saying that we have to make a type an instance of `Eq` before\n", "we can make it an instance of [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num). Before some type can be considered a\n", "number, it makes sense that we can determine whether values of that type\n", "can be equated or not. That's all there is to subclassing really, it's\n", "just a class constraint on a *class* declaration! When defining function\n", "bodies in the *class* declaration or when defining them in *instance*\n", "declarations, we can assume that a is `a` part of `Eq` and so we can use [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-)\n", "on values of that type.\n", "\n", "But how are the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) or list types made as instances of typeclasses?\n", "What makes [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) different from, say, `TrafficLight` is that [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) in\n", "itself isn't a concrete type, it's a type constructor that takes one\n", "type parameter (like [`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char) or something) to produce a concrete type (like\n", "`Maybe Char`). Let's take a look at the `Eq` typeclass again:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class Eq a where\n",
    "    (==) :: a -> a -> Bool\n",
    "    (/=) :: a -> a -> Bool\n",
    "    x == y = not (x /= y)\n",
    "    x /= y = not (x == y)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From the type declarations, we see that the `a` is used as a concrete type\n", "because all the types in functions have to be concrete (remember, you\n", "can't have a function of the type `a -> Maybe` but you can have a\n", "function of `a -> Maybe a` or `Maybe Int -> Maybe String`). That's why we\n", "can't do something like" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Eq Maybe where\n",
    "    ...
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because like we've seen, the `a` has to be a concrete type but [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) isn't\n", "a concrete type. It's a type constructor that takes one parameter and\n", "then produces a concrete type. It would also be tedious to write\n", "`instance Eq (Maybe Int) where`, `instance Eq (Maybe Char) where`, etc. for\n", "every type ever. So we could write it out like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Eq (Maybe m) where\n",
    "    Just x == Just y = x == y\n",
    "    Nothing == Nothing = True\n",
    "    _ == _ = False
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is like saying that we want to make all types of the form\n", "`Maybe something` an instance of `Eq`. We actually could have written\n", "`(Maybe something)`, but we usually opt for single letters to be true to the\n", "Haskell style. The `(Maybe m)` here plays the role of the `a` from\n", "`class Eq a where`. While [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) isn't a concrete type, `Maybe m` is.\n", "By specifying a\n", "type parameter (`m`, which is in lowercase), we said that we want all\n", "types that are in the form of `Maybe m`, where `m` is any type, to be an\n", "instance of `Eq`.\n", "\n", "There's one problem with this though. Can you spot it? We use [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) on the\n", "contents of the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) but we have no assurance that what the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)\n", "contains can be used with `Eq`! That's why we have to modify our\n", "*instance* declaration like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance (Eq m) => Eq (Maybe m) where\n",
    "    Just x == Just y = x == y\n",
    "    Nothing == Nothing = True\n",
    "    _ == _ = False
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We had to add a class constraint! With this *instance* declaration, we\n", "say this: we want all types of the form `Maybe m` to be part of the `Eq`\n", "typeclass, but only those types where the `m` (so what's contained inside\n", "the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)) is also a part of `Eq`. This is actually how Haskell would\n", "derive the instance too.\n", "\n", "Most of the times, class constraints in *class* declarations are used\n", "for making a typeclass a subclass of another typeclass and class\n", "constraints in *instance* declarations are used to express requirements\n", "about the contents of some type. For instance, here we required the\n", "contents of the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) to also be part of the `Eq` typeclass.\n", "\n", "When making instances, if you see that a type is used as a concrete type\n", "in the type declarations (like the `a` in `a -> a -> Bool`), you have to\n", "supply type parameters and add parentheses so that you end up with a\n", "concrete type.\n", "\n", "> Take into account that the type you're trying to make an instance of\n", "> will replace the parameter in the *class* declaration. The `a` from\n", "> `class Eq a where`\n", "> will be replaced with a real type when you make an instance,\n", "> so try mentally putting your type into the function type declarations as\n", "> well. `(==) :: Maybe -> Maybe -> Bool` doesn't make much sense but\n", "> `(==) :: (Eq m) => Maybe m -> Maybe m -> Bool` does. But this is just\n", "> something to think about, because [`==`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-61--61-) will always have a type of\n", "> `(==) :: (Eq a) => a -> a -> Bool`, no matter what instances we make.\n", "\n", "Ooh, one more thing, check this out! If you want to see what the\n", "instances of a typeclass are, just do `:info YourTypeClass` in GHCI. So\n", "typing `:info Num` will show which functions the typeclass defines and it\n", "will give you a list of the types in the typeclass. `:info` works for\n", "types and type constructors too. If you do `:info Maybe`, it will show you\n", "all the typeclasses that [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) is an instance of. Also `:info` can show\n", "you the type declaration of a function. I think that's pretty cool.\n", "\n", "A yes-no typeclass\n", "------------------\n", "\n", "\n", "\n", "In JavaScript and some other weakly typed languages, you can put almost\n", "anything inside an if expression. For example, you can do all of the\n", "following:\n", "`if (0) alert(\"YEAH!\") else alert(\"NO!\")`,\n", "`if (\"\") alert (\"YEAH!\") else alert(\"NO!\")`,\n", "`if (false) alert(\"YEAH\") else alert(\"NO!)`,\n", "etc. and all of these will throw an alert of `NO!`. If you do\n", "`if (\"WHAT\") alert (\"YEAH\") else alert(\"NO!\")`,\n", "it will alert a `\"YEAH!\"` because\n", "JavaScript considers non-empty strings to be a sort of true-ish value.\n", "\n", "Even though strictly using [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) for boolean semantics works better in\n", "Haskell, let's try and implement that JavaScript-ish behavior anyway.\n", "For fun! Let's start out with a *class* declaration." ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "class YesNo a where\n", " yesno :: a -> Bool" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty simple. The `YesNo` typeclass defines one function. That function\n", "takes one value of a type that can be considered to hold some concept of\n", "true-ness and tells us for sure if it's true or not. Notice that from\n", "the way we use the `a` in the function, `a` has to be a concrete type.\n", "\n", "Next up, let's define some instances. For numbers, we'll assume that\n", "(like in JavaScript) any number that isn't 0 is true-ish and 0 is\n", "false-ish." ] }, { "cell_type": "code", "execution_count": 138, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance YesNo Int where\n", " yesno 0 = False\n", " yesno _ = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Empty lists (and by extensions, strings) are a no-ish value, while\n", "non-empty lists are a yes-ish value." ] }, { "cell_type": "code", "execution_count": 139, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance YesNo [a] where\n", " yesno [] = False\n", " yesno _ = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice how we just put in a type parameter `a` in there to make the list a\n", "concrete type, even though we don't make any assumptions about the type\n", "that's contained in the list. What else, hmm ... I know, [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) itself\n", "also holds true-ness and false-ness and it's pretty obvious which is\n", "which." ] }, { "cell_type": "code", "execution_count": 140, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance YesNo Bool where\n", " yesno = id" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Huh? What's [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id)? It's just a standard library function that takes a\n", "parameter and returns the same thing, which is what we would be writing\n", "here anyway.\n", "\n", "Let's make `Maybe a` an instance too." ] }, { "cell_type": "code", "execution_count": 141, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance YesNo (Maybe a) where\n", " yesno (Just _) = True\n", " yesno Nothing = False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We didn't need a class constraint because we made no assumptions about\n", "the contents of the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe). We just said that it's true-ish if it's a\n", "[`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value and false-ish if it's a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). We still had to write out\n", "`(Maybe a)` instead of just [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) because if you think about it, a\n", "`Maybe -> Bool` function can't exist (because [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) isn't a concrete type),\n", "whereas a `Maybe a -> Bool` is fine and dandy. Still, this is really cool\n", "because now, any type of the form `Maybe something` is part of `YesNo` and\n", "it doesn't matter what that `something` is.\n", "\n", "Previously, we defined a `Tree a` type, that represented a binary search\n", "tree. We can say an empty tree is false-ish and anything that's not an\n", "empty tree is true-ish." ] }, { "cell_type": "code", "execution_count": 142, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance YesNo (Tree a) where\n", " yesno EmptyTree = False\n", " yesno _ = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Can a traffic light be a yes or no value? Sure. If it's red, you stop.\n", "If it's green, you go. If it's yellow? Eh, I usually run the yellows\n", "because I live for adrenaline." ] }, { "cell_type": "code", "execution_count": 143, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance YesNo TrafficLight where\n", " yesno Red = False\n", " yesno _ = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool, now that we have some instances, let's go play!" ] }, { "cell_type": "code", "execution_count": 144, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesno $ length []" ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesno \"haha\"" ] }, { "cell_type": "code", "execution_count": 146, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesno \"\"" ] }, { "cell_type": "code", "execution_count": 147, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesno $ Just 0" ] }, { "cell_type": "code", "execution_count": 148, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesno True" ] }, { "cell_type": "code", "execution_count": 149, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesno EmptyTree" ] }, { "cell_type": "code", "execution_count": 150, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesno []" ] }, { "cell_type": "code", "execution_count": 151, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesno [0,0,0]" ] }, { "cell_type": "code", "execution_count": 152, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "yesno :: forall a. YesNo a => a -> Bool" ], "text/plain": [ "yesno :: forall a. YesNo a => a -> Bool" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t yesno" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Right, it works! Let's make a function that mimics the if statement, but\n", "it works with `YesNo` values." ] }, { "cell_type": "code", "execution_count": 153, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "yesnoIf :: (YesNo y) => y -> a -> a -> a\n", "yesnoIf yesnoVal yesResult noResult = if yesno yesnoVal then yesResult else noResult" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty straightforward. It takes a yes-no-ish value and two things. If\n", "the yes-no-ish value is more of a yes, it returns the first of the two\n", "things, otherwise it returns the second of them." ] }, { "cell_type": "code", "execution_count": 154, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"NO!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesnoIf [] \"YEAH!\" \"NO!\"" ] }, { "cell_type": "code", "execution_count": 155, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"YEAH!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesnoIf [2,3,4] \"YEAH!\" \"NO!\"" ] }, { "cell_type": "code", "execution_count": 156, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"YEAH!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesnoIf True \"YEAH!\" \"NO!\"" ] }, { "cell_type": "code", "execution_count": 157, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"YEAH!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesnoIf (Just 500) \"YEAH!\" \"NO!\"" ] }, { "cell_type": "code", "execution_count": 158, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"NO!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yesnoIf Nothing \"YEAH!\" \"NO!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "The Functor typeclass\n", "---------------------\n", "\n", "So far, we've encountered a lot of the typeclasses in the standard\n", "library. We've played with [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord), which is for stuff that can be ordered.\n", "We've palled around with `Eq`, which is for things that can be equated.\n", "We've seen [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show), which presents an interface for types whose values can\n", "be displayed as strings. Our good friend [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read) is there whenever we need\n", "to convert a string to a value of some type. And now, we're going to\n", "take a look at the __[`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor)__ typeclass, which is basically for things that\n", "can be mapped over. You're probably thinking about lists now, since\n", "mapping over lists is such a dominant idiom in Haskell. And you're\n", "right, the list type is part of the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) typeclass.\n", "\n", "What better way to get to know the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) typeclass than to see how\n", "it's implemented? Let's take a peek." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class Functor f where\n",
    "    fmap :: (a -> b) -> f a -> f b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Alright. We see that it defines one function, [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap), and doesn't provide\n", "any default implementation for it. The type of [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) is interesting. In\n", "the definitions of typeclasses so far, the type variable that played the\n", "role of the type in the typeclass was a concrete type, like the `a` in\n", "`(==) :: (Eq a) => a -> a -> Bool`. But now, the `f` is not a concrete\n", "type (a type that a value can hold, like [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) or `Maybe String`), but\n", "a type constructor that takes one type parameter. A quick refresher\n", "example: `Maybe Int` is a concrete type, but [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) is a type constructor\n", "that takes one type as the parameter. Anyway, we see that [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) takes a\n", "function from one type to another and a functor applied with one type\n", "and returns a functor applied with another type.\n", "\n", "If this sounds a bit confusing, don't worry. All will be revealed soon\n", "when we check out a few examples. Hmm, this type declaration for [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap)\n", "reminds me of something. If you don't know what the type signature of\n", "[`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map) is, it's: `map :: (a -> b) -> [a] -> [b]`.\n", "\n", "Ah, interesting! It takes a function from one type to another and a list\n", "of one type and returns a list of another type. My friends, I think we\n", "have ourselves a functor! In fact, [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map) is just a [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) that works only on\n", "lists. Here's how the list is an instance of the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) typeclass." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Functor [] where\n",
    "    fmap = map
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's it! Notice how we didn't write `instance Functor [a] where`,\n", "because from `fmap :: (a -> b) -> f a -> f b`, we see that the `f` has to\n", "be a type constructor that takes one type. `[a]` is already a concrete\n", "type (of a list with any type inside it), while `[]` is a type constructor\n", "that takes one type and can produce types such as `[Int]`, `[String]` or\n", "even `[[String]]`.\n", "\n", "Since for lists, [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) is just [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map), we get the same results when using\n", "them on lists." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
map :: (a -> b) -> [a] -> [b]
" ] }, { "cell_type": "code", "execution_count": 159, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[2,4,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (*2) [1..3]" ] }, { "cell_type": "code", "execution_count": 160, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[2,4,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (*2) [1..3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What happens when we [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map) or [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) over an empty list? Well, of course, we\n", "get an empty list. It just turns an empty list of type `[a]` into an empty\n", "list of type `[b]`.\n", "\n", "Types that can act like a box can be functors. You can think of a list\n", "as a box that has an infinite amount of little compartments and they can\n", "all be empty, one can be full and the others empty or a number of them\n", "can be full. So, what else has the properties of being like a box? For\n", "one, the `Maybe a` type. In a way, it's like a box that can either hold\n", "nothing, in which case it has the value of [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), or it can hold one\n", "item, like `\"HAHA\"`, in which case it has a value of `Just \"HAHA\"`. Here's\n", "how [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) is a functor." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Functor Maybe where\n",
    "    fmap f (Just x) = Just (f x)\n",
    "    fmap f Nothing = Nothing
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, notice how we wrote `instance Functor Maybe where` instead of\n", "`instance Functor (Maybe m)` where, like we did when we were dealing with\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) and `YesNo`. [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) wants a type constructor that takes one type\n", "and not a concrete type. If you mentally replace the `fs` with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)s,\n", "[`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) acts like a `(a -> b) -> Maybe a -> Maybe b` for this particular\n", "type, which looks OK. But if you replace `f` with `(Maybe m)`, then it would\n", "seem to act like a `(a -> b) -> Maybe m a -> Maybe m b`, which doesn't\n", "make any damn sense because [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) takes just one type parameter.\n", "\n", "Anyway, the [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) implementation is pretty simple. If it's an empty value\n", "of [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), then just return a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). If we map over an empty box, we\n", "get an empty box. It makes sense. Just like if we map over an empty\n", "list, we get back an empty list. If it's not an empty value, but rather\n", "a single value packed up in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just), then we apply the function on the\n", "contents of the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just)." ] }, { "cell_type": "code", "execution_count": 161, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"Something serious. HEY GUYS IM INSIDE THE JUST\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (++ \" HEY GUYS IM INSIDE THE JUST\") (Just \"Something serious.\")" ] }, { "cell_type": "code", "execution_count": 162, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (++ \" HEY GUYS IM INSIDE THE JUST\") Nothing" ] }, { "cell_type": "code", "execution_count": 163, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 400" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (*2) (Just 200)" ] }, { "cell_type": "code", "execution_count": 164, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (*2) Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another thing that can be mapped over and made an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) is\n", "our `Tree a` type. It can be thought of as a box in a way (holds several\n", "or no values) and the `Tree` type constructor takes exactly one type\n", "parameter. If you look at [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) as if it were a function made only for\n", "`Tree`, its type signature would look like `(a -> b) -> Tree a -> Tree b`.\n", "We're going to use recursion on this one. Mapping over an empty tree\n", "will produce an empty tree. Mapping over a non-empty tree will be a tree\n", "consisting of our function applied to the root value and its left and\n", "right sub-trees will be the previous sub-trees, only our function will\n", "be mapped over them." ] }, { "cell_type": "code", "execution_count": 165, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Functor Tree where\n", " fmap f EmptyTree = EmptyTree\n", " fmap f (Node x leftsub rightsub) = Node (f x) (fmap f leftsub) (fmap f rightsub)" ] }, { "cell_type": "code", "execution_count": 166, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "EmptyTree" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (*2) EmptyTree" ] }, { "cell_type": "code", "execution_count": 167, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Node 28 (Node 4 EmptyTree (Node 8 EmptyTree (Node 12 EmptyTree (Node 20 EmptyTree EmptyTree)))) EmptyTree" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (*4) (foldr treeInsert EmptyTree [5,7,3,2,1,7])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nice! Now how about `Either a b`? Can this be made a functor? The [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor)\n", "typeclass wants a type constructor that takes only one type parameter\n", "but [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) takes two. Hmmm! I know, we'll partially apply [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) by\n", "feeding it only one parameter so that it has one free parameter. Here's\n", "how `Either a` is a functor in the standard libraries:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Functor (Either a) where\n",
    "    fmap f (Right x) = Right (f x)\n",
    "    fmap f (Left x) = Left x
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well well, what did we do here? You can see how we made `Either a` an\n", "instance instead of just [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either). That's because `Either a` is a type\n", "constructor that takes one parameter, whereas [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) takes two. If [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap)\n", "was specifically for Either a, the type signature would then be\n", "`(b -> c) -> Either a b -> Either a c`\n", "because that's the same as\n", "`(b -> c) -> (Either a) b -> (Either a) c`.\n", "In the implementation, we mapped in\n", "the case of a [`Right`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Right) value constructor, but we didn't in the case of a\n", "[`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left). Why is that? Well, if we look back at how the `Either a b` type is\n", "defined, it's kind of like:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data Either a b = Left a | Right b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well, if we wanted to map one function over both of them, `a` and `b` would\n", "have to be the same type. I mean, if we tried to map a function that\n", "takes a string and returns a string and the `b` was a string but the `a` was\n", "a number, that wouldn't really work out. Also, from seeing what [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap)'s\n", "type would be if it operated only on [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) values, we see that the\n", "first parameter has to remain the same while the second one can change\n", "and the first parameter is actualized by the [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) value constructor.\n", "\n", "This also goes nicely with our box analogy if we think of the [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) part\n", "as sort of an empty box with an error message written on the side\n", "telling us why it's empty.\n", "\n", "Maps from `Data.Map` can also be made a functor because they hold values\n", "(or not!). In the case of `Map k v`, [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) will map a function `v -> v'`\n", "over a map of type `Map k v` and return a map of type `Map k v'`.\n", "\n", "> Note, the `'` has no special meaning in types just like it doesn't have\n", "> special meaning when naming values. It's used to denote things that are\n", "> similar, only slightly changed.\n", "\n", "Try figuring out how `Map k` is made an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) by yourself!\n", "\n", "With the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) typeclass, we've seen how typeclasses can represent\n", "pretty cool higher-order concepts. We've also had some more practice\n", "with partially applying types and making instances. In one of the next\n", "chapters, we'll also take a look at some laws that apply for functors.\n", "\n", "> __Just one more thing!__ Functors should obey some laws so that they may\n", "> have some properties that we can depend on and not think about too much.\n", "> If we use `fmap (+1)` over the list `[1,2,3,4]`, we expect the result to be\n", "> `[2,3,4,5]` and not its reverse, `[5,4,3,2]`. If we use `fmap (\\a -> a)`\n", "> (the identity function, which just returns its parameter) over some\n", "> list, we expect to get back the same list as a result. For example, if\n", "> we gave the wrong functor instance to our `Tree` type, using [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) over a\n", "> tree where the left sub-tree of a node only has elements that are\n", "> smaller than the node and the right sub-tree only has nodes that are\n", "> larger than the node might produce a tree where that's not the case.\n", "> We'll go over the functor laws in more detail in one of the next\n", "> chapters.\n", "\n", "Kinds and some type-foo\n", "-----------------------\n", "\n", "\n", "\n", "Type constructors take other types as parameters to eventually produce\n", "concrete types. That kind of reminds me of functions, which take values\n", "as parameters to produce values. We've seen that type constructors can\n", "be partially applied (`Either String` is a type that takes one type and\n", "produces a concrete type, like `Either String Int`), just like functions\n", "can. This is all very interesting indeed. In this section, we'll take a\n", "look at formally defining how types are applied to type constructors,\n", "just like we took a look at formally defining how values are applied to\n", "functions by using type declarations. *You don't really have to read\n", "this section to continue on your magical Haskell quest* and if you don't\n", "understand it, don't worry about it. However, getting this will give you\n", "a very thorough understanding of the type system.\n", "\n", "So, values like `3`, `\"YEAH\"` or [`takeWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:takeWhile) (functions are also values,\n", "because we can pass them around and such) each have their own type.\n", "Types are little labels that values carry so that we can reason about\n", "the values. But types have their own little labels, called *kinds*. A\n", "kind is more or less the type of a type. This may sound a bit weird and\n", "confusing, but it's actually a really cool concept.\n", "\n", "What are kinds and what are they good for? Well, let's examine the kind\n", "of a type by using the `:k` command in GHCI." ] }, { "cell_type": "code", "execution_count": 168, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Int :: *" ], "text/plain": [ "Int :: *" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":k Int" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A star? How quaint. What does that mean? A [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) means that the type is a\n", "concrete type. A concrete type is a type that doesn't take any type\n", "parameters and values can only have types that are concrete types. If I\n", "had to read [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) out loud (I haven't had to do that so far), I'd say\n", "*star* or just *type*.\n", "\n", "Okay, now let's see what the kind of Maybe is." ] }, { "cell_type": "code", "execution_count": 169, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Maybe :: * -> *" ], "text/plain": [ "Maybe :: * -> *" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":k Maybe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) type constructor takes one concrete type (like [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int)) and then\n", "returns a concrete type like `Maybe Int`. And that's what this kind tells\n", "us. Just like `Int -> Int` means that a function takes an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) and returns\n", "an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), `* -> *` means that the type constructor takes one concrete\n", "type and returns a concrete type. Let's apply the type parameter to\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) and see what the kind of that type is." ] }, { "cell_type": "code", "execution_count": 170, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Maybe Int :: *" ], "text/plain": [ "Maybe Int :: *" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":k Maybe Int" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just like I expected! We applied the type parameter to [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) and got\n", "back a concrete type (that's what `* -> *` means. A parallel (although\n", "not equivalent, types and kinds are two different things) to this is if\n", "we do `:t isUpper` and `:t isUpper 'A'`. [`isUpper`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isUpper) has a type of `Char -> Bool`\n", "and `isUpper 'A'` has a type of [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool), because its value is basically [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True).\n", "Both those types, however, have a kind of [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-).\n", "\n", "We used `:k` on a type to get its kind, just like we can use `:t` on a value\n", "to get its type. Like we said, types are the labels of values and kinds\n", "are the labels of types and there are parallels between the two.\n", "\n", "Let's look at another kind." ] }, { "cell_type": "code", "execution_count": 171, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Either :: * -> * -> *" ], "text/plain": [ "Either :: * -> * -> *" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":k Either" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aha, this tells us that [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) takes two concrete types as type\n", "parameters to produce a concrete type. It also looks kind of like a type\n", "declaration of a function that takes two values and returns something.\n", "Type constructors are curried (just like functions), so we can partially\n", "apply them." ] }, { "cell_type": "code", "execution_count": 172, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Either String :: * -> *" ], "text/plain": [ "Either String :: * -> *" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":k Either String" ] }, { "cell_type": "code", "execution_count": 173, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Either String Int :: *" ], "text/plain": [ "Either String Int :: *" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":k Either String Int" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we wanted to make [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) a part of the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) typeclass, we had to\n", "partially apply it because [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) wants types that take only one\n", "parameter while [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) takes two. In other words, [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) wants types of\n", "kind `* -> *` and so we had to partially apply [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) to get a type of\n", "kind `* -> *` instead of its original kind `* -> * -> *`. If we look\n", "at the definition of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) again" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class Functor f where\n",
    "    fmap :: (a -> b) -> f a -> f b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "we see that the `f` type variable is used as a type that takes one\n", "concrete type to produce a concrete type. We know it has to produce a\n", "concrete type because it's used as the type of a value in a function.\n", "And from that, we can deduce that types that want to be friends with\n", "[`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) have to be of kind `* -> *`.\n", "\n", "Now, let's do some type-foo. Take a look at this typeclass that I'm just\n", "going to make up right now:" ] }, { "cell_type": "code", "execution_count": 174, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "class Tofu t where\n", " tofu :: j a -> t a j" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Man, that looks weird. How would we make a type that could be an\n", "instance of that strange typeclass? Well, let's look at what its kind\n", "would have to be. Because `j a` is used as the type of a value that the\n", "`tofu` function takes as its parameter, `j a` has to have a kind of [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-). We\n", "assume [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) for `a` and so we can infer that `j` has to have a kind of\n", "`* -> *`. We see that t has to produce a concrete value too and that it takes\n", "two types. And knowing that a has a kind of [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) and `j` has a kind of\n", "`* -> *`, we infer that t has to have a kind of\n", "`* -> (* -> *) -> *`.\n", "So it takes a concrete type (`a`), a type constructor that takes one\n", "concrete type (`j`) and produces a concrete type. Wow.\n", "\n", "OK, so let's make a type with a kind of `* -> (* -> *) -> *`.\n", "Here's one way of going about it." ] }, { "cell_type": "code", "execution_count": 175, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Frank a b = Frank {frankField :: b a} deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How do we know this type has a kind of `* -> (* -> *) -> *`? Well,\n", "fields in ADTs are made to hold values, so they must be of kind [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-),\n", "obviously. We assume [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) for `a`, which means that `b` takes one type\n", "parameter and so its kind is `* -> *`. Now we know the kinds of both\n", "`a` and `b` and because they're parameters for `Frank`, we see that `Frank` has a\n", "kind of `* -> (* -> *) -> *` The first [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) represents `a` and the\n", "`(* -> *)` represents `b`. Let's make some `Frank` values and check out their\n", "types." ] }, { "cell_type": "code", "execution_count": 176, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Frank {frankField = Just \"HAHA\"} :: Frank String Maybe" ], "text/plain": [ "Frank {frankField = Just \"HAHA\"} :: Frank String Maybe" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Frank {frankField = Just \"HAHA\"}" ] }, { "cell_type": "code", "execution_count": 177, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Frank {frankField = Node 'a' EmptyTree EmptyTree} :: Frank Char Tree" ], "text/plain": [ "Frank {frankField = Node 'a' EmptyTree EmptyTree} :: Frank Char Tree" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Frank {frankField = Node 'a' EmptyTree EmptyTree}" ] }, { "cell_type": "code", "execution_count": 178, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Frank {frankField = \"YES\"} :: Frank Char []" ], "text/plain": [ "Frank {frankField = \"YES\"} :: Frank Char []" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Frank {frankField = \"YES\"}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hmm. Because `frankField` has a type of form `a b`, its values must have\n", "types that are of a similar form as well. So they can be `Just \"HAHA\"`,\n", "which has a type of `Maybe [Char]` or it can have a value of\n", "`['Y','E','S']`, which has a type of `[Char]` (if we used our own list type\n", "for this, it would have a type of `List Char`). And we see that the types\n", "of the `Frank` values correspond with the kind for `Frank`. `[Char]` has a\n", "kind of [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) and [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) has a kind of `* -> *`. Because in order to have a\n", "value, it has to be a concrete type and thus has to be fully applied,\n", "every value of `Frank blah blaah` has a kind of [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-).\n", "\n", "Making `Frank` an instance of `Tofu` is pretty simple. We see that `tofu`\n", "takes a `j a` (so an example type of that form would be `Maybe Int`) and\n", "returns a `t a j`. So if we replace `Frank` with `j`, the result type would be\n", "`Frank Int Maybe`." ] }, { "cell_type": "code", "execution_count": 179, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Tofu Frank where\n", " tofu x = Frank x" ] }, { "cell_type": "code", "execution_count": 180, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Frank {frankField = Just 'a'}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "tofu (Just 'a') :: Frank Char Maybe" ] }, { "cell_type": "code", "execution_count": 181, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Frank {frankField = [\"HELLO\"]}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "tofu [\"HELLO\"] :: Frank [Char] []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Not very useful, but we did flex our type muscles. Let's do some more\n", "type-foo. We have this data type:" ] }, { "cell_type": "code", "execution_count": 182, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Barry t k p = Barry { yabba :: p, dabba :: t k }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And now we want to make it an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor). [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) wants types\n", "of kind `* -> *` but `Barry` doesn't look like it has that kind. What is\n", "the kind of `Barry`? Well, we see it takes three type parameters, so it's\n", "going to be `something -> something -> something -> *`. It's safe to\n", "say that `p` is a concrete type and thus has a kind of [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-). For `k`, we\n", "assume [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) and so by extension, `t` has a kind of `* -> *`. Now let's just\n", "replace those kinds with the *somethings* that we used as placeholders\n", "and we see it has a kind of `(* -> *) -> * -> * -> *`. Let's\n", "check that with GHCI." ] }, { "cell_type": "code", "execution_count": 183, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Barry :: forall {k}. (k -> *) -> k -> * -> *" ], "text/plain": [ "Barry :: forall {k}. (k -> *) -> k -> * -> *" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":k Barry" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah, we were right. How satisfying. Now, to make this type a part of\n", "[`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) we have to partially apply the first two type parameters so that\n", "we're left with `* -> *`. That means that the start of the instance\n", "declaration will be: `instance Functor (Barry a b) where`. If we look at\n", "[`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) as if it was made specifically for `Barry`, it would have a type of\n", "`fmap :: (a -> b) -> Barry c d a -> Barry c d b`, because we just\n", "replace the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor)'s `f` with `Barry c d`. The third type parameter from\n", "`Barry` will have to change and we see that it's conveniently in its own\n", "field." ] }, { "cell_type": "code", "execution_count": 184, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Functor (Barry a b) where\n", " fmap f (Barry {yabba = x, dabba = y}) = Barry {yabba = f x, dabba = y}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There we go! We just mapped the `f` over the first field.\n", "\n", "In this section, we took a good look at how type parameters work and\n", "kind of formalized them with kinds, just like we formalized function\n", "parameters with type declarations. We saw that there are interesting\n", "parallels between functions and type constructors. They are, however,\n", "two completely different things. When working on real Haskell, you\n", "usually won't have to mess with kinds and do kind inference by hand like\n", "we did now. Usually, you just have to partially apply your own type to\n", "`* -> *` or [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) when making it an instance of one of the standard\n", "typeclasses, but it's good to know how and why that actually works. It's\n", "also interesting to see that types have little types of their own.\n", "Again, you don't really have to understand everything we did here to\n", "read on, but if you understand how kinds work, chances are that you have\n", "a very solid grasp of Haskell's type system." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/09-input-and-output.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Input and Output\n", "================\n", "\n", "\n", "\n", "We've mentioned that Haskell is a purely functional language. Whereas in\n", "imperative languages you usually get things done by giving the computer\n", "a series of steps to execute, functional programming is more of defining\n", "what stuff is. In Haskell, a function can't change some state, like\n", "changing the contents of a variable (when a function changes state, we\n", "say that the function has *side-effects*). The only thing a function can\n", "do in Haskell is give us back some result based on the parameters we\n", "gave it. If a function is called two times with the same parameters, it\n", "has to return the same result. While this may seem a bit limiting when\n", "you're coming from an imperative world, we've seen that it's actually\n", "really cool. In an imperative language, you have no guarantee that a\n", "simple function that should just crunch some numbers won't burn down\n", "your house, kidnap your dog and scratch your car with a potato while\n", "crunching those numbers. For instance, when we were making a binary\n", "search tree, we didn't insert an element into a tree by modifying some\n", "tree in place. Our function for inserting into a binary search tree\n", "actually returned a new tree, because it can't change the old one.\n", "\n", "While functions being unable to change state is good because it helps us\n", "reason about our programs, there's one problem with that. If a function\n", "can't change anything in the world, how is it supposed to tell us what\n", "it calculated? In order to tell us what it calculated, it has to change\n", "the state of an output device (usually the state of the screen), which\n", "then emits photons that travel to our brain and change the state of our\n", "mind, man.\n", "\n", "Do not despair, all is not lost. It turns out that Haskell actually has\n", "a really clever system for dealing with functions that have side-effects\n", "that neatly separates the part of our program that is pure and the part\n", "of our program that is impure, which does all the dirty work like\n", "talking to the keyboard and the screen. With those two parts separated,\n", "we can still reason about our pure program and take advantage of all the\n", "things that purity offers, like laziness, robustness and modularity\n", "while efficiently communicating with the outside world." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "> __Jupyter Note:__ About the following functions.\n", ">\n", "> `withStdin` takes a string and runs an [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) action with that string as the standard input.\n", "This is for simulating an interactive command line environment for programs. Inspired by [Test.Main.withStdin](http://hackage.haskell.org/package/main-tester-0.2.0.1/docs/src/Test.Main.html#withStdin).\n", "> The original book instructs the reader to run interactive programs from a shell command line, but those instructions don't work in a Jupyter notebook environment\n", "> because we want to be able to run the notebook from beginning to end without waiting for user interaction. The programs in this chapter can still be run\n", "> interactively in the notebook, but if a program doesn't terminate, you will have to Restart the kernel.\n", ">\n", "> `catchPrint` catches an exception and prints it to standard output. We use this because Jupyter won't show us the output of a program if the program throws an exception. We want to see both the output and the exception." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.IO\n", "import GHC.IO.Handle\n", "import Control.Exception\n", "import System.Directory\n", "\n", "withStdin :: String -> IO a -> IO a\n", "withStdin s action = do\n", " writeFile \"/tmp/stdin.txt\" s\n", " finally\n", " (withFile \"/tmp/stdin.txt\" ReadWriteMode\n", " (\\h -> do\n", " stdin' <- hDuplicate stdin\n", " hDuplicateTo h stdin\n", " finally action (hDuplicateTo stdin' stdin) \n", " )\n", " )\n", " (removeFile \"/tmp/stdin.txt\")\n", "\n", "catchPrint = flip catch p where\n", " p :: SomeException -> IO ()\n", " p = print" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hello, world!\n", "-------------\n", "\n", "\n", "\n", "Up until now, we've always loaded our functions into GHCI to test them\n", "out and play with them. We've also explored the standard library\n", "functions that way. But now, after eight or so chapters, we're finally\n", "going to write our first *real* Haskell program! Yay! And sure enough,\n", "we're going to do the good old `\"hello, world\"` schtick." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = putStrLn \"hello, world\"" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "hello, world" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We just defined a name called `main` and in it we call a function called\n", "[`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) with the parameter `\"hello, world\"`. Looks pretty much run of the\n", "mill, but it isn't, as we'll see in just a few moments." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's examine what we wrote. First, let's look at the type of the\n", "function [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn)." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "putStrLn :: String -> IO ()" ], "text/plain": [ "putStrLn :: String -> IO ()" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t putStrLn" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "putStrLn \"hello, world\" :: IO ()" ], "text/plain": [ "putStrLn \"hello, world\" :: IO ()" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t putStrLn \"hello, world\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can read the type of [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) like this: [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) takes a string and\n", "returns an *I/O action* that has a result type of `()` (i.e. the empty\n", "tuple, also know as unit). An I/O action is something that, when\n", "performed, will carry out an action with a side-effect (that's usually\n", "either reading from the input or printing stuff to the screen) and will\n", "also contain some kind of return value inside it. Printing a string to\n", "the terminal doesn't really have any kind of meaningful return value, so\n", "a dummy value of `()` is used.\n", "\n", "> The empty tuple is a value of `()` and it also has a type of `()`.\n", "\n", "So, when will an I/O action be performed? Well, this is where `main` comes\n", "in. An I/O action will be performed when we give it a name of `main` and\n", "then run our program.\n", "\n", "Having your whole program be just one I/O action seems kind of limiting.\n", "That's why we can use *do* syntax to glue together several I/O actions\n", "into one. Take a look at the following example:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " putStrLn \"Hello, what's your name?\"\n", " name <- getLine\n", " putStrLn (\"Hey \" ++ name ++ \", you rock!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah, interesting, new syntax! And this reads pretty much like an\n", "imperative program. If you compile it and try it out, it will probably\n", "behave just like you expect it to. Notice that we said *do* and then we\n", "laid out a series of steps, like we would in an imperative program. Each\n", "of these steps is an I/O action. By putting them together with *do*\n", "syntax, we glued them into one I/O action. The action that we got has a\n", "type of `IO ()`, because that's the type of the last I/O action inside.\n", "\n", "Because of that, `main` always has a type signature of\n", "`main :: IO `,\n", "where `` is some concrete type. By convention, we\n", "don't usually specify a type declaration for `main`.\n", "\n", "An interesting thing that we haven't met before is the third line, which\n", "states `name <- getLine`. It looks like it reads a line from the input\n", "and stores it into a variable called `name`. Does it really? Well, let's\n", "examine the type of [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "getLine :: IO String" ], "text/plain": [ "getLine :: IO String" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t getLine" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Aha, o-kay. __[`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine)__ is an I/O action that contains a result type of\n", "[`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String). That makes sense, because it will wait for the user to input\n", "something at the terminal and then that something will be represented as\n", "a string. So what's up with `name <- getLine` then? You can read that\n", "piece of code like this: *perform the I/O action [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) and then bind\n", "its result value to `name`*. [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) has a type of `IO String`, so `name` will\n", "have a type of [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String). You can think of an I/O action as a box with\n", "little feet that will go out into the real world and do something there\n", "(like write some graffiti on a wall) and maybe bring back some data.\n", "Once it's fetched that data for you, the only way to open the box and\n", "get the data inside it is to use the `<-` construct. And if we're taking\n", "data out of an I/O action, we can only take it out when we're inside\n", "another I/O action. This is how Haskell manages to neatly separate the\n", "pure and impure parts of our code. [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) is in a sense impure because\n", "its result value is not guaranteed to be the same when performed twice.\n", "That's why it's sort of *tainted* with the [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) type constructor and we\n", "can only get that data out in I/O code. And because I/O code is tainted\n", "too, any computation that depends on tainted I/O data will have a\n", "tainted result.\n", "\n", "When I say *tainted*, I don't mean tainted in such a way that we can\n", "never use the result contained in an I/O action ever again in pure code.\n", "No, we temporarily *un-taint* the data inside an I/O action when we bind\n", "it to a name. When we do `name <- getLine`, `name` is just a normal string,\n", "because it represents what's inside the box. We can have a really\n", "complicated function that, say, takes your name (a normal string) as a\n", "parameter and tells you your fortune and your whole life's future based\n", "on your name. We can do this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
main = do\n",
    "    putStrLn \"Hello, what's your name?\"\n",
    "    name <- getLine\n",
    "    putStrLn $ \"Read this carefully, because this is your future: \" ++ tellFortune name
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and `tellFortune` (or any of the functions it passes `name` to) doesn't have\n", "to know anything about I/O, it's just a normal `String -> String`\n", "function!\n", "\n", "Take a look at this piece of code. Is it valid?" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
nameTag = \"Hello, my name is \" ++ getLine
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you said no, go eat a cookie. If you said yes, drink a bowl of molten\n", "lava. Just kidding, don't! The reason that this doesn't work is that [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-)\n", "requires both its parameters to be lists over the same type. The left\n", "parameter has a type of [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String) (or `[Char]` if you will), whilst [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine)\n", "has a type of `IO String`. You can't concatenate a string and an I/O\n", "action. We first have to get the result out of the I/O action to get a\n", "value of type [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String) and the only way to do that is to say something\n", "like `name <- getLine` inside some other I/O action. If we want to deal\n", "with impure data, we have to do it in an impure environment. So the\n", "taint of impurity spreads around much like the undead scourge and it's\n", "in our best interest to keep the I/O parts of our code as small as\n", "possible.\n", "\n", "Every I/O action that gets performed has a result encapsulated within\n", "it. That's why our previous example program could also have been written\n", "like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
main = do\n",
    "    foo <- putStrLn \"Hello, what's your name?\"\n",
    "    name <- getLine\n",
    "    putStrLn (\"Hey \" ++ name ++ \", you rock!\")
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, `foo` would just have a value of `()`, so doing that would be kind\n", "of moot. Notice that we didn't bind the last [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) to anything.\n", "That's because in a *do* block, *the last action cannot be bound to a\n", "name* like the first two were. We'll see exactly why that is so a bit\n", "later when we venture off into the world of monads. For now, you can\n", "think of it in the way that the *do* block automatically extracts the\n", "value from the last action and binds it to its own result.\n", "\n", "Except for the last line, every line in a *do* block that doesn't bind\n", "can also be written with a bind. So `putStrLn \"BLAH\"` can be written as\n", "`_ <- putStrLn \"BLAH\"`. But that's useless, so we leave out the `<-` for I/O\n", "actions that don't contain an important result, like `putStrLn `.\n", "\n", "Beginners sometimes think that doing" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
name = getLine
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "will read from the input and then bind the value of that to `name`. Well,\n", "it won't, all this does is give the [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) I/O action a different name\n", "called, well, `name`. Remember, to get the value out of an I/O action, you\n", "have to perform it inside another I/O action by binding it to a name\n", "with `<-`.\n", "\n", "I/O actions will only be performed when they are given a name of `main` or\n", "when they're inside a bigger I/O action that we composed with a *do*\n", "block. We can also use a *do* block to glue together a few I/O actions\n", "and then we can use that I/O action in another *do* block and so on.\n", "Either way, they'll be performed only if they eventually fall into `main`.\n", "\n", "Oh, right, there's also one more case when I/O actions will be\n", "performed. When we type out an I/O action in GHCI and press return, it\n", "will be performed." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "HEEY" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "putStrLn \"HEEY\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Even when we just punch out a number or call a function in GHCI and\n", "press return, it will evaluate it (as much as it needs) and then call\n", "[`show`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:show) on it and then it will print that string to the terminal using\n", "[`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) implicitly.\n", "\n", "Remember *let* bindings? If you don't, refresh your memory on them by\n", "reading [this section](http://learnyouahaskell.com/syntax-in-functions#let-it-be). They have to be\n", "in the form of `let in `, where `` are\n", "names to be given to expressions and `` is the expression that\n", "is to be evaluated that sees them. We also said that in list\n", "comprehensions, the *in* part isn't needed. Well, you can use them in\n", "*do* blocks pretty much like you use them in list comprehensions. Check\n", "this out:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Char\n", "\n", "main = do\n", " putStrLn \"What's your first name?\"\n", " firstName <- getLine\n", " putStrLn \"What's your last name?\"\n", " lastName <- getLine\n", " let bigFirstName = map toUpper firstName\n", " bigLastName = map toUpper lastName\n", " putStrLn $ \"hey \" ++ bigFirstName ++ \" \" ++ bigLastName ++ \", how are you?\"" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "What's your first name?\n", "What's your last name?\n", "hey JOHN BROWN, how are you?" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin (unlines [\"John\", \"Brown\"]) main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See how the I/O actions in the *do* block are lined up? Also notice how\n", "the *let* is lined up with the I/O actions and the names of the *let*\n", "are lined up with each other? That's good practice, because indentation\n", "is important in Haskell. Now, we did `map toUpper firstName`, which turns\n", "something like `\"John\"` into a much cooler string like `\"JOHN\"`. We bound\n", "that uppercased string to a name and then used it in a string later on\n", "that we printed to the terminal.\n", "\n", "You may be wondering when to use `<-` and when to use *let* bindings?\n", "Well, remember, `<-` is (for now) for performing I/O actions and binding\n", "their results to names. map toUpper firstName, however, isn't an I/O\n", "action. It's a pure expression in Haskell. So use `<-` when you want to\n", "bind results of I/O actions to names and you can use *let* bindings to\n", "bind pure expressions to names. Had we done something like\n", "`let firstName = getLine`, we would have just called the [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) I/O action a different\n", "name and we'd still have to run it through a `<-` to perform it.\n", "\n", "Now we're going to make a program that continuously reads a line and\n", "prints out the same line with the words reversed. The program's\n", "execution will stop when we input a blank line. This is the program:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " line <- getLine\n", " if null line\n", " then return ()\n", " else do\n", " putStrLn $ reverseWords line\n", " main\n", "\n", "reverseWords :: String -> String\n", "reverseWords = unwords . map reverse . words" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get a feel of what it does, you can run it before we go over the\n", "code." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "yeh ereht nam\n", ": hGetLine: end of file" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin \"hey there man\" $ catchPrint main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, let's take a look at the `reverseWords` function. It's just a\n", "normal function that takes a string like \"hey there man\" and then calls\n", "[`words`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:words) with it to produce a list of words like `[\"hey\",\"there\",\"man\"]`.\n", "Then we map [`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse) on the list, getting `[\"yeh\",\"ereht\",\"nam\"]` and then\n", "we put that back into one string by using [`unwords`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:unwords) and the final result\n", "is `\"yeh ereht nam\"`. See how we used function composition here. Without\n", "function composition, we'd have to write something like\n", "`reverseWords st = unwords (map reverse (words st))`.\n", "\n", "What about `main`? First, we get a line from the terminal by performing\n", "[`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) call that line `line`. And now, we have a conditional expression.\n", "Remember that in Haskell, every *if* must have a corresponding *else*\n", "because every expression has to have some sort of value. We make the\n", "*if* so that when a condition is true (in our case, the line that we\n", "entered is blank), we perform one I/O action and when it isn't, the I/O\n", "action under the *else* is performed. That's why in an I/O *do* block,\n", "*if*s have to have a form of\n", "`if then else `.\n", "\n", "Let's first take a look at what happens under the *else* clause.\n", "Because, we have to have exactly one I/O action after the *else*, we use\n", "a *do* block to glue together two I/O actions into one. You could also\n", "write that part out as:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
        else (do\n",
    "            putStrLn $ reverseWords line\n",
    "            main)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This makes it more explicit that the *do* block can be viewed as one I/O\n", "action, but it's uglier. Anyway, inside the *do* block, we call\n", "`reverseWords` on the line that we got from [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) and then print that\n", "out to the terminal. After that, we just perform `main`. It's called\n", "recursively and that's okay, because `main` is itself an I/O action. So in\n", "a sense, we go back to the start of the program.\n", "\n", "Now what happens when `null line` holds true? What's after the *then* is\n", "performed in that case. If we look up, we'll see that it says\n", "`then return ()`. If you've done imperative languages like C, Java or Python,\n", "you're probably thinking that you know what this [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) does and chances\n", "are you've already skipped this really long paragraph. Well, here's the\n", "thing: *the [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) in Haskell is really nothing like the [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) in most\n", "other languages!* It has the same name, which confuses a lot of people,\n", "but in reality it's quite different. In imperative languages, [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return)\n", "usually ends the execution of a method or subroutine and makes it report\n", "some sort of value to whoever called it. In Haskell (in I/O actions\n", "specifically), it makes an I/O action out of a pure value. If you think\n", "about the box analogy from before, it takes a value and wraps it up in a\n", "box. The resulting I/O action doesn't actually do anything, it just has\n", "that value encapsulated as its result. So in an I/O context,\n", "`return \"haha\"` will have a type of `IO String`. What's the point of just\n", "transforming a pure value into an I/O action that doesn't do anything?\n", "Why taint our program with [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) more than it has to be? Well, we needed\n", "some I/O action to carry out in the case of an empty input line. That's\n", "why we just made a bogus I/O action that doesn't do anything by writing\n", "`return ()`.\n", "\n", "Using [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) doesn't cause the I/O *do* block to end in execution or\n", "anything like that. For instance, this program will quite happily carry\n", "out all the way to the last line:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " return ()\n", " return \"HAHAHA\"\n", " line <- getLine\n", " return \"BLAH BLAH BLAH\"\n", " return 4\n", " putStrLn line" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All these [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return)s do is that they make I/O actions that don't really do\n", "anything except have an encapsulated result and that result is thrown\n", "away because it isn't bound to a name. We can use [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) in combination\n", "with `<-` to bind stuff to names." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " a <- return \"hell\"\n", " b <- return \"yeah!\"\n", " putStrLn $ a ++ \" \" ++ b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So you see, [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) is sort of the opposite to `<-`. While [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) takes a\n", "value and wraps it up in a box, `<-` takes a box (and performs it) and\n", "takes the value out of it, binding it to a name. But doing this is kind\n", "of redundant, especially since you can use *let* bindings in *do* blocks\n", "to bind to names, like so:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " let a = \"hell\"\n", " b = \"yeah\"\n", " putStrLn $ a ++ \" \" ++ b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When dealing with I/O *do* blocks, we mostly use [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) either because\n", "we need to create an I/O action that doesn't do anything or because we\n", "don't want the I/O action that's made up from a *do* block to have the\n", "result value of its last action, but we want it to have a different\n", "result value, so we use [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) to make an I/O action that always has our\n", "desired result contained and we put it at the end.\n", "\n", "> A *do* block can also have just one I/O action. In that case, it's the\n", "> same as just writing the I/O action. Some people would prefer writing\n", "> `then do return ()` in this case because the *else* also has a *do*.\n", "\n", "Before we move on to files, let's take a look at some functions that are\n", "useful when dealing with I/O.\n", "\n", "__[`putStr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStr)__ is much like [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) in that it takes a string as a parameter\n", "and returns an I/O action that will print that string to the terminal,\n", "only [`putStr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStr) doesn't jump into a new line after printing out the string\n", "while [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) does." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do putStr \"Hey, \"\n", " putStr \"I'm \"\n", " putStrLn \"Andy!\"" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Hey, I'm Andy!" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Its type signature is `putStr :: String -> IO ()`, so the result\n", "encapsulated within the resulting I/O action is the unit. A dud value,\n", "so it doesn't make sense to bind it.\n", "\n", "__[`putChar`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putChar)__ takes a character and returns an I/O action that will print it\n", "out to the terminal." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do putChar 't'\n", " putChar 'e'\n", " putChar 'h'" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "teh" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[`putStr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStr) is actually defined recursively with the help of [`putChar`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putChar). The\n", "edge condition of [`putStr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStr) is the empty string, so if we're printing an\n", "empty string, just return an I/O action that does nothing by using\n", "`return ()`. If it's not empty, then print the first character of the\n", "string by doing [`putChar`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putChar) and then print of them using [`putStr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStr)." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
putStr :: String -> IO ()\n",
    "putStr [] = return ()\n",
    "putStr (x:xs) = do\n",
    "    putChar x\n",
    "    putStr xs
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See how we can use recursion in I/O, just like we can use it in pure\n", "code. Just like in pure code, we define the edge case and then think\n", "what the result actually is. It's an action that first outputs the first\n", "character and then outputs the rest of the string.\n", "\n", "__[`print`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:print)__ takes a value of any type that's an instance of [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) (meaning that\n", "we know how to represent it as a string), calls [`show`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:show) with that value to\n", "stringify it and then outputs that string to the terminal. Basically,\n", "it's just `putStrLn . show`. It first runs [`show`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:show) on a value and then feeds\n", "that to [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn), which returns an I/O action that will print out our\n", "value." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do print True\n", " print 2\n", " print \"haha\"\n", " print 3.2\n", " print [3,4,3]" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True\n", "2\n", "\"haha\"\n", "3.2\n", "[3,4,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, it's a very handy function. Remember how we talked about\n", "how I/O actions are performed only when they fall into `main` or when we\n", "try to evaluate them in the GHCI prompt? When we type out a value (like\n", "`3` or `[1,2,3]`) and press the return key, GHCI actually uses [`print`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:print) on that\n", "value to display it on our terminal!" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "3" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "print 3" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"hey!\",\"ho!\",\"woo!\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (++\"!\") [\"hey\",\"ho\",\"woo\"]" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"hey!\",\"ho!\",\"woo!\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "print (map (++\"!\") [\"hey\",\"ho\",\"woo\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we want to print out strings, we usually use [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) because we\n", "don't want the quotes around them, but for printing out values of other\n", "types to the terminal, [`print`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:print) is used the most.\n", "\n", "__[`getChar`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getChar)__ is an I/O action that reads a character from the input. Thus,\n", "its type signature is `getChar :: IO Char`, because the result contained\n", "within the I/O action is a [`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char). Note that due to buffering, reading of\n", "the characters won't actually happen until the user mashes the return\n", "key." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " c <- getChar\n", " if c /= ' '\n", " then do\n", " putChar c\n", " main\n", " else return ()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This program looks like it should read a character and then check if\n", "it's a space. If it is, halt execution and if it isn't, print it to the\n", "terminal and then do the same thing all over again. Well, it kind of\n", "does, only not in the way you might expect. Check this out:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "hello" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin \"hello sir\" main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The second line is the input. We input `hello sir` and then press return.\n", "Due to buffering, the execution of the program will begin only when\n", "after we've hit return and not after every inputted character. But once\n", "we press return, it acts on what we've been putting in so far. Try\n", "playing with this program to get a feel for it!\n", "\n", "The __[`when`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:when)__ function is found in `Control.Monad` (to get access to it, do\n", "`import Control.Monad`). It's interesting because in a *do* block it looks\n", "like a control flow statement, but it's actually a normal function. It\n", "takes a boolean value and an I/O action if that boolean value is [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True),\n", "it returns the same I/O action that we supplied to it. However, if it's\n", "[`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False), it returns the `return ()`, action, so an I/O action that doesn't\n", "do anything. Here's how we could rewrite the previous piece of code with\n", "which we demonstrated [`getChar`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getChar) by using [`when`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:when):" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad\n", "\n", "main = do\n", " c <- getChar\n", " when (c /= ' ') $ do\n", " putChar c\n", " main" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "hello" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin \"hello sir\" main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So as you can see, it's useful for encapsulating the\n", "`if then do else return ()` pattern.\n", "\n", "__[`sequence`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequence)__ takes a list of I/O actions and returns an I/O action that\n", "will perform those actions one after the other. The result contained in\n", "that I/O action will be a list of the results of all the I/O actions\n", "that were performed. Its type signature is\n", "`sequence :: [IO a] -> IO [a]`. Doing this:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " a <- getLine\n", " b <- getLine\n", " c <- getLine\n", " print [a,b,c]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Is exactly the same as doing this:." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " rs <- sequence [getLine, getLine, getLine]\n", " print rs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So `sequence [getLine, getLine, getLine]` makes an I/O action that will\n", "perform [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) three times. If we bind that action to a name, the\n", "result is a list of all the results, so in our case, a list of three\n", "things that the user entered at the prompt.\n", "\n", "A common pattern with [`sequence`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequence) is when we map functions like [`print`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:print) or\n", "[`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) over lists. Doing `map print [1,2,3,4]` won't create an I/O\n", "action. It will create a list of I/O actions, because that's like\n", "writing `[print 1, print 2, print 3, print 4]`. If we want to transform\n", "that list of I/O actions into an I/O action, we have to sequence it." ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1\n", "2\n", "3\n", "4\n", "5\n", "[(),(),(),(),()]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sequence (map print [1,2,3,4,5])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What's with the `[(),(),(),(),()]` at the end? Well, when we evaluate an\n", "I/O action in GHCI, it's performed and then its result is printed out,\n", "unless that result is `()`, in which case it's not printed out. That's why\n", "evaluating `putStrLn \"hehe\"` in GHCI just prints out `hehe` (because the\n", "contained result in `putStrLn \"hehe\"` is `()`). But when we do [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) in\n", "GHCI, the result of that I/O action is printed out, because [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) has\n", "a type of `IO String`.\n", "\n", "Because mapping a function that returns an I/O action over a list and\n", "then sequencing it is so common, the utility functions __[`mapM`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mapM)__ and __`mapM_`__\n", "were introduced. [`mapM`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mapM) takes a function and a list, maps the function\n", "over the list and then sequences it. `mapM_` does the same, only it\n", "throws away the result later. We usually use `mapM_` when we don't care\n", "what result our sequenced I/O actions have." ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1\n", "2\n", "3\n", "[(),(),()]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mapM print [1,2,3]" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1\n", "2\n", "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mapM_ print [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`forever`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:forever)__ takes an I/O action and returns an I/O action that just repeats\n", "the I/O action it got forever. It's located in `Control.Monad`. This\n", "little program will indefinitely ask the user for some input and spit it\n", "back to him, CAPSLOCKED:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad\n", "import Data.Char\n", "\n", "main = forever $ do\n", " putStr \"Give me some input: \"\n", " l <- getLine\n", " putStrLn $ map toUpper l" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Give me some input: CAPSLOCK\n", "Give me some input: : hGetLine: end of file" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin \"capslock\" $ catchPrint main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`forM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:forM)__ (located in `Control.Monad`) is like [`mapM`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mapM), only that it has its\n", "parameters switched around. The first parameter is the list and the\n", "second one is the function to map over that list, which is then\n", "sequenced. Why is that useful? Well, with some creative use of lambdas\n", "and *do* notation, we can do stuff like this:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad\n", "\n", "main = do\n", " colors <- forM [1,2,3,4] (\\a -> do\n", " putStrLn $ \"Which color do you associate with the number \" ++ show a ++ \"?\"\n", " color <- getLine\n", " return color)\n", " putStrLn \"The colors that you associate with 1, 2, 3 and 4 are: \"\n", " mapM_ putStrLn colors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `(\\a -> do ... )` is a function that takes a number and returns an\n", "I/O action. We have to surround it with parentheses, otherwise the\n", "lambda thinks the last two I/O actions belong to it. Notice that we do\n", "`return color` in the inside *do* block. We do that so that the I/O action\n", "which the *do* block defines has the result of our color contained\n", "within it. We actually didn't have to do that, because [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) already\n", "has that contained within it. Doing `color <- getLine` and then\n", "`return color` is just unpacking the result from [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) and then repackaging it\n", "again, so it's the same as just doing [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine). The [`forM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:forM) (called with its\n", "two parameters) produces an I/O action, whose result we bind to `colors`.\n", "`colors` is just a normal list that holds strings. At the end, we print\n", "out all those colors by doing `mapM putStrLn colors`.\n", "\n", "You can think of [`forM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:forM) as meaning: make an I/O action for every element\n", "in this list. What each I/O action will do can depend on the element\n", "that was used to make the action. Finally, perform those actions and\n", "bind their results to something. We don't have to bind it, we can also\n", "just throw it away." ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Which color do you associate with the number 1?\n", "Which color do you associate with the number 2?\n", "Which color do you associate with the number 3?\n", "Which color do you associate with the number 4?\n", "The colors that you associate with 1, 2, 3 and 4 are: \n", "white\n", "blue\n", "red\n", "orange" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin (unlines [\"white\", \"blue\", \"red\", \"orange\"]) main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could have actually done that without [`forM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:forM), only with [`forM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:forM) it's more\n", "readable. Normally we write [`forM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:forM) when we want to map and sequence some\n", "actions that we define there on the spot using *do* notation. In the\n", "same vein, we could have replaced the last line with\n", "`forM colors putStrLn`.\n", "\n", "In this section, we learned the basics of input and output. We also\n", "found out what I/O actions are, how they enable us to do input and\n", "output and when they are actually performed. To reiterate, I/O actions\n", "are values much like any other value in Haskell. We can pass them as\n", "parameters to functions and functions can return I/O actions as results.\n", "What's special about them is that if they fall into the `main` function\n", "(or are the result in a GHCI line), they are performed. And that's when\n", "they get to write stuff on your screen or play Yakety Sax through your\n", "speakers. Each I/O action can also encapsulate a result with which it\n", "tells you what it got from the real world.\n", "\n", "Don't think of a function like [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) as a function that takes a\n", "string and prints it to the screen. Think of it as a function that takes\n", "a string and returns an I/O action. That I/O action will, when\n", "performed, print beautiful poetry to your terminal.\n", "\n", "Files and streams\n", "-----------------\n", "\n", "\n", "\n", "[`getChar`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getChar) is an I/O action that reads a single character from the\n", "terminal. [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) is an I/O action that reads a line from the terminal.\n", "These two are pretty straightforward and most programming languages have\n", "some functions or statements that are parallel to them. But now, let's\n", "meet __[`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents)__. [`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents) is an I/O action that reads everything\n", "from the standard input until it encounters an end-of-file character.\n", "Its type is `getContents :: IO String`. What's cool about [`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents) is\n", "that it does lazy I/O. When we do `foo <- getContents`, it doesn't read\n", "all of the input at once, store it in memory and then bind it to `foo`.\n", "No, it's lazy! It'll say: *\"Yeah yeah, I'll read the input from the\n", "terminal later as we go along, when you really need it!\"*.\n", "\n", "[`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents) is really useful when we're piping the output from one\n", "program into the input of our program. In case you don't know how piping\n", "works in unix-y systems, here's a quick primer. Let's make a text file\n", "that contains the following little haiku:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "writeFile \"/tmp/haiku.txt\" $ unlines \n", " [ \"I'm a lil' teapot\"\n", " , \"What's with that airplane food, huh?\"\n", " , \"It's so small, tasteless\"\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yeah, the haiku sucks, what of it? If anyone knows of any good haiku\n", "tutorials, let me know.\n", "\n", "Now, recall the little program we wrote when we were introducing the\n", "[`forever`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:forever) function. It prompted the user for a line, returned it to him in\n", "CAPSLOCK and then did that all over again, indefinitely. Just so you\n", "don't have to scroll all the way back, here it is again:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad\n", "import Data.Char\n", "\n", "main = forever $ do\n", " putStr \"Give me some input: \"\n", " l <- getLine\n", " putStrLn $ map toUpper l" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "Check it out, booyaka!" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Give me some input: I'M A LIL' TEAPOT\n", "Give me some input: WHAT'S WITH THAT AIRPLANE FOOD, HUH?\n", "Give me some input: IT'S SO SMALL, TASTELESS\n", "Give me some input: : hGetLine: end of file" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "readFile \"/tmp/haiku.txt\" >>= flip withStdin (catchPrint main)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So what we're essentially doing with that use of [`forever`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:forever) is taking the\n", "input and transforming it into some output. That's why we can use\n", "[`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents) to make our program even shorter and better:" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Char\n", "\n", "main = do\n", " contents <- getContents\n", " putStr (map toUpper contents)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We run the [`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents) I/O action and name the string it produces\n", "`contents`. Then, we map [`toUpper`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:toUpper) over that string and print that to the\n", "terminal. Keep in mind that because strings are basically lists, which\n", "are lazy, and [`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents) is I/O lazy, it won't try to read the whole\n", "content at once and store it into memory before printing out the\n", "capslocked version. Rather, it will print out the capslocked version as\n", "it reads it, because it will only read a line from the input when it\n", "really needs to." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "I'M A LIL' TEAPOT\n", "WHAT'S WITH THAT AIRPLANE FOOD, HUH?\n", "IT'S SO SMALL, TASTELESS" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "readFile \"/tmp/haiku.txt\" >>= flip withStdin main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool, it works." ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "HEY HO\n", "LETS GO" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin (unlines [\"hey ho\", \"lets go\"]) main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, it\n", "prints out our capslocked input back to us line by line. When the result\n", "of [`readFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:readFile) is bound to `contents`, it's not represented in memory as a\n", "real string, but more like a promise that it will produce the string\n", "eventually. When we map [`toUpper`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:toUpper) over `contents`, that's also a promise to\n", "map that function over the eventual contents. And finally when [`putStr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStr)\n", "happens, it says to the previous promise: *\"Hey, I need a capslocked\n", "line!\"*. It doesn't have any lines yet, so it says to `contents`: *\"Hey,\n", "how about actually getting a line from the terminal?\"*. So that's when\n", "[`readFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:readFile) actually reads from the file and gives a line to the\n", "code that asked it to produce something tangible. That code then maps\n", "[`toUpper`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:toUpper) over that line and gives it to [`putStr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStr), which prints it. And\n", "then, [`putStr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStr) says: *\"Hey, I need the next line, come on!\"* and this\n", "repeats until there's no more input, which is signified by an\n", "end-of-file character.\n", "\n", "Let's make program that takes some input and prints out only those lines\n", "that are shorter than 10 characters. Observe:" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " contents <- getContents\n", " putStr (shortLinesOnly contents)\n", "\n", "shortLinesOnly :: String -> String\n", "shortLinesOnly input =\n", " let allLines = lines input\n", " shortLines = filter (\\line -> length line < 10) allLines\n", " result = unlines shortLines\n", " in result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We've made our I/O part of the program as short as possible. Because our\n", "program is supposed to take some input and print out some output based\n", "on the input, we can implement it by reading the input contents, running\n", "a function on them and then printing out what the function gave back.\n", "\n", "The `shortLinesOnly` function works like this: it takes a string, like\n", "`\"short\\nlooooooooooooooong\\nshort again\"`. That string has three lines,\n", "two of them are short and the middle one is long. It runs the lines\n", "function on that string, which converts it to\n", "`[\"short\", \"looooooooooooooong\", \"short again\"]`, which we then bind to the name\n", "`allLines`. That list of string is then filtered so that only those lines\n", "that are shorter than 10 characters remain in the list, producing\n", "`[\"short\", \"short again\"]`. And finally, [`unlines`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:unlines) joins that list into a\n", "single newline delimited string, giving `\"short\\nshort again\"`. Let's\n", "give it a go." ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "attributes": { "classes": [ "plain:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "writeFile \"/tmp/shortlines.txt\" $ unlines \n", " [ \"i'm short\"\n", " , \"so am i\"\n", " , \"i am a loooooooooong line!!!\"\n", " , \"yeah i'm long so what hahahaha!!!!!!\"\n", " , \"short line\"\n", " , \"loooooooooooooooooooooooooooong\"\n", " , \"short\"\n", " ]" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "attributes": { "classes": [ "plain:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "i'm short\n", "so am i\n", "short" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "readFile \"/tmp/shortlines.txt\" >>= flip withStdin main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As the output, we only get the short lines.\n", "\n", "This pattern of getting some string from the input, transforming it with\n", "a function and then outputting that is so common that there exists a\n", "function which makes that even easier, called __[`interact`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:interact)__. [`interact`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:interact) takes a\n", "function of type `String -> String` as a parameter and returns an I/O\n", "action that will take some input, run that function on it and then print\n", "out the function's result. Let's modify our program to use that." ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = interact shortLinesOnly\n", "\n", "shortLinesOnly :: String -> String\n", "shortLinesOnly input =\n", " let allLines = lines input\n", " shortLines = filter (\\line -> length line < 10) allLines\n", " result = unlines shortLines\n", " in result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just to show that this can be achieved in much less code (even though it\n", "will be less readable) and to demonstrate our function composition\n", "skill, we're going to rework that a bit further." ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = interact $ unlines . filter ((<10) . length) . lines" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "i'm short\n", "so am i\n", "short" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "readFile \"/tmp/shortlines.txt\" >>= flip withStdin main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wow, we actually reduced that to just one line, which is pretty cool!\n", "\n", "[`interact`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:interact) can be used to make programs that are piped some contents into\n", "them and then dump some result out or it can be used to make programs\n", "that appear to take a line of input from the user, give back some result\n", "based on that line and then take another line and so on. There isn't\n", "actually a real distinction between the two, it just depends on how the\n", "user is supposed to use them.\n", "\n", "Let's make a program that continuously reads a line and then tells us if\n", "the line is a palindrome or not. We could just use [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) to read a\n", "line, tell the user if it's a palindrome and then run `main` all over\n", "again. But it's simpler if we use [`interact`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:interact). When using [`interact`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:interact), think\n", "about what you need to do to transform some input into the desired\n", "output. In our case, we have to replace each line of the input with\n", "either `\"palindrome\"` or `\"not a palindrome\"`. So we have to write a\n", "function that transforms something like `\"elephant\\nABCBA\\nwhatever\"`\n", "into `\"not a palindrome\\npalindrome\\nnot a palindrome\"`. Let's do this!" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "respondPalindromes contents = unlines (map (\\xs -> if isPalindrome xs then \"palindrome\" else \"not a palindrome\") (lines contents))\n", " where isPalindrome xs = xs == reverse xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's write this in point-free." ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "respondPalindromes = unlines . map (\\xs -> if isPalindrome xs then \"palindrome\" else \"not a palindrome\") . lines\n", " where isPalindrome xs = xs == reverse xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty straightforward. First it turns something like\n", "`\"elephant\\nABCBA\\nwhatever\"` into `[\"elephant\", \"ABCBA\", \"whatever\"]` and\n", "then it maps that lambda over it, giving\n", "`[\"not a palindrome\", \"palindrome\", \"not a palindrome\"]`\n", "and then [`unlines`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:unlines) joins that list into\n", "a single, newline delimited string. Now we can do" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [], "source": [ "main = interact respondPalindromes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's test this out:" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "not a palindrome\n", "palindrome\n", "not a palindrome" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin (unlines [\"hehe\", \"ABCBA\", \"cookie\"]) main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Even though we made a program that transforms one big string of input into another, it acts like we made a program that does it line by line. That's because Haskell is lazy and it wants to print the first line of the result string, but it can't because it doesn't have the first line of the input yet. So as soon as we give it the first line of input, it prints the first line of the output. We get out of the program by issuing an end-of-line character." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also use this program by just piping a file into it. Let's say we\n", "have this file:" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "writeFile \"/tmp/words.txt\" $ unlines\n", " [ \"dogaroo\"\n", " , \"radar\"\n", " , \"rotor\"\n", " , \"madam\"\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and we save it as `/tmp/words.txt`. This is what we get by piping it into our\n", "program:" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "not a palindrome\n", "palindrome\n", "palindrome\n", "palindrome" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "readFile \"/tmp/words.txt\" >>= flip withStdin main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, we get the same output as if we had run our program and put in\n", "the words ourselves at the standard input. We just don't see the input\n", "that `palindromes.hs` because the input came from the file and not from us\n", "typing the words in.\n", "\n", "So now you probably see how lazy I/O works and how we can use it to our\n", "advantage. You can just think in terms of what the output is supposed to\n", "be for some given input and write a function to do that transformation.\n", "In lazy I/O, nothing is eaten from the input until it absolutely has to\n", "be because what we want to print right now depends on that input.\n", "\n", "So far, we've worked with I/O by printing out stuff to the terminal and\n", "reading from it. But what about reading and writing files? Well, in a\n", "way, we've already been doing that. One way to think about reading from\n", "the terminal is to imagine that it's like reading from a (somewhat\n", "special) file. Same goes for writing to the terminal, it's kind of like\n", "writing to a file. We can call these two files [`stdout`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:stdout) and [`stdin`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:stdin), meaning\n", "*standard output* and *standard input*, respectively. Keeping that in\n", "mind, we'll see that writing to and reading from files is very much like\n", "writing to the standard output and reading from the standard input.\n", "\n", "We'll start off with a really simple program that opens a file called\n", "`girlfriend.txt`, which contains a verse from Avril Lavigne's \\#1 hit\n", "*Girlfriend*, and just prints out out to the terminal. Here's\n", "`girlfriend.txt`:" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "writeFile \"/tmp/girlfriend.txt\" $ unlines\n", " [ \"Hey! Hey! You! You!\"\n", " , \"I don't like your girlfriend!\"\n", " , \"No way! No way!\"\n", " , \"I think you need a new one!\"\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And here's our program:" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.IO\n", "\n", "main = do\n", " handle <- openFile \"/tmp/girlfriend.txt\" ReadMode\n", " contents <- hGetContents handle\n", " putStr contents\n", " hClose handle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Running it, we get the expected result:" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Hey! Hey! You! You!\n", "I don't like your girlfriend!\n", "No way! No way!\n", "I think you need a new one!" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's go over this line by line. The first line is just four\n", "exclamations, to get our attention. In the second line, Avril tells us\n", "that she doesn't like our current romantic partner. The third line\n", "serves to emphasize that disapproval, whereas the fourth line suggests\n", "we should seek out a new girlfriend.\n", "\n", "Let's also go over the program line by line! Our program is several I/O\n", "actions glued together with a *do* block. In the first line of the *do*\n", "block, we notice a new function called __[`openFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:openFile)__. This is its type\n", "signature: `openFile :: FilePath -> IOMode -> IO Handle`. If you read\n", "that out loud, it states: [`openFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:openFile) takes a file path and an [`IOMode`](https://hackage.haskell.org/package/base/docs/System-IO.html#t:IOMode) and\n", "returns an I/O action that will open a file and have the file's\n", "associated handle encapsulated as its result.\n", "\n", "[`FilePath`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:FilePath) is just a [type synonym](http://learnyouahaskell.com/making-our-own-types-and-typeclasses#type-synonyms) for [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String),\n", "simply defined as:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
type FilePath = String
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[`IOMode`](https://hackage.haskell.org/package/base/docs/System-IO.html#t:IOMode) is a type that's defined like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Just like our type that represents the seven possible values for the\n", "days of the week, this type is an enumeration that represents what we\n", "want to do with our opened file. Very simple. Just note that this type\n", "is [`IOMode`](https://hackage.haskell.org/package/base/docs/System-IO.html#t:IOMode) and not `IO Mode`. `IO Mode` would be the type of an I/O action\n", "that has a value of some type `Mode` as its result, but [`IOMode`](https://hackage.haskell.org/package/base/docs/System-IO.html#t:IOMode) is just a\n", "simple enumeration.\n", "\n", "Finally, it returns an I/O action that will open the specified file in\n", "the specified mode. If we bind that action to something we get a [`Handle`](https://hackage.haskell.org/package/base/docs/System-IO.html#t:Handle).\n", "A value of type [`Handle`](https://hackage.haskell.org/package/base/docs/System-IO.html#t:Handle) represents where our file is. We'll use that\n", "handle so we know which file to read from. It would be stupid to read a\n", "file but not bind that read to a handle because we wouldn't be able to\n", "do anything with the file. So in our case, we bound the handle to\n", "[`handle`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:handle).\n", "\n", "In the next line, we see a function called __[`hGetContents`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hGetContents)__. It takes a\n", "[`Handle`](https://hackage.haskell.org/package/base/docs/System-IO.html#t:Handle), so it knows which file to get the contents from and returns an\n", "`IO String` — an I/O action that holds as its result the contents of the\n", "file. This function is pretty much like [`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents). The only difference\n", "is that [`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents) will automatically read from the standard input\n", "(that is from the terminal), whereas [`hGetContents`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hGetContents) takes a file handle\n", "which tells it which file to read from. In all other respects, they work\n", "the same. And just like [`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents), [`hGetContents`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hGetContents) won't attempt to read\n", "the file at once and store it in memory, but it will read it as needed.\n", "That's really cool because we can treat `contents` as the whole contents\n", "of the file, but it's not really loaded in memory. So if this were a\n", "really huge file, doing [`hGetContents`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hGetContents) wouldn't choke up our memory, but\n", "it would read only what it needed to from the file, when it needed to.\n", "\n", "Note the difference between the handle used to identify a file and the\n", "contents of the file, bound in our program to [`handle`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:handle) and `contents`. The\n", "handle is just something by which we know what our file is. If you\n", "imagine your whole file system to be a really big book and each file is\n", "a chapter in the book, the handle is a bookmark that shows where you're\n", "currently reading (or writing) a chapter, whereas the contents are the\n", "actual chapter.\n", "\n", "With `putStr contents` we just print the contents out to the standard\n", "output and then we do __[`hClose`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hClose)__, which takes a handle and returns an I/O\n", "action that closes the file. You have to close the file yourself after\n", "opening it with openFile!\n", "\n", "Another way of doing what we just did is to use the __[`withFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:withFile)__ function,\n", "which has a type signature of\n", "`withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a`.\n", "It takes a path to a file, an [`IOMode`](https://hackage.haskell.org/package/base/docs/System-IO.html#t:IOMode) and\n", "then it takes a function that takes a handle and returns some I/O\n", "action. What it returns is an I/O action that will open that file, do\n", "something we want with the file and then close it. The result\n", "encapsulated in the final I/O action that's returned is the same as the\n", "result of the I/O action that the function we give it returns. This\n", "might sound a bit complicated, but it's really simple, especially with\n", "lambdas, here's our previous example rewritten to use [`withFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:withFile):" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.IO\n", "\n", "main =\n", " withFile \"/tmp/girlfriend.txt\" ReadMode (\\handle -> do\n", " contents <- hGetContents handle\n", " putStr contents)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, it's very similar to the previous piece of code.\n", "`(\\handle -> ... )` is the function that takes a handle and returns an\n", "I/O action and it's usually done like this, with a lambda. The reason it\n", "has to take a function that returns an I/O action instead of just taking\n", "an I/O action to do and then close the file is because the I/O action\n", "that we'd pass to it wouldn't know on which file to operate. This way,\n", "[`withFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:withFile) opens the file and then passes the handle to the function we\n", "gave it. It gets an I/O action back from that function and then makes an\n", "I/O action that's just like it, only it closes the file afterwards.\n", "Here's how we can make our own [`withFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:withFile) function:" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a\n", "withFile' path mode f = do\n", " handle <- openFile path mode\n", " result <- f handle\n", " hClose handle\n", " return result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "We know the result will be an I/O action so we can just start off with a\n", "*do*. First we open the file and get a [`handle`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:handle) from it. Then, we apply\n", "handle to our function to get back the I/O action that does all the\n", "work. We bind that action to `result`, close the handle and then do\n", "`return result`. By [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return)ing the result encapsulated in the I/O action that we\n", "got from `f`, we make it so that our I/O action encapsulates the same\n", "result as the one we got from `f handle`. So if `f handle` returns an action\n", "that will read a number of lines from the standard input and write them\n", "to a file and have as its result encapsulated the number of lines it\n", "read, if we used that with `withFile'`, the resulting I/O action would\n", "also have as its result the number of lines read.\n", "\n", "Just like we have [`hGetContents`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hGetContents) that works like [`getContents`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getContents) but for a\n", "specific file, there's also __[`hGetLine`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hGetLine)__, __[`hPutStr`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hPutStr)__, __[`hPutStrLn`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hPutStrLn)__, __[`hGetChar`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hGetChar)__, etc.\n", "They work just like their counterparts without the *h*, only they take a\n", "handle as a parameter and operate on that specific file instead of\n", "operating on standard input or standard output. Example: [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn) is a\n", "function that takes a string and returns an I/O action that will print\n", "out that string to the terminal and a newline after it. [`hPutStrLn`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hPutStrLn) takes\n", "a handle and a string and returns an I/O action that will write that\n", "string to the file associated with the handle and then put a newline\n", "after it. In the same vein, [`hGetLine`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hGetLine) takes a handle and returns an I/O\n", "action that reads a line from its file.\n", "\n", "Loading files and then treating their contents as strings is so common\n", "that we have these three nice little functions to make our work even\n", "easier:\n", "\n", "__[`readFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:readFile)__ has a type signature of `readFile :: FilePath -> IO String`.\n", "Remember, [`FilePath`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:FilePath) is just a fancy name for [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String). [`readFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:readFile) takes a\n", "path to a file and returns an I/O action that will read that file\n", "(lazily, of course) and bind its contents to something as a string. It's\n", "usually more handy than doing [`openFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:openFile) and binding it to a handle and\n", "then doing [`hGetContents`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hGetContents). Here's how we could have written our previous\n", "example with [`readFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:readFile):" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.IO\n", "\n", "main = do\n", " contents <- readFile \"/tmp/girlfriend.txt\"\n", " putStr contents" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because we don't get a handle with which to identify our file, we can't\n", "close it manually, so Haskell does that for us when we use [`readFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:readFile).\n", "\n", "__[`writeFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:writeFile)__ has a type of `writeFile :: FilePath -> String -> IO ()`. It\n", "takes a path to a file and a string to write to that file and returns an\n", "I/O action that will do the writing. If such a file already exists, it\n", "will be stomped down to zero length before being written on. Here's how\n", "to turn `girlfriend.txt` into a CAPSLOCKED version and write it to\n", "`girlfriendcaps.txt`:" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.IO\n", "import Data.Char\n", "\n", "main = do\n", " contents <- readFile \"/tmp/girlfriend.txt\"\n", " writeFile \"/tmp/girlfriendcaps.txt\" (map toUpper contents)" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "HEY! HEY! YOU! YOU!\n", "I DON'T LIKE YOUR GIRLFRIEND!\n", "NO WAY! NO WAY!\n", "I THINK YOU NEED A NEW ONE!" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "putStrLn =<< readFile \"/tmp/girlfriendcaps.txt\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__[`appendFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:appendFile)__ has a type signature that's just like [`writeFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:writeFile), only\n", "[`appendFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:appendFile) doesn't truncate the file to zero length if it already exists\n", "but it appends stuff to it.\n", "\n", "Let's say we have a file `todo.txt` that has one task per line that we\n", "have to do. Now let's make a program that takes a line from the standard\n", "input and adds that to our to-do list." ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.IO\n", "\n", "main = do \n", " todoItem <- getLine \n", " appendFile \"/tmp/todo.txt\" (todoItem ++ \"\\n\")" ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "writeFile \"/tmp/todo.txt\" \"\"\n", "withStdin \"Iron the dishes\" main\n", "withStdin \"Dust the dog\" main\n", "withStdin \"Take salad out of the oven\" main" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Iron the dishes\n", "Dust the dog\n", "Take salad out of the oven" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "putStrLn =<< readFile \"/tmp/todo.txt\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We needed to add the `\"\\n\"` to the end of each line because [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine)\n", "doesn't give us a newline character at the end.\n", "\n", "Ooh, one more thing. We talked about how doing\n", "`contents <- hGetContents handle`\n", "doesn't cause the whole file to be read at once and stored\n", "in-memory. It's I/O lazy, so doing this:" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " withFile \"/tmp/something.txt\" ReadMode (\\handle -> do\n", " contents <- hGetContents handle\n", " putStr contents)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "is actually like connecting a pipe from the file to the output. Just\n", "like you can think of lists as streams, you can also think of files as\n", "streams. This will read one line at a time and print it out to the\n", "terminal as it goes along. So you may be asking, how wide is this pipe\n", "then? How often will the disk be accessed? Well, for text files, the\n", "default buffering is line-buffering usually. That means that the\n", "smallest part of the file to be read at once is one line. That's why in\n", "this case it actually reads a line, prints it to the output, reads the\n", "next line, prints it, etc. For binary files, the default buffering is\n", "usually block-buffering. That means that it will read the file chunk by\n", "chunk. The chunk size is some size that your operating system thinks is\n", "cool.\n", "\n", "You can control how exactly buffering is done by using the [`hSetBuffering`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hSetBuffering)\n", "function. It takes a handle and a [`BufferMode`](https://hackage.haskell.org/package/base/docs/System-IO.html#t:BufferMode) and returns an I/O action\n", "that sets the buffering. [`BufferMode`](https://hackage.haskell.org/package/base/docs/System-IO.html#t:BufferMode) is a simple enumeration data type\n", "and the possible values it can hold are: [`NoBuffering`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:NoBuffering), [`LineBuffering`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:LineBuffering) or\n", "`BlockBuffering (Maybe Int)`. The `Maybe Int` is for how big the chunk\n", "should be, in bytes. If it's [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), then the operating system\n", "determines the chunk size. [`NoBuffering`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:NoBuffering) means that it will be read one\n", "character at a time. [`NoBuffering`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:NoBuffering) usually sucks as a buffering mode\n", "because it has to access the disk so much.\n", "\n", "Here's our previous piece of code, only it doesn't read it line by line\n", "but reads the whole file in chunks of 2048 bytes." ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " withFile \"/tmp/something.txt\" ReadMode (\\handle -> do\n", " hSetBuffering handle $ BlockBuffering (Just 2048)\n", " contents <- hGetContents handle\n", " putStr contents)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Reading files in bigger chunks can help if we want to minimize disk\n", "access or when our file is actually a slow network resource.\n", "\n", "We can also use __[`hFlush`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hFlush)__, which is a function that takes a handle and\n", "returns an I/O action that will flush the buffer of the file associated\n", "with the handle. When we're doing line-buffering, the buffer is flushed\n", "after every line. When we're doing block-buffering, it's after we've\n", "read a chunk. It's also flushed after closing a handle. That means that\n", "when we've reached a newline character, the reading (or writing)\n", "mechanism reports all the data so far. But we can use [`hFlush`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hFlush) to force\n", "that reporting of data that has been read so far. After flushing, the\n", "data is available to other programs that are running at the same time.\n", "\n", "Think of reading a block-buffered file like this: your toilet bowl is\n", "set to flush itself after it has one gallon of water inside it. So you\n", "start pouring in water and once the gallon mark is reached, that water\n", "is automatically flushed and the data in the water that you've poured in\n", "so far is read. But you can flush the toilet manually too by pressing\n", "the button on the toilet. This makes the toilet flush and all the water\n", "(data) inside the toilet is read. In case you haven't noticed, flushing\n", "the toilet manually is a metaphor for [`hFlush`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:hFlush). This is not a very great\n", "analogy by programming analogy standards, but I wanted a real world\n", "object that can be flushed for the punchline.\n", "\n", "We already made a program to add a new item to our to-do list in\n", "`todo.txt`, now let's make a program to remove an item. I'll just paste\n", "the code and then we'll go over the program together so you see that\n", "it's really easy. We'll be using a few new functions from\n", "`System.Directory` and one new function from `System.IO`, but they'll all be\n", "explained.\n", "\n", "Anyway, here's the program for removing an item from `todo.txt`:" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.IO\n", "import System.Directory\n", "import Data.List\n", "\n", "main = do\n", " handle <- openFile \"/tmp/todo.txt\" ReadMode\n", " (tempName, tempHandle) <- openTempFile \"/tmp\" \"todotemp\"\n", " contents <- hGetContents handle\n", " let todoTasks = lines contents\n", " numberedTasks = zipWith (\\n line -> show n ++ \" - \" ++ line) [0..] todoTasks\n", " putStrLn \"These are your TO-DO items:\"\n", " putStr $ unlines numberedTasks\n", " putStrLn \"Which one do you want to delete?\"\n", " numberString <- getLine\n", " let number = read numberString\n", " newTodoItems = delete (todoTasks !! number) todoTasks\n", " hPutStr tempHandle $ unlines newTodoItems\n", " hClose handle\n", " hClose tempHandle\n", " removeFile \"/tmp/todo.txt\"\n", " renameFile tempName \"/tmp/todo.txt\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "At first, we just open `todo.txt` in read mode and bind its handle to\n", "[`handle`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:handle).\n", "\n", "Next up, we use a function that we haven't met before which is from\n", "`System.IO` — __[`openTempFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:openTempFile)__. Its name is pretty self-explanatory. It takes\n", "a path to a temporary directory and a template name for a file and opens\n", "a temporary file. We used `\".\"` for the temporary directory, because .\n", "denotes the current directory on just about any OS. We used `\"temp\"` as\n", "the template name for the temporary file, which means that the temporary\n", "file will be named *temp* plus some random characters. It returns an I/O\n", "action that makes the temporary file and the result in that I/O action\n", "is a pair of values: the name of the temporary file and a handle. We\n", "could just open a normal file called `todo2.txt` or something like that\n", "but it's better practice to use [`openTempFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:openTempFile) so you know you're probably\n", "not overwriting anything.\n", "\n", "The reason we didn't use `getCurrentDirectory` to get the current\n", "directory and then pass it to [`openTempFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:openTempFile) but instead just passed `\".\"`\n", "to [`openTempFile`](https://hackage.haskell.org/package/base/docs/System-IO.html#v:openTempFile) is because [`.`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:.) refers to the current directory on\n", "unix-like system and Windows\n", "\n", "Next up, we bind the contents of `todo.txt` to `contents`. Then, split\n", "that string into a list of strings, each string one line. So `todoTasks`\n", "is now something like\n", "`[\"Iron the dishes\", \"Dust the dog\", \"Take salad out of the oven\"]`.\n", "We zip the numbers from 0 onwards and that list with\n", "a function that takes a number, like 3, and a string, like `\"hey\"` and\n", "returns `\"3 - hey\"`, so `numberedTasks` is\n", "`[\"0 - Iron the dishes\", \"1 - Dust the dog\" ...`.\n", "We join that list of strings into a single newline\n", "delimited string with [`unlines`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:unlines) and print that string out to the terminal.\n", "Note that instead of doing that, we could have also done\n", "`mapM putStrLn numberedTasks`.\n", "\n", "We ask the user which one they want to delete and wait for them to enter\n", "a number. Let's say they want to delete number 1, which is `Dust the dog`,\n", "so they punch in `1`. `numberString` is now `\"1\"` and because we want a\n", "number, not a string, we run [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) on that to get `1` and bind that to\n", "`number`.\n", "\n", "Remember the [`delete`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:delete) and `!!` functions from `Data.List`. `!!` returns an\n", "element from a list with some index and [`delete`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:delete) deletes the first\n", "occurence of an element in a list and returns a new list without that\n", "occurence. `(todoTasks !! number)` (number is now `1`) returns\n", "`\"Dust the dog\"`. We bind `todoTasks` without the first occurence of `\"Dust the dog\"` to\n", "`newTodoItems` and then join that into a single string with [`unlines`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:unlines) before\n", "writing it to the temporary file that we opened. The old file is now\n", "unchanged and the temporary file contains all the lines that the old one\n", "does, except the one we deleted.\n", "\n", "After that we close both the original and the temporary files and then\n", "we remove the original one with __`removeFile`__, which, as you can see, takes\n", "a path to a file and deletes it. After deleting the old `todo.txt`, we\n", "use __`renameFile`__ to rename the temporary file to `todo.txt`. Be careful,\n", "`removeFile` and `renameFile` (which are both in `System.Directory` by the\n", "way) take file paths as their parameters, not handles.\n", "\n", "And that's that! We could have done this in even fewer lines, but we\n", "were very careful not to overwrite any existing files and politely asked\n", "the operating system to tell us where we can put our temporary file.\n", "Let's give this a go!" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "writeFile \"/tmp/todo.txt\" $ unlines\n", " [ \"Iron the dishes\"\n", " , \"Dust the dog\"\n", " , \"Take salad out of the oven\"\n", " ]" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "These are your TO-DO items:\n", "0 - Iron the dishes\n", "1 - Dust the dog\n", "2 - Take salad out of the oven\n", "Which one do you want to delete?" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin \"1\" main" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Iron the dishes\n", "Take salad out of the oven" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "putStrLn =<< readFile \"/tmp/todo.txt\"" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "These are your TO-DO items:\n", "0 - Iron the dishes\n", "1 - Take salad out of the oven\n", "Which one do you want to delete?" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin \"0\" main" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Take salad out of the oven" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "putStrLn =<< readFile \"/tmp/todo.txt\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Command line arguments\n", "----------------------\n", "\n", "\n", "\n", "Dealing with command line arguments is pretty much a necessity if you\n", "want to make a script or application that runs on a terminal. Luckily,\n", "Haskell's standard library has a nice way of getting command line\n", "arguments of a program.\n", "\n", "In the previous section, we made one program for adding a to-do item to\n", "our to-do list and one program for removing an item. There are two\n", "problems with the approach we took. The first one is that we just\n", "hardcoded the name of our to-do file in our code. We just decided that\n", "the file will be named `todo.txt` and that the user will never have a\n", "need for managing several to-do lists.\n", "\n", "One way to solve that is to always ask the user which file they want to\n", "use as their to-do list. We used that approach when we wanted to know\n", "which item the user wants to delete. It works, but it's not so good,\n", "because it requires the user to run the program, wait for the program to\n", "ask something and then tell that to the program. That's called an\n", "interactive program and the difficult bit with interactive command line\n", "programs is this — what if you want to automate the execution of that\n", "program, like with a batch script? It's harder to make a batch script\n", "that interacts with a program than a batch script that just calls one\n", "program or several of them.\n", "\n", "That's why it's sometimes better to have the user tell the program what\n", "they want when they run the program, instead of having the program ask\n", "the user once it's run. And what better way to have the user tell the\n", "program what they want it to do when they run it than via command line\n", "arguments!\n", "\n", "The `System.Environment` module has two cool I/O actions. One is __[`getArgs`](https://hackage.haskell.org/package/base/docs/System-Environment.html#v:getArgs)__,\n", "which has a type of `getArgs :: IO [String]` and is an I/O action that\n", "will get the arguments that the program was run with and have as its\n", "contained result a list with the arguments. __[`getProgName`](https://hackage.haskell.org/package/base/docs/System-Environment.html#v:getProgName)__ has a type of\n", "`getProgName :: IO String` and is an I/O action that contains the program\n", "name.\n", "\n", "Here's a small program that demonstrates how these two work:" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ " import System.Environment\n", " import Data.List\n", "\n", " main = do\n", " args <- getArgs\n", " progName <- getProgName\n", " putStrLn \"The arguments are:\"\n", " mapM_ putStrLn args\n", " putStrLn \"The program name is:\"\n", " putStrLn progName" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We bind [`getArgs`](https://hackage.haskell.org/package/base/docs/System-Environment.html#v:getArgs) and `progName` to `args` and `progName`. We say\n", "`The arguments are:` and then for every argument in `args`, we do [`putStrLn`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:putStrLn). Finally, we\n", "also print out the program name. Let's compile this as `arg-test`." ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "The arguments are:\n", "first\n", "second\n", "w00t\n", "multi word arg\n", "The program name is:\n", "ihaskell" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withArgs [\"first\", \"second\", \"w00t\", \"multi word arg\"] main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nice. Armed with this knowledge you could create some cool command line\n", "apps. In fact, let's go ahead and make one. In the previous section, we\n", "made a separate program for adding tasks and a separate program for\n", "deleting them. Now, we're going to join that into one program, what it\n", "does will depend on the command line arguments. We're also going to make\n", "it so it can operate on different files, not just `todo.txt`.\n", "\n", "We'll call it simply `todo` and it'll be able to do (haha!) three\n", "different things:\n", "\n", "- View tasks\n", "- Add tasks\n", "- Delete tasks\n", "\n", "We're not going to concern ourselves with possible bad input too much\n", "right now.\n", "\n", "Our program will be made so that if we want to add the task\n", "`Find the magic sword of power` to the file `todo.txt`, we have to punch in\n", "`todo add todo.txt \"Find the magic sword of power\"` in our terminal. To view\n", "the tasks we'll just do `todo view todo.txt` and to remove the task with\n", "the index of 2, we'll do `todo remove todo.txt 2`.\n", "\n", "We'll start by making a dispatch association list. It's going to be a\n", "simple association list that has command line arguments as keys and\n", "functions as their corresponding values. All these functions will be of\n", "type `[String] -> IO ()`. They're going to take the argument list as a\n", "parameter and return an I/O action that does the viewing, adding,\n", "deleting, etc." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
import System.Environment\n",
    "import System.Directory\n",
    "import System.IO\n",
    "import Data.List\n",
    "\n",
    "dispatch :: [(String, [String] -> IO ())]\n",
    "dispatch =  [ (\"add\", add)\n",
    "            , (\"view\", view)\n",
    "            , (\"remove\", remove)\n",
    "            ]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have yet to define `main`, `add`, `view` and `remove`, so let's start with\n", "`main`:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
main = do\n",
    "    (command:args) <- getArgs\n",
    "    let (Just action) = lookup command dispatch\n",
    "    action args
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we get the arguments and bind them to `(command:args)`. If you\n", "remember your pattern matching, this means that the first argument will\n", "get bound to `command` and the rest of them will get bound to `args`. If we\n", "call our program like `todo add todo.txt \"Spank the monkey\"`, `command` will\n", "be `\"add\"` and args will be `[\"todo.txt\", \"Spank the monkey\"]`.\n", "\n", "In the next line, we look up our command in the dispatch list. Because\n", "`\"add\"` points to `add`, we get `Just add` as a result. We use pattern\n", "matching again to extract our function out of the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe). What happens if\n", "our command isn't in the dispatch list? Well then the lookup will return\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), but we said we won't concern ourselves with failing gracefully\n", "too much, so the pattern matching will fail and our program will throw a\n", "fit.\n", "\n", "Finally, we call our `action` function with the rest of the argument list.\n", "That will return an I/O action that either adds an item, displays a list\n", "of items or deletes an item and because that action is part of the `main`\n", "*do* block, it will get performed. If we follow our concrete example so\n", "far and our `action` function is `add`, it will get called with `args` (so\n", "`[\"todo.txt\", \"Spank the monkey\"]`) and return an I/O action that adds\n", "`Spank the monkey` to `todo.txt`.\n", "\n", "Great! All that's left now is to implement `add`, `view` and `remove`. Let's\n", "start with `add`:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
add :: [String] -> IO ()\n",
    "add [fileName, todoItem] = appendFile fileName (todoItem ++ \"\\n\")
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we call our program like `todo add todo.txt \"Spank the monkey\"`, the\n", "`\"add\"` will get bound to `command` in the first pattern match in the `main`\n", "block, whereas `[\"todo.txt\", \"Spank the monkey\"]` will get passed to the\n", "function that we get from the dispatch list. So, because we're not\n", "dealing with bad input right now, we just pattern match against a list\n", "with those two elements right away and return an I/O action that appends\n", "that line to the end of the file, along with a newline character.\n", "\n", "Next, let's implement the list viewing functionality. If we want to view\n", "the items in a file, we do `todo view todo.txt`. So in the first pattern\n", "match, `command` will be `\"view\"` and `args` will be `[\"todo.txt\"]`." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
view :: [String] -> IO ()\n",
    "view [fileName] = do\n",
    "    contents <- readFile fileName\n",
    "    let todoTasks = lines contents\n",
    "        numberedTasks = zipWith (\\n line -> show n ++ \" - \" ++ line) [0..] todoTasks\n",
    "    putStr $ unlines numberedTasks
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We already did pretty much the same thing in the program that only\n", "deleted tasks when we were displaying the tasks so that the user can\n", "choose one for deletion, only here we just display the tasks.\n", "\n", "And finally, we're going to implement `remove`. It's going to be very\n", "similar to the program that only deleted the tasks, so if you don't\n", "understand how deleting an item here works, check out the explanation\n", "under that program. The main difference is that we're not hardcoding\n", "`todo.txt` but getting it as an argument. We're also not prompting the\n", "user for the task number to delete, we're getting it as an argument." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
remove :: [String] -> IO ()\n",
    "remove [fileName, numberString] = do\n",
    "    handle <- openFile fileName ReadMode\n",
    "    (tempName, tempHandle) <- openTempFile \".\" \"temp\"\n",
    "    contents <- hGetContents handle\n",
    "    let number = read numberString\n",
    "        todoTasks = lines contents\n",
    "        newTodoItems = delete (todoTasks !! number) todoTasks\n",
    "    hPutStr tempHandle $ unlines newTodoItems\n",
    "    hClose handle\n",
    "    hClose tempHandle\n",
    "    removeFile fileName\n",
    "    renameFile tempName fileName
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We opened up the file based on `fileName` and opened a temporary file,\n", "deleted the line with the index that the user wants to delete, wrote\n", "that to the temporary file, removed the original file and renamed the\n", "temporary file back to `fileName`.\n", "\n", "Here's the whole program at once, in all its glory!" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Environment\n", "import System.Directory\n", "import System.IO\n", "import Data.List\n", "\n", "dispatch :: [(String, [String] -> IO ())]\n", "dispatch = [ (\"add\", add)\n", " , (\"view\", view)\n", " , (\"remove\", remove)\n", " ]\n", "\n", "main = do\n", " (command:args) <- getArgs\n", " let (Just action) = lookup command dispatch\n", " action args\n", "\n", "add :: [String] -> IO ()\n", "add [fileName, todoItem] = appendFile fileName (todoItem ++ \"\\n\")\n", "\n", "view :: [String] -> IO ()\n", "view [fileName] = do\n", " contents <- readFile fileName\n", " let todoTasks = lines contents\n", " numberedTasks = zipWith (\\n line -> show n ++ \" - \" ++ line) [0..] todoTasks\n", " putStr $ unlines numberedTasks\n", "\n", "remove :: [String] -> IO ()\n", "remove [fileName, numberString] = do\n", " handle <- openFile fileName ReadMode\n", " (tempName, tempHandle) <- openTempFile \"/tmp\" \"todotemp\"\n", " contents <- hGetContents handle\n", " let number = read numberString\n", " todoTasks = lines contents\n", " newTodoItems = delete (todoTasks !! number) todoTasks\n", " hPutStr tempHandle $ unlines newTodoItems\n", " hClose handle\n", " hClose tempHandle\n", " removeFile fileName\n", " renameFile tempName fileName" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "To summarize our solution: we made a dispatch association that maps from\n", "commands to functions that take some command line arguments and return\n", "an I/O action. We see what the command is and based on that we get the\n", "appropriate function from the dispatch list. We call that function with\n", "the rest of the command line arguments to get back an I/O action that\n", "will do the appropriate thing and then just perform that action!\n", "\n", "In other languages, we might have implemented this with a big switch\n", "case statement or whatever, but using higher order functions allows us\n", "to just tell the dispatch list to give us the appropriate function and\n", "then tell that function to give us an I/O action for some command line\n", "arguments.\n", "\n", "Let's try our app out!" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "writeFile \"/tmp/todo.txt\" $ unlines\n", " [ \"Iron the dishes\"\n", " , \"Dust the dog\"\n", " , \"Take salad out of the oven\"\n", " ]" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "0 - Iron the dishes\n", "1 - Dust the dog\n", "2 - Take salad out of the oven" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withArgs [\"view\", \"/tmp/todo.txt\"] main" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "withArgs [\"add\", \"/tmp/todo.txt\", \"Pick up children from drycleaners\"] main" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "0 - Iron the dishes\n", "1 - Dust the dog\n", "2 - Take salad out of the oven\n", "3 - Pick up children from drycleaners" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withArgs [\"view\", \"/tmp/todo.txt\"] main" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "withArgs [\"remove\", \"/tmp/todo.txt\", \"2\"] main" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "0 - Iron the dishes\n", "1 - Dust the dog\n", "2 - Pick up children from drycleaners" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withArgs [\"view\", \"/tmp/todo.txt\"] main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another cool thing about this is that it's easy to add extra\n", "functionality. Just add an entry in the dispatch association list and\n", "implement the corresponding function and you're laughing! As an\n", "exercise, you can try implementing a `bump` function that will take a file\n", "and a task number and return an I/O action that bumps that task to the\n", "top of the to-do list.\n", "\n", "You could make this program fail a bit more gracefully in case of bad\n", "input (for example, if someone runs `todo UP YOURS HAHAHAHA`) by making an\n", "I/O action that just reports there has been an error (say,\n", "`errorExit :: IO ()`) and then check for possible erroneous input and if there is\n", "erroneous input, perform the error reporting I/O action. Another way is\n", "to use exceptions, which we will meet soon.\n", "\n", "\n", "\n", "Randomness\n", "----------\n", "\n", "\n", "\n", "Many times while programming, you need to get some random data. Maybe\n", "you're making a game where a die needs to be thrown or you need to\n", "generate some test data to test out your program. There are a lot of\n", "uses for random data when programming. Well, actually, pseudo-random,\n", "because we all know that the only true source of randomness is a monkey\n", "on a unicycle with a cheese in one hand and its butt in the other. In\n", "this section, we'll take a look at how to make Haskell generate\n", "seemingly random data.\n", "\n", "In most other programming languages, you have functions that give you\n", "back some random number. Each time you call that function, you get back\n", "a (hopefully) different random number. How about Haskell? Well,\n", "remember, Haskell is a pure functional language. What that means is that\n", "it has referential transparency. What THAT means is that a function, if\n", "given the same parameters twice, must produce the same result twice.\n", "That's really cool because it allows us to reason differently about\n", "programs and it enables us to defer evaluation until we really need it.\n", "If I call a function, I can be sure that it won't do any funny stuff\n", "before giving me the results. All that matters are its results. However,\n", "this makes it a bit tricky for getting random numbers. If I have a\n", "function like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
randomNumber :: (Num a) => a\n",
    "randomNumber = 4
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's not very useful as a random number function because it will always\n", "return `4`, even though I can assure you that the 4 is completely random,\n", "because I used a die to determine it.\n", "\n", "How do other languages make seemingly random numbers? Well, they take\n", "various info from your computer, like the current time, how much and\n", "where you moved your mouse and what kind of noises you made behind your\n", "computer and based on that, give a number that looks really random. The\n", "combination of those factors (that randomness) is probably different in\n", "any given moment in time, so you get a different random number.\n", "\n", "Ah. So in Haskell, we can make a random number then if we make a\n", "function that takes as its parameter that randomness and based on that\n", "returns some number (or other data type).\n", "\n", "Enter the `System.Random` module. It has all the functions that satisfy\n", "our need for randomness. Let's just dive into one of the functions it\n", "exports then, namely __`random`__. Here's its type:\n", "`random :: (RandomGen g, Random a) => g -> (a, g)`. Whoa! Some new typeclasses in this type\n", "declaration up in here! The __`RandomGen`__ typeclass is for types that can\n", "act as sources of randomness. The __`Random`__ typeclass is for things that\n", "can take on random values. A boolean value can take on a random value,\n", "namely [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) or [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False). A number can also take up a plethora of different\n", "random values. Can a function take on a random value? I don't think so,\n", "probably not! If we try to translate the type declaration of `random` to\n", "English, we get something like: it takes a random generator (that's our\n", "source of randomness) and returns a random value and a new random\n", "generator. Why does it also return a new generator as well as a random\n", "value? Well, we'll see in a moment.\n", "\n", "To use our `random` function, we have to get our hands on one of those\n", "random generators. The `System.Random` module exports a cool type, namely\n", "__`StdGen`__ that is an instance of the `RandomGen` typeclass. We can either\n", "make a `StdGen` manually or we can tell the system to give us one based on\n", "a multitude of sort of random stuff.\n", "\n", "To manually make a random generator, use the __`mkStdGen`__ function. It has a\n", "type of `mkStdGen :: Int -> StdGen`. It takes an integer and based on\n", "that, gives us a random generator. Okay then, let's try using `random` and\n", "`mkStdGen` in tandem to get a (hardly random) number." ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(9216477508314497915,StdGen {unStdGen = SMGen 712633246999323047 2532601429470541125})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import System.Random\n", "\n", "random (mkStdGen 100)" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "source": [ "
\\:1:0:\n",
    "    Ambiguous type variable `a' in the constraint:\n",
    "      `Random a' arising from a use of `random' at \\:1:0-20\n",
    "    Probable fix: add a type signature that fixes these type variable(s)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What's this? Ah, right, the `random` function can return a value of any\n", "type that's part of the `Random` typeclass, so we have to inform Haskell\n", "what kind of type we want. Also let's not forget that it returns a\n", "random value and a random generator in a pair." ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(9216477508314497915,StdGen {unStdGen = SMGen 712633246999323047 2532601429470541125})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "random (mkStdGen 100) :: (Int, StdGen)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally! A number that looks kind of random! The first component of the\n", "tuple is our number whereas the second component is a textual\n", "representation of our new random generator. What happens if we call\n", "`random` with the same random generator again?" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(9216477508314497915,StdGen {unStdGen = SMGen 712633246999323047 2532601429470541125})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "random (mkStdGen 100) :: (Int, StdGen)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Of course. The same result for the same parameters. So let's try giving\n", "it a different random generator as a parameter." ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(-3721561930999131179,StdGen {unStdGen = SMGen 3529285061597105018 656261746462218851})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "random (mkStdGen 949494) :: (Int, StdGen)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alright, cool, great, a different number. We can use the type annotation\n", "to get different types back from that function." ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(0.28144592,StdGen {unStdGen = SMGen 2179256218976226416 4388660484137712901})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "random (mkStdGen 949488) :: (Float, StdGen)" ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(True,StdGen {unStdGen = SMGen 2179256218976226416 4388660484137712901})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "random (mkStdGen 949488) :: (Bool, StdGen)" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(-1991610726167140167,StdGen {unStdGen = SMGen 2179256218976226416 4388660484137712901})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "random (mkStdGen 949488) :: (Integer, StdGen)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make a function that simulates tossing a coin three times. If\n", "`random` didn't return a new generator along with a random value, we'd\n", "have to make this function take three random generators as a parameter\n", "and then return coin tosses for each of them. But that sounds wrong\n", "because if one generator can make a random value of type [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) (which can\n", "take on a load of different values), it should be able to make three\n", "coin tosses (which can take on precisely eight combinations). So this is\n", "where `random` returning a new generator along with a value really comes\n", "in handy.\n", "\n", "We'll represent a coin with a simple [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool). [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) is tails, [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) is\n", "heads." ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "threeCoins :: StdGen -> (Bool, Bool, Bool)\n", "threeCoins gen =\n", " let (firstCoin, newGen) = random gen :: (Bool, StdGen)\n", " (secondCoin, newGen') = random newGen :: (Bool, StdGen)\n", " (thirdCoin, newGen'') = random newGen' :: (Bool, StdGen)\n", " in (firstCoin, secondCoin, thirdCoin)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We call `random` with the generator we got as a parameter to get a coin\n", "and a new generator. Then we call it again, only this time with our new\n", "generator, to get the second coin. We do the same for the third coin.\n", "Had we called it with the same generator every time, all the coins would\n", "have had the same value and we'd only be able to get\n", "`(False, False, False)` or `(True, True, True)` as a result." ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(True,False,False)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "threeCoins (mkStdGen 21)" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(False,True,True)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "threeCoins (mkStdGen 22)" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(True,False,True)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "threeCoins (mkStdGen 943)" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(False,False,False)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "threeCoins (mkStdGen 944)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that we didn't have to do `random gen :: (Bool, StdGen)`. That's\n", "because we already specified that we want booleans in the type\n", "declaration of the function. That's why Haskell can infer that we want a\n", "boolean value in this case.\n", "\n", "So what if we want to flip four coins? Or five? Well, there's a function\n", "called __`randoms`__ that takes a generator and returns an infinite sequence\n", "of values based on that generator." ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[-8691282579076269733,-6401397088113744335,-7708536135073118066,1428399377092000640,-6279174627358091186]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 5 $ randoms (mkStdGen 11) :: [Int]" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[True,True,False,False,False]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 5 $ randoms (mkStdGen 11) :: [Bool]" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[6.3991964e-2,0.3095299,0.95818657,0.636883,0.10283136]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 5 $ randoms (mkStdGen 11) :: [Float]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Why doesn't `randoms` return a new generator as well as a list? We could\n", "implement the `randoms` function very easily like this:" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":extension ExplicitForAll\n", ":extension ScopedTypeVariables\n", "\n", "randoms' :: forall g a. (RandomGen g, Random a) => g -> [a]\n", "randoms' gen = let (value :: a, newGen :: g) = random gen in value:randoms' newGen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A recursive definition. We get a random value and a new generator from\n", "the current generator and then make a list that has the value as its\n", "head and random numbers based on the new generator as its tail. Because\n", "we have to be able to potentially generate an infinite amount of\n", "numbers, we can't give the new random generator back.\n", "\n", "We could make a function that generates a finite stream of numbers and a\n", "new generator like this:" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":extension ExplicitForAll\n", ":extension ScopedTypeVariables\n", "\n", "finiteRandoms :: forall g a n. (RandomGen g, Random a, Num n, Eq n) => n -> g -> ([a], g)\n", "finiteRandoms 0 gen = ([], gen)\n", "finiteRandoms n gen =\n", " let (value :: a, newGen :: g) = random gen\n", " (restOfList :: [a], finalGen :: g) = finiteRandoms (n-1) newGen\n", " in (value:restOfList, finalGen)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, a recursive definition. We say that if we want 0 numbers, we just\n", "return an empty list and the generator that was given to us. For any\n", "other number of random values, we first get one random number and a new\n", "generator. That will be the head. Then we say that the tail will be *n -\n", "1* numbers generated with the new generator. Then we return the head and\n", "the rest of the list joined and the final generator that we got from\n", "getting the *n - 1* random numbers.\n", "\n", "What if we want a random value in some sort of range? All the random\n", "integers so far were outrageously big or small. What if we want to to\n", "throw a die? Well, we use __`randomR`__ for that purpose. It has a type of\n", "`randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g)`, meaning\n", "that it's kind of like `random`, only it takes as its first parameter a\n", "pair of values that set the lower and upper bounds and the final value\n", "produced will be within those bounds." ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(4,StdGen {unStdGen = SMGen 10995245518073353784 11826319359189470311})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "randomR (1,6) (mkStdGen 359353)" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(5,StdGen {unStdGen = SMGen 16742454905583792488 1242096894807673913})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "randomR (1,6) (mkStdGen 35935335)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There's also `randomRs`, which produces a stream of random values within\n", "our defined ranges. Check this out:" ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"zkjowfddqk\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 10 $ randomRs ('a','z') (mkStdGen 3) :: [Char]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nice, looks like a super secret password or something.\n", "\n", "You may be asking yourself, what does this section have to do with I/O\n", "anyway? We haven't done anything concerning I/O so far. Well, so far\n", "we've always made our random number generator manually by making it with\n", "some arbitrary integer. The problem is, if we do that in our real\n", "programs, they will always return the same random numbers, which is no\n", "good for us. That's why `System.Random` offers the __`getStdGen`__ I/O action,\n", "which has a type of `IO StdGen`. When your program starts, it asks the\n", "system for a good random number generator and stores that in a so called\n", "global generator. `getStdGen` fetches you that global random generator\n", "when you bind it to something.\n", "\n", "Here's a simple program that generates a random string." ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Random\n", "\n", "main = do\n", " gen <- getStdGen\n", " putStr $ take 20 (randomRs ('a','z') gen)" ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "hbmoaudsjukvmbdlpbli" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "main" ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "hbmoaudsjukvmbdlpbli" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "main" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "hbmoaudsjukvmbdlpbli" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Be careful though, just performing `getStdGen` twice will ask the system\n", "for the same global generator twice. If you do this:" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Random\n", "\n", "main = do\n", " gen <- getStdGen\n", " putStrLn $ take 20 (randomRs ('a','z') gen)\n", " gen2 <- getStdGen\n", " putStr $ take 20 (randomRs ('a','z') gen2)" ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "hbmoaudsjukvmbdlpbli\n", "hbmoaudsjukvmbdlpbli" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "you will get the same string printed out twice! One way to get two\n", "different strings of length 20 is to set up an infinite stream and then\n", "take the first 20 characters and print them out in one line and then\n", "take the second set of 20 characters and print them out in the second\n", "line. For this, we can use the [`splitAt`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:splitAt) function from `Data.List`, which\n", "splits a list at some index and returns a tuple that has the first part\n", "as the first component and the second part as the second component." ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Random\n", "import Data.List\n", "\n", "main = do\n", " gen <- getStdGen\n", " let randomChars = randomRs ('a','z') gen\n", " (first20, rest) = splitAt 20 randomChars\n", " (second20, _) = splitAt 20 rest\n", " putStrLn first20\n", " putStr second20" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another way is to use the __`newStdGen`__ action, which splits our current\n", "random generator into two generators. It updates the global random\n", "generator with one of them and encapsulates the other as its result." ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Random\n", "\n", "main = do\n", " gen <- getStdGen\n", " putStrLn $ take 20 (randomRs ('a','z') gen)\n", " gen' <- newStdGen\n", " putStr $ take 20 (randomRs ('a','z') gen')" ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "hbmoaudsjukvmbdlpbli\n", "yoppicysriuacldsumvf" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Not only do we get a new random generator when we bind `newStdGen` to\n", "something, the global one gets updated as well, so if we do `getStdGen`\n", "again and bind it to something, we'll get a generator that's not the\n", "same as `gen`.\n", "\n", "Here's a little program that will make the user guess which number it's\n", "thinking of." ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Random\n", "import Control.Monad(when)\n", "\n", "main = do\n", " gen <- getStdGen\n", " askForNumber gen\n", "\n", "askForNumber :: StdGen -> IO ()\n", "askForNumber gen = do\n", " let (randNumber, newGen) = randomR (1,10) gen :: (Int, StdGen)\n", " putStrLn \"Which number in the range from 1 to 10 am I thinking of? \"\n", " numberString <- getLine\n", " when (not $ null numberString) $ do\n", " let number = read numberString\n", " if randNumber == number\n", " then putStrLn \"You are correct!\"\n", " else putStrLn $ \"Sorry, it was \" ++ show randNumber\n", " askForNumber newGen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "We make a function `askForNumber`, which takes a random number generator\n", "and returns an I/O action that will prompt the user for a number and\n", "tell him if he guessed it right. In that function, we first generate a\n", "random number and a new generator based on the generator that we got as\n", "a parameter and call them `randNumber` and `newGen`. Let's say that the\n", "number generated was `7`. Then we tell the user to guess which number\n", "we're thinking of. We perform [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) and bind its result to\n", "`numberString`. When the user enters `7`, `numberString` becomes `\"7\"`. Next, we\n", "use [`when`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:when) to check if the string the user entered is an empty string. If\n", "it is, an empty I/O action of `return ()` is performed, which effectively\n", "ends the program. If it isn't, the action consisting of that *do* block\n", "right there gets performed. We use [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) on `numberString` to convert it to\n", "a `number`, so number is now `7`.\n", "\n", "> __Excuse me!__ If the user gives us some input here that [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) can't read\n", "> (like `\"haha\"`), our program will crash with an ugly error message. If you\n", "> don't want your program to crash on erroneous input, use __[`reads`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reads)__, which\n", "> returns an empty list when it fails to read a string. When it succeeds,\n", "> it returns a singleton list with a tuple that has our desired value as\n", "> one component and a string with what it didn't consume as the other.\n", "\n", "We check if the number that we entered is equal to the one generated\n", "randomly and give the user the appropriate message. And then we call\n", "`askForNumber` recursively, only this time with the new generator that we\n", "got, which gives us an I/O action that's just like the one we performed,\n", "only it depends on a different generator and we perform it.\n", "\n", "`main` consists of just getting a random generator from the system and\n", "calling `askForNumber` with it to get the initial action.\n", "\n", "Here's our program in action!" ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Which number in the range from 1 to 10 am I thinking of? \n", "You are correct!\n", "Which number in the range from 1 to 10 am I thinking of? \n", "Sorry, it was 4\n", "Which number in the range from 1 to 10 am I thinking of? \n", "Sorry, it was 7\n", "Which number in the range from 1 to 10 am I thinking of? \n", "Sorry, it was 4\n", "Which number in the range from 1 to 10 am I thinking of? \n", ": hGetLine: end of file" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin (unlines [\"4\", \"10\", \"2\", \"5\"]) $ catchPrint main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another way to make this same program is like this:" ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Random\n", "import Control.Monad(when)\n", "\n", "main = do\n", " gen <- getStdGen\n", " let (randNumber, _) = randomR (1,10) gen :: (Int, StdGen)\n", " putStr \"Which number in the range from 1 to 10 am I thinking of? \"\n", " numberString <- getLine\n", " when (not $ null numberString) $ do\n", " let number = read numberString\n", " if randNumber == number\n", " then putStrLn \"You are correct!\"\n", " else putStrLn $ \"Sorry, it was \" ++ show randNumber\n", " newStdGen\n", " main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's very similar to the previous version, only instead of making a\n", "function that takes a generator and then calls itself recursively with\n", "the new updated generator, we do all the work in `main`. After telling the\n", "user whether they were correct in their guess or not, we update the\n", "global generator and then call `main` again. Both approaches are valid but\n", "I like the first one more since it does less stuff in `main` and also\n", "provides us with a function that we can reuse easily.\n", "\n", "Bytestrings\n", "-----------\n", "\n", "\n", "\n", "Lists are a cool and useful data structure. So far, we've used them\n", "pretty much everywhere. There are a multitude of functions that operate\n", "on them and Haskell's laziness allows us to exchange the for and while\n", "loops of other languages for filtering and mapping over lists, because\n", "evaluation will only happen once it really needs to, so things like\n", "infinite lists (and even infinite lists of infinite lists!) are no\n", "problem for us. That's why lists can also be used to represent streams,\n", "either when reading from the standard input or when reading from files.\n", "We can just open a file and read it as a string, even though it will\n", "only be accessed when the need arises.\n", "\n", "However, processing files as strings has one drawback: it tends to be\n", "slow. As you know, [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String) is a type synonym for `[Char]`. [`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char)s don't have\n", "a fixed size, because it takes several bytes to represent a character\n", "from, say, Unicode. Furthermore, lists are really lazy. If you have a\n", "list like `[1,2,3,4]`, it will be evaluated only when completely\n", "necessary. So the whole list is sort of a promise of a list. Remember\n", "that `[1,2,3,4]` is syntactic sugar for `1:2:3:4:[]`. When the first element\n", "of the list is forcibly evaluated (say by printing it), the rest of the\n", "list `2:3:4:[]` is still just a promise of a list, and so on. So you can\n", "think of lists as promises that the next element will be delivered once\n", "it really has to and along with it, the promise of the element after it.\n", "It doesn't take a big mental leap to conclude that processing a simple\n", "list of numbers as a series of promises might not be the most efficient\n", "thing in the world.\n", "\n", "That overhead doesn't bother us so much most of the time, but it turns\n", "out to be a liability when reading big files and manipulating them.\n", "That's why Haskell has *bytestrings*. Bytestrings are sort of like\n", "lists, only each element is one byte (or 8 bits) in size. The way they\n", "handle laziness is also different.\n", "\n", "Bytestrings come in two flavors: strict and lazy ones. Strict\n", "bytestrings reside in `Data.ByteString` and they do away with the laziness\n", "completely. There are no promises involved; a strict bytestring\n", "represents a series of bytes in an array. You can't have things like\n", "infinite strict bytestrings. If you evaluate the first byte of a strict\n", "bytestring, you have to evaluate it whole. The upside is that there's\n", "less overhead because there are no thunks (the technical term for\n", "*promise*) involved. The downside is that they're likely to fill your\n", "memory up faster because they're read into memory at once.\n", "\n", "The other variety of bytestrings resides in `Data.ByteString.Lazy`.\n", "They're lazy, but not quite as lazy as lists. Like we said before, there\n", "are as many thunks in a list as there are elements. That's what makes\n", "them kind of slow for some purposes. Lazy bytestrings take a different\n", "approach — they are stored in chunks (not to be confused with thunks!),\n", "each chunk has a size of 64K. So if you evaluate a byte in a lazy\n", "bytestring (by printing it or something), the first 64K will be\n", "evaluated. After that, it's just a promise for the rest of the chunks.\n", "Lazy bytestrings are kind of like lists of strict bytestrings with a\n", "size of 64K. When you process a file with lazy bytestrings, it will be\n", "read chunk by chunk. This is cool because it won't cause the memory\n", "usage to skyrocket and the 64K probably fits neatly into your CPU's L2\n", "cache.\n", "\n", "If you look through the\n", "[documentation](http://hackage.haskell.org/package/bytestring/docs/Data-ByteString-Lazy.html)\n", "for `Data.ByteString.Lazy`, you'll see that it has a lot of functions that\n", "have the same names as the ones from `Data.List`, only the type signatures\n", "have `ByteString` instead of `[a]` and `Word8` instead of `a` in them. The\n", "functions with the same names mostly act the same as the ones that work\n", "on lists. Because the names are the same, we're going to do a qualified\n", "import in a script and then load that script into GHCI to play with\n", "bytestrings." ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import qualified Data.ByteString.Lazy as B\n", "import qualified Data.ByteString as S" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`B` has lazy bytestring types and functions, whereas [`S`](https://hackage.haskell.org/package/base/docs/GHC-Generics.html#t:S) has strict ones.\n", "We'll mostly be using the lazy version.\n", "\n", "The function __`pack`__ has the type signature `pack :: [Word8] -> ByteString`.\n", "What that means is that it takes a list of bytes of type `Word8` and\n", "returns a `ByteString`. You can think of it as taking a list, which is\n", "lazy, and making it less lazy, so that it's lazy only at 64K intervals.\n", "\n", "What's the deal with that `Word8` type? Well, it's like [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), only that it\n", "has a much smaller range, namely 0-255. It represents an 8-bit number.\n", "And just like [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), it's in the [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) typeclass. For instance, we know that\n", "the value `5` is polymorphic in that it can act like any numeral type.\n", "Well, it can also take the type of `Word8`." ] }, { "cell_type": "code", "execution_count": 120, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"can\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "B.pack [99,97,110]" ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"bcdefghijklmnopqrstuvwx\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "B.pack [98..120]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, you usually don't have to worry about the `Word8` too\n", "much, because the type system can makes the numbers choose that type. If\n", "you try to use a big number, like `336` as a `Word8`, it will just wrap\n", "around to `80`.\n", "\n", "We packed only a handful of values into a `ByteString`, so they fit inside\n", "one chunk. The `Empty` is like the `[]` for lists.\n", "\n", "__`unpack`__ is the inverse function of `pack`. It takes a bytestring and turns\n", "it into a list of bytes.\n", "\n", "__`fromChunks`__ takes a list of strict bytestrings and converts it to a lazy\n", "bytestring. __`toChunks`__ takes a lazy bytestring and converts it to a list\n", "of strict ones." ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"()*+,-./0\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "B.fromChunks [S.pack [40,41,42], S.pack [43,44,45], S.pack [46,47,48]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is good if you have a lot of small strict bytestrings and you want\n", "to process them efficiently without joining them into one big strict\n", "bytestring in memory first.\n", "\n", "The bytestring version of `:` is called __[`cons`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:cons)__ It takes a byte and a\n", "bytestring and puts the byte at the beginning. It's lazy though, so it\n", "will make a new chunk even if the first chunk in the bytestring isn't\n", "full. That's why it's better to use the strict version of [`cons`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:cons), __`cons'`__ if\n", "you're going to be inserting a lot of bytes at the beginning of a\n", "bytestring." ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"UPQRT\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "B.cons 85 $ B.pack [80,81,82,84]" ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"UPQRT\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "B.cons' 85 $ B.pack [80,81,82,84]" ] }, { "cell_type": "code", "execution_count": 125, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"23456789:;<\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldr B.cons B.empty [50..60]" ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"23456789:;<\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldr B.cons' B.empty [50..60]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see __[`empty`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#v:empty)__ makes an empty bytestring. See the difference\n", "between [`cons`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:cons) and `cons'`? With the [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr), we started with an empty\n", "bytestring and then went over the list of numbers from the right, adding\n", "each number to the beginning of the bytestring. When we used [`cons`](https://hackage.haskell.org/package/base/docs/Data-List-NonEmpty.html#v:cons), we\n", "ended up with one chunk for every byte, which kind of defeats the\n", "purpose.\n", "\n", "Otherwise, the bytestring modules have a load of functions that are\n", "analogous to those in `Data.List`, including, but not limited to, [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head),\n", "[`tail`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:tail), [`init`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:init), [`null`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:null), [`length`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:length), [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map), [`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse), [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl), [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr), [`concat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:concat), [`takeWhile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:takeWhile),\n", "[`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter), etc.\n", "\n", "It also has functions that have the same name and behave the same as\n", "some functions found in `System.IO`, only [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String)s are replaced with\n", "`ByteString`s. For instance, the [`readFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:readFile) function in `System.IO` has a type\n", "of `readFile :: FilePath -> IO String`, while the __[`readFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:readFile)__ from the\n", "bytestring modules has a type of `readFile :: FilePath -> IO ByteString`.\n", "Watch out, if you're using strict bytestrings and you attempt to read a\n", "file, it will read it into memory at once! With lazy bytestrings, it\n", "will read it into neat chunks.\n", "\n", "Let's make a simple program that takes two filenames as command-line\n", "arguments and copies the first file into the second file. Note that\n", "`System.Directory` already has a function called `copyFile`, but we're going\n", "to implement our own file copying function and program anyway." ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Environment\n", "import qualified Data.ByteString.Lazy as B\n", "\n", "main = do\n", " (fileName1:fileName2:_) <- getArgs\n", " copyFile fileName1 fileName2\n", "\n", "copyFile :: FilePath -> FilePath -> IO ()\n", "copyFile source dest = do\n", " contents <- B.readFile source\n", " B.writeFile dest contents" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We make our own function that takes two [`FilePath`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:FilePath)s (remember, [`FilePath`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:FilePath) is\n", "just a synonym for [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String)) and returns an I/O action that will copy one\n", "file into another using bytestring. In the `main` function, we just get\n", "the arguments and call our function with them to get the I/O action,\n", "which is then performed." ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "withArgs [\"/tmp/haiku.txt\", \"/tmp/haiku_mou_iku.txt\"] main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that a program that doesn't use bytestrings could look just like\n", "this, the only difference is that we used `B.readFile` and `B.writeFile`\n", "instead of [`readFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:readFile) and [`writeFile`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:writeFile). Many times, you can convert a program\n", "that uses normal strings to a program that uses bytestrings by just\n", "doing the necessary imports and then putting the qualified module names\n", "in front of some functions. Sometimes, you have to convert functions\n", "that you wrote to work on strings so that they work on bytestrings, but\n", "that's not hard.\n", "\n", "Whenever you need better performance in a program that reads a lot of\n", "data into strings, give bytestrings a try, chances are you'll get some\n", "good performance boosts with very little effort on your part. I usually\n", "write programs by using normal strings and then convert them to use\n", "bytestrings if the performance is not satisfactory.\n", "\n", "Exceptions\n", "----------\n", "\n", "\n", "\n", "All languages have procedures, functions, and pieces of code that might\n", "fail in some way. That's just a fact of life. Different languages have\n", "different ways of handling those failures. In C, we usually use some\n", "abnormal return value (like `-1` or a null pointer) to indicate that what\n", "a function returned shouldn't be treated like a normal value. Java and\n", "C\\#, on the other hand, tend to use exceptions to handle failure. When\n", "an exception is thrown, the control flow jumps to some code that we've\n", "defined that does some cleanup and then maybe re-throws the exception so\n", "that some other error handling code can take care of some other stuff.\n", "\n", "Haskell has a very good type system. Algebraic data types allow for\n", "types like [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) and [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) and we can use values of those types to\n", "represent results that may be there or not. In C, returning, say, `-1` on\n", "failure is completely a matter of convention. It only has special\n", "meaning to humans. If we're not careful, we might treat these abnormal\n", "values as ordinary ones and then they can cause havoc and dismay in our\n", "code. Haskell's type system gives us some much-needed safety in that\n", "aspect. A function `a -> Maybe b` clearly indicates that it it may\n", "produce a `b` wrapped in [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) or that it may return [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). The type is\n", "different from just plain `a -> b` and if we try to use those two\n", "functions interchangeably, the compiler will complain at us.\n", "\n", "Despite having expressive types that support failed computations,\n", "Haskell still has support for exceptions, because they make more sense\n", "in I/O contexts. A lot of things can go wrong when dealing with the\n", "outside world because it is so unreliable. For instance, when opening a\n", "file, a bunch of things can go wrong. The file might be locked, it might\n", "not be there at all or the hard disk drive or something might not be\n", "there at all. So it's good to be able to jump to some error handling\n", "part of our code when such an error occurs.\n", "\n", "Okay, so I/O code (i.e. impure code) can throw exceptions. It makes\n", "sense. But what about pure code? Well, it can throw exceptions too.\n", "Think about the [`div`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:div) and [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head) functions. They have types of\n", "`(Integral a) => a -> a -> a` and `[a] -> a`, respectively. No [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) or [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) in\n", "their return type and yet they can both fail! [`div`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:div) explodes in your face\n", "if you try to divide by zero and [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head) throws a tantrum when you give it\n", "an empty list." ] }, { "cell_type": "code", "execution_count": 129, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "divide by zero" ] } ], "source": [ "4 `div` 0" ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Prelude.head: empty list\nCallStack (from HasCallStack):\n error, called at libraries/base/GHC/List.hs:2004:3 in base:GHC.List\n errorEmptyList, called at libraries/base/GHC/List.hs:90:11 in base:GHC.List\n badHead, called at libraries/base/GHC/List.hs:84:28 in base:GHC.List\n head, called at :1:1 in interactive:Ghci3610" ] } ], "source": [ "head []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Pure code can throw exceptions, but they can only be caught in the\n", "I/O part of our code (when we're inside a *do* block that goes into\n", "`main`). That's because you don't know when (or if) anything will be\n", "evaluated in pure code, because it is lazy and doesn't have a\n", "well-defined order of execution, whereas I/O code does.\n", "\n", "Earlier, we talked about how we should spend as little time as possible\n", "in the I/O part of our program. The logic of our program should reside\n", "mostly within our pure functions, because their results are dependant\n", "only on the parameters that the functions are called with. When dealing\n", "with pure functions, you only have to think about what a function\n", "returns, because it can't do anything else. This makes your life easier.\n", "Even though doing some logic in I/O is necessary (like opening files and\n", "the like), it should preferably be kept to a minimum. Pure functions are\n", "lazy by default, which means that we don't know when they will be\n", "evaluated and that it really shouldn't matter. However, once pure\n", "functions start throwing exceptions, it matters when they are evaluated.\n", "That's why we can only catch exceptions thrown from pure functions in\n", "the I/O part of our code. And that's bad, because we want to keep the\n", "I/O part as small as possible. However, if we don't catch them in the\n", "I/O part of our code, our program crashes. The solution? Don't mix\n", "exceptions and pure code. Take advantage of Haskell's powerful type\n", "system and use types like [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) and [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) to represent results that may\n", "have failed.\n", "\n", "That's why we'll just be looking at how to use I/O exceptions for now.\n", "I/O exceptions are exceptions that are caused when something goes wrong\n", "while we are communicating with the outside world in an I/O action\n", "that's part of `main`. For example, we can try opening a file and then it\n", "turns out that the file has been deleted or something. Take a look at\n", "this program that opens a file whose name is given to it as a command\n", "line argument and tells us how many lines the file has." ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Environment\n", "import System.IO\n", "\n", "main = do (fileName:_) <- getArgs\n", " contents <- readFile fileName\n", " putStrLn $ \"The file has \" ++ show (length (lines contents)) ++ \" lines!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A very simple program. We perform the [`getArgs`](https://hackage.haskell.org/package/base/docs/System-Environment.html#v:getArgs) I/O action and bind the\n", "first string in the list that it yields to `fileName`. Then we call the\n", "`contents` of the file with that name contents. Lastly, we apply [`lines`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:lines) to\n", "those contents to get a list of lines and then we get the length of that\n", "list and give it to [`show`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:show) to get a string representation of that number.\n", "It works as expected, but what happens when we give it the name of a\n", "file that doesn't exist?" ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "i_dont_exist.txt: openFile: does not exist (No such file or directory)" ] } ], "source": [ "withArgs [\"i_dont_exist.txt\"] main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aha, we get an error from GHC, telling us that the file does not exist.\n", "Our program crashes. What if we wanted to print out a nicer message if\n", "the file doesn't exist? One way to do that is to check if the file\n", "exists before trying to open it by using the __`doesFileExist`__ function from\n", "`System.Directory`." ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Environment\n", "import System.IO\n", "import System.Directory\n", "\n", "main = do (fileName:_) <- getArgs\n", " fileExists <- doesFileExist fileName\n", " if fileExists\n", " then do contents <- readFile fileName\n", " putStrLn $ \"The file has \" ++ show (length (lines contents)) ++ \" lines!\"\n", " else do putStrLn \"The file doesn't exist!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We did `fileExists <- doesFileExist fileName` because `doesFileExist` has a\n", "type of `doesFileExist :: FilePath -> IO Bool`, which means that it\n", "returns an I/O action that has as its result a boolean value which tells\n", "us if the file exists. We can't just use `doesFileExist` in an *if*\n", "expression directly.\n", "\n", "Another solution here would be to use exceptions. It's perfectly\n", "acceptable to use them in this context. A file not existing is an\n", "exception that arises from I/O, so catching it in I/O is fine and dandy.\n", "\n", "To deal with this by using exceptions, we're going to take advantage of\n", "the __[`catch`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:catch)__ function from `System.IO.Error`. Its type is\n", "`catch :: IO a -> (IOError -> IO a) -> IO a`.\n", "It takes two parameters. The first one is\n", "an I/O action. For instance, it could be an I/O action that tries to\n", "open a file. The second one is the so-called handler. If the first I/O\n", "action passed to [`catch`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:catch) throws an I/O exception, that exception gets\n", "passed to the handler, which then decides what to do. So the final\n", "result is an I/O action that will either act the same as the first\n", "parameter or it will do what the handler tells it if the first I/O\n", "action throws an exception.\n", "\n", "\n", "\n", "If you're familiar with *try-catch* blocks in languages like Java or\n", "Python, the [`catch`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:catch) function is similar to them. The first parameter is\n", "the thing to try, kind of like the stuff in the *try* block in other,\n", "imperative languages. The second parameter is the handler that takes an\n", "exception, just like most *catch* blocks take exceptions that you can\n", "then examine to see what happened. The handler is invoked if an\n", "exception is thrown.\n", "\n", "The handler takes a value of type [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError), which is a value that\n", "signifies that an I/O exception occurred. It also carries information\n", "regarding the type of the exception that was thrown. How this type is\n", "implemented depends on the implementation of the language itself, which\n", "means that we can't inspect values of the type [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError) by pattern\n", "matching against them, just like we can't pattern match against values\n", "of type `IO `. We can use a bunch of useful predicates to find\n", "out stuff about values of type [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError) as we'll learn in a second.\n", "\n", "So let's put our new friend [`catch`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:catch) to use!" ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Environment\n", "import System.IO\n", "import System.IO.Error\n", "import Control.Exception\n", "\n", "main = toTry `catch` handler\n", "\n", "toTry :: IO ()\n", "toTry = do (fileName:_) <- getArgs\n", " contents <- readFile fileName\n", " putStrLn $ \"The file has \" ++ show (length (lines contents)) ++ \" lines!\"\n", "\n", "handler :: IOError -> IO ()\n", "handler e = putStrLn \"Whoops, had some trouble!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First of all, you'll see that put backticks around it so that we can use\n", "it as an infix function, because it takes two parameters. Using it as an\n", "infix function makes it more readable. So ``toTry `catch` handler`` is the\n", "same as `catch toTry handler`, which fits well with its type. `toTry` is the\n", "I/O action that we try to carry out and `handler` is the function that\n", "takes an [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError) and returns an action to be carried out in case of an\n", "exception.\n", "\n", "Let's give this a go:" ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "The file has 3 lines!" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withArgs [\"/tmp/haiku.txt\"] main" ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Whoops, had some trouble!" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withArgs [\"i_dont_exists.txt\"] main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the handler, we didn't check to see what kind of [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError) we got. We\n", "just say `\"Whoops, had some trouble!\"` for any kind of error. Just\n", "catching all types of exceptions in one handler is bad practice in\n", "Haskell just like it is in most other languages. What if some other\n", "exception happens that we don't want to catch, like us interrupting the\n", "program or something? That's why we're going to do the same thing that's\n", "usually done in other languages as well: we'll check to see what kind of\n", "exception we got. If it's the kind of exception we're waiting to catch,\n", "we do our stuff. If it's not, we throw that exception back into the\n", "wild. Let's modify our program to catch only the exceptions caused by a\n", "file not existing." ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Environment\n", "import System.IO\n", "import System.IO.Error\n", "\n", "main = toTry `catch` handler\n", "\n", "toTry :: IO ()\n", "toTry = do (fileName:_) <- getArgs\n", " contents <- readFile fileName\n", " putStrLn $ \"The file has \" ++ show (length (lines contents)) ++ \" lines!\"\n", "\n", "handler :: IOError -> IO ()\n", "handler e\n", " | isDoesNotExistError e = putStrLn \"The file doesn't exist!\"\n", " | otherwise = ioError e" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Everything stays the same except the handler, which we modified to only\n", "catch a certain group of I/O exceptions. Here we used two new functions\n", "from `System.IO.Error` — __[`isDoesNotExistError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isDoesNotExistError)__ and __[`ioError`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:ioError)__.\n", "[`isDoesNotExistError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isDoesNotExistError) is a predicate over `IOErrors`, which means that it's\n", "a function that takes an [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError) and returns a [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) or [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False), meaning it\n", "has a type of `isDoesNotExistError :: IOError -> Bool`. We use it on the\n", "exception that gets passed to our handler to see if it's an error caused\n", "by a file not existing. We use\n", "[guard](http://learnyouahaskell.com/syntax-in-functions#guards-guards) syntax here, but we could\n", "have also used an *if else*. If it's not caused by a file not existing,\n", "we re-throw the exception that was passed by the handler with the\n", "[`ioError`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:ioError) function. It has a type of `ioError :: IOException -> IO a`, so\n", "it takes an [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError) and produces an I/O action that will throw it. The\n", "I/O action has a type of `IO a`, because it never actually yields a\n", "result, so it can act as [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) *`anything`*.\n", "\n", "So the exception thrown in the `toTry` I/O action that we glued together\n", "with a *do* block isn't caused by a file existing,\n", "``toTry `catch` handler`` will catch that and then re-throw it. Pretty cool, huh?\n", "\n", "There are several predicates that act on [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError) and if a guard doesn't\n", "evaluate to [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), evaluation falls through to the next guard. The\n", "predicates that act on [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError) are:\n", "\n", "- __[`isAlreadyExistsError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isAlreadyExistsError)__\n", "- __[`isDoesNotExistError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isDoesNotExistError)__\n", "- __[`isAlreadyInUseError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isAlreadyInUseError)__\n", "- __[`isFullError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isFullError)__\n", "- __[`isEOFError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isEOFError)__\n", "- __[`isIllegalOperation`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isIllegalOperation)__\n", "- __[`isPermissionError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isPermissionError)__\n", "- __[`isUserError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isUserError)__\n", "\n", "Most of these are pretty self-explanatory. __[`isUserError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isUserError)__ evaluates to [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True)\n", "when we use the function [`userError`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:userError) to make the exception, which is used\n", "for making exceptions from our code and equipping them with a string.\n", "For instance, you can do\n", "`ioError $ userError \"remote computer unplugged!\"`,\n", "although it's prefered you use types like [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) and [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)\n", "to express possible failure instead of throwing exceptions yourself with\n", "[`userError`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:userError).\n", "\n", "So you could have a handler that looks something like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
handler :: IOError -> IO ()\n",
    "handler e\n",
    "    | isDoesNotExistError e = putStrLn \"The file doesn't exist!\"\n",
    "    | isFullError e = freeSomeSpace\n",
    "    | isIllegalOperation e = notifyCops\n",
    "    | otherwise = ioError e
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Where `notifyCops` and `freeSomeSpace` are some I/O actions that you define.\n", "Be sure to re-throw exceptions if they don't match any of your criteria,\n", "otherwise you're causing your program to fail silently in some cases\n", "where it shouldn't.\n", "\n", "`System.IO.Error` also exports functions that enable us to ask our\n", "exceptions for some attributes, like what the handle of the file that\n", "caused the error is, or what the filename is. These start with `ioe` and\n", "you can see a [full list of\n", "them](http://www.haskell.org/ghc/docs/6.10.1/html/libraries/base/System-IO-Error.html#3)\n", "in the documentation. Say we want to print the filename that caused our\n", "error. We can't print the `fileName` that we got from [`getArgs`](https://hackage.haskell.org/package/base/docs/System-Environment.html#v:getArgs), because\n", "only the [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError) is passed to the handler and the handler doesn't know\n", "about anything else. A function depends only on the parameters it was\n", "called with. That's why we can use the __[`ioeGetFileName`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:ioeGetFileName)__ function, which\n", "has a type of `ioeGetFileName :: IOError -> Maybe FilePath`. It takes an\n", "[`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError) as a parameter and maybe returns a [`FilePath`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:FilePath) (which is just a\n", "type synonym for [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String), remember, so it's kind of the same thing).\n", "Basically, what it does is it extracts the file path from the [`IOError`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IOError),\n", "if it can. Let's modify our program to print out the file path that's\n", "responsible for the exception occurring." ] }, { "cell_type": "code", "execution_count": 138, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Environment\n", "import System.IO\n", "import System.IO.Error\n", "\n", "main = toTry `catch` handler\n", "\n", "toTry :: IO ()\n", "toTry = do (fileName:_) <- getArgs\n", " contents <- readFile fileName\n", " putStrLn $ \"The file has \" ++ show (length (lines contents)) ++ \" lines!\"\n", "\n", "handler :: IOError -> IO ()\n", "handler e\n", " | isDoesNotExistError e =\n", " case ioeGetFileName e of Just path -> putStrLn $ \"Whoops! File does not exist at: \" ++ path\n", " Nothing -> putStrLn \"Whoops! File does not exist at unknown location!\"\n", " | otherwise = ioError e" ] }, { "cell_type": "code", "execution_count": 139, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Whoops! File does not exist at: ./i_dont_exist.txt" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withArgs [\"./i_dont_exist.txt\"] main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the guard where [`isDoesNotExistError`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:isDoesNotExistError) is [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), we used a *case*\n", "expression to call [`ioeGetFileName`](https://hackage.haskell.org/package/base/docs/System-IO-Error.html#v:ioeGetFileName) with `e` and then pattern match against\n", "the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value that it returned. Using *case* expressions is commonly\n", "used when you want to pattern match against something without bringing\n", "in a new function.\n", "\n", "You don't have to use one handler to [`catch`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:catch) exceptions in your whole I/O\n", "part. You can just cover certain parts of your I/O code with [`catch`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:catch) or\n", "you can cover several of them with [`catch`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:catch) and use different handlers for\n", "them, like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
main = do toTry `catch` handler1\n",
    "          thenTryThis `catch` handler2\n",
    "          launchRockets
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, `toTry` uses `handler1` as the handler and `thenTryThis` uses `handler2`.\n", "`launchRockets` isn't a parameter to [`catch`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:catch), so whichever exceptions it\n", "might throw will likely crash our program, unless `launchRockets` uses\n", "[`catch`](https://hackage.haskell.org/package/base/docs/Control-Exception.html#v:catch) internally to handle its own exceptions. Of course `toTry`,\n", "`thenTryThis` and `launchRockets` are I/O actions that have been glued\n", "together using *do* syntax and hypothetically defined somewhere else.\n", "This is kind of similar to *try-catch* blocks of other languages, where\n", "you can surround your whole program in a single *try-catch* or you can\n", "use a more fine-grained approach and use different ones in different\n", "parts of your code to control what kind of error handling happens where.\n", "\n", "Now you know how to deal with I/O exceptions! Throwing exceptions from\n", "pure code and dealing with them hasn't been covered here, mainly\n", "because, like we said, Haskell offers much better ways to indicate\n", "errors than reverting to I/O to catch them. Even when glueing together\n", "I/O actions that might fail, I prefer to have their type be something\n", "like `IO (Either a b)`, meaning that they're normal I/O actions but the\n", "result that they yield when performed is of type `Either a b`, meaning\n", "it's either `Left a` or `Right b`." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.8.2" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/10-functionally-solving-problems.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Functionally Solving Problems\n", "=============================\n", "\n", "In this chapter, we'll take a look at a few interesting problems and how\n", "to think functionally to solve them as elegantly as possible. We\n", "probably won't be introducing any new concepts, we'll just be flexing\n", "our newly acquired Haskell muscles and practicing our coding skills.\n", "Each section will present a different problem. First we'll describe the\n", "problem, then we'll try and find out what the best (or least worst) way\n", "of solving it is." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Reverse Polish notation calculator\n", "----------------------------------\n", "\n", "Usually when we write mathematical expressions in school, we write them\n", "in an infix manner. For instance, we write `10 - (4 + 3) * 2`. [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-), [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) and\n", "[`-`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-45-) are infix operators, just like the infix functions we met in Haskell\n", "([`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-), `` `elem` ``, etc.). This makes it handy because we, as humans, can\n", "parse it easily in our minds by looking at such an expression. The\n", "downside to it is that we have to use parentheses to denote precedence.\n", "\n", "[Reverse Polish\n", "notation](http://en.wikipedia.org/wiki/Reverse_Polish_notation) is\n", "another way of writing down mathematical expressions. Initially it looks\n", "a bit weird, but it's actually pretty easy to understand and use because\n", "there's no need for parentheses and it's very easy to punch into a\n", "calculator. While most modern calculators use infix notation, some\n", "people still swear by RPN calculators. This is what the previous infix\n", "expression looks like in RPN: `10 4 3 + 2 * -`. How do we calculate what\n", "the result of that is? Well, think of a stack. You go over the\n", "expression from left to right. Every time a number is encountered, push\n", "it on to the stack. When we encounter an operator, take the two numbers\n", "that are on top of the stack (we also say that we *pop* them), use the\n", "operator and those two and then push the resulting number back onto the\n", "stack. When you reach the end of the expression, you should be left with\n", "a single number if the expression was well-formed and that number\n", "represents the result.\n", "\n", "\n", "\n", "Let's go over the expression `10 4 3 + 2 * -` together! First we push `10`\n", "on to the stack and the stack is now `10`. The next item is `4`, so we push\n", "it to the stack as well. The stack is now `10, 4`. We do the same with `3`\n", "and the stack is now `10, 4, 3`. And now, we encounter an operator, namely\n", "[`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-)! We pop the two top numbers from the stack (so now the stack is just\n", "`10`), add those numbers together and push that result to the stack. The\n", "stack is now `10, 7`. We push `2` to the stack, the stack for now is\n", "`10, 7, 2`. We've encountered an operator again, so let's pop `7` and `2` off the\n", "stack, multiply them and push that result to the stack. Multiplying `7`\n", "and `2` produces a `14`, so the stack we have now is `10, 14`. Finally,\n", "there's a [`-`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-45-). We pop `10` and `14` from the stack, subtract `14` from `10` and\n", "push that back. The number on the stack is now `-4` and because there are\n", "no more numbers or operators in our expression, that's our result!\n", "\n", "Now that we know how we'd calculate any RPN expression by hand, let's\n", "think about how we could make a Haskell function that takes as its\n", "parameter a string that contains a RPN expression, like\n", "`\"10 4 3 + 2 * -\"` and gives us back its result.\n", "\n", "What would the type of that function be? We want it to take a string as\n", "a parameter and produce a number as its result. So it will probably be\n", "something like `solveRPN :: (Num a) => String -> a`.\n", "\n", "> __Protip:__ it really helps to first think what the type declaration of a\n", "> function should be before concerning ourselves with the implementation\n", "> and then write it down. In Haskell, a function's type declaration tells\n", "> us a whole lot about the function, due to the very strong type system.\n", "\n", "\n", "\n", "Cool. When implementing a solution to a problem in Haskell, it's also\n", "good to think back on how you did it by hand and maybe try to see if you\n", "can gain any insight from that. Here we see that we treated every number\n", "or operator that was separated by a space as a single item. So it might\n", "help us if we start by breaking a string like\n", "`\"10 4 3 + 2 * -\"` into a\n", "list of items like `[\"10\",\"4\",\"3\",\"+\",\"2\",\"*\",\"-\"]`.\n", "\n", "Next up, what did we do with that list of items in our head? We went\n", "over it from left to right and kept a stack as we did that. Does the\n", "previous sentence remind you of anything? Remember, in the section about\n", "[folds](http://learnyouahaskell.com/higher-order-functions#folds), we said that pretty much any\n", "function where you traverse a list from left to right or right to left\n", "one element by element and build up (accumulate) some result (whether\n", "it's a number, a list, a stack, whatever) can be implemented with a\n", "fold.\n", "\n", "In this case, we're going to use a left fold, because we go over the\n", "list from left to right. The accumulator value will be our stack and\n", "hence, the result from the fold will also be a stack, only as we've\n", "seen, it will only have one item.\n", "\n", "One more thing to think about is, well, how are we going to represent\n", "the stack? I propose we use a list. Also I propose that we keep the top\n", "of our stack at the head of the list. That's because adding to the head\n", "(beginning) of a list is much faster than adding to the end of it. So if\n", "we have a stack of, say, `10, 4, 3`, we'll represent that as the list\n", "`[3,4,10]`.\n", "\n", "Now we have enough information to roughly sketch our function. It's\n", "going to take a string, like, `\"10 4 3 + 2 * -\"` and break it down into a\n", "list of items by using [`words`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:words) to get `[\"10\",\"4\",\"3\",\"+\",\"2\",\"*\",\"-\"]`.\n", "Next, we'll do a left fold over that list and end up with a stack that\n", "has a single item, so `[-4]`. We take that single item out of the list and\n", "that's our final result!\n", "\n", "So here's a sketch of that function:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
import Data.List\n",
    "\n",
    "solveRPN :: (Num a) => String -> a\n",
    "solveRPN expression = head (foldl foldingFunction [] (words expression))\n",
    "    where   foldingFunction stack item = ...
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We take the expression and turn it into a list of items. Then we fold\n", "over that list of items with the folding function. Mind the `[]`, which\n", "represents the starting accumulator. The accumulator is our stack, so `[]`\n", "represents an empty stack, which is what we start with. After getting\n", "the final stack with a single item, we call [`head`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:head) on that list to get the\n", "item out and then we apply [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read).\n", "\n", "So all that's left now is to implement a folding function that will take\n", "a stack, like `[4,10]`, and an item, like `\"3\"` and return a new stack\n", "`[3,4,10]`. If the stack is `[4,10]` and the item `\"*\"`, then it will have to\n", "return `[40]`. But before that, let's turn our function into [point-free\n", "style](http://learnyouahaskell.com/higher-order-functions#composition) because it has a lot of\n", "parentheses that are kind of freaking me out:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
import Data.List\n",
    "\n",
    "solveRPN :: (Num a) => String -> a\n",
    "solveRPN = head . foldl foldingFunction [] . words\n",
    "    where   foldingFunction stack item = ...
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah, there we go. Much better. So, the folding function will take a stack\n", "and an item and return a new stack. We'll use pattern matching to get\n", "the top items of a stack and to pattern match against operators like\n", "`\"*\"` and `\"-\"`." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "solveRPN :: (Num a, Read a) => String -> a\n", "solveRPN = head . foldl foldingFunction [] . words\n", " where foldingFunction (x:y:ys) \"*\" = (x * y):ys\n", " foldingFunction (x:y:ys) \"+\" = (x + y):ys\n", " foldingFunction (x:y:ys) \"-\" = (y - x):ys\n", " foldingFunction xs numberString = read numberString:xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We laid this out as four patterns. The patterns will be tried from top\n", "to bottom. First the folding function will see if the current item is\n", "`\"*\"`. If it is, then it will take a list like `[3,4,9,3]` and call its\n", "first two elements `x` and `y` respectively. So in this case, `x` would be `3`\n", "and `y` would be `4`. `ys` would be `[9,3]`. It will return a list that's just\n", "like `ys`, only it has `x` and `y` multiplied as its head. So with this we pop\n", "the two topmost numbers off the stack, multiply them and push the result\n", "back on to the stack. If the item is not `\"*\"`, the pattern matching will\n", "fall through and `\"+\"` will be checked, and so on.\n", "\n", "If the item is none of the operators, then we assume it's a string that\n", "represents a number. If it's a number, we just call [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) on that string\n", "to get a number from it and return the previous stack but with that\n", "number pushed to the top.\n", "\n", "And that's it! Also noticed that we added an extra class constraint of\n", "`Read a` to the function declaration, because we call [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) on our string\n", "to get the number. So this declaration means that the result can be of\n", "any type that's part of the [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num) and [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read) typeclasses (like [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), [`Float`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Float),\n", "etc.).\n", "\n", "For the list of items `[\"2\",\"3\",\"+\"]`, our function will start folding\n", "from the left. The initial stack will be `[]`. It will call the folding\n", "function with `[]` as the stack (accumulator) and `\"2\"` as the item. Because\n", "that item is not an operator, it will be [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) and the added to the\n", "beginning of `[]`. So the new stack is now `[2]` and the folding function\n", "will be called with `[2]` as the stack and `[\"3\"]` as the item, producing a\n", "new stack of `[3,2]`. Then, it's called for the third time with `[3,2]` as\n", "the stack and `\"+\"` as the item. This causes these two numbers to be\n", "popped off the stack, added together and pushed back. The final stack is\n", "`[5]`, which is the number that we return.\n", "\n", "Let's play around with our function:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "-4" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"10 4 3 + 2 * -\"" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"2 3 +\"" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "-3947" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"90 34 12 33 55 66 + * - +\"" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "4037" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"90 34 12 33 55 66 + * - + -\"" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "4037" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"90 34 12 33 55 66 + * - + -\"" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "87" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"90 3 -\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool, it works! One nice thing about this function is that it can be\n", "easily modified to support various other operators. They don't even have\n", "to be binary operators. For instance, we can make an operator `\"log\"` that\n", "just pops one number off the stack and pushes back its logarithm. We can\n", "also make a ternary operators that pop three numbers off the stack and\n", "push back a result or operators like `\"sum\"` which pop off all the numbers\n", "and push back their sum.\n", "\n", "Let's modify our function to take a few more operators. For simplicity's\n", "sake, we'll change its type declaration so that it returns a number of\n", "type [`Float`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Float)." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.List\n", "\n", "solveRPN :: String -> Float\n", "solveRPN = head . foldl foldingFunction [] . words\n", " where foldingFunction (x:y:ys) \"*\" = (x * y):ys\n", " foldingFunction (x:y:ys) \"+\" = (x + y):ys\n", " foldingFunction (x:y:ys) \"-\" = (y - x):ys\n", " foldingFunction (x:y:ys) \"/\" = (y / x):ys\n", " foldingFunction (x:y:ys) \"^\" = (y ** x):ys\n", " foldingFunction (x:xs) \"ln\" = log x:xs\n", " foldingFunction xs \"sum\" = [sum xs]\n", " foldingFunction xs numberString = read numberString:xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wow, great! [`/`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-47-) is division of course and [`**`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42--42-) is floating point\n", "exponentiation. With the logarithm operator, we just pattern match\n", "against a single element and the rest of the stack because we only need\n", "one element to perform its natural logarithm. With the sum operator, we\n", "just return a stack that has only one element, which is the sum of the\n", "stack so far." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "0.9932518" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"2.7 ln\"" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "10.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"10 10 10 10 sum 4 /\"" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "12.5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"10 10 10 10 10 sum 4 /\"" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "100.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"10 2 ^\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that we can include floating point numbers in our expression\n", "because [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) knows how to read them." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "6.575903" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"43.2425 0.5 ^\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I think that making a function that can calculate arbitrary floating\n", "point RPN expressions and has the option to be easily extended in 10\n", "lines is pretty awesome.\n", "\n", "One thing to note about this function is that it's not really fault\n", "tolerant. When given input that doesn't make sense, it will just crash\n", "everything. We'll make a fault tolerant version of this with a type\n", "declaration of `solveRPN :: String -> Maybe Float` once we get to know\n", "monads (they're not scary, trust me!). We could make one right now, but\n", "it would be a bit tedious because it would involve a lot of checking for\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) on every step. If you're feeling up to the challenge though, you\n", "can go ahead and try it! Hint: you can use [`reads`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reads) to see if a read was\n", "successful or not.\n", "\n", "Heathrow to London\n", "------------------\n", "\n", "Our next problem is this: your plane has just landed in England and you\n", "rent a car. You have a meeting really soon and you have to get from\n", "Heathrow Airport to London as fast as you can (but safely!).\n", "\n", "There are two main roads going from Heathrow to London and there's a\n", "number of regional roads crossing them. It takes you a fixed amount of\n", "time to travel from one crossroads to another. It's up to you to find\n", "the optimal path to take so that you get to London as fast as you can!\n", "You start on the left side and can either cross to the other main road\n", "or go forward.\n", "\n", "\n", "\n", "As you can see in the picture, the shortest path from Heathrow to London\n", "in this case is to start on main road B, cross over, go forward on A,\n", "cross over again and then go forward twice on B. If we take this path,\n", "it takes us 75 minutes. Had we chosen any other path, it would take more\n", "than that.\n", "\n", "Our job is to make a program that takes input that represents a road\n", "system and print out what the shortest path across it is. Here's what\n", "the input would look like for this case:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "source": [ "
50\n",
    "10\n",
    "30\n",
    "5\n",
    "90\n",
    "20\n",
    "40\n",
    "2\n",
    "25\n",
    "10\n",
    "8\n",
    "0
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To mentally parse the input file, read it in threes and mentally split\n", "the road system into sections. Each section is comprised of a road A,\n", "road B and a crossing road. To have it neatly fit into threes, we say\n", "that there's a last crossing section that takes 0 minutes to drive over.\n", "That's because we don't care where we arrive in London, as long as we're\n", "in London.\n", "\n", "Just like we did when solving the RPN calculator problem, we're going to\n", "solve this problem in three steps:\n", "\n", "- Forget Haskell for a minute and think about how we'd solve the\n", " problem by hand\n", "- Think about how we're going to represent our data in Haskell\n", "- Figure out how to operate on that data in Haskell so that we produce\n", " at a solution\n", "\n", "In the RPN calculator section, we first figured out that when\n", "calculating an expression by hand, we'd keep a sort of stack in our\n", "minds and then go over the expression one item at a time. We decided to\n", "use a list of strings to represent our expression. Finally, we used a\n", "left fold to walk oevr the list of strings while keeping a stack to\n", "produce a solution.\n", "\n", "Okay, so how would we figure out the shortest path from Heathrow to\n", "London by hand? Well, we can just sort of look at the whole picture and\n", "try to guess what the shortest path is and hopefully we'll make a guess\n", "that's right. That solution works for very small inputs, but what if we\n", "have a road that has 10,000 sections? Yikes! We also won't be able to\n", "say for certain that our solution is the optimal one, we can just sort\n", "of say that we're pretty sure.\n", "\n", "That's not a good solution then. Here's a simplified picture of our road\n", "system:\n", "\n", "\n", "\n", "Alright, can you figure out what the shortest path to the first\n", "crossroads (the first blue dot on A, marked *A1*) on road A is? That's\n", "pretty trivial. We just see if it's shorter to go directly forward on A\n", "or if it's shorter to go forward on B and then cross over. Obviously,\n", "it's cheaper to go forward via B and then cross over because that takes\n", "40 minutes, whereas going directly via A takes 50 minutes. What about\n", "crossroads *B1*? Same thing. We see that it's a lot cheaper to just go\n", "directly via B (incurring a cost of 10 minutes), because going via A and\n", "then crossing over would take us a whole 80 minutes!\n", "\n", "Now we know what the cheapest path to *A1* is (go via B and then cross\n", "over, so we'll say that's `B, C` with a cost of 40) and we know what the\n", "cheapest path to *B1* is (go directly via B, so that's just `B`, going at\n", "10). Does this knowledge help us at all if we want to know the cheapest\n", "path to the next crossroads on both main roads? Gee golly, it sure does!\n", "\n", "Let's see what the shortest path to *A2* would be. To get to *A2*, we'll\n", "either go directly to *A2* from *A1* or we'll go forward from *B1* and\n", "then cross over (remember, we can only move forward or cross to the\n", "other side). And because we know the cost to *A1* and *B1*, we can\n", "easily figure out what the best path to *A2* is. It costs 40 to get to\n", "*A1* and then 5 to get from *A1* to *A2*, so that's `B, C, A` for a cost\n", "of 45. It costs only 10 to get to *B1*, but then it would take an\n", "additional 110 minutes to go to *B2* and then cross over! So obviously,\n", "the cheapest path to *A2* is `B, C, A`. In the same way, the cheapest way\n", "to *B2* is to go forward from *A1* and then cross over.\n", "\n", "> __Maybe you're asking yourself__: but what about getting to *A2* by first\n", "> crossing over at *B1* and then going on forward? Well, we already\n", "> covered crossing from *B1* to *A1* when we were looking for the best way\n", "> to *A1*, so we don't have to take that into account in the next step as\n", "> well.\n", "\n", "Now that we have the best path to *A2* and *B2*, we can repeat this\n", "indefinitely until we reach the end. Once we've gotten the best paths\n", "for *A4* and *B4*, the one that's cheaper is the optimal path!\n", "\n", "So in essence, for the second section, we just repeat the step we did at\n", "first, only we take into account what the previous best paths on A and\n", "B. We could say that we also took into account the best paths on A and\n", "on B in the first step, only they were both empty paths with a cost of\n", "0.\n", "\n", "Here's a summary. To get the bast path from Heathrow to London, we do\n", "this: first we see what the best path to the next crossroads on main\n", "road A is. The two options are going directly forward or starting at the\n", "opposite road, going forward and then crossing over. We remember the\n", "cost and the path. We use the same method to see what the best path to\n", "the next crossroads on main road B is and remember that. Then, we see if\n", "the path to the next crossroads on A is cheaper if we go from the\n", "previous A crossroads or if we go from the previous B crossroads and\n", "then cross over. We remember the cheaper path and then we do the same\n", "for the crossroads opposite of it. We do this for every section until we\n", "reach the end. Once we've reached the end, the cheapest of the two paths\n", "that we have is our optimal path!\n", "\n", "So in essence, we keep one shortest path on the A road and one shortest\n", "path on the B road and when we reach the end, the shorter of those two\n", "is our path. We now know how to figure out the shortest path by hand. If\n", "you had enough time, paper and pencils, you could figure out the\n", "shortest path through a road system with any number of sections.\n", "\n", "Next step! How do we represent this road system with Haskell's data\n", "types? One way is to think of the starting points and crossroads as\n", "nodes of a graph that point to other crossroads. If we imagine that the\n", "starting points actually point to each other with a road that has a\n", "length of one, we see that every crossroads (or node) points to the node\n", "on the other side and also to the next one on its side. Except for the\n", "last nodes, they just point to the other side." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Node = Node Road Road | EndNode Road\n", "data Road = Road Int Node" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A node is either a normal node and has information about the road that\n", "leads to the other main road and the road that leads to the next node or\n", "an end node, which only has information about the road to the other main\n", "road. A road keeps information about how long it is and which node it\n", "points to. For instance, the first part of the road on the A main road\n", "would be `Road 50 a1` where `a1` would be a node `Node x y`, where `x` and `y` are\n", "roads that point to *B1* and *A2*.\n", "\n", "Another way would be to use [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) for the road parts that point forward.\n", "Each node has a road part that point to the opposite road, but only\n", "those nodes that aren't the end ones have road parts that point forward." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Node = Node Road (Maybe Road)\n", "data Road = Road Int Node" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is an alright way to represent the road system in Haskell and we\n", "could certainly solve this problem with it, but maybe we could come up\n", "with something simpler? If we think back to our solution by hand, we\n", "always just checked the lengths of three road parts at once: the road\n", "part on the A road, its opposite part on the B road and part C, which\n", "touches those two parts and connects them. When we were looking for the\n", "shortest path to *A1* and *B1*, we only had to deal with the lengths of\n", "the first three parts, which have lengths of 50, 10 and 30. We'll call\n", "that one section. So the road system that we use for this example can be\n", "easily represented as four sections: `50, 10, 30`, `5, 90, 20`, `40, 2, 25`,\n", "and `10, 8, 0`.\n", "\n", "It's always good to keep our data types as simple as possible, although\n", "not any simpler!" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Section = Section { getA :: Int, getB :: Int, getC :: Int } deriving (Show)\n", "type RoadSystem = [Section]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is pretty much perfect! It's as simple as it goes and I have a\n", "feeling it'll work perfectly for implementing our solution. `Section` is a\n", "simple algebraic data type that holds three integers for the lengths of\n", "its three road parts. We introduce a type synonym as well, saying that\n", "`RoadSystem` is a list of sections.\n", "\n", "> We could also use a triple of `(Int, Int, Int)` to represent a road\n", "> section. Using tuples instead of making your own algebraic data types is\n", "> good for some small localized stuff, but it's usually better to make a\n", "> new type for things like this. It gives the type system more information\n", "> about what's what. We can use `(Int, Int, Int)` to represent a road\n", "> section or a vector in 3D space and we can operate on those two, but\n", "> that allows us to mix them up. If we use `Section` and `Vector` data types,\n", "> then we can't accidentally add a vector to a section of a road system.\n", "\n", "Our road system from Heathrow to London can now be represented like\n", "this:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "heathrowToLondon :: RoadSystem\n", "heathrowToLondon = [Section 50 10 30, Section 5 90 20, Section 40 2 25, Section 10 8 0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All we need to do now is to implement the solution that we came up with\n", "previously in Haskell. What should the type declaration for a function\n", "that calculates a shortest path for any given road system be? It should\n", "take a road system as a parameter and return a path. We'll represent a\n", "path as a list as well. Let's introduce a `Label` type that's just an\n", "enumeration of either `A`, `B` or [`C`](https://hackage.haskell.org/package/base/docs/Foreign-C.html). We'll also make a type synonym: `Path`." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Label = A | B | C deriving (Show)\n", "type Path = [(Label, Int)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our function, we'll call it `optimalPath` should thus have a type\n", "declaration of `optimalPath :: RoadSystem -> Path`. If called with the\n", "road system `heathrowToLondon`, it should return the following path:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
[(B,10),(C,30),(A,5),(C,20),(B,2),(B,8)]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We're going to have to walk over the list with the sections from left to\n", "right and keep the optimal path on A and optimal path on B as we go\n", "along. We'll accumulate the best path as we walk over the list, left to\n", "right. What does that sound like? Ding, ding, ding! That's right, A LEFT\n", "FOLD!\n", "\n", "When doing the solution by hand, there was a step that we repeated over\n", "and over again. It involved checking the optimal paths on A and B so far\n", "and the current section to produce the new optimal paths on A and B. For\n", "instance, at the beginning the optimal paths were `[]` and `[]` for A and B\n", "respectively. We examined the section `Section 50 10 30` and concluded\n", "that the new optimal path to *A1* is `[(B,10),(C,30)]` and the optimal\n", "path to *B1* is `[(B,10)]`. If you look at this step as a function, it\n", "takes a pair of paths and a section and produces a new pair of paths.\n", "The type is `(Path, Path) -> Section -> (Path, Path)`. Let's go ahead\n", "and implement this function, because it's bound to be useful.\n", "\n", "> __Hint:__ it will be useful because\n", "> `(Path, Path) -> Section -> (Path, Path)`\n", "> can be used as the binary function for a left fold, which has to\n", "> have a type of `a -> b -> a`" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "roadStep :: (Path, Path) -> Section -> (Path, Path)\n", "roadStep (pathA, pathB) (Section a b c) =\n", " let priceA = sum $ map snd pathA\n", " priceB = sum $ map snd pathB\n", " forwardPriceToA = priceA + a\n", " crossPriceToA = priceB + b + c\n", " forwardPriceToB = priceB + b\n", " crossPriceToB = priceA + a + c\n", " newPathToA = if forwardPriceToA <= crossPriceToA\n", " then (A,a):pathA\n", " else (C,c):(B,b):pathB\n", " newPathToB = if forwardPriceToB <= crossPriceToB\n", " then (B,b):pathB\n", " else (C,c):(A,a):pathA\n", " in (newPathToA, newPathToB)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "What's going on here? First, calculate the optimal price on road A based\n", "on the best so far on A and we do the same for B. We do\n", "`sum $ map snd pathA`, so if `pathA` is something like\n", "`[(A,100),(C,20)]`, `priceA` becomes\n", "`120`. `forwardPriceToA` is the price that we would pay if we went to the\n", "next crossroads on A if we went there directly from the previous\n", "crossroads on A. It equals the best price to our previous A, plus the\n", "length of the A part of the current section. `crossPriceToA` is the price\n", "that we would pay if we went to the next A by going forward from the\n", "previous B and then crossing over. It's the best price to the previous B\n", "so far plus the B length of the section plus the C length of the\n", "section. We determine `forwardPriceToB` and `crossPriceToB` in the same\n", "manner.\n", "\n", "Now that we know what the best way to A and B is, we just need to make\n", "the new paths to A and B based on that. If it's cheaper to go to A by\n", "just going forwards, we set `newPathToA` to be `(A,a):pathA`. Basically we\n", "prepend the `Label` `A` and the section length `a` to the optimal path path on\n", "A so far. Basically, we say that the best path to the next A crossroads\n", "is the path to the previous A crossroads and then one section forward\n", "via A. Remember, `A` is just a label, whereas `a` has a type of [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int). Why do\n", "we prepend instead of doing `pathA ++ [(A,a)]`? Well, adding an element to\n", "the beginning of a list (also known as consing) is much faster than\n", "adding it to the end. This means that the path will be the wrong way\n", "around once we fold over a list with this function, but it's easy to\n", "reverse the list later. If it's cheaper to get to the next A crossroads\n", "by going forward from road B and then crossing over, then `newPathToA` is\n", "the old path to B that then goes forward and crosses to A. We do the\n", "same thing for `newPathToB`, only everything's mirrored.\n", "\n", "Finally, we return `newPathToA` and `newPathToB` in a pair.\n", "\n", "Let's run this function on the first section of `heathrowToLondon`.\n", "Because it's the first section, the best paths on A and B parameter will\n", "be a pair of empty lists." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "([(C,30),(B,10)],[(B,10)])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "roadStep ([], []) (head heathrowToLondon)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, the paths are reversed, so read them from right to left. From\n", "this we can read that the best path to the next A is to start on B and\n", "then cross over to A and that the best path to the next B is to just go\n", "directly forward from the starting point at B.\n", "\n", "> __Optimization tip:__ when we do `priceA = sum $ map snd pathA`, we're\n", "> calculating the price from the path on every step. We wouldn't have to\n", "> do that if we implemented `roadStep` as a\n", "> `(Path, Path, Int, Int) -> Section -> (Path, Path, Int, Int)`\n", "> function where the integers represent\n", "> the best price on A and B.\n", "\n", "Now that we have a function that takes a pair of paths and a section and\n", "produces a new optimal path, we can just easily do a left fold over a\n", "list of sections. `roadStep` is called with `([],[])` and the first section\n", "and returns a pair of optimal paths to that section. Then, it's called\n", "with that pair of paths and the next section and so on. When we've\n", "walked over all the sections, we're left with a pair of optimal paths\n", "and the shorter of them is our answer. With this in mind, we can\n", "implement `optimalPath`." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "optimalPath :: RoadSystem -> Path\n", "optimalPath roadSystem =\n", " let (bestAPath, bestBPath) = foldl roadStep ([],[]) roadSystem\n", " in if sum (map snd bestAPath) <= sum (map snd bestBPath)\n", " then reverse bestAPath\n", " else reverse bestBPath" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We left fold over `roadSystem` (remember, it's a list of sections) with\n", "the starting accumulator being a pair of empty paths. The result of that\n", "fold is a pair of paths, so we pattern match on the pair to get the\n", "paths themselves. Then, we check which one of these was cheaper and\n", "return it. Before returning it, we also reverse it, because the optimal\n", "paths so far were reversed due to us choosing consing over appending.\n", "\n", "Let's test this!" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(B,10),(C,30),(A,5),(C,20),(B,2),(B,8),(C,0)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "optimalPath heathrowToLondon" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the result that we were supposed to get! Awesome! It differs\n", "from our expected result a bit because there's a step `(C,0)` at the end,\n", "which means that we cross over to the other road once we're in London,\n", "but because that crossing doesn't cost anything, this is still the\n", "correct result.\n", "\n", "We have the function that finds an optimal path based on, now we just\n", "have to read a textual representation of a road system from the standard\n", "input, convert it into a type of `RoadSystem`, run that through our\n", "`optimalPath` function and print the path.\n", "\n", "First off, let's make a function that takes a list and splits it into\n", "groups of the same size. We'll call it `groupsOf`. For a parameter of\n", "`[1..10]`, `groupsOf 3` should return `[[1,2,3],[4,5,6],[7,8,9],[10]]`." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "groupsOf :: Int -> [a] -> [[a]]\n", "groupsOf 0 _ = undefined\n", "groupsOf _ [] = []\n", "groupsOf n xs = take n xs : groupsOf n (drop n xs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A standard recursive function. For an `xs` of `[1..10]` and an `n` of `3`, this\n", "equals `[1,2,3] : groupsOf 3 [4,5,6,7,8,9,10]`. When the recursion is\n", "done, we get our list in groups of three. And here's our `main` function,\n", "which reads from the standard input, makes a `RoadSystem` out of it and\n", "prints out the shortest path:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.List\n", "\n", "main contents = do\n", " let threes = groupsOf 3 (map read $ lines contents)\n", " roadSystem = map (\\[a,b,c] -> Section a b c) threes\n", " path = optimalPath roadSystem\n", " pathString = concat $ map (show . fst) path\n", " pathPrice = sum $ map snd path\n", " putStrLn $ \"The best path to take is: \" ++ pathString\n", " putStrLn $ \"The price is: \" ++ show pathPrice" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we get all the contents from the standard input. Then, we call\n", "[`lines`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:lines) with our contents to convert something like `\"50\\n10\\n30\\n...` to\n", "`[\"50\",\"10\",\"30\"..` and then we map [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) to that to convert it to a list\n", "of numbers. We call `groupsOf 3` on it so that we turn it to a list of\n", "lists of length 3. We map the lambda (`\\[a,b,c] -> Section a b c`) over\n", "that list of lists. As you can see, the lambda just takes a list of\n", "length 3 and turns it into a section. So `roadSystem` is now our system of\n", "roads and it even has the correct type, namely `RoadSystem` (or\n", "`[Section]`). We call `optimalPath` with that and then get the path and the\n", "price in a nice textual representation and print it out.\n", "\n", "We save the following text" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "writeFile \"paths.txt\" $ unlines $ map show\n", " [ 50\n", " , 10\n", " , 30\n", " , 5\n", " , 90\n", " , 20\n", " , 40\n", " , 2\n", " , 25\n", " , 10\n", " , 8\n", " , 0\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "in a file called `paths.txt` and then feed it to our program." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "The best path to take is: BCACBBC\n", "The price is: 75" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "readFile \"paths.txt\" >>= main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Works like a charm! You can use your knowledge of the `Data.Random` module\n", "to generate a much longer system of roads, which you can then feed to\n", "what we just wrote. If you get stack overflows, try using `foldl'` instead\n", "of [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl), because `foldl'` is strict." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/11-functors-applicative-functors-and-monoids.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Functors, Applicative Functors and Monoids\n", "==========================================\n", "\n", "Haskell's combination of purity, higher order functions, parameterized\n", "algebraic data types, and typeclasses allows us to implement\n", "polymorphism on a much higher level than possible in other languages. We\n", "don't have to think about types belonging to a big hierarchy of types.\n", "Instead, we think about what the types can act like and then connect\n", "them with the appropriate typeclasses. An [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) can act like a lot of\n", "things. It can act like an equatable thing, like an ordered thing, like\n", "an enumerable thing, etc.\n", "\n", "Typeclasses are open, which means that we can define our own data type,\n", "think about what it can act like and connect it with the typeclasses\n", "that define its behaviors. Because of that and because of Haskell's\n", "great type system that allows us to know a lot about a function just by\n", "knowing its type declaration, we can define typeclasses that define\n", "behavior that's very general and abstract. We've met typeclasses that\n", "define operations for seeing if two things are equal or comparing two\n", "things by some ordering. Those are very abstract and elegant behaviors,\n", "but we just don't think of them as anything very special because we've\n", "been dealing with them for most of our lives. We recently met functors,\n", "which are basically things that can be mapped over. That's an example of\n", "a useful and yet still pretty abstract property that typeclasses can\n", "describe. In this chapter, we'll take a closer look at functors, along\n", "with slightly stronger and more useful versions of functors called\n", "applicative functors. We'll also take a look at monoids, which are sort\n", "of like socks." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "> __Jupyter Note:__ About the following functions.\n", ">\n", "> `withStdin` takes a string and runs an [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) action with that string as the standard input.\n", "This is for simulating an interactive command line environment for programs. Inspired by [Test.Main.withStdin](http://hackage.haskell.org/package/main-tester-0.2.0.1/docs/src/Test.Main.html#withStdin).\n", "> The original book instructs the reader to run interactive programs from a shell command line, but those instructions don't work in a Jupyter notebook environment\n", "> because we want to be able to run the notebook from beginning to end without waiting for user interaction. The programs in this chapter can still be run\n", "> interactively in the notebook, but if a program doesn't terminate, you will have to Restart the kernel.\n", ">\n", "> `catchPrint` catches an exception and prints it to standard output. We use this because Jupyter won't show us the output of a program if the program throws an exception. We want to see both the output and the exception." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.IO\n", "import GHC.IO.Handle\n", "import Control.Exception\n", "import System.Directory\n", "\n", "withStdin :: String -> IO a -> IO a\n", "withStdin s action = do\n", " writeFile \"/tmp/stdin.txt\" s\n", " finally\n", " (withFile \"/tmp/stdin.txt\" ReadWriteMode\n", " (\\h -> do\n", " stdin' <- hDuplicate stdin\n", " hDuplicateTo h stdin\n", " finally action (hDuplicateTo stdin' stdin) \n", " )\n", " )\n", " (removeFile \"/tmp/stdin.txt\")\n", "\n", "catchPrint = flip catch p where\n", " p :: SomeException -> IO ()\n", " p = print" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Functors redux\n", "--------------\n", "\n", "\n", "\n", "We've already talked about functors in [their own little\n", "section](http://learnyouahaskell.com/making-our-own-types-and-typeclasses#the-functor-typeclass). If\n", "you haven't read it yet, you should probably give it a glance right now,\n", "or maybe later when you have more time. Or you can just pretend you read\n", "it.\n", "\n", "Still, here's a quick refresher: Functors are things that can be mapped\n", "over, like lists, [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)s, trees, and such. In Haskell, they're described\n", "by the typeclass [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor), which has only one typeclass method, namely\n", "[`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap), which has a type of `fmap :: (a -> b) -> f a -> f b`. It says:\n", "give me a function that takes an `a` and returns a `b` and a box with an `a`\n", "(or several of them) inside it and I'll give you a box with a `b` (or\n", "several of them) inside it. It kind of applies the function to the\n", "element inside the box.\n", "\n", "> __A word of advice.__ Many times the box analogy is used to help you get\n", "> some intuition for how functors work, and later, we'll probably use the\n", "> same analogy for applicative functors and monads. It's an okay analogy\n", "> that helps people understand functors at first, just don't take it too\n", "> literally, because for some functors the box analogy has to be stretched\n", "> really thin to still hold some truth. A more correct term for what a\n", "> functor is would be *computational context*. The context might be that\n", "> the computation can have a value or it might have failed ([`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) and\n", "> `Either a`) or that there might be more values (lists), stuff like that.\n", "\n", "If we want to make a type constructor an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor), it has to\n", "have a kind of `* -> *`, which means that it has to take exactly one\n", "concrete type as a type parameter. For example, [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) can be made an\n", "instance because it takes one type parameter to produce a concrete type,\n", "like `Maybe Int` or `Maybe String`. If a type constructor takes two\n", "parameters, like [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either), we have to partially apply the type constructor\n", "until it only takes one type parameter. So we can't write\n", "`instance Functor Either where`, but we can write\n", "`instance Functor (Either a) where`\n", "and then if we imagine that [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) is only for `Either a`, it would have a\n", "type declaration of `fmap :: (b -> c) -> Either a b -> Either a c`. As\n", "you can see, the `Either a` part is fixed, because `Either a` takes only one\n", "type parameter, whereas just [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) takes two so\n", "`fmap :: (b -> c) -> Either b -> Either c` wouldn't really make sense.\n", "\n", "We've learned by now how a lot of types (well, type constructors really)\n", "are instances of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor), like `[]`, [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), `Either a` and a `Tree` type that\n", "we made on our own. We saw how we can map functions over them for great\n", "good. In this section, we'll take a look at two more instances of\n", "functor, namely [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) and `(->) r`.\n", "\n", "If some value has a type of, say, `IO String`, that means that it's an I/O\n", "action that, when performed, will go out into the real world and get\n", "some string for us, which it will yield as a result. We can use `<-` in\n", "*do* syntax to bind that result to a name. We mentioned that I/O actions\n", "are like boxes with little feet that go out and fetch some value from\n", "the outside world for us. We can inspect what they fetched, but after\n", "inspecting, we have to wrap the value back in [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO). By thinking about this\n", "box with little feet analogy, we can see how [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) acts like a functor.\n", "\n", "Let's see how [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) is an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor). When we [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) a function over\n", "an I/O action, we want to get back an I/O action that does the same\n", "thing, but has our function applied over its result value." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Functor IO where\n",
    "    fmap f action = do\n",
    "        result <- action\n",
    "        return (f result)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The result of mapping something over an I/O action will be an I/O\n", "action, so right off the bat we use *do* syntax to glue two actions and\n", "make a new one. In the implementation for [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap), we make a new I/O action\n", "that first performs the original I/O action and calls its result `result`.\n", "Then, we do `return (f result)`. [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) is, as you know, a function that\n", "makes an I/O action that doesn't do anything but only presents something\n", "as its result. The action that a *do* block produces will always have\n", "the result value of its last action. That's why we use return to make an\n", "I/O action that doesn't really do anything, it just presents `f result` as\n", "the result of the new I/O action.\n", "\n", "We can play around with it to gain some intuition. It's pretty simple\n", "really. Check out this piece of code:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do line <- getLine\n", " let line' = reverse line\n", " putStrLn $ \"You said \" ++ line' ++ \" backwards!\"\n", " putStrLn $ \"Yes, you really said\" ++ line' ++ \" backwards!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The user is prompted for a line and we give it back to the user, only\n", "reversed. Here's how to rewrite this by using [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap):" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do line <- fmap reverse getLine\n", " putStrLn $ \"You said \" ++ line ++ \" backwards!\"\n", " putStrLn $ \"Yes, you really said \" ++ line ++ \" backwards!\"" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "You said halb backwards!\n", "Yes, you really said halb backwards!" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin \"blah\" main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Just like when we [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) [`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse) over `Just \"blah\"` to get `Just \"halb\"`, we\n", "can [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) [`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse) over [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine). [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) is an I/O action that has a type\n", "of `IO String` and mapping [`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse) over it gives us an I/O action that\n", "will go out into the real world and get a line and then apply [`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse) to\n", "its result. Like we can apply a function to something that's inside a\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) box, we can apply a function to what's inside an [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) box, only it\n", "has to go out into the real world to get something. Then when we bind it\n", "to a name by using `<-`, the name will reflect the result that already\n", "has [`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse) applied to it.\n", "\n", "The I/O action `fmap (++\"!\") getLine` behaves just like [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine), only that\n", "its result always has `\"!\"` appended to it!\n", "\n", "If we look at what [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap)'s type would be if it were limited to [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO), it\n", "would be `fmap :: (a -> b) -> IO a -> IO b`. [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) takes a function and\n", "an I/O action and returns a new I/O action that's like the old one,\n", "except that the function is applied to its contained result.\n", "\n", "If you ever find yourself binding the result of an I/O action to a name,\n", "only to apply a function to that and call that something else, consider\n", "using [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap), because it looks prettier. If you want to apply multiple\n", "transformations to some data inside a functor, you can declare your own\n", "function at the top level, make a lambda function or ideally, use\n", "function composition:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Char\n", "import Data.List\n", "\n", "main = do line <- fmap (intersperse '-' . reverse . map toUpper) getLine\n", " putStrLn line" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "plain" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "E-R-E-H-T- -O-L-L-E-H" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin \"hello there\" main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you probably know, `intersperse '-' . reverse . map toUpper` is a\n", "function that takes a string, maps [`toUpper`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:toUpper) over it, then applies [`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse)\n", "to that result and then applies `intersperse '-'` to that result. It's\n", "like writing `(\\xs -> intersperse '-' (reverse (map toUpper xs)))`, only\n", "prettier.\n", "\n", "Another instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) that we've been dealing with all along but\n", "didn't know was a [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) is `(->) r`. You're probably slightly confused\n", "now, since what the heck does `(->) r` mean? The function type `r -> a`\n", "can be rewritten as `(->) r a`, much like we can write `2 + 3` as `(+) 2 3`.\n", "When we look at it as `(->) r a`, we can see `(->)` in a slightly different\n", "light, because we see that it's just a type constructor that takes two\n", "type parameters, just like [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either). But remember, we said that a type\n", "constructor has to take exactly one type parameter so that it can be\n", "made an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor). That's why we can't make `(->)` an instance\n", "of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor), but if we partially apply it to `(->) r`, it doesn't pose any\n", "problems. If the syntax allowed for type constructors to be partially\n", "applied with sections (like we can partially apply [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-) by doing `(2+)`,\n", "which is the same as `(+) 2`), you could write `(->) r` as `(r ->)`. How are\n", "functions functors? Well, let's take a look at the implementation, which\n", "lies in `Control.Monad.Instances`\n", "\n", "> We usually mark functions that take anything and return anything as\n", "> `a -> b`. `r -> a` is the same thing, we just used different letters for the\n", "> type variables." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Functor ((->) r) where\n",
    "    fmap f g = (\\x -> f (g x))
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the syntax allowed for it, it could have been written as" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Functor (r ->) where\n",
    "    fmap f g = (\\x -> f (g x))
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But it doesn't, so we have to write it in the former fashion.\n", "\n", "First of all, let's think about [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap)'s type. It's\n", "`fmap :: (a -> b) -> f a -> f b`. Now what we'll do is mentally replace all the f's, which\n", "are the role that our functor instance plays, with `(->) r`'s. We'll do\n", "that to see how [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) should behave for this particular instance. We get\n", "`fmap :: (a -> b) -> ((->) r a) -> ((->) r b)`. Now what we can do is\n", "write the `(->) r a` and `(-> r b)` types as infix `r -> a` and `r -> b`,\n", "like we normally do with functions. What we get now is\n", "`fmap :: (a -> b) -> (r -> a) -> (r -> b)`.\n", "\n", "Hmmm OK. Mapping one function over a function has to produce a function,\n", "just like mapping a function over a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) has to produce a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) and\n", "mapping a function over a list has to produce a list. What does the type\n", "`fmap :: (a -> b) -> (r -> a) -> (r -> b)` for this instance tell us?\n", "Well, we see that it takes a function from `a` to `b` and a function from `r`\n", "to `a` and returns a function from `r` to `b`. Does this remind you of\n", "anything? Yes! Function composition! We pipe the output of `r -> a` into\n", "the input of `a -> b` to get a function `r -> b`, which is exactly what\n", "function composition is about. If you look at how the instance is\n", "defined above, you'll see that it's just function composition. Another\n", "way to write this instance would be:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Functor ((->) r) where\n",
    "    fmap = (.)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This makes the revelation that using [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) over functions is just\n", "composition sort of obvious. Do `:m + Control.Monad.Instances`, since\n", "that's where the instance is defined and then try playing with mapping\n", "over functions." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "fmap (*3) (+100) :: forall {b}. Num b => b -> b" ], "text/plain": [ "fmap (*3) (+100) :: forall {b}. Num b => b -> b" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t fmap (*3) (+100)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "303" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (*3) (+100) 1" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "303" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(*3) `fmap` (+100) $ 1" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "303" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(*3) . (+100) $ 1" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"300\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (show . (*3)) (*100) 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can call [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) as an infix function so that the resemblance to [`.`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:.) is\n", "clear. In the second input line, we're mapping `(*3)` over `(+100)`, which\n", "results in a function that will take an input, call `(+100)` on that and\n", "then call `(*3)` on that result. We call that function with `1`.\n", "\n", "How does the box analogy hold here? Well, if you stretch it, it holds.\n", "When we use `fmap (+3)` over `Just 3`, it's easy to imagine the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) as a\n", "box that has some contents on which we apply the function `(+3)`. But what\n", "about when we're doing `fmap (*3) (+100)`? Well, you can think of the\n", "function `(+100)` as a box that contains its eventual result. Sort of like\n", "how an I/O action can be thought of as a box that will go out into the\n", "real world and fetch some result. Using `fmap (*3)` on `(+100)` will create\n", "another function that acts like `(+100)`, only before producing a result,\n", "`(*3)` will be applied to that result. Now we can see how [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) acts just\n", "like [`.`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:.) for functions.\n", "\n", "The fact that [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) is function composition when used on functions isn't\n", "so terribly useful right now, but at least it's very interesting. It\n", "also bends our minds a bit and let us see how things that act more like\n", "computations than boxes ([`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) and `(->) r`) can be functors. The function\n", "being mapped over a computation results in the same computation but the\n", "result of that computation is modified with the function.\n", "\n", "\n", "\n", "Before we go on to the rules that [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) should follow, let's think about\n", "the type of [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) once more. Its type is `fmap :: (a -> b) -> f a -> f b`.\n", "We're missing the class constraint `(Functor f) =>`, but we left it\n", "out here for brevity, because we're talking about functors anyway so we\n", "know what the `f` stands for. When we first learned about [curried\n", "functions](http://learnyouahaskell.com/higher-order-functions#curried-functions), we said that all\n", "Haskell functions actually take one parameter. A function `a -> b -> c`\n", "actually takes just one parameter of type `a` and then returns a function\n", "`b -> c`, which takes one parameter and returns a `c`. That's how if we\n", "call a function with too few parameters (i.e. partially apply it), we\n", "get back a function that takes the number of parameters that we left out\n", "(if we're thinking about functions as taking several parameters again).\n", "So `a -> b -> c` can be written as `a -> (b -> c)`, to make the currying\n", "more apparent.\n", "\n", "In the same vein, if we write `fmap :: (a -> b) -> (f a -> f b)`, we\n", "can think of [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) not as a function that takes one function and a\n", "functor and returns a functor, but as a function that takes a function\n", "and returns a new function that's just like the old one, only it takes a\n", "functor as a parameter and returns a functor as the result. It takes an\n", "`a -> b` function and returns a function `f a -> f b`. This is called\n", "*lifting* a function. Let's play around with that idea by using GHCI's\n", "`:t` command:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "fmap (*2) :: forall {f :: * -> *} {b}. (Functor f, Num b) => f b -> f b" ], "text/plain": [ "fmap (*2) :: forall {f :: * -> *} {b}. (Functor f, Num b) => f b -> f b" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t fmap (*2)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "fmap (replicate 3) :: forall {f :: * -> *} {a}. Functor f => f a -> f [a]" ], "text/plain": [ "fmap (replicate 3) :: forall {f :: * -> *} {a}. Functor f => f a -> f [a]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t fmap (replicate 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The expression `fmap (*2)` is a function that takes a functor `f` over\n", "numbers and returns a functor over numbers. That functor can be a list,\n", "a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), an `Either String`, whatever. The expression `fmap (replicate 3)`\n", "will take a functor over any type and return a functor over a list of\n", "elements of that type.\n", "\n", "> When we say *a functor over numbers*, you can think of that as *a\n", "> functor that has numbers in it*. The former is a bit fancier and more\n", "> technically correct, but the latter is usually easier to get.\n", "\n", "This is even more apparent if we partially apply, say, `fmap (++\"!\")` and\n", "then bind it to a name in GHCI.\n", "\n", "You can think of [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) as either a function that takes a function and a\n", "functor and then maps that function over the functor, or you can think\n", "of it as a function that takes a function and lifts that function so\n", "that it operates on functors. Both views are correct and in Haskell,\n", "equivalent.\n", "\n", "The type `fmap (replicate 3) :: (Functor f) => f a -> f [a]` means that\n", "the function will work on any functor. What exactly it will do depends\n", "on which functor we use it on. If we use `fmap (replicate 3)` on a list,\n", "the list's implementation for [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) will be chosen, which is just [`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map). If\n", "we use it on a `Maybe a`, it'll apply `replicate 3` to the value inside the\n", "[`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just), or if it's [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), then it stays [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing)." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,1,1],[2,2,2],[3,3,3],[4,4,4]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (replicate 3) [1,2,3,4]" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just [4,4,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (replicate 3) (Just 4)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Right [\"blah\",\"blah\",\"blah\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (replicate 3) (Right \"blah\")" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (replicate 3) Nothing" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Left \"foo\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (replicate 3) (Left \"foo\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next up, we're going to look at the __functor laws__. In order for\n", "something to be a functor, it should satisfy some laws. All functors are\n", "expected to exhibit certain kinds of functor-like properties and\n", "behaviors. They should reliably behave as things that can be mapped\n", "over. Calling [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) on a functor should just map a function over the\n", "functor, nothing more. This behavior is described in the functor laws.\n", "There are two of them that all instances of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) should abide by.\n", "They aren't enforced by Haskell automatically, so you have to test them\n", "out yourself.\n", "\n", "__The first functor law states that if we map the [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) function over a\n", "functor, the functor that we get back should be the same as the original\n", "functor.__ If we write that a bit more formally, it means that\n", "**`fmap id = id`**.\n", "So essentially, this says that if we do `fmap id` over a functor, it\n", "should be the same as just calling [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) on the functor. Remember, [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) is\n", "the identity function, which just returns its parameter unmodified. It\n", "can also be written as `\\x -> x`. If we view the functor as something\n", "that can be mapped over, the **`fmap id = id`** law seems kind of trivial or\n", "obvious.\n", "\n", "Let's see if this law holds for a few values of functors." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap id (Just 3)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "id (Just 3)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap id [1..5]" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "id [1..5]" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap id []" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap id Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we look at the implementation of [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) for, say, [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), we can figure\n", "out why the first functor law holds." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Functor Maybe where\n",
    "    fmap f (Just x) = Just (f x)\n",
    "    fmap f Nothing = Nothing
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We imagine that [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) plays the role of the `f` parameter in the\n", "implementation. We see that if we `fmap id` over `Just x`, the result will\n", "be `Just (id x)`, and because [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) just returns its parameter, we can deduce\n", "that `Just (id x)` equals `Just x`. So now we know that if we map [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) over a\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value with a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value constructor, we get that same value back.\n", "\n", "Seeing that mapping [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) over a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) value returns the same value is\n", "trivial. So from these two equations in the implementation for [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap), we\n", "see that the law `fmap id = id` holds.\n", "\n", "\n", "\n", "__The second law says that composing two functions and then mapping the\n", "resulting function over a functor should be the same as first mapping\n", "one function over the functor and then mapping the other one.__ Formally\n", "written, that means that **`fmap (f . g) = fmap f . fmap g`**. Or to write it\n", "in another way, for any functor `F`, the following should \n", "hold: **`fmap (f . g) F = fmap f (fmap g F)`**.\n", "\n", "If we can show that some type obeys both functor laws, we can rely on it\n", "having the same fundamental behaviors as other functors when it comes to\n", "mapping. We can know that when we use [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) on it, there won't be\n", "anything other than mapping going on behind the scenes and that it will\n", "act like a thing that can be mapped over, i.e. a functor. You figure out\n", "how the second law holds for some type by looking at the implementation\n", "of [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) for that type and then using the method that we used to check if\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) obeys the first law.\n", "\n", "If you want, we can check out how the second functor law holds for\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe). If we do `fmap (f . g)` over [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), we get [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), because doing\n", "a [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) with any function over [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) returns [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). If we do\n", "`fmap f (fmap g Nothing)`, we get [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), for the same reason. OK, seeing how\n", "the second law holds for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) if it's a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) value is pretty easy,\n", "almost trivial.\n", "\n", "How about if it's a `Just ` value? Well, if we do\n", "`fmap (f . g) (Just x)`,\n", "we see from the implementation that it's implemented as\n", "`Just ((f . g) x)`, which is, of course, `Just (f (g x))`.\n", "If we do `fmap f (fmap g (Just x))`,\n", "we see from the implementation that `fmap g (Just x)` is\n", "`Just (g x)`. Ergo, `fmap f (fmap g (Just x))` equals\n", "`fmap f (Just (g x))` and\n", "from the implementation we see that this equals `Just (f (g x))`.\n", "\n", "If you're a bit confused by this proof, don't worry. Be sure that you\n", "understand how [function\n", "composition](http://learnyouahaskell.com/higher-order-functions#composition) works. Many times, you\n", "can intuitively see how these laws hold because the types act like\n", "containers or functions. You can also just try them on a bunch of\n", "different values of a type and be able to say with some certainty that a\n", "type does indeed obey the laws.\n", "\n", "Let's take a look at a pathological example of a type constructor being\n", "an instance of the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) typeclass but not really being a functor,\n", "because it doesn't satisfy the laws. Let's say that we have a type:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data CMaybe a = CNothing | CJust Int a deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The C here stands for *counter*. It's a data type that looks much like\n", "`Maybe a`, only the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) part holds two fields instead of one. The first\n", "field in the `CJust` value constructor will always have a type of [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), and\n", "it will be some sort of counter and the second field is of type `a`, which\n", "comes from the type parameter and its type will, of course, depend on\n", "the concrete type that we choose for `CMaybe a`. Let's play with our new\n", "type to get some intuition for it." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "CNothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "CNothing" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "CJust 0 \"haha\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "CJust 0 \"haha\"" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "CNothing :: forall a. CMaybe a" ], "text/plain": [ "CNothing :: forall a. CMaybe a" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t CNothing" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "CJust 0 \"haha\" :: CMaybe String" ], "text/plain": [ "CJust 0 \"haha\" :: CMaybe String" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t CJust 0 \"haha\"" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "CJust 100 [1,2,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "CJust 100 [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we use the `CNothing` constructor, there are no fields, and if we use\n", "the `CJust` constructor, the first field is an integer and the second\n", "field can be any type. Let's make this an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) so that\n", "every time we use [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap), the function gets applied to the second field,\n", "whereas the first field gets increased by 1." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Functor CMaybe where\n", " fmap f CNothing = CNothing\n", " fmap f (CJust counter x) = CJust (counter+1) (f x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is kind of like the instance implementation for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), except that\n", "when we do [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) over a value that doesn't represent an empty box (a\n", "`CJust` value), we don't just apply the function to the contents, we also\n", "increase the counter by 1. Everything seems cool so far, we can even\n", "play with this a bit:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "CJust 1 \"hoha\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (++\"ha\") (CJust 0 \"ho\")" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "CJust 2 \"hohahe\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (++\"he\") (fmap (++\"ha\") (CJust 0 \"ho\"))" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "CNothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (++\"blah\") CNothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Does this obey the functor laws? In order to see that something doesn't\n", "obey a law, it's enough to find just one counter-example." ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "CJust 1 \"haha\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap id (CJust 0 \"haha\")" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "CJust 0 \"haha\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "id (CJust 0 \"haha\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah! We know that the first functor law states that if we map [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) over a\n", "functor, it should be the same as just calling [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) with the same functor,\n", "but as we've seen from this example, this is not true for our `CMaybe`\n", "functor. Even though it's part of the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) typeclass, it doesn't obey\n", "the functor laws and is therefore not a functor. If someone used our\n", "`CMaybe` type as a functor, they would expect it to obey the functor laws\n", "like a good functor. But `CMaybe` fails at being a functor even though it\n", "pretends to be one, so using it as a functor might lead to some faulty\n", "code. When we use a functor, it shouldn't matter if we first compose a\n", "few functions and then map them over the functor or if we just map each\n", "function over a functor in succession. But with `CMaybe`, it matters,\n", "because it keeps track of how many times it's been mapped over. Not\n", "cool! If we wanted `CMaybe` to obey the functor laws, we'd have to make it\n", "so that the [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) field stays the same when we use [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap).\n", "\n", "At first, the functor laws might seem a bit confusing and unnecessary,\n", "but then we see that if we know that a type obeys both laws, we can make\n", "certain assumptions about how it will act. If a type obeys the functor\n", "laws, we know that calling [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) on a value of that type will only map\n", "the function over it, nothing more. This leads to code that is more\n", "abstract and extensible, because we can use laws to reason about\n", "behaviors that any functor should have and make functions that operate\n", "reliably on any functor.\n", "\n", "All the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) instances in the standard library obey these laws, but\n", "you can check for yourself if you don't believe me. And the next time\n", "you make a type an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor), take a minute to make sure that\n", "it obeys the functor laws. Once you've dealt with enough functors, you\n", "kind of intuitively see the properties and behaviors that they have in\n", "common and it's not hard to intuitively see if a type obeys the functor\n", "laws. But even without the intuition, you can always just go over the\n", "implementation line by line and see if the laws hold or try to find a\n", "counter-example.\n", "\n", "We can also look at functors as things that output values in a context.\n", "For instance, `Just 3` outputs the value `3` in the context that it might or\n", "not output any values at all. `[1,2,3]` outputs three values—`1`, `2`, and `3`,\n", "the context is that there may be multiple values or no values. The\n", "function `(+3)` will output a value, depending on which parameter it is\n", "given.\n", "\n", "If you think of functors as things that output values, you can think of\n", "mapping over functors as attaching a transformation to the output of the\n", "functor that changes the value. When we do `fmap (+3) [1,2,3]`, we attach\n", "the transformation `(+3)` to the output of `[1,2,3]`, so whenever we look at\n", "a number that the list outputs, `(+3)` will be applied to it. Another\n", "example is mapping over functions. When we do `fmap (+3) (*3)`, we attach\n", "the transformation `(+3)` to the eventual output of `(*3)`. Looking at it\n", "this way gives us some intuition as to why using [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) on functions is\n", "just composition (`fmap (+3) (*3)` equals `(+3) . (*3)`, which equals\n", "`\\x -> ((x*3)+3)`), because we take a function like `(*3)` then we attach\n", "the transformation `(+3)` to its output. The result is still a function,\n", "only when we give it a number, it will be multiplied by three and then\n", "it will go through the attached transformation where it will be added to\n", "three. This is what happens with composition.\n", "\n", "\n", "\n", "Applicative functors\n", "--------------------\n", "\n", "\n", "\n", "In this section, we'll take a look at applicative functors, which are\n", "beefed up functors, represented in Haskell by the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) typeclass,\n", "found in the `Control.Applicative` module.\n", "\n", "As you know, functions in Haskell are curried by default, which means\n", "that a function that seems to take several parameters actually takes\n", "just one parameter and returns a function that takes the next parameter\n", "and so on. If a function is of type `a -> b -> c`, we usually say that\n", "it takes two parameters and returns a `c`, but actually it takes an `a` and\n", "returns a function `b -> c`. That's why we can call a function as `f x y`\n", "or as `(f x) y`. This mechanism is what enables us to partially apply\n", "functions by just calling them with too few parameters, which results in\n", "functions that we can then pass on to other functions.\n", "\n", "So far, when we were mapping functions over functors, we usually mapped\n", "functions that take only one parameter. But what happens when we map a\n", "function like [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-), which takes two parameters, over a functor? Let's take\n", "a look at a couple of concrete examples of this. If we have `Just 3` and\n", "we do `fmap (*) (Just 3)`, what do we get? From the instance\n", "implementation of [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) for [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor), we know that if it's a\n", "`Just ` value, it will apply the function to the `` inside\n", "the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just). Therefore, doing `fmap (*) (Just 3)` results in `Just ((*) 3)`,\n", "which can also be written as `Just (* 3)` if we use sections.\n", "Interesting! We get a function wrapped in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just)!" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "fmap (++) (Just \"hey\") :: Maybe ([Char] -> [Char])" ], "text/plain": [ "fmap (++) (Just \"hey\") :: Maybe ([Char] -> [Char])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t fmap (++) (Just \"hey\")" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "fmap compare (Just 'a') :: Maybe (Char -> Ordering)" ], "text/plain": [ "fmap compare (Just 'a') :: Maybe (Char -> Ordering)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t fmap compare (Just 'a')" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "fmap compare \"A LIST OF CHARS\" :: [Char -> Ordering]" ], "text/plain": [ "fmap compare \"A LIST OF CHARS\" :: [Char -> Ordering]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t fmap compare \"A LIST OF CHARS\"" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "fmap (\\x y z -> x + y / z) [3,4,5,6] :: forall {a}. Fractional a => [a -> a -> a]" ], "text/plain": [ "fmap (\\x y z -> x + y / z) [3,4,5,6] :: forall {a}. Fractional a => [a -> a -> a]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t fmap (\\x y z -> x + y / z) [3,4,5,6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we map [`compare`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:compare), which has a type of `(Ord a) => a -> a -> Ordering`\n", "over a list of characters, we get a list of functions of type\n", "`Char -> Ordering`, because the function [`compare`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:compare) gets partially applied with the\n", "characters in the list. It's not a list of `(Ord a) => a -> Ordering`\n", "function, because the first `a` that got applied was a [`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char) and so the\n", "second `a` has to decide to be of type [`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char).\n", "\n", "We see how by mapping \"multi-parameter\" functions over functors, we get\n", "functors that contain functions inside them. So now what can we do with\n", "them? Well for one, we can map functions that take these functions as\n", "parameters over them, because whatever is inside a functor will be given\n", "to the function that we're mapping over it as a parameter." ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "a :: forall {a}. Num a => [a -> a]" ], "text/plain": [ "a :: forall {a}. Num a => [a -> a]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let a = fmap (*) [1,2,3,4]\n", ":t a" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[9,18,27,36]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (\\f -> f 9) a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But what if we have a functor value of `Just (3 *)` and a functor value\n", "of `Just 5` and we want to take out the function from `Just (3 *)` and map\n", "it over `Just 5`? With normal functors, we're out of luck, because all\n", "they support is just mapping normal functions over existing functors.\n", "Even when we mapped `\\f -> f 9` over a functor that contained functions\n", "inside it, we were just mapping a normal function over it. But we can't\n", "map a function that's inside a functor over another functor with what\n", "[`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) offers us. We could pattern-match against the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) constructor to\n", "get the function out of it and then map it over `Just 5`, but we're\n", "looking for a more general and abstract way of doing that, which works\n", "across functors.\n", "\n", "Meet the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) typeclass. It lies in the `Control.Applicative`\n", "module and it defines two methods, [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) and [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-). It doesn't provide a\n", "default implementation for any of them, so we have to define them both\n", "if we want something to be an applicative functor. The class is defined\n", "like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class (Functor f) => Applicative f where\n",
    "    pure :: a -> f a\n",
    "    (<*>) :: f (a -> b) -> f a -> f b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This simple three line class definition tells us a lot! Let's start at\n", "the first line. It starts the definition of the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) class and it\n", "also introduces a class constraint. It says that if we want to make a\n", "type constructor part of the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) typeclass, it has to be in\n", "[`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) first. That's why if we know that if a type constructor is part\n", "of the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) typeclass, it's also in [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor), so we can use [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap)\n", "on it.\n", "\n", "The first method it defines is called [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure). Its type declaration is\n", "`pure :: a -> f a`. `f` plays the role of our applicative functor instance here.\n", "Because Haskell has a very good type system and because everything a\n", "function can do is take some parameters and return some value, we can\n", "tell a lot from a type declaration and this is no exception. [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) should\n", "take a value of any type and return an applicative functor with that\n", "value inside it. When we say *inside it*, we're using the box analogy\n", "again, even though we've seen that it doesn't always stand up to\n", "scrutiny. But the `a -> f a` type declaration is still pretty\n", "descriptive. We take a value and we wrap it in an applicative functor\n", "that has that value as the result inside it.\n", "\n", "A better way of thinking about [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) would be to say that it takes a\n", "value and puts it in some sort of default (or pure) context—a minimal\n", "context that still yields that value.\n", "\n", "The [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) function is really interesting. It has a type declaration of\n", "`f (a -> b) -> f a -> f b`. Does this remind you of anything? Of\n", "course, `fmap :: (a -> b) -> f a -> f b`. It's a sort of a beefed up\n", "[`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap). Whereas [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) takes a function and a functor and applies the\n", "function inside the functor, [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) takes a functor that has a function\n", "in it and another functor and sort of extracts that function from the\n", "first functor and then maps it over the second one. When I say\n", "*extract*, I actually sort of mean *run* and then extract, maybe even\n", "*sequence*. We'll see why soon.\n", "\n", "Let's take a look at the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) instance implementation for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Applicative Maybe where\n",
    "    pure = Just\n",
    "    Nothing <*> _ = Nothing\n",
    "    (Just f) <*> something = fmap f something
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, from the class definition we see that the `f` that plays the role\n", "of the applicative functor should take one concrete type as a parameter,\n", "so we write `instance Applicative Maybe where` instead of writing\n", "`instance Applicative (Maybe a) where`.\n", "\n", "First off, [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure). We said earlier that it's supposed to take something\n", "and wrap it in an applicative functor. We wrote `pure = Just`, because\n", "value constructors like [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) are normal functions. We could have also\n", "written `pure x = Just x`.\n", "\n", "Next up, we have the definition for [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-). We can't extract a function\n", "out of a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), because it has no function inside it. So we say that\n", "if we try to extract a function from a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), the result is a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "If you look at the class definition for [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative), you'll see that\n", "there's a [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) class constraint, which means that we can assume that\n", "both of [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-)'s parameters are functors. If the first parameter is not\n", "a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), but a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) with some function inside it, we say that we then\n", "want to map that function over the second parameter. This also takes\n", "care of the case where the second parameter is [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), because doing\n", "[`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) with any function over a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) will return a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "\n", "So for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) extracts the function from the left value if it's a\n", "[`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) and maps it over the right value. If any of the parameters is\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) is the result.\n", "\n", "OK cool great. Let's give this a whirl." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 12" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just (+3) <*> Just 9" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 13" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pure (+3) <*> Just 10" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 12" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pure (+3) <*> Just 9" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just (++\"hahah\") <*> Nothing" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing <*> Just \"woot\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see how doing `pure (+3)` and `Just (+3)` is the same in this case. Use\n", "[`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) if you're dealing with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values in an applicative context (i.e.\n", "using them with [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-)), otherwise stick to [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just). The first four input\n", "lines demonstrate how the function is extracted and then mapped, but in\n", "this case, they could have been achieved by just mapping unwrapped\n", "functions over functors. The last line is interesting, because we try to\n", "extract a function from a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) and then map it over something, which\n", "of course results in a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "\n", "With normal functors, you can just map a function over a functor and\n", "then you can't get the result out in any general way, even if the result\n", "is a partially applied function. Applicative functors, on the other\n", "hand, allow you to operate on several functors with a single function.\n", "Check out this piece of code:" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 8" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pure (+) <*> Just 3 <*> Just 5" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pure (+) <*> Just 3 <*> Nothing" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pure (+) <*> Nothing <*> Just 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "What's going on here? Let's take a look, step by step. [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) is\n", "left-associative, which means that `pure (+) <*> Just 3 <*> Just 5`\n", "is the same as `(pure (+) <*> Just 3) <*> Just 5`. First, the [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-)\n", "function is put in a functor, which is in this case a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value that\n", "contains the function. So at first, we have `pure (+)`, which is `Just (+)`.\n", "Next, `Just (+) <*> Just 3` happens. The result of this is `Just (3+)`.\n", "This is because of partial application. Only applying `3` to the [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-)\n", "function results in a function that takes one parameter and adds 3 to\n", "it. Finally, `Just (3+) <*> Just 5` is carried out, which results in a\n", "`Just 8`.\n", "\n", "Isn't this awesome?! Applicative functors and the applicative style of\n", "doing `pure f <*> x <*> y <*> ...` allow us to take a function\n", "that expects parameters that aren't necessarily wrapped in functors and\n", "use that function to operate on several values that are in functor\n", "contexts. The function can take as many parameters as we want, because\n", "it's always partially applied step by step between occurrences of [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-).\n", "\n", "This becomes even more handy and apparent if we consider the fact that\n", "`pure f <*> x` equals `fmap f x`. This is one of the applicative laws.\n", "We'll take a closer look at them later, but for now, we can sort of\n", "intuitively see that this is so. Think about it, it makes sense. Like we\n", "said before, [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) puts a value in a default context. If we just put a\n", "function in a default context and then extract and apply it to a value\n", "inside another applicative functor, we did the same as just mapping that\n", "function over that applicative functor. Instead of writing\n", "`pure f <*> x <*> y <*> ...`,\n", "we can write `fmap f x <*> y <*> ...`. This\n", "is why `Control.Applicative` exports a function called [`<$>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--36--62-), which is\n", "just [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) as an infix operator. Here's how it's defined:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(<$>) :: (Functor f) => (a -> b) -> f a -> f b\n",
    "f <$> x = fmap f x
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Yo!__ Quick reminder: type variables are independent of parameter names\n", "> or other value names. The `f` in the function declaration here is a type\n", "> variable with a class constraint saying that any type constructor that\n", "> replaces `f` should be in the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) typeclass. The `f` in the function\n", "> body denotes a function that we map over `x`. The fact that we used `f` to\n", "> represent both of those doesn't mean that they somehow represent the\n", "> same thing.\n", "\n", "By using [`<$>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--36--62-), the applicative style really shines, because now if we\n", "want to apply a function `f` between three applicative functors, we can\n", "write `f <$> x <*> y <*> z`. If the parameters weren't\n", "applicative functors but normal values, we'd write `f x y z`.\n", "\n", "Let's take a closer look at how this works. We have a value of\n", "`Just \"johntra\"` and a value of `Just \"volta\"` and we want to join them into one\n", "[`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String) inside a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) functor. We do this:" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"johntravolta\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(++) <$> Just \"johntra\" <*> Just \"volta\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before we see how this happens, compare the above line with this:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"johntravolta\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(++) \"johntra\" \"volta\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Awesome! To use a normal function on applicative functors, just sprinkle\n", "some [`<$>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--36--62-) and [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) about and the function will operate on\n", "applicatives and return an applicative. How cool is that?\n", "\n", "Anyway, when we do `(++) <$> Just \"johntra\" <*> Just \"volta\"`, first\n", "`(++)`, which has a type of `(++) :: [a] -> [a] -> [a]` gets mapped over\n", "`Just \"johntra\"`, resulting in a value that's the same as\n", "`Just (\"johntra\"++)` and has a type of `Maybe ([Char] -\\> [Char])`. Notice how\n", "the first parameter of `(++)` got eaten up and how the `a`s turned into\n", "[`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char)s. And now `Just (\"johntra\"++) <*> Just \"volta\"` happens, which\n", "takes the function out of the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) and maps it over `Just \"volta\"`,\n", "resulting in `Just \"johntravolta\"`. Had any of the two values been\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), the result would have also been [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "\n", "So far, we've only used [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) in our examples and you might be thinking\n", "that applicative functors are all about [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe). There are loads of other\n", "instances of [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative), so let's go and meet them!\n", "\n", "Lists (actually the list type constructor, `[]`) are applicative functors.\n", "What a surprise! Here's how `[]` is an instance of [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative):" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Applicative [] where\n",
    "    pure x = [x]\n",
    "    fs <*> xs = [f x | f <- fs, x <- xs]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Earlier, we said that [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) takes a value and puts it in a default\n", "context. Or in other words, a minimal context that still yields that\n", "value. The minimal context for lists would be the empty list, `[]`, but\n", "the empty list represents the lack of a value, so it can't hold in\n", "itself the value that we used [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) on. That's why [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) takes a value and\n", "puts it in a singleton list. Similarly, the minimal context for the\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) applicative functor would be a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), but it represents the lack\n", "of a value instead of a value, so [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) is implemented as [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) in the\n", "instance implementation for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)." ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"Hey\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pure \"Hey\" :: [String]" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"Hey\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pure \"Hey\" :: Maybe String" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What about [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-)? If we look at what [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-)'s type would be if it were\n", "limited only to lists, we get `(<*>) :: [a -> b] -> [a] -> [b]`.\n", "It's implemented with a [list\n", "comprehension](http://learnyouahaskell.com/starting-out#im-a-list-comprehension). [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) has to\n", "somehow extract the function out of its left parameter and then map it\n", "over the right parameter. But the thing here is that the left list can\n", "have zero functions, one function, or several functions inside it. The\n", "right list can also hold several values. That's why we use a list\n", "comprehension to draw from both lists. We apply every possible function\n", "from the left list to every possible value from the right list. The\n", "resulting list has every possible combination of applying a function\n", "from the left list to a value in the right one." ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[0,0,0,101,102,103,1,4,9]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[(*0),(+100),(^2)] <*> [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The left list has three functions and the right list has three values,\n", "so the resulting list will have nine elements. Every function in the\n", "left list is applied to every function in the right one. If we have a\n", "list of functions that take two parameters, we can apply those functions\n", "between two lists." ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[4,5,5,6,3,4,6,8]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[(+),(*)] <*> [1,2] <*> [3,4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) is left-associative, `[(+),(*)] <*> [1,2]` happens\n", "first, resulting in a list that's the same as `[(1+),(2+),(1*),(2*)]`,\n", "because every function on the left gets applied to every value on the\n", "right. Then, `[(1+),(2+),(1*),(2*)] <*> [3,4]` happens, which\n", "produces the final result.\n", "\n", "Using the applicative style with lists is fun! Watch:" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"ha?\",\"ha!\",\"ha.\",\"heh?\",\"heh!\",\"heh.\",\"hmm?\",\"hmm!\",\"hmm.\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(++) <$> [\"ha\",\"heh\",\"hmm\"] <*> [\"?\",\"!\",\".\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, see how we used a normal function that takes two strings between\n", "two applicative functors of strings just by inserting the appropriate\n", "applicative operators.\n", "\n", "You can view lists as non-deterministic computations. A value like `100`\n", "or `\"what\"` can be viewed as a deterministic computation that has only one\n", "result, whereas a list like `[1,2,3]` can be viewed as a computation that\n", "can't decide on which result it wants to have, so it presents us with\n", "all of the possible results. So when you do something like\n", "`(+) <$> [1,2,3] <*> [4,5,6]`, you can think of it as adding together two\n", "non-deterministic computations with +, only to produce another\n", "non-deterministic computation that's even less sure about its result.\n", "\n", "Using the applicative style on lists is often a good replacement for\n", "list comprehensions. In the second chapter, we wanted to see all the\n", "possible products of `[2,5,10]` and `[8,10,11]`, so we did this:" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[16,20,22,40,50,55,80,100,110]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[ x*y | x <- [2,5,10], y <- [8,10,11]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We're just drawing from two lists and applying a function between every\n", "combination of elements. This can be done in the applicative style as\n", "well:" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[16,20,22,40,50,55,80,100,110]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(*) <$> [2,5,10] <*> [8,10,11]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This seems clearer to me, because it's easier to see that we're just\n", "calling [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) between two non-deterministic computations. If we wanted all\n", "possible products of those two lists that are more than 50, we'd just\n", "do:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[55,80,100,110]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's easy to see how `pure f <*> xs` equals `fmap f xs` with lists.\n", "`pure f` is just `[f]` and `[f] <*> xs` will apply every function in the left\n", "list to every value in the right one, but there's just one function in\n", "the left list, so it's like mapping.\n", "\n", "Another instance of [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) that we've already encountered is [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO).\n", "This is how the instance is implemented:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Applicative IO where\n",
    "    pure = return\n",
    "    a <*> b = do\n",
    "        f <- a\n",
    "        x <- b\n",
    "        return (f x)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Since [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) is all about putting a value in a minimal context that still\n", "holds it as its result, it makes sense that [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) is just [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return), because\n", "[`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) does exactly that; it makes an I/O action that doesn't do\n", "anything, it just yields some value as its result, but it doesn't really\n", "do any I/O operations like printing to the terminal or reading from a\n", "file.\n", "\n", "If [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) were specialized for [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) it would have a type of\n", "`(<*>) :: IO (a -> b) -> IO a -> IO b`. It would take an I/O action that yields a\n", "function as its result and another I/O action and create a new I/O\n", "action from those two that, when performed, first performs the first one\n", "to get the function and then performs the second one to get the value\n", "and then it would yield that function applied to the value as its\n", "result. We used *do* syntax to implement it here. Remember, *do* syntax\n", "is about taking several I/O actions and gluing them into one, which is\n", "exactly what we do here.\n", "\n", "With [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) and `[]`, we could think of [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) as simply extracting a\n", "function from its left parameter and then sort of applying it over the\n", "right one. With [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO), extracting is still in the game, but now we also\n", "have a notion of *sequencing*, because we're taking two I/O actions and\n", "we're sequencing, or gluing, them into one. We have to extract the\n", "function from the first I/O action, but to extract a result from an I/O\n", "action, it has to be performed.\n", "\n", "Consider this:" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "myAction :: IO String\n", "myAction = do\n", " a <- getLine\n", " b <- getLine\n", " return $ a ++ b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is an I/O action that will prompt the user for two lines and yield\n", "as its result those two lines concatenated. We achieved it by gluing\n", "together two [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) I/O actions and a [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return), because we wanted our new\n", "glued I/O action to hold the result of `a ++ b`. Another way of writing\n", "this would be to use the applicative style." ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "myAction :: IO String\n", "myAction = (++) <$> getLine <*> getLine" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What we were doing before was making an I/O action that applied a\n", "function between the results of two other I/O actions, and this is the\n", "same thing. Remember, [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) is an I/O action with the type\n", "`getLine :: IO String`. When we use [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) between two applicative functors, the\n", "result is an applicative functor, so this all makes sense.\n", "\n", "If we regress to the box analogy, we can imagine [`getLine`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:getLine) as a box that\n", "will go out into the real world and fetch us a string. Doing\n", "`(++) <$> getLine <*> getLine` makes a new, bigger box that sends those two\n", "boxes out to fetch lines from the terminal and then presents the\n", "concatenation of those two lines as its result.\n", "\n", "The type of the expression `(++) <$> getLine <*> getLine` is\n", "`IO String`, which means that this expression is a completely normal I/O\n", "action like any other, which also holds a result value inside it, just\n", "like other I/O actions. That's why we can do stuff like:" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "main = do\n", " a <- (++) <$> getLine <*> getLine\n", " putStrLn $ \"The two lines concatenated turn out to be: \" ++ a" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "The two lines concatenated turn out to be: line 1line 2" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin (unlines [\"line 1\", \"line 2\"]) main" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you ever find yourself binding some I/O actions to names and then\n", "calling some function on them and presenting that as the result by using\n", "[`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return), consider using the applicative style because it's arguably a bit\n", "more concise and terse.\n", "\n", "Another instance of [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) is `(->) r`, so functions. They are\n", "rarely used with the applicative style outside of code golf, but they're\n", "still interesting as applicatives, so let's take a look at how the\n", "function instance is implemented.\n", "\n", "> If you're confused about what `(->) r` means, check out the previous\n", "> section where we explain how `(->) r` is a functor." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Applicative ((->) r) where\n",
    "    pure x = (\\_ -> x)\n",
    "    f <*> g = \\x -> f x (g x)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we wrap a value into an applicative functor with [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure), the result\n", "it yields always has to be that value. A minimal default context that\n", "still yields that value as a result. That's why in the function instance\n", "implementation, [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) takes a value and creates a function that ignores\n", "its parameter and always returns that value. If we look at the type for\n", "[`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure), but specialized for the `(->) r` instance, it's\n", "`pure :: a -> (r -> a)`." ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(pure 3) \"blah\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because of currying, function application is left-associative, so we can\n", "omit the parentheses." ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pure 3 \"blah\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The instance implementation for [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) is a bit cryptic, so it's best if\n", "we just take a look at how to use functions as applicative functors in\n", "the applicative style." ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "(+) <$> (+3) <*> (*100) :: forall {b}. Num b => b -> b" ], "text/plain": [ "(+) <$> (+3) <*> (*100) :: forall {b}. Num b => b -> b" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t (+) <$> (+3) <*> (*100)" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "508" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(+) <$> (+3) <*> (*100) $ 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Calling [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) with two applicative functors results in an applicative\n", "functor, so if we use it on two functions, we get back a function. So\n", "what goes on here? When we do `(+) <$> (+3) <*> (*100)`, we're\n", "making a function that will use + on the results of `(+3)` and `(*100)` and\n", "return that. To demonstrate on a real example, when we did\n", "`(+) <$> (+3) <*> (*100) $ 5`, the `5` first got applied to `(+3)` and `(*100)`,\n", "resulting in `8` and `500`. Then, [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-) gets called with `8` and `500`, resulting in\n", "`508`." ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[8.0,10.0,2.5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Same here. We create a function that will call the function\n", "`\\x y z -> [x,y,z]` with the eventual results from `(+3)`, `(*2)` and `(/2)`.\n", "The `5` gets fed to each of the three functions and then `\\x y z -> [x, y, z]` gets\n", "called with those results.\n", "\n", "You can think of functions as boxes that contain their eventual results,\n", "so doing `k <$> f <*> g` creates a function that will call `k` with\n", "the eventual results from `f` and `g`. When we do something like\n", "`(+) <$> Just 3 <*> Just 5`, we're using [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-) on values that might or might not be\n", "there, which also results in a value that might or might not be there.\n", "When we do `(+) <$> (+10) <*> (+5)`, we're using [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-) on the future\n", "return values of `(+10)` and `(+5)` and the result is also something that\n", "will produce a value only when called with a parameter.\n", "\n", "We don't often use functions as applicatives, but this is still really\n", "interesting. It's not very important that you get how the `(->) r`\n", "instance for [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) works, so don't despair if you're not getting\n", "this right now. Try playing with the applicative style and functions to\n", "build up an intuition for functions as applicatives.\n", "\n", "An instance of [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) that we haven't encountered yet is [`ZipList`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:ZipList),\n", "and it lives in `Control.Applicative`.\n", "\n", "It turns out there are actually more ways for lists to be applicative\n", "functors. One way is the one we already covered, which says that calling\n", "[`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) with a list of functions and a list of values results in a list\n", "which has all the possible combinations of applying functions from the\n", "left list to the values in the right list. If we do\n", "`[(+3),(*2)] <*> [1,2]`, `(+3)` will be applied to both `1` and `2` and `(*2)` will also be\n", "applied to both `1` and `2`, resulting in a list that has four elements,\n", "namely `[4,5,2,4]`.\n", "\n", "However, `[(+3),(*2)] <*> [1,2]` could also work in such a way that\n", "the first function in the left list gets applied to the first value in\n", "the right one, the second function gets applied to the second value, and\n", "so on. That would result in a list with two values, namely `[4,4]`. You\n", "could look at it as `[1 + 3, 2 * 2]`.\n", "\n", "Because one type can't have two instances for the same typeclass, the\n", "`ZipList a` type was introduced, which has one constructor [`ZipList`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:ZipList) that\n", "has just one field, and that field is a list. Here's the instance:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Applicative ZipList where\n",
    "        pure x = ZipList (repeat x)\n",
    "        ZipList fs <*> ZipList xs = ZipList (zipWith (\\f x -> f x) fs xs)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) does just what we said. It applies the first function to the\n", "first value, the second function to the second value, etc. This is done\n", "with `zipWith (\\f x -> f x) fs xs`. Because of how [`zipWith`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zipWith) works, the\n", "resulting list will be as long as the shorter of the two lists.\n", "\n", "[`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) is also interesting here. It takes a value and puts it in a list\n", "that just has that value repeating indefinitely. `pure \"haha\"` results in\n", "`ZipList ([\"haha\",\"haha\",\"haha\"...`. This might be a bit confusing since\n", "we said that [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) should put a value in a minimal context that still\n", "yields that value. And you might be thinking that an infinite list of\n", "something is hardly minimal. But it makes sense with zip lists, because\n", "it has to produce the value on every position. This also satisfies the\n", "law that `pure f <*> xs` should equal `fmap f xs`. If `pure 3` just\n", "returned `ZipList [3]`, `pure (*2) <*> ZipList [1,5,10]` would result in\n", "`ZipList [2]`, because the resulting list of two zipped lists has the\n", "length of the shorter of the two. If we zip a finite list with an\n", "infinite list, the length of the resulting list will always be equal to\n", "the length of the finite list.\n", "\n", "So how do zip lists work in an applicative style? Let's see. Oh, the\n", "`ZipList a` type doesn't have a [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) instance, so we have to use the\n", "__[`getZipList`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#v:getZipList)__ function to extract a raw list out of a zip list." ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Applicative" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[101,102,103]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100,100]" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[101,102,103]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100..]" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[5,3,3,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getZipList $ max <$> ZipList [1,2,3,4,5,3] <*> ZipList [5,3,1,2]" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[('d','c','r'),('o','a','a'),('g','t','t')]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getZipList $ (,,) <$> ZipList \"dog\" <*> ZipList \"cat\" <*> ZipList \"rat\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> The `(,,)` function is the same as `\\x y z -> (x,y,z)`. Also, the `(,)`\n", "> function is the same as `\\x y -\\> (x,y)`.\n", "\n", "Aside from [`zipWith`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zipWith), the standard library has functions such as `zipWith3`,\n", "`zipWith4`, all the way up to 7. [`zipWith`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zipWith) takes a function that takes two\n", "parameters and zips two lists with it. `zipWith3` takes a function that\n", "takes three parameters and zips three lists with it, and so on. By using\n", "zip lists with an applicative style, we don't have to have a separate\n", "zip function for each number of lists that we want to zip together. We\n", "just use the applicative style to zip together an arbitrary amount of\n", "lists with a function, and that's pretty cool.\n", "\n", "`Control.Applicative` defines a function that's called __`liftA2`__, which has a\n", "type of `liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c`.\n", "It's defined like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c\n",
    "liftA2 f a b = f <$> a <*> b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nothing special, it just applies a function between two applicatives,\n", "hiding the applicative style that we've become familiar with. The reason\n", "we're looking at it is because it clearly showcases why applicative\n", "functors are more powerful than just ordinary functors. With ordinary\n", "functors, we can just map functions over one functor. But with\n", "applicative functors, we can apply a function between several functors.\n", "It's also interesting to look at this function's type as\n", "`(a -> b -> c) -> (f a -> f b -> f c)`. When we look at it like this, we can say that\n", "`liftA2` takes a normal binary function and promotes it to a function that\n", "operates on two functors.\n", "\n", "Here's an interesting concept: we can take two applicative functors and\n", "combine them into one applicative functor that has inside it the results\n", "of those two applicative functors in a list. For instance, we have\n", "`Just 3` and `Just 4`. Let's assume that the second one has a singleton list\n", "inside it, because that's really easy to achieve:" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just [4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (\\x -> [x]) (Just 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "OK, so let's say we have `Just 3` and `Just [4]`. How do we get `Just [3,4]`?\n", "Easy." ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just [3,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "liftA2 (:) (Just 3) (Just [4])" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just [3,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(:) <$> Just 3 <*> Just [4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, : is a function that takes an element and a list and returns a\n", "new list with that element at the beginning. Now that we have\n", "`Just [3,4]`, could we combine that with `Just 2` to produce `Just [2,3,4]`? Of\n", "course we could. It seems that we can combine any amount of applicatives\n", "into one applicative that has a list of the results of those\n", "applicatives inside it. Let's try implementing a function that takes a\n", "list of applicatives and returns an applicative that has a list as its\n", "result value. We'll call it [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA)." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
sequenceA :: (Applicative f) => [f a] -> f [a]\n",
    "sequenceA [] = pure []\n",
    "sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah, recursion! First, we look at the type. It will transform a list of\n", "applicatives into an applicative with a list. From that, we can lay some\n", "groundwork for an edge condition. If we want to turn an empty list into\n", "an applicative with a list of results, well, we just put an empty list\n", "in a default context. Now comes the recursion. If we have a list with a\n", "head and a tail (remember, `x` is an applicative and `xs` is a list of\n", "them), we call [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) on the tail, which results in an applicative\n", "with a list. Then, we just prepend the value inside the applicative `x`\n", "into that applicative with a list, and that's it!\n", "\n", "So if we do `sequenceA [Just 1, Just 2]`, that's\n", "`(:) <$> Just 1 <*> sequenceA [Just 2]`.\n", "That equals\n", "`(:) <$> Just 1 <*> ((:) <$> Just 2 <*> sequenceA [])`.\n", "Ah! We know that `sequenceA []` ends up as\n", "being `Just []`, so this expression is now\n", "`(:) <$> Just 1 <*> ((:) <$> Just 2 <*> Just [])`, which is\n", "`(:) <$> Just 1 <*> Just [2]`, which is `Just [1,2]`!\n", "\n", "Another way to implement [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) is with a fold. Remember, pretty much\n", "any function where we go over a list element by element and accumulate a\n", "result along the way can be implemented with a fold." ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
sequenceA :: (Applicative f) => [f a] -> f [a]\n",
    "sequenceA = foldr (liftA2 (:)) (pure [])
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We approach the list from the right and start off with an accumulator\n", "value of `pure []`. We do `liftA2 (:)` between the accumulator and the last\n", "element of the list, which results in an applicative that has a\n", "singleton in it. Then we do `liftA2 (:)` with the now last element and the\n", "current accumulator and so on, until we're left with just the\n", "accumulator, which holds a list of the results of all the applicatives.\n", "\n", "Let's give our function a whirl on some applicatives." ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just [3,2,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sequenceA [Just 3, Just 2, Just 1]" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sequenceA [Just 3, Nothing, Just 1]" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[6,5,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sequenceA [(+3),(+2),(+1)] 3" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sequenceA [[1,2,3],[4,5,6]]" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sequenceA [[1,2,3],[4,5,6],[3,4,4],[]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah! Pretty cool. When used on [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values, [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) creates a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)\n", "value with all the results inside it as a list. If one of the values was\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), then the result is also a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). This is cool when you have a\n", "list of [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values and you're interested in the values only if none of\n", "them is a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "\n", "When used with functions, [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) takes a list of functions and\n", "returns a function that returns a list. In our example, we made a\n", "function that took a number as a parameter and applied it to each\n", "function in the list and then returned a list of results.\n", "`sequenceA [(+3),(+2),(+1)] 3` will call `(+3)` with `3`, `(+2)` with `3` and `(+1)` with `3`\n", "and present all those results as a list.\n", "\n", "Doing `(+) <$> (+3) <*> (*2)` will create a function that takes a\n", "parameter, feeds it to both `(+3)` and `(*2)` and then calls + with those\n", "two results. In the same vein, it makes sense that\n", "`sequenceA [(+3),(*2)]` makes a function that takes a parameter and feeds it to all\n", "of the functions in the list. Instead of calling [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-) with the results of\n", "the functions, a combination of `:` and `pure []` is used to gather those\n", "results in a list, which is the result of that function.\n", "\n", "Using [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) is cool when we have a list of functions and we want to\n", "feed the same input to all of them and then view the list of results.\n", "For instance, we have a number and we're wondering whether it satisfies\n", "all of the predicates in a list. One way to do that would be like so:" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[True,True,True]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map (\\f -> f 7) [(>4),(<10),odd]" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "and $ map (\\f -> f 7) [(>4),(<10),odd]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, [`and`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:and) takes a list of booleans and returns [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) if they're all\n", "[`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True). Another way to achieve the same thing would be with [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA):" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[True,True,True]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sequenceA [(>4),(<10),odd] 7" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "and $ sequenceA [(>4),(<10),odd] 7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`sequenceA [(>4),(<10),odd]` creates a function that will take a number\n", "and feed it to all of the predicates in `[(>4),(<10),odd]` and return a\n", "list of booleans. It turns a list with the type `(Num a) => [a -> Bool]`\n", "into a function with the type `(Num a) => a -> [Bool]`. Pretty neat,\n", "huh?\n", "\n", "Because lists are homogeneous, all the functions in the list have to be\n", "functions of the same type, of course. You can't have a list like\n", "`[ord, (+3)]`, because [`ord`](https://hackage.haskell.org/package/base/docs/Data-Char.html#v:ord) takes a character and returns a number, whereas `(+3)`\n", "takes a number and returns a number.\n", "\n", "When used with `[]`, [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) takes a list of lists and returns a list of\n", "lists. Hmm, interesting. It actually creates lists that have all\n", "possible combinations of their elements. For illustration, here's the\n", "above done with [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) and then done with a list comprehension:" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sequenceA [[1,2,3],[4,5,6]]" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[[x,y] | x <- [1,2,3], y <- [4,5,6]]" ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,3],[1,4],[2,3],[2,4]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sequenceA [[1,2],[3,4]]" ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,3],[1,4],[2,3],[2,4]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[[x,y] | x <- [1,2], y <- [3,4]]" ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sequenceA [[1,2],[3,4],[5,6]]" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[[x,y,z] | x <- [1,2], y <- [3,4], z <- [5,6]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This might be a bit hard to grasp, but if you play with it for a while,\n", "you'll see how it works. Let's say that we're doing\n", "`sequenceA [[1,2],[3,4]]`. To see how this happens, let's use the\n", "`sequenceA (x:xs) = (:) <$> x <*> sequenceA xs` definition of [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) and the edge\n", "condition `sequenceA [] = pure []`. You don't have to follow this\n", "evaluation, but it might help you if have trouble imagining how\n", "[`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) works on lists of lists, because it can be a bit mind-bending.\n", "\n", "- We start off with `sequenceA [[1,2],[3,4]]`\n", "- That evaluates to `(:) <$> [1,2] <*> sequenceA [[3,4]]`\n", "- Evaluating the inner [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) further, we get `(:) <$> [1,2] <*> ((:) <$> [3,4] <*> sequenceA [])`\n", "- We've reached the edge condition, so this is now `(:) <$> [1,2] <*> ((:) <$> [3,4] <*> [[]])`\n", "- Now, we evaluate the `(:) <$> [3,4] <*> [[]]` part, which will\n", " use `:` with every possible value in the left list (possible values\n", " are `3` and `4`) with every possible value on the right list (only\n", " possible value is `[]`), which results in `[3:[], 4:[]]`, which is\n", " `[[3],[4]]`. So now we have `(:) <$> [1,2] <*> [[3],[4]]`\n", "- Now, `:` is used with every possible value from the left list (`1` and\n", " `2`) with every possible value in the right list (`[3]` and `[4]`), which\n", " results in `[1:[3], 1:[4], 2:[3], 2:[4]]`, which is\n", " `[[1,3],[1,4],[2,3],[2,4]`\n", "\n", "Doing `(+) <$> [1,2] <*> [4,5,6]` results in a non-deterministic\n", "computation `x + y` where `x` takes on every value from `[1,2]` and `y` takes on\n", "every value from `[4,5,6]`. We represent that as a list which holds all of\n", "the possible results. Similarly, when we do\n", "`sequence [[1,2],[3,4],[5,6],[7,8]]`, the result is a non-deterministic computation\n", "`[x,y,z,w]`, where `x` takes on every value from `[1,2]`, `y` takes on every\n", "value from `[3,4]` and so on. To represent the result of that\n", "non-deterministic computation, we use a list, where each element in the\n", "list is one possible list. That's why the result is a list of lists.\n", "\n", "When used with I/O actions, [`sequenceA`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequenceA) is the same thing as [`sequence`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:sequence)! It\n", "takes a list of I/O actions and returns an I/O action that will perform\n", "each of those actions and have as its result a list of the results of\n", "those I/O actions. That's because to turn an `[IO a]` value into an `IO [a]`\n", "value, to make an I/O action that yields a list of results when\n", "performed, all those I/O actions have to be sequenced so that they're\n", "then performed one after the other when evaluation is forced. You can't\n", "get the result of an I/O action without performing it." ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"heyh\",\"ho\",\"woo\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "withStdin (unlines [\"heyh\",\"ho\",\"woo\"] ) $ sequenceA [getLine, getLine, getLine]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Like normal functors, applicative functors come with a few laws. The\n", "most important one is the one that we already mentioned, namely that\n", "**`pure f <*> x = fmap f x`** holds. As an exercise, you can prove this law\n", "for some of the applicative functors that we've met in this chapter.The\n", "other functor laws are:\n", "\n", "- **`pure id <*> v = v`**\n", "- **`pure (.) <*> u <*> v <*> w = u <*> (v <*> w)`**\n", "- **`pure f <*> pure x = pure (f x)`**\n", "- **`u <*> pure y = pure ($ y) <*> u`**\n", "\n", "We won't go over them in detail right now because that would take up a\n", "lot of pages and it would probably be kind of boring, but if you're up\n", "to the task, you can take a closer look at them and see if they hold for\n", "some of the instances.\n", "\n", "In conclusion, applicative functors aren't just interesting, they're\n", "also useful, because they allow us to combine different computations,\n", "such as I/O computations, non-deterministic computations, computations\n", "that might have failed, etc. by using the applicative style. Just by\n", "using [`<$>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--36--62-) and [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) we can use normal functions to uniformly operate\n", "on any number of applicative functors and take advantage of the\n", "semantics of each one.\n", "\n", "The newtype keyword\n", "-------------------\n", "\n", "\n", "\n", "So far, we've learned how to make our own algebraic data types by using\n", "the *data* keyword. We've also learned how to give existing types\n", "synonyms with the *type* keyword. In this section, we'll be taking a\n", "look at how to make new types out of existing data types by using the\n", "*newtype* keyword and why we'd want to do that in the first place.\n", "\n", "In the previous section, we saw that there are actually more ways for\n", "the list type to be an applicative functor. One way is to have [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-)\n", "take every function out of the list that is its left parameter and apply\n", "it to every value in the list that is on the right, resulting in every\n", "possible combination of applying a function from the left list to a\n", "value in the right list." ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[2,3,4,100,200,300,5,10,15]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[(+1),(*100),(*5)] <*> [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The second way is to take the first function on the left side of [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-)\n", "and apply it to the first value on the right, then take the second\n", "function from the list on the left side and apply it to the second value\n", "on the right, and so on. Ultimately, it's kind of like zipping the two\n", "lists together. But lists are already an instance of [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative), so how\n", "did we also make lists an instance of [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) in this second way? If\n", "you remember, we said that the `ZipList a` type was introduced for this\n", "reason, which has one value constructor, [`ZipList`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:ZipList), that has just one\n", "field. We put the list that we're wrapping in that field. Then, [`ZipList`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:ZipList)\n", "was made an instance of [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative), so that when we want to use lists\n", "as applicatives in the zipping manner, we just wrap them with the\n", "[`ZipList`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:ZipList) constructor and then once we're done, unwrap them with\n", "[`getZipList`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#v:getZipList):" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[2,200,15]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getZipList $ ZipList [(+1),(*100),(*5)] <*> ZipList [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, what does this have to do with this *newtype* keyword? Well, think\n", "about how we might write the data declaration for our `ZipList a` type.\n", "One way would be to do it like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data ZipList a = ZipList [a]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A type that has just one value constructor and that value constructor\n", "has just one field that is a list of things. We might also want to use\n", "record syntax so that we automatically get a function that extracts a\n", "list from a [`ZipList`](https://hackage.haskell.org/package/base/docs/Control-Applicative.html#t:ZipList):" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data ZipList a = ZipList { getZipList :: [a] }
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This looks fine and would actually work pretty well. We had two ways of\n", "making an existing type an instance of a type class, so we used the\n", "*data* keyword to just wrap that type into another type and made the\n", "other type an instance in the second way.\n", "\n", "The *newtype* keyword in Haskell is made exactly for these cases when we\n", "want to just take one type and wrap it in something to present it as\n", "another type. In the actual libraries, `ZipList a` is defined like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
newtype ZipList a = ZipList { getZipList :: [a] }
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead of the *data* keyword, the *newtype* keyword is used. Now why is\n", "that? Well for one, *newtype* is faster. If you use the *data* keyword\n", "to wrap a type, there's some overhead to all that wrapping and\n", "unwrapping when your program is running. But if you use *newtype*,\n", "Haskell knows that you're just using it to wrap an existing type into a\n", "new type (hence the name), because you want it to be the same internally\n", "but have a different type. With that in mind, Haskell can get rid of the\n", "wrapping and unwrapping once it resolves which value is of what type.\n", "\n", "So why not just use *newtype* all the time instead of *data* then? Well,\n", "when you make a new type from an existing type by using the *newtype*\n", "keyword, you can only have one value constructor and that value\n", "constructor can only have one field. But with *data*, you can make data\n", "types that have several value constructors and each constructor can have\n", "zero or more fields:" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Profession = Fighter | Archer | Accountant\n", "\n", "data Race = Human | Elf | Orc | Goblin\n", "\n", "data PlayerCharacter = PlayerCharacter Race Profession" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When using *newtype*, you're restricted to just one constructor with one\n", "field.\n", "\n", "We can also use the *deriving* keyword with *newtype* just like we would\n", "with *data*. We can derive instances for `Eq`, [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord), [`Enum`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Enum), [`Bounded`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bounded), [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show)\n", "and [`Read`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Read). If we derive the instance for a type class, the type that\n", "we're wrapping has to be in that type class to begin with. It makes\n", "sense, because *newtype* just wraps an existing type. So now if we do\n", "the following, we can print and equate values of our new type:" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "newtype CharList = CharList { getCharList :: [Char] } deriving (Eq, Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's give that a go:" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "CharList {getCharList = \"this will be shown!\"}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "CharList \"this will be shown!\"" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "CharList \"benny\" == CharList \"benny\"" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "CharList \"benny\" == CharList \"oisters\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this particular *newtype*, the value constructor has the following\n", "type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
CharList :: [Char] -> CharList
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It takes a `[Char]` value, such as `\"my sharona\"` and returns a `CharList`\n", "value. From the above examples where we used the `CharList` value\n", "constructor, we see that really is the case. Conversely, the `getCharList`\n", "function, which was generated for us because we used record syntax in\n", "our *newtype*, has this type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
getCharList :: CharList -> [Char]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It takes a `CharList` value and converts it to a `[Char]` value. You can\n", "think of this as wrapping and unwrapping, but you can also think of it\n", "as converting values from one type to the other.\n", "\n", "### Using newtype to make type class instances\n", "\n", "Many times, we want to make our types instances of certain type classes,\n", "but the type parameters just don't match up for what we want to do. It's\n", "easy to make [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor), because the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) type\n", "class is defined like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class Functor f where\n",
    "    fmap :: (a -> b) -> f a -> f b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we just start out with:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Functor Maybe where
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then implement [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap). All the type parameters add up because the\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) takes the place of f in the definition of the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) type class\n", "and so if we look at [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) like it only worked on [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), it ends up\n", "behaving like:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
fmap :: (a -> b) -> Maybe a -> Maybe b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Isn't that just peachy? Now what if we wanted to make the tuple an\n", "instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) in such a way that when we [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) a function over a\n", "tuple, it gets applied to the first component of the tuple? That way,\n", "doing `fmap (+3) (1,1)` would result in `(4,1)`. It turns out that writing\n", "the instance for that is kind of hard. With [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), we just say\n", "`instance Functor Maybe where` because only type constructors that take exactly one\n", "parameter can be made an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor). But it seems like there's\n", "no way to do something like that with `(a,b)` so that the type parameter `a`\n", "ends up being the one that changes when we use [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap). To get around this,\n", "we can *newtype* our tuple in such a way that the second type parameter\n", "represents the type of the first component in the tuple:" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "newtype Pair b a = Pair { getPair :: (a,b) }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And now, we can make it an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) so that the function is\n", "mapped over the first component:" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Functor (Pair c) where\n", " fmap f (Pair (x,y)) = Pair (f x, y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, we can pattern match on types defined with *newtype*. We\n", "pattern match to get the underlying tuple, then we apply the function `f`\n", "to the first component in the tuple and then we use the [`Pair`](https://hackage.haskell.org/package/base/docs/Data-Functor-Product.html#v:Pair) value\n", "constructor to convert the tuple back to our `Pair b a`. If we imagine\n", "what the type [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) would be if it only worked on our new pairs, it would\n", "be:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
fmap :: (a -> b) -> Pair c a -> Pair c b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, we said `instance Functor (Pair c) where` and so `Pair c` took the\n", "place of the `f` in the type class definition for [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor):" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class Functor f where\n",
    "    fmap :: (a -> b) -> f a -> f b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So now, if we convert a tuple into a `Pair b a`, we can use [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) over it\n", "and the function will be mapped over the first component:" ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(200,3)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getPair $ fmap (*100) (Pair (2,3))" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(\"gnillac nodnol\",3)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getPair $ fmap reverse (Pair (\"london calling\", 3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### On newtype laziness\n", "\n", "We mentioned that *newtype* is usually faster than *data*. The only\n", "thing that can be done with *newtype* is turning an existing type into a\n", "new type, so internally, Haskell can represent the values of types\n", "defined with *newtype* just like the original ones, only it has to keep\n", "in mind that the their types are now distinct. This fact means that not\n", "only is *newtype* faster, it's also lazier. Let's take a look at what\n", "this means.\n", "\n", "Like we've said before, Haskell is lazy by default, which means that\n", "only when we try to actually print the results of our functions will any\n", "computation take place. Furthermore, only those computations that are\n", "necessary for our function to tell us the result will get carried out.\n", "The [`undefined`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:undefined) value in Haskell represents an erroneous computation. If we\n", "try to evaluate it (that is, force Haskell to actually compute it) by\n", "printing it to the terminal, Haskell will throw a hissy fit (technically\n", "referred to as an exception):" ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Prelude.undefined\nCallStack (from HasCallStack):\n undefined, called at :1:1 in interactive:Ghci3170" ] } ], "source": [ "undefined" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, if we make a list that has some [`undefined`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:undefined) values in it but\n", "request only the head of the list, which is not [`undefined`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:undefined), everything\n", "will go smoothly because Haskell doesn't really need to evaluate any\n", "other elements in a list if we only want to see what the first element\n", "is:" ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\n", "\u001b[;1m:1:1: \u001b[;1m\u001b[35mwarning\u001b[0m\u001b[0m\u001b[;1m: [\u001b[;1m\u001b[35mGHC-63394\u001b[0m\u001b[0m\u001b[;1m] [\u001b[;1m\u001b[35m-Wx-partial\u001b[0m\u001b[0m\u001b[;1m]\u001b[0m\u001b[0m\u001b[;1m\n", " In the use of `head' (imported from Prelude, but defined in GHC.List):\n", " \"This is a partial function, it throws an error on empty lists. Use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty.\"\u001b[0m\u001b[0m\n", "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "head [3,4,5,undefined,2,undefined]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now consider the following type:" ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data CoolBool = CoolBool { getCoolBool :: Bool }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's your run-of-the-mill algebraic data type that was defined with the\n", "*data* keyword. It has one value constructor, which has one field whose\n", "type is [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool). Let's make a function that pattern matches on a `CoolBool`\n", "and returns the value `\"hello\"` regardless of whether the [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) inside the\n", "`CoolBool` was [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) or [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False):" ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "helloMe :: CoolBool -> String\n", "helloMe (CoolBool _) = \"hello\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead of applying this function to a normal `CoolBool`, let's throw it a\n", "curveball and apply it to [`undefined`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:undefined)!" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Prelude.undefined\nCallStack (from HasCallStack):\n undefined, called at :1:9 in interactive:Ghci3256" ] } ], "source": [ "helloMe undefined" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yikes! An exception! Now why did this exception happen? Types defined\n", "with the *data* keyword can have multiple value constructors (even\n", "though `CoolBool` only has one). So in order to see if the value given to\n", "our function conforms to the `(CoolBool _)` pattern, Haskell has to\n", "evaluate the value just enough to see which value constructor was used\n", "when we made the value. And when we try to evaluate an [`undefined`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:undefined) value,\n", "even a little, an exception is thrown.\n", "\n", "Instead of using the *data* keyword for `CoolBool`, let's try using\n", "*newtype*:" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "newtype CoolBool = CoolBool { getCoolBool :: Bool }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We don't have to change our `helloMe` function, because the pattern\n", "matching syntax is the same if you use *newtype* or *data* to define\n", "your type." ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "helloMe :: CoolBool -> String\n", "helloMe (CoolBool _) = \"hello\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's do the same thing here and apply `helloMe` to an\n", "[`undefined`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:undefined) value:" ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"hello\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "helloMe undefined" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "It worked! Hmmm, why is that? Well, like we've said, when we use\n", "*newtype*, Haskell can internally represent the values of the new type\n", "in the same way as the original values. It doesn't have to add another\n", "box around them, it just has to be aware of the values being of\n", "different types. And because Haskell knows that types made with the\n", "*newtype* keyword can only have one constructor, it doesn't have to\n", "evaluate the value passed to the function to make sure that it conforms\n", "to the `(CoolBool _)` pattern because *newtype* types can only have one\n", "possible value constructor and one field!\n", "\n", "This difference in behavior may seem trivial, but it's actually pretty\n", "important because it helps us realize that even though types defined\n", "with *data* and *newtype* behave similarly from the programmer's point\n", "of view because they both have value constructors and fields, they are\n", "actually two different mechanisms. Whereas *data* can be used to make\n", "your own types from scratch, *newtype* is for making a completely new\n", "type out of an existing type. Pattern matching on *newtype* values isn't\n", "like taking something out of a box (like it is with *data*), it's more\n", "about making a direct conversion from one type to another.\n", "\n", "### `type` vs. `newtype` vs. `data`\n", "\n", "At this point, you may be a bit confused about what exactly the\n", "difference between *type*, *data* and *newtype* is, so let's refresh our\n", "memory a bit.\n", "\n", "The *type* keyword is for making type synonyms. What that means is that\n", "we just give another name to an already existing type so that the type\n", "is easier to refer to. Say we did the following:" ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type IntList = [Int]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All this does is to allow us to refer to the `[Int]` type as `IntList`. They\n", "can be used interchangeably. We don't get an `IntList` value constructor\n", "or anything like that. Because `[Int]` and `IntList` are only two ways to\n", "refer to the same type, it doesn't matter which name we use in our type\n", "annotations:" ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,1,2,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "([1,2,3] :: IntList) ++ ([1,2,3] :: [Int])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We use type synonyms when we want to make our type signatures more\n", "descriptive by giving types names that tell us something about their\n", "purpose in the context of the functions where they're being used. For\n", "instance, when we used an association list of type `[(String,String)]` to\n", "represent a phone book, we gave it the type synonym of `PhoneBook` so that\n", "the type signatures of our functions were easier to read.\n", "\n", "The *newtype* keyword is for taking existing types and wrapping them in\n", "new types, mostly so that it's easier to make them instances of certain\n", "type classes. When we use *newtype* to wrap an existing type, the type\n", "that we get is separate from the original type. If we make the following\n", "*newtype*:" ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "newtype CharList = CharList { getCharList :: [Char] }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can't use [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) to put together a `CharList` and a list of type `[Char]`. We\n", "can't even use [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) to put together two `CharList`s, because [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) works only\n", "on lists and the `CharList` type isn't a list, even though it could be\n", "said that it contains one. We can, however, convert two `CharList`s to\n", "lists, [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) them and then convert that back to a `CharList`.\n", "\n", "When we use record syntax in our *newtype* declarations, we get\n", "functions for converting between the new type and the original type:\n", "namely the value constructor of our *newtype* and the function for\n", "extracting the value in its field. The new type also isn't automatically\n", "made an instance of the type classes that the original type belongs to,\n", "so we have to derive or manually write them.\n", "\n", "In practice, you can think of *newtype* declarations as *data*\n", "declarations that can only have one constructor and one field. If you\n", "catch yourself writing such a *data* declaration, consider using\n", "*newtype*.\n", "\n", "The *data* keyword is for making your own data types and with them, you\n", "can go hog wild. They can have as many constructors and fields as you\n", "wish and can be used to implement any algebraic data type by yourself.\n", "Everything from lists and [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)-like types to trees.\n", "\n", "If you just want your type signatures to look cleaner and be more\n", "descriptive, you probably want type synonyms. If you want to take an\n", "existing type and wrap it in a new type in order to make it an instance\n", "of a type class, chances are you're looking for a *newtype*. And if you\n", "want to make something completely new, odds are good that you're looking\n", "for the *data* keyword.\n", "\n", "\n", "\n", "Monoids\n", "-------\n", "\n", "\n", "\n", "Type classes in Haskell are used to present an interface for types that\n", "have some behavior in common. We started out with simple type classes\n", "like `Eq`, which is for types whose values can be equated, and [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord), which\n", "is for things that can be put in an order and then moved on to more\n", "interesting ones, like [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) and [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative).\n", "\n", "When we make a type, we think about which behaviors it supports, i.e.\n", "what it can act like and then based on that we decide which type classes\n", "to make it an instance of. If it makes sense for values of our type to\n", "be equated, we make it an instance of the `Eq` type class. If we see that\n", "our type is some kind of functor, we make it an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor), and\n", "so on.\n", "\n", "Now consider the following: [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) is a function that takes two numbers and\n", "multiplies them. If we multiply some number with a `1`, the result is\n", "always equal to that number. It doesn't matter if we do `1 * x` or\n", "`x * 1`, the result is always `x`. Similarly, [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) is also a function which takes\n", "two things and returns a third. Only instead of multiplying numbers, it\n", "takes two lists and concatenates them. And much like [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-), it also has a\n", "certain value which doesn't change the other one when used with [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-). That\n", "value is the empty list: `[]`." ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "4 * 1" ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "9" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "1 * 9" ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1,2,3] ++ []" ] }, { "cell_type": "code", "execution_count": 120, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[0.5,2.5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[] ++ [0.5, 2.5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It seems that both [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) together with `1` and [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) along with `[]` share some\n", "common properties:\n", "\n", "- The function takes two parameters.\n", "- The parameters and the returned value have the same type.\n", "- There exists such a value that doesn't change other values when used\n", " with the binary function.\n", "\n", "There's another thing that these two operations have in common that may\n", "not be as obvious as our previous observations: when we have three or\n", "more values and we want to use the binary function to reduce them to a\n", "single result, the order in which we apply the binary function to the\n", "values doesn't matter. It doesn't matter if we do `(3 * 4) * 5` or\n", "`3 * (4 * 5)`. Either way, the result is `60`. The same goes for [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-):" ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "240" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(3 * 2) * (8 * 5)" ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "240" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "3 * (2 * (8 * 5))" ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"ladida\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"la\" ++ (\"di\" ++ \"da\")" ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"ladida\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\"la\" ++ \"di\") ++ \"da\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We call this property *associativity*. [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) is associative, and so is [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-),\n", "but [`-`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-45-), for example, is not. The expressions `(5 - 3) - 4` and `5 - (3 - 4)`\n", "result in different numbers.\n", "\n", "By noticing and writing down these properties, we have chanced upon\n", "*monoids*! A monoid is when you have an associative binary function and\n", "a value which acts as an identity with respect to that function. When\n", "something acts as an identity with respect to a function, it means that\n", "when called with that function and some other value, the result is\n", "always equal to that other value. `1` is the identity with respect to [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-)\n", "and `[]` is the identity with respect to [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-). There are a lot of other\n", "monoids to be found in the world of Haskell, which is why the [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid)\n", "type class exists. It's for types which can act like monoids. Let's see\n", "how the type class is defined:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class Monoid m where\n",
    "    mempty :: m\n",
    "    mappend :: m -> m -> m\n",
    "    mconcat :: [m] -> m\n",
    "    mconcat = foldr mappend mempty
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "The [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) type class is defined in `import Data.Monoid`. Let's take some\n", "time and get properly acquainted with it.\n", "\n", "First of all, we see that only concrete types can be made instances of\n", "[`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid), because the `m` in the type class definition doesn't take any type\n", "parameters. This is different from [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) and [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative), which\n", "require their instances to be type constructors which take one\n", "parameter.\n", "\n", "The first function is [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty). It's not really a function, since it\n", "doesn't take parameters, so it's a polymorphic constant, kind of like\n", "[`minBound`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:minBound) from [`Bounded`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bounded). [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty) represents the identity value for a\n", "particular monoid.\n", "\n", "Next up, we have [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend), which, as you've probably guessed, is the\n", "binary function. It takes two values of the same type and returns a\n", "value of that type as well. It's worth noting that the decision to name\n", "[`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) as it's named was kind of unfortunate, because it implies that\n", "we're appending two things in some way. While [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) does take two lists and\n", "append one to the other, [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) doesn't really do any appending, it just\n", "multiplies two numbers together. When we meet other instances of [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid),\n", "we'll see that most of them don't append values either, so avoid\n", "thinking in terms of appending and just think in terms of [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) being\n", "a binary function that takes two monoid values and returns a third.\n", "\n", "The last function in this type class definition is [`mconcat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mconcat). It takes a\n", "list of monoid values and reduces them to a single value by doing\n", "[`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) between the list's elements. It has a default implementation,\n", "which just takes [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty) as a starting value and folds the list from the\n", "right with [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend). Because the default implementation is fine for most\n", "instances, we won't concern ourselves with [`mconcat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mconcat) too much from now on.\n", "When making a type an instance of [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid), it suffices to just implement\n", "[`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty) and [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend). The reason [`mconcat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mconcat) is there at all is because for\n", "some instances, there might be a more efficient way to implement\n", "[`mconcat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mconcat), but for most instances the default implementation is just fine.\n", "\n", "Before moving on to specific instances of [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid), let's take a brief\n", "look at the monoid laws. We mentioned that there has to be a value that\n", "acts as the identity with respect to the binary function and that the\n", "binary function has to be associative. It's possible to make instances\n", "of [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) that don't follow these rules, but such instances are of no\n", "use to anyone because when using the [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) type class, we rely on its\n", "instances acting like monoids. Otherwise, what's the point? That's why\n", "when making instances, we have to make sure they follow these laws:\n", "\n", "- **``mempty `mappend` x = x``**\n", "- **``x `mappend` mempty = x``**\n", "- **``(x `mappend` y) `mappend` z = x `mappend` (y `mappend` z)``**\n", "\n", "The first two state that [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty) has to act as the identity with respect\n", "to [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) and the third says that [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) has to be associative i.e.\n", "that it the order in which we use [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) to reduce several monoid\n", "values into one doesn't matter. Haskell doesn't enforce these laws, so\n", "we as the programmer have to be careful that our instances do indeed\n", "obey them.\n", "\n", "### Lists are monoids\n", "\n", "Yes, lists are monoids! Like we've seen, the [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) function and the empty\n", "list `[]` form a monoid. The instance is very simple:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monoid [a] where\n",
    "    mempty = []\n",
    "    mappend = (++)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lists are an instance of the [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) type class regardless of the type of\n", "the elements they hold. Notice that we wrote `instance Monoid [a]` and not\n", "`instance Monoid []`, because [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) requires a concrete type for an\n", "instance.\n", "\n", "Giving this a test run, we encounter no surprises:" ] }, { "cell_type": "code", "execution_count": 125, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1,2,3] `mappend` [4,5,6]" ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"onetwotree\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\"one\" `mappend` \"two\") `mappend` \"tree\"" ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"onetwotree\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"one\" `mappend` (\"two\" `mappend` \"tree\")" ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"onetwotree\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"one\" `mappend` \"two\" `mappend` \"tree\"" ] }, { "cell_type": "code", "execution_count": 129, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"pang\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"pang\" `mappend` mempty" ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,6,9]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mconcat [[1,2],[3,6],[9]]" ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(mempty :: [a])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Notice that in the last line, we had to write an explicit type\n", "annotation, because if we just did [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty), GHCi wouldn't know which\n", "instance to use, so we had to say we want the list instance. We were\n", "able to use the general type of `[a]` (as opposed to specifying `[Int]` or\n", "`[String]`) because the empty list can act as if it contains any type.\n", "\n", "Because [`mconcat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mconcat) has a default implementation, we get it for free when we\n", "make something an instance of [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid). In the case of the list, [`mconcat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mconcat)\n", "turns out to be just [`concat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:concat). It takes a list of lists and flattens it,\n", "because that's the equivalent of doing [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) between all the adjacent lists\n", "in a list.\n", "\n", "The monoid laws do indeed hold for the list instance. When we have\n", "several lists and we [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) (or [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-)) them together, it doesn't matter\n", "which ones we do first, because they're just joined at the ends anyway.\n", "Also, the empty list acts as the identity so all is well. Notice that\n", "monoids don't require that ``a `mappend` b`` be equal to ``b `mappend` a``.\n", "In the case of the list, they clearly aren't:" ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"onetwo\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"one\" `mappend` \"two\"" ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"twoone\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\"two\" `mappend` \"one\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And that's okay. The fact that for multiplication `3 * 5` and `5 * 3` are\n", "the same is just a property of multiplication, but it doesn't hold for\n", "all (and indeed, most) monoids.\n", "\n", "### [`Product`](https://hackage.haskell.org/package/base/docs/Data-Functor-Product.html) and [`Sum`](https://hackage.haskell.org/package/base/docs/Data-Functor-Sum.html)\n", "\n", "We already examined one way for numbers to be considered monoids. Just\n", "have the binary function be [`*`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-42-) and the identity value `1`. It turns out\n", "that that's not the only way for numbers to be monoids. Another way is\n", "to have the binary function be [`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-) and the identity value `0`:" ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "0 + 4" ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "5 + 0" ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "9" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(1 + 3) + 5" ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "9" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "1 + (3 + 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The monoid laws hold, because if you add 0 to any number, the result is\n", "that number. And addition is also associative, so we get no problems\n", "there. So now that there are two equally valid ways for numbers to be\n", "monoids, which way do choose? Well, we don't have to. Remember, when\n", "there are several ways for some type to be an instance of the same type\n", "class, we can wrap that type in a *newtype* and then make the new type\n", "an instance of the type class in a different way. We can have our cake\n", "and eat it too." ] }, { "cell_type": "code", "execution_count": 138, "metadata": {}, "outputs": [], "source": [ "import Data.Monoid" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `Data.Monoid` module exports two types for this, namely [`Product`](https://hackage.haskell.org/package/base/docs/Data-Functor-Product.html) and\n", "[`Sum`](https://hackage.haskell.org/package/base/docs/Data-Functor-Sum.html). [`Product`](https://hackage.haskell.org/package/base/docs/Data-Functor-Product.html) is defined like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
newtype Product a =  Product { getProduct :: a }\n",
    "    deriving (Eq, Ord, Read, Show, Bounded)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Simple, just a *newtype* wrapper with one type parameter along with some\n", "derived instances. Its instance for [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) goes a little something like\n", "this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Num a => Monoid (Product a) where\n",
    "    mempty = Product 1\n",
    "    Product x `mappend` Product y = Product (x * y)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty) is just `1` wrapped in a [`Product`](https://hackage.haskell.org/package/base/docs/Data-Functor-Product.html) constructor. [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) pattern\n", "matches on the [`Product`](https://hackage.haskell.org/package/base/docs/Data-Functor-Product.html) constructor, multiplies the two numbers and then\n", "wraps the resulting number back. As you can see, there's a `Num a` class\n", "constraint. So this means that `Product a` is an instance of [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) for\n", "all `a`'s that are already an instance of [`Num`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Num). To use `Product a` as a\n", "monoid, we have to do some *newtype* wrapping and unwrapping:" ] }, { "cell_type": "code", "execution_count": 139, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "27" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getProduct $ Product 3 `mappend` Product 9" ] }, { "cell_type": "code", "execution_count": 140, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getProduct $ Product 3 `mappend` mempty" ] }, { "cell_type": "code", "execution_count": 141, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "24" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getProduct $ Product 3 `mappend` Product 4 `mappend` Product 2" ] }, { "cell_type": "code", "execution_count": 142, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "24" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getProduct . mconcat . map Product $ [3,4,2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is nice as a showcase of the [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) type class, but no one in their\n", "right mind would use this way of multiplying numbers instead of just\n", "writing `3 * 9` and `3 * 1`. But a bit later, we'll see how these [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid)\n", "instances that may seem trivial at this time can come in handy.\n", "\n", "[`Sum`](https://hackage.haskell.org/package/base/docs/Data-Functor-Sum.html) is defined like [`Product`](https://hackage.haskell.org/package/base/docs/Data-Functor-Product.html) and the instance is similar as well. We use\n", "it in the same way:" ] }, { "cell_type": "code", "execution_count": 143, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "11" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getSum $ Sum 2 `mappend` Sum 9" ] }, { "cell_type": "code", "execution_count": 144, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getSum $ mempty `mappend` Sum 3" ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "6" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getSum . mconcat . map Sum $ [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [`Any`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Any) and [`All`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:All)\n", "\n", "Another type which can act like a monoid in two distinct but equally\n", "valid ways is [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool). The first way is to have the *or* function [`||`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-124--124-) act as\n", "the binary function along with [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) as the identity value. The way *or*\n", "works in logic is that if any of its two parameters is [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), it returns\n", "[`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), otherwise it returns [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False). So if we use [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) as the identity\n", "value, it will return [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) when *or*-ed with [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) and [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) when\n", "*or*-ed with [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True). The [`Any`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Any) *newtype* constructor is an instance of\n", "[`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) in this fashion. It's defined like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
newtype Any = Any { getAny :: Bool }\n",
    "    deriving (Eq, Ord, Read, Show, Bounded)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Its instance looks goes like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monoid Any where\n",
    "        mempty = Any False\n",
    "        Any x `mappend` Any y = Any (x || y)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The reason it's called [`Any`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Any) is because ``x `mappend` y`` will be [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) if\n", "*any* one of those two is [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True). Even if three or more [`Any`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Any) wrapped [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool)s\n", "are [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend)ed together, the result will hold [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) if any of them are\n", "[`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True):" ] }, { "cell_type": "code", "execution_count": 146, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getAny $ Any True `mappend` Any False" ] }, { "cell_type": "code", "execution_count": 147, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getAny $ mempty `mappend` Any True" ] }, { "cell_type": "code", "execution_count": 148, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getAny . mconcat . map Any $ [False, False, False, True]" ] }, { "cell_type": "code", "execution_count": 149, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getAny $ mempty `mappend` mempty" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The other way for [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) to be an instance of [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) is to kind of do the\n", "opposite: have [`&&`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-38--38-) be the binary function and then make [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) the identity\n", "value. Logical *and* will return [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) only if both of its parameters are\n", "[`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True). This is the *newtype* declaration, nothing fancy:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
newtype All = All { getAll :: Bool }\n",
    "        deriving (Eq, Ord, Read, Show, Bounded)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And this is the instance:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monoid All where\n",
    "        mempty = All True\n",
    "        All x `mappend` All y = All (x && y)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) values of the [`All`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:All) type, the result will be [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) only if\n", "*all* the values used in the [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) operations are [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True):" ] }, { "cell_type": "code", "execution_count": 150, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getAll $ mempty `mappend` All True" ] }, { "cell_type": "code", "execution_count": 151, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getAll $ mempty `mappend` All False" ] }, { "cell_type": "code", "execution_count": 152, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getAll . mconcat . map All $ [True, True, True]" ] }, { "cell_type": "code", "execution_count": 153, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getAll . mconcat . map All $ [True, True, False]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just like with multiplication and addition, we usually explicitly state\n", "the binary functions instead of wrapping them in *newtype*s and then\n", "using [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) and [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty). [`mconcat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mconcat) seems useful for [`Any`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Any) and [`All`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:All), but\n", "usually it's easier to use the [`or`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:or) and [`and`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:and) functions, which take lists of\n", "[`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool)s and return [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) if any of them are [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) or if all of them are\n", "[`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), respectively.\n", "\n", "### The [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering) monoid\n", "\n", "Hey, remember the [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering) type? It's used as the result when comparing\n", "things and it can have three values: [`LT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:LT), [`EQ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:EQ) and [`GT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:GT), which stand for\n", "*less than*, *equal* and *greater than* respectively:" ] }, { "cell_type": "code", "execution_count": 154, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "1 `compare` 2" ] }, { "cell_type": "code", "execution_count": 155, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "EQ" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "2 `compare` 2" ] }, { "cell_type": "code", "execution_count": 156, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "3 `compare` 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With lists, numbers and boolean values, finding monoids was just a\n", "matter of looking at already existing commonly used functions and seeing\n", "if they exhibit some sort of monoid behavior. With [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering), we have to\n", "look a bit harder to recognize a monoid, but it turns out that its\n", "[`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) instance is just as intuitive as the ones we've met so far and\n", "also quite useful:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monoid Ordering where\n",
    "    mempty = EQ\n",
    "    LT `mappend` _ = LT\n",
    "    EQ `mappend` y = y\n",
    "    GT `mappend` _ = GT
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "The instance is set up like this: when we [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) two [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering) values,\n", "the one on the left is kept, unless the value on the left is [`EQ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:EQ), in\n", "which case the right one is the result. The identity is [`EQ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:EQ). At first,\n", "this may seem kind of arbitrary, but it actually resembles the way we\n", "alphabetically compare words. We compare the first two letters and if\n", "they differ, we can already decide which word would go first in a\n", "dictionary. However, if the first two letters are equal, then we move on\n", "to comparing the next pair of letters and repeat the process.\n", "\n", "For instance, if we were to alphabetically compare the words `\"ox\"` and\n", "`\"on\"`, we'd first compare the first two letters of each word, see that\n", "they are equal and then move on to comparing the second letter of each\n", "word. We see that `'x'` is alphabetically greater than `'n'`, and so we know\n", "how the words compare. To gain some intuition for [`EQ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:EQ) being the identity,\n", "we can notice that if we were to cram the same letter in the same\n", "position in both words, it wouldn't change their alphabetical ordering.\n", "`\"oix\"` is still alphabetically greater than and `\"oin\"`.\n", "\n", "It's important to note that in the [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) instance for [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering),\n", "``x `mappend` y`` doesn't equal ``y `mappend` x``. Because the first parameter\n", "is kept unless it's [`EQ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:EQ), ``LT `mappend` GT`` will result in [`LT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:LT), whereas\n", "``GT `mappend` LT`` will result in [`GT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:GT):" ] }, { "cell_type": "code", "execution_count": 157, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "LT `mappend` GT" ] }, { "cell_type": "code", "execution_count": 158, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "GT `mappend` LT" ] }, { "cell_type": "code", "execution_count": 159, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mempty `mappend` LT" ] }, { "cell_type": "code", "execution_count": 160, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mempty `mappend` GT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "OK, so how is this monoid useful? Let's say you were writing a function\n", "that takes two strings, compares their lengths, and returns an [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering).\n", "But if the strings are of the same length, then instead of returning [`EQ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:EQ)\n", "right away, we want to compare them alphabetically. One way to write\n", "this would be like so:" ] }, { "cell_type": "code", "execution_count": 161, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "lengthCompare :: String -> String -> Ordering\n", "lengthCompare x y = let a = length x `compare` length y\n", " b = x `compare` y\n", " in if a == EQ then b else a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We name the result of comparing the lengths `a` and the result of the\n", "alphabetical comparison `b` and then if it turns out that the lengths were\n", "equal, we return their alphabetical ordering.\n", "\n", "But by employing our understanding of how [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering) is a monoid, we can\n", "rewrite this function in a much simpler manner:" ] }, { "cell_type": "code", "execution_count": 162, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Monoid\n", "\n", "lengthCompare :: String -> String -> Ordering\n", "lengthCompare x y = (length x `compare` length y) `mappend`\n", " (x `compare` y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can try this out:" ] }, { "cell_type": "code", "execution_count": 163, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lengthCompare \"zen\" \"ants\"" ] }, { "cell_type": "code", "execution_count": 164, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lengthCompare \"zen\" \"ant\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, when we use [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend), its left parameter is always kept unless\n", "it's [`EQ`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:EQ), in which case the right one is kept. That's why we put the\n", "comparison that we consider to be the first, more important criterion as\n", "the first parameter. If we wanted to expand this function to also\n", "compare for the number of vowels and set this to be the second most\n", "important criterion for comparison, we'd just modify it like this:" ] }, { "cell_type": "code", "execution_count": 165, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Monoid\n", "\n", "lengthCompare :: String -> String -> Ordering\n", "lengthCompare x y = (length x `compare` length y) `mappend`\n", " (vowels x `compare` vowels y) `mappend`\n", " (x `compare` y)\n", " where vowels = length . filter (`elem` \"aeiou\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We made a helper function, which takes a string and tells us how many\n", "vowels it has by first filtering it only for letters that are in the\n", "string `\"aeiou\"` and then applying [`length`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:length) to that." ] }, { "cell_type": "code", "execution_count": 166, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lengthCompare \"zen\" \"anna\"" ] }, { "cell_type": "code", "execution_count": 167, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lengthCompare \"zen\" \"ana\"" ] }, { "cell_type": "code", "execution_count": 168, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "GT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lengthCompare \"zen\" \"ann\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Very cool. Here, we see how in the first example the lengths are found\n", "to be different and so [`LT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:LT) is returned, because the length of `\"zen\"` is\n", "less than the length of `\"anna\"`. In the second example, the lengths are\n", "the same, but the second string has more vowels, so [`LT`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:LT) is returned\n", "again. In the third example, they both have the same length and the same\n", "number of vowels, so they're compared alphabetically and `\"zen\"` wins.\n", "\n", "The [`Ordering`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ordering) monoid is very cool because it allows us to easily compare\n", "things by many different criteria and put those criteria in an order\n", "themselves, ranging from the most important to the least.\n", "\n", "### [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) the monoid\n", "\n", "Let's take a look at the various ways that `Maybe a` can be made an\n", "instance of [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) and what those instances are useful for.\n", "\n", "One way is to treat `Maybe a` as a monoid only if its type parameter `a` is\n", "a monoid as well and then implement [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) in such a way that it uses\n", "the [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) operation of the values that are wrapped with [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just). We use\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) as the identity, and so if one of the two values that we're\n", "[`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend)ing is [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), we keep the other value. Here's the instance\n", "declaration:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monoid a => Monoid (Maybe a) where\n",
    "    mempty = Nothing\n",
    "    Nothing `mappend` m = m\n",
    "    m `mappend` Nothing = m\n",
    "    Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice the class constraint. It says that `Maybe a` is an instance of\n", "[`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) only if `a` is an instance of [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid). If we [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) something with\n", "a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), the result is that something. If we [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) two [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) values,\n", "the contents of the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just)s get `mappended` and then wrapped back in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just).\n", "We can do this because the class constraint ensures that the type of\n", "what's inside the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) is an instance of [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid)." ] }, { "cell_type": "code", "execution_count": 169, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"andy\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing `mappend` Just \"andy\"" ] }, { "cell_type": "code", "execution_count": 170, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just LT" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just LT `mappend` Nothing" ] }, { "cell_type": "code", "execution_count": 171, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (Sum {getSum = 7})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just (Sum 3) `mappend` Just (Sum 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This comes in use when you're dealing with monoids as results of\n", "computations that may have failed. Because of this instance, we don't\n", "have to check if the computations have failed by seeing if they're a\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) or [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value; we can just continue to treat them as normal\n", "monoids.\n", "\n", "But what if the type of the contents of the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) aren't an instance of\n", "[`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid)? Notice that in the previous instance declaration, the only case\n", "where we have to rely on the contents being monoids is when both\n", "parameters of mappend are Just values. But if we don't know if the\n", "contents are monoids, we can't use mappend between them, so what are we\n", "to do? Well, one thing we can do is to just discard the second value and\n", "keep the first one. For this, the First a type exists and this is its\n", "definition:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
newtype First a = First { getFirst :: Maybe a }\n",
    "    deriving (Eq, Ord, Read, Show)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We take a Maybe a and we wrap it with a *newtype*. The Monoid instance\n", "is as follows:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monoid (First a) where\n",
    "    mempty = First Nothing\n",
    "    First (Just x) `mappend` _ = First (Just x)\n",
    "    First Nothing `mappend` x = x
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just like we said. mempty is just a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) wrapped with the [`First`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:First)\n", "*newtype* constructor. If [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend)'s first parameter is a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value, we\n", "ignore the second one. If the first one is a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), then we present\n", "the second parameter as a result, regardless of whether it's a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) or a\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing):" ] }, { "cell_type": "code", "execution_count": 172, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 'a'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getFirst $ First (Just 'a') `mappend` First (Just 'b')" ] }, { "cell_type": "code", "execution_count": 173, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 'b'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getFirst $ First Nothing `mappend` First (Just 'b')" ] }, { "cell_type": "code", "execution_count": 174, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 'a'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getFirst $ First (Just 'a') `mappend` First Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[`First`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:First) is useful when we have a bunch of [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values and we just want to\n", "know if any of them is a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just). The [`mconcat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mconcat) function comes in handy:" ] }, { "cell_type": "code", "execution_count": 175, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 9" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getFirst . mconcat . map First $ [Nothing, Just 9, Just 10]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want a monoid on `Maybe a` such that the second parameter is kept if\n", "both parameters of [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) are [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) values, `Data.Monoid` provides a the\n", "`Last a` type, which works like `First a`, only the last non-[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) value\n", "is kept when [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend)ing and using [`mconcat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mconcat):" ] }, { "cell_type": "code", "execution_count": 176, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 10" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getLast . mconcat . map Last $ [Nothing, Just 9, Just 10]" ] }, { "cell_type": "code", "execution_count": 177, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"two\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getLast $ Last (Just \"one\") `mappend` Last (Just \"two\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using monoids to fold data structures\n", "\n", "One of the more interesting ways to put monoids to work is to make them\n", "help us define folds over various data structures. So far, we've only\n", "done folds over lists, but lists aren't the only data structure that can\n", "be folded over. We can define folds over almost any data structure.\n", "Trees especially lend themselves well to folding.\n", "\n", "Because there are so many data structures that work nicely with folds,\n", "the __[`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable)__ type class was introduced. Much like [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) is for things\n", "that can be mapped over, [`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable) is for things that can be folded up!\n", "It can be found in `Data.Foldable` and because it exports functions whose\n", "names clash with the ones from the [`Prelude`](https://hackage.haskell.org/package/base/docs/Prelude.html), it's best imported qualified\n", "(and served with basil):" ] }, { "cell_type": "code", "execution_count": 178, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import qualified Data.Foldable as F" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To save ourselves precious keystrokes, we've chosen to import it\n", "qualified as `F`. Alright, so what are some of the functions that this\n", "type class defines? Well, among them are [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr), [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl), `foldr1` and\n", "`foldl1`. Huh? But we already know these functions, what's so new about\n", "this? Let's compare the types of [`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable)'s [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr) and the [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr) from the\n", "[`Prelude`](https://hackage.haskell.org/package/base/docs/Prelude.html) to see how they differ:" ] }, { "cell_type": "code", "execution_count": 179, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "foldr :: forall (t :: * -> *) a b. Foldable t => (a -> b -> b) -> b -> t a -> b" ], "text/plain": [ "foldr :: forall (t :: * -> *) a b. Foldable t => (a -> b -> b) -> b -> t a -> b" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t foldr" ] }, { "cell_type": "code", "execution_count": 180, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "F.foldr :: forall (t :: * -> *) a b. Foldable t => (a -> b -> b) -> b -> t a -> b" ], "text/plain": [ "F.foldr :: forall (t :: * -> *) a b. Foldable t => (a -> b -> b) -> b -> t a -> b" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t F.foldr" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah! So whereas [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr) takes a list and folds it up, the [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr) from\n", "`Data.Foldable` accepts any type that can be folded up, not just lists! As\n", "expected, both [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr) functions do the same for lists:" ] }, { "cell_type": "code", "execution_count": 181, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "6" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldr (*) 1 [1,2,3]" ] }, { "cell_type": "code", "execution_count": 182, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "6" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "F.foldr (*) 1 [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Okay then, what are some other data structures that support folds? Well,\n", "there's the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) we all know and love!" ] }, { "cell_type": "code", "execution_count": 183, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "11" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "F.foldl (+) 2 (Just 9)" ] }, { "cell_type": "code", "execution_count": 184, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "F.foldr (||) False (Just True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But folding over a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value isn't terribly interesting, because when\n", "it comes to folding, it just acts like a list with one element if it's a\n", "[`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value and as an empty list if it's [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). So let's examine a data\n", "structure that's a little more complex then.\n", "\n", "Remember the tree data structure from the [Making Our Own Types and\n", "Typeclasses](http://learnyouahaskell.com/making-our-own-types-and-typeclasses#recursive-data-structures)\n", "chapter? We defined it like this:" ] }, { "cell_type": "code", "execution_count": 185, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We said that a tree is either an empty tree that doesn't hold any values\n", "or it's a node that holds one value and also two other trees. After\n", "defining it, we made it an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) and with that we gained\n", "the ability to [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) functions over it. Now, we're going to make it an\n", "instance of [`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable) so that we get the ability to fold it up. One way to\n", "make a type constructor an instance of [`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable) is to just directly\n", "implement [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr) for it. But another, often much easier way, is to\n", "implement the [`foldMap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldMap) function, which is also a part of the [`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable)\n", "type class. The [`foldMap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldMap) function has the following type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Its first parameter is a function that takes a value of the type that\n", "our foldable structure contains (denoted here with `a`) and returns a\n", "monoid value. Its second parameter is a foldable structure that contains\n", "values of type `a`. It maps that function over the foldable structure,\n", "thus producing a foldable structure that contains monoid values. Then,\n", "by doing [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) between those monoid values, it joins them all into a\n", "single monoid value. This function may sound kind of odd at the moment,\n", "but we'll see that it's very easy to implement. What's also cool is that\n", "implementing this function is all it takes for our type to be made an\n", "instance of [`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable). So if we just implement [`foldMap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldMap) for some type, we\n", "get [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr) and [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl) on that type for free!\n", "\n", "This is how we make `Tree` an instance of [`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable):" ] }, { "cell_type": "code", "execution_count": 186, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance F.Foldable Tree where\n", " foldMap f Empty = mempty\n", " foldMap f (Node x l r) = F.foldMap f l `mappend`\n", " f x `mappend`\n", " F.foldMap f r" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "We think like this: if we are provided with a function that takes an\n", "element of our tree and returns a monoid value, how do we reduce our\n", "whole tree down to one single monoid value? When we were doing [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) over\n", "our tree, we applied the function that we were mapping to a node and\n", "then we recursively mapped the function over the left sub-tree as well\n", "as the right one. Here, we're tasked with not only mapping a function,\n", "but with also joining up the results into a single monoid value by using\n", "[`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend). First we consider the case of the empty tree — a sad and lonely\n", "tree that has no values or sub-trees. It doesn't hold any value that we\n", "can give to our monoid-making function, so we just say that if our tree\n", "is empty, the monoid value it becomes is [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty).\n", "\n", "The case of a non-empty node is a bit more interesting. It contains two\n", "sub-trees as well as a value. In this case, we recursively [`foldMap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldMap) the\n", "same function `f` over the left and the right sub-trees. Remember, our\n", "[`foldMap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldMap) results in a single monoid value. We also apply our function `f`\n", "to the value in the node. Now we have three monoid values (two from our\n", "sub-trees and one from applying `f` to the value in the node) and we just\n", "have to bang them together into a single value. For this purpose we use\n", "[`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend), and naturally the left sub-tree comes first, then the node\n", "value and then the right sub-tree.\n", "\n", "Notice that we didn't have to provide the function that takes a value\n", "and returns a monoid value. We receive that function as a parameter to\n", "[`foldMap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldMap) and all we have to decide is where to apply that function and\n", "how to join up the resulting monoids from it.\n", "\n", "Now that we have a [`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable) instance for our tree type, we get [`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr) and\n", "[`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl) for free! Consider this tree:" ] }, { "cell_type": "code", "execution_count": 187, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "testTree = Node 5\n", " (Node 3\n", " (Node 1 Empty Empty)\n", " (Node 6 Empty Empty)\n", " )\n", " (Node 9\n", " (Node 8 Empty Empty)\n", " (Node 10 Empty Empty)\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It has `5` at its root and then its left node is has `3` with `1` on the left\n", "and `6` on the right. The root's right node has a `9` and then an `8` to its\n", "left and a `10` on the far right side. With a [`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable) instance, we can do\n", "all of the folds that we can do on lists:" ] }, { "cell_type": "code", "execution_count": 188, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "42" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "F.foldl (+) 0 testTree" ] }, { "cell_type": "code", "execution_count": 189, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "64800" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "F.foldl (*) 1 testTree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And also, [`foldMap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldMap) isn't only useful for making new instances of\n", "[`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable); it comes in handy for reducing our structure to a single\n", "monoid value. For instance, if we want to know if any number in our tree\n", "is equal to `3`, we can do this:" ] }, { "cell_type": "code", "execution_count": 190, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getAny $ F.foldMap (\\x -> Any $ x == 3) testTree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, `\\x -> Any $ x == 3` is a function that takes a number and\n", "returns a monoid value, namely a [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) wrapped in [`Any`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Any). [`foldMap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldMap) applies\n", "this function to every element in our tree and then reduces the\n", "resulting monoids into a single monoid with [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend). If we do this:" ] }, { "cell_type": "code", "execution_count": 191, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getAny $ F.foldMap (\\x -> Any $ x > 15) testTree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All of the nodes in our tree would hold the value `Any False` after having\n", "the function in the lambda applied to them. But to end up [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend)\n", "for [`Any`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Any) has to have at least one [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) value as a parameter. That's why\n", "the final result is [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False), which makes sense because no value in our\n", "tree is greater than `15`.\n", "\n", "We can also easily turn our tree into a list by doing a [`foldMap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldMap) with the\n", "`\\x -> [x]` function. By first projecting that function onto our tree,\n", "each element becomes a singleton list. The [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) action that takes\n", "place between all those singleton list results in a single list that\n", "holds all of the elements that are in our tree:" ] }, { "cell_type": "code", "execution_count": 192, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,3,6,5,8,9,10]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "F.foldMap (\\x -> [x]) testTree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What's cool is that all of these trick aren't limited to trees, they\n", "work on any instance of [`Foldable`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Foldable)." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.8.2" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/12-a-fistful-of-monads.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "A Fistful of Monads\n", "===================\n", "\n", "When we first talked about functors, we saw that they were a useful\n", "concept for values that can be mapped over. Then, we took that concept\n", "one step further by introducing applicative functors, which allow us to\n", "view values of certain data types as values with contexts and use normal\n", "functions on those values while preserving the meaning of those\n", "contexts." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this chapter, we'll learn about monads, which are just beefed up\n", "applicative functors, much like applicative functors are only beefed up\n", "functors." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "When we started off with functors, we saw that it's possible to map\n", "functions over various data types. We saw that for this purpose, the\n", "[`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) type class was introduced and it had us asking the question:\n", "when we have a function of type `a -> b` and some data type `f a`, how do\n", "we map that function over the data type to end up with `f b`? We saw how\n", "to map something over a `Maybe a`, a list `[a]`, an `IO a` etc. We even saw\n", "how to map a function `a -> b` over other functions of type `r -> a` to\n", "get functions of type `r -> b`. To answer this question of how to map a\n", "function over some data type, all we had to do was look at the type of\n", "[`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap):" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
fmap :: (Functor f) => (a -> b) -> f a -> f b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then make it work for our data type by writing the appropriate\n", "[`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) instance.\n", "\n", "Then we saw a possible improvement of functors and said, hey, what if\n", "that function `a -> b` is already wrapped inside a functor value? Like,\n", "what if we have `Just (*3)`, how do we apply that to `Just 5`? What if we\n", "don't want to apply it to `Just 5` but to a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) instead? Or if we have\n", "`[(*2),(+4)]`, how would we apply that to `[1,2,3]`? How would that work\n", "even? For this, the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) type class was introduced, in which we\n", "wanted the answer to the following type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We also saw that we can take a normal value and wrap it inside a data\n", "type. For instance, we can take a `1` and wrap it so that it becomes a\n", "`Just 1`. Or we can make it into a `[1]`. Or an I/O action that does nothing\n", "and just yields `1`. The function that does this is called [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure).\n", "\n", "Like we said, an applicative value can be seen as a value with an added\n", "context. A *fancy* value, to put it in technical terms. For instance,\n", "the character `'a'` is just a normal character, whereas `Just 'a'` has some\n", "added context. Instead of a [`Char`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Char), we have a `Maybe Char`, which tells us\n", "that its value might be a character, but it could also be an absence of\n", "a character.\n", "\n", "It was neat to see how the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) type class allowed us to use\n", "normal functions on these values with context and how that context was\n", "preserved. Observe:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 16" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(*) <$> Just 2 <*> Just 8" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(++) <$> Just \"klingon\" <*> Nothing" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[2,1,0,3,2,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(-) <$> [3,4] <*> [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah, cool, so now that we treat them as applicative values, `Maybe a`\n", "values represent computations that might have failed, `[a]` values\n", "represent computations that have several results (non-deterministic\n", "computations), `IO a` values represent values that have side-effects, etc.\n", "\n", "Monads are a natural extension of applicative functors and with them\n", "we're concerned with this: if you have a value with a context, `m a`, how\n", "do you apply to it a function that takes a normal `a` and returns a value\n", "with a context? That is, how do you apply a function of type `a -> m b`\n", "to a value of type `m a`? So essentially, we will want this function:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**If we have a fancy value and a function that takes a normal value but\n", "returns a fancy value, how do we feed that fancy value into the\n", "function?** This is the main question that we will concern ourselves\n", "when dealing with monads. We write `m a` instead of `f a` because the `m`\n", "stands for [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad), but monads are just applicative functors that support\n", "[`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-). The [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) function is pronounced as *bind*.\n", "\n", "When we have `a` normal value a and a normal function `a -> b` it's really\n", "easy to feed the value to the function — you just apply the function to\n", "the value normally and that's it. But when we're dealing with values\n", "that come with certain contexts, it takes a bit of thinking to see how\n", "these fancy values are fed to functions and how to take into account\n", "their behavior, but you'll see that it's easy as one two three.\n", "\n", "Getting our feet wet with Maybe\n", "-------------------------------\n", "\n", "\n", "\n", "Now that we have a vague idea of what monads are about, let's see if we\n", "can make that idea a bit less vague.\n", "\n", "Much to no one's surprise, [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) is a monad, so let's explore it a bit\n", "more and see if we can combine it with what we know about monads.\n", "\n", "> Make sure you understand\n", "> [applicatives](http://learnyouahaskell.com/functors-applicative-functors-and-monoids#applicative-functors)\n", "> at this point. It's good if you have a feel for how the various\n", "> [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) instances work and what kind of computations they represent,\n", "> because monads are nothing more than taking our existing applicative\n", "> knowledge and upgrading it.\n", "\n", "A value of type `Maybe a` represents a value of type `a` with the context of\n", "possible failure attached. A value of `Just \"dharma\"` means that the\n", "string `\"dharma\"` is there whereas a value of [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) represents its\n", "absence, or if you look at the string as the result of a computation, it\n", "means that the computation has failed.\n", "\n", "When we looked at [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) as a functor, we saw that if we want to [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) a\n", "function over it, it gets mapped over the insides if it's a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value,\n", "otherwise the [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) is kept because there's nothing to map it over!\n", "\n", "Like this:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"wisdom!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (++\"!\") (Just \"wisdom\")" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (++\"!\") Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As an applicative functor, it functions similarly. However, applicatives\n", "also have the function wrapped. [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) is an applicative functor in such\n", "a way that when we use [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) to apply a function inside a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) to a\n", "value that's inside a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), they both have to be [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) values for the\n", "result to be a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value, otherwise the result is [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). It makes\n", "sense because if you're missing either the function or the thing you're\n", "applying it to, you can't make something up out of thin air, so you have\n", "to propagate the failure:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 6" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just (+3) <*> Just 3" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing <*> Just \"greed\"" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Char" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just ord <*> Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we use the applicative style to have normal functions act on [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)\n", "values, it's similar. All the values have to be [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) values, otherwise\n", "it's all for [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing)!" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 6" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "max <$> Just 3 <*> Just 6" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "max <$> Just 3 <*> Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And now, let's think about how we would do [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe). Like we\n", "said, [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) takes a monadic value, and a function that takes a normal\n", "value and returns a monadic value and manages to apply that function to\n", "the monadic value. How does it do that, if the function takes a normal\n", "value? Well, to do that, it has to take into account the context of that\n", "monadic value.\n", "\n", "In this case, [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) would take a `Maybe a` value and a function of type\n", "`a -> Maybe b` and somehow apply the function to the `Maybe a`. To figure out\n", "how it does that, we can use the intuition that we have from [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) being\n", "an applicative functor. Let's say that we have a function `\\x -> Just (x+1)`.\n", "It takes a number, adds `1` to it and wraps it in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just):" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 2" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\\x -> Just (x+1)) 1" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 101" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\\x -> Just (x+1)) 100" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we feed it `1`, it evaluates to `Just 2`. If we give it the number `100`,\n", "the result is `Just 101`. Very straightforward. Now here's the kicker: how\n", "do we feed a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value to this function? If we think about how [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)\n", "acts as an applicative functor, answering this is pretty easy. If we\n", "feed it a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value, take what's inside the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) and apply the function\n", "to it. If give it a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), hmm, well, then we're left with a function\n", "but [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) to apply it to. In that case, let's just do what we did\n", "before and say that the result is [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "\n", "Instead of calling it [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), let's call it `applyMaybe` for now. It takes\n", "a `Maybe a` and a function that returns a `Maybe b` and manages to apply\n", "that function to the `Maybe a`. Here it is in code:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "applyMaybe :: Maybe a -> (a -> Maybe b) -> Maybe b\n", "applyMaybe Nothing f = Nothing\n", "applyMaybe (Just x) f = f x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Okay, now let's play with it for a bit. We'll use it as an infix\n", "function so that the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value is on the left side and the function on\n", "the right:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 4" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 3 `applyMaybe` \\x -> Just (x+1)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"smile :)\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just \"smile\" `applyMaybe` \\x -> Just (x ++ \" :)\")" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing `applyMaybe` \\x -> Just (x+1)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing `applyMaybe` \\x -> Just (x ++ \" :)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the above example, we see that when we used `applyMaybe` with a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just)\n", "value and a function, the function simply got applied to the value\n", "inside the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just). When we tried to use it with a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), the whole\n", "result was [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). What about if the function returns a Nothing? Let's\n", "see:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 3 `applyMaybe` \\x -> if x > 2 then Just x else Nothing" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 1 `applyMaybe` \\x -> if x > 2 then Just x else Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just what we expected. If the monadic value on the left is a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing),\n", "the whole thing is [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). And if the function on the right returns a\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), the result is [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) again. This is very similar to when we\n", "used [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) as an applicative and we got a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) result if somewhere in\n", "there was a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "\n", "It looks like that for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), we've figured out how to take a fancy\n", "value and feed it to a function that takes a normal value and returns a\n", "fancy one. We did this by keeping in mind that a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value represents\n", "a computation that might have failed.\n", "\n", "You might be asking yourself, how is this useful? It may seem like\n", "applicative functors are stronger than monads, since applicative\n", "functors allow us to take a normal function and make it operate on\n", "values with contexts. We'll see that monads can do that as well because\n", "they're an upgrade of applicative functors, and that they can also do\n", "some cool stuff that applicative functors can't.\n", "\n", "We'll come back to [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) in a minute, but first, let's check out the\n", "type class that belongs to monads.\n", "\n", "The Monad type class\n", "--------------------\n", "\n", "Just like functors have the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) type class and applicative functors\n", "have the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) type class, monads come with their own type class:\n", "[`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad)! Wow, who would have thought? This is what the type class looks\n", "like:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class Monad m where\n",
    "    return :: a -> m a\n",
    "\n",
    "    (>>=) :: m a -> (a -> m b) -> m b\n",
    "\n",
    "    (>>) :: m a -> m b -> m b\n",
    "    x >> y = x >>= \\_ -> y\n",
    "\n",
    "    fail :: String -> m a\n",
    "    fail msg = error msg
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Let's start with the first line. It says `class Monad m where`. But wait,\n", "didn't we say that monads are just beefed up applicative functors?\n", "Shouldn't there be a class constraint in there along the lines of\n", "`class (Applicative m) => Monad m where` so that a type has to be an\n", "applicative functor first before it can be made a monad? Well, there\n", "should, but when Haskell was made, it hadn't occurred to people that\n", "applicative functors are a good fit for Haskell so they weren't in\n", "there. But rest assured, every monad is an applicative functor, even if\n", "the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) class declaration doesn't say so.\n", "\n", "The first function that the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) type class defines is [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return). It's the\n", "same as [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure), only with a different name. Its type is\n", "`(Monad m) => a -> m a`. It takes a value and puts it in a minimal default context that\n", "still holds that value. In other words, it takes something and wraps it\n", "in a monad. It always does the same thing as the [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) function from the\n", "[`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) type class, which means we're already acquainted with\n", "[`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return). We already used [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) when doing I/O. We used it to take a\n", "value and make a bogus I/O action that does nothing but yield that\n", "value. For [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) it takes a value and wraps it in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just).\n", "\n", "> Just a reminder: [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) is nothing like the [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) that's in most other\n", "> languages. It doesn't end function execution or anything, it just takes\n", "> a normal value and puts it in a context.\n", "\n", "\n", "\n", "The next function is [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), or bind. It's like function application,\n", "only instead of taking a normal value and feeding it to a normal\n", "function, it takes a monadic value (that is, a value with a context) and\n", "feeds it to a function that takes a normal value but returns a monadic\n", "value.\n", "\n", "Next up, we have [`>>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62-). We won't pay too much attention to it for now\n", "because it comes with a default implementation and we pretty much never\n", "implement it when making [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instances.\n", "\n", "The final function of the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) type class is [`fail`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fail). We never use it\n", "explicitly in our code. Instead, it's used by Haskell to enable failure\n", "in a special syntactic construct for monads that we'll meet later. We\n", "don't need to concern ourselves with [`fail`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fail) too much for now.\n", "\n", "Now that we know what the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) type class looks like, let's take a look\n", "at how [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) is an instance of [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad)!" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monad Maybe where\n",
    "    return x = Just x\n",
    "    Nothing >>= f = Nothing\n",
    "    Just x >>= f  = f x\n",
    "    fail _ = Nothing
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) is the same as [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure), so that one's a no-brainer. We do what we\n", "did in the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) type class and wrap it in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just).\n", "\n", "The [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) function is the same as our `applyMaybe`. When feeding the\n", "`Maybe a` to our function, we keep in mind the context and return a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) if\n", "the value on the left is [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) because if there's no value then\n", "there's no way to apply our function to it. If it's a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) we take\n", "what's inside and apply `f` to it.\n", "\n", "We can play around with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) as a monad:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"WHAT\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return \"WHAT\" :: Maybe String" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 90" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 9 >>= \\x -> return (x*10)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing >>= \\x -> return (x*10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nothing new or exciting on the first line since we already used [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure)\n", "with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) and we know that [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) is just [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) with a different name.\n", "The next two lines showcase [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) a bit more.\n", "\n", "Notice how when we fed `Just 9` to the function `\\x -> return (x*10)`,\n", "the `x` took on the value `9` inside the function. It seems as though we\n", "were able to extract the value from a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) without pattern-matching.\n", "And we still didn't lose the context of our [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value, because when\n", "it's [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), the result of using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) will be [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) as well.\n", "\n", "\n", "\n", "Walk the line\n", "-------------\n", "\n", "\n", "\n", "Now that we know how to feed a `Maybe a` value to a function of type\n", "`a -> Maybe b` while taking into account the context of possible failure, let's\n", "see how we can use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) repeatedly to handle computations of several\n", "`Maybe a` values.\n", "\n", "Pierre has decided to take a break from his job at the fish farm and try\n", "tightrope walking. He's not that bad at it, but he does have one\n", "problem: birds keep landing on his balancing pole! They come and they\n", "take a short rest, chat with their avian friends and then take off in\n", "search of breadcrumbs. This wouldn't bother him so much if the number of\n", "birds on the left side of the pole was always equal to the number of\n", "birds on the right side. But sometimes, all the birds decide that they\n", "like one side better and so they throw him off balance, which results in\n", "an embarrassing tumble for Pierre (he's using a safety net).\n", "\n", "Let's say that he keeps his balance if the number of birds on the left\n", "side of the pole and on the right side of the pole is within three. So\n", "if there's one bird on the right side and four birds on the left side,\n", "he's okay. But if a fifth bird lands on the left side, then he loses his\n", "balance and takes a dive.\n", "\n", "We're going to simulate birds landing on and flying away from the pole\n", "and see if Pierre is still at it after a certain number of birdy\n", "arrivals and departures. For instance, we want to see what happens to\n", "Pierre if first one bird arrives on the left side, then four birds\n", "occupy the right side and then the bird that was on the left side\n", "decides to fly away.\n", "\n", "We can represent the pole with a simple pair of integers. The first\n", "component will signify the number of birds on the left side and the\n", "second component the number of birds on the right side:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type Birds = Int\n", "type Pole = (Birds,Birds)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we made a type synonym for [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int), called `Birds`, because we're using\n", "integers to represent how many birds there are. And then we made a type\n", "synonym `(Birds,Birds)` and we called it `Pole` (not to be confused with a\n", "person of Polish descent).\n", "\n", "Next up, how about we make a function that takes a number of birds and\n", "lands them on one side of the pole. Here are the functions:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "landLeft :: Birds -> Pole -> Pole\n", "landLeft n (left,right) = (left + n,right)\n", "\n", "landRight :: Birds -> Pole -> Pole\n", "landRight n (left,right) = (left,right + n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty straightforward stuff. Let's try them out:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(2,0)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "landLeft 2 (0,0)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(1,3)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "landRight 1 (1,2)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(1,1)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "landRight (-1) (1,2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To make birds fly away we just had a negative number of birds land on\n", "one side. Because landing a bird on the `Pole` returns a `Pole`, we can\n", "chain applications of `landLeft` and `landRight`:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(3,1)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "landLeft 2 (landRight 1 (landLeft 1 (0,0)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we apply the function `landLeft 1` to `(0,0)` we get `(1,0)`. Then, we\n", "land a bird on the right side, resulting in `(1,1)`. Finally two birds\n", "land on the left side, resulting in `(3,1)`. We apply a function to\n", "something by first writing the function and then writing its parameter,\n", "but here it would be better if the pole went first and then the landing\n", "function. If we make a function like this:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "x -: f = f x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can apply functions by first writing the parameter and then the\n", "function:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "300" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "100 -: (*3)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "True -: not" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(2,0)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(0,0) -: landLeft 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By using this, we can repeatedly land birds on the pole in a more\n", "readable manner:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(3,1)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(0,0) -: landLeft 1 -: landRight 1 -: landLeft 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty cool! This example is equivalent to the one before where we\n", "repeatedly landed birds on the pole, only it looks neater. Here, it's\n", "more obvious that we start off with `(0,0)` and then land one bird one the\n", "left, then one on the right and finally two on the left.\n", "\n", "So far so good, but what happens if 10 birds land on one side?" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(10,3)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "landLeft 10 (0,3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "10 birds on the left side and only 3 on the right? That's sure to send\n", "poor Pierre falling through the air! This is pretty obvious here but\n", "what if we had a sequence of landings like this:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(0,2)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(0,0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It might seem like everything is okay but if you follow the steps here,\n", "you'll see that at one time there are 4 birds on the right side and no\n", "birds on the left! To fix this, we have to take another look at our\n", "`landLeft` and `landRight` functions. From what we've seen, we want these\n", "functions to be able to fail. That is, we want them to return a new pole\n", "if the balance is okay but fail if the birds land in a lopsided manner.\n", "And what better way to add a context of failure to value than by using\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)! Let's rework these functions:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "landLeft :: Birds -> Pole -> Maybe Pole\n", "landLeft n (left,right)\n", " | abs ((left + n) - right) < 4 = Just (left + n, right)\n", " | otherwise = Nothing\n", "\n", "landRight :: Birds -> Pole -> Maybe Pole\n", "landRight n (left,right)\n", " | abs (left - (right + n)) < 4 = Just (left, right + n)\n", " | otherwise = Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead of returning a `Pole` these functions now return a `Maybe Pole`.\n", "They still take the number of birds and the old pole as before, but then\n", "they check if landing that many birds on the pole would throw Pierre off\n", "balance. We use guards to check if the difference between the number of\n", "birds on the new pole is less than 4. If it is, we wrap the new pole in\n", "a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) and return that. If it isn't, we return a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), indicating\n", "failure.\n", "\n", "Let's give these babies a go:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (2,0)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "landLeft 2 (0,0)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "landLeft 10 (0,3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nice! When we land birds without throwing Pierre off balance, we get a\n", "new pole wrapped in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just). But when many more birds end up on one side\n", "of the pole, we get a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). This is cool, but we seem to have lost\n", "the ability to repeatedly land birds on the pole. We can't do\n", "`landLeft 1 (landRight 1 (0,0))` anymore because when we apply `landRight 1` to `(0,0)`,\n", "we don't get a `Pole`, but a `Maybe Pole`. `landLeft 1` takes a `Pole` and not a\n", "`Maybe Pole`.\n", "\n", "We need a way of taking a `Maybe Pole` and feeding it to a function that\n", "takes a `Pole` and returns a `Maybe Pole`. Luckily, we have [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), which\n", "does just that for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe). Let's give it a go:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (2,1)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "landRight 1 (0,0) >>= landLeft 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, `landLeft 2` has a type of `Pole -> Maybe Pole`. We couldn't just\n", "feed it the `Maybe Pole` that is the result of `landRight 1 (0,0)`, so we\n", "use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to take that value with a context and give it to `landLeft 2`.\n", "[`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) does indeed allow us to treat the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value as a value with\n", "context because if we feed a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) into `landLeft 2`, the result is\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) and the failure is propagated:" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing >>= landLeft 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With this, we can now chain landings that may fail because [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) allows\n", "us to feed a monadic value to a function that takes a normal one.\n", "\n", "Here's a sequence of birdy landings:" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (2,4)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return (0,0) >>= landRight 2 >>= landLeft 2 >>= landRight 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "At the beginning, we used [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) to take a pole and wrap it in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just).\n", "We could have just applied `landRight 2` to `(0,0)`, it would have been the\n", "same, but this way we can be more consistent by using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) for every\n", "function. `Just (0,0)` gets fed to `landRight 2`, resulting in `Just (0,2)`.\n", "This, in turn, gets fed to `landLeft 2`, resulting in `Just (2,2)`, and so\n", "on.\n", "\n", "Remember this example from before we introduced failure into Pierre's\n", "routine:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(0,0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It didn't simulate his interaction with birds very well because in the\n", "middle there his balance was off but the result didn't reflect that. But\n", "let's give that a go now by using monadic application ([`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-)) instead of\n", "normal application:" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return (0,0) >>= landLeft 1 >>= landRight 4 >>= landLeft (-1) >>= landRight (-2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Awesome. The final result represents failure, which is what we expected.\n", "Let's see how this result was obtained. First, [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) puts `(0,0)` into a\n", "default context, making it a `Just (0,0)`. Then, `Just (0,0) >>= landLeft 1`\n", "happens. Since the `Just (0,0)` is a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value, `landLeft 1` gets applied\n", "to `(0,0)`, resulting in a `Just (1,0)`, because the birds are still\n", "relatively balanced. Next, `Just (1,0) >>= landRight 4` takes place and\n", "the result is `Just (1,4)` as the balance of the birds is still intact,\n", "although just barely. Just (1,4) gets fed to `landLeft (-1)`. This means\n", "that `landLeft (-1) (1,4)` takes place. Now because of how `landLeft` works,\n", "this results in a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), because the resulting pole is off balance.\n", "Now that we have a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), it gets fed to `landRight (-2)`, but because\n", "it's a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), the result is automatically [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), as we have nothing\n", "to apply `landRight (-2)` to.\n", "\n", "We couldn't have achieved this by just using [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) as an applicative. If\n", "you try it, you'll get stuck, because applicative functors don't allow\n", "for the applicative values to interact with each other very much. They\n", "can, at best, be used as parameters to a function by using the\n", "applicative style. The applicative operators will fetch their results\n", "and feed them to the function in a manner appropriate for each\n", "applicative and then put the final applicative value together, but there\n", "isn't that much interaction going on between them. Here, however, each\n", "step relies on the previous one's result. On every landing, the possible\n", "result from the previous one is examined and the pole is checked for\n", "balance. This determines whether the landing will succeed or fail.\n", "\n", "We may also devise a function that ignores the current number of birds\n", "on the balancing pole and just makes Pierre slip and fall. We can call\n", "it `banana`:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "banana :: Pole -> Maybe Pole\n", "banana _ = Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can chain it together with our bird landings. It will always\n", "cause our walker to fall, because it ignores whatever's passed to it and\n", "always returns a failure. Check it:" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return (0,0) >>= landLeft 1 >>= banana >>= landRight 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The value `Just (1,0)` gets fed to `banana`, but it produces a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing),\n", "which causes everything to result in a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). How unfortunate!\n", "\n", "Instead of making functions that ignore their input and just return a\n", "predetermined monadic value, we can use the [`>>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62-) function, whose default\n", "implementation is this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(>>) :: (Monad m) => m a -> m b -> m b\n",
    "m >> n = m >>= \\_ -> n
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Normally, passing some value to a function that ignores its parameter\n", "and always just returns some predetermined value would always result in\n", "that predetermined value. With monads however, their context and meaning\n", "has to be considered as well. Here's how [`>>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62-) acts with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe):" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing >> Just 3" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 4" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 3 >> Just 4" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 3 >> Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you replace [`>>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62-) with `>>= \\_ ->`, it's easy to see why it acts\n", "like it does.\n", "\n", "We can replace our `banana` function in the chain with a [`>>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62-) and then a\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing):" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return (0,0) >>= landLeft 1 >> Nothing >>= landRight 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There we go, guaranteed and obvious failure!\n", "\n", "It's also worth taking a look at what this would look like if we hadn't\n", "made the clever choice of treating [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values as values with a failure\n", "context and feeding them to functions like we did. Here's how a series\n", "of bird landings would look like:" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "routine :: Maybe Pole\n", "routine = case landLeft 1 (0,0) of\n", " Nothing -> Nothing\n", " Just pole1 -> case landRight 4 pole1 of\n", " Nothing -> Nothing\n", " Just pole2 -> case landLeft 2 pole2 of\n", " Nothing -> Nothing\n", " Just pole3 -> landLeft 1 pole3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "We land a bird on the left and then we examine the possibility of\n", "failure and the possibility of success. In the case of failure, we\n", "return a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). In the case of success, we land birds on the right and\n", "then do the same thing all over again. Converting this monstrosity into\n", "a neat chain of monadic applications with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) is a classic example of\n", "how the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad saves us a lot of time when we have to successively\n", "do computations that are based on computations that might have failed.\n", "\n", "Notice how the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) implementation of [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) features exactly this logic\n", "of seeing if a value is [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) and if it is, returning a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) right\n", "away and if it isn't, going forward with what's inside the [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just).\n", "\n", "In this section, we took some functions that we had and saw that they\n", "would work better if the values that they returned supported failure. By\n", "turning those values into [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values and replacing normal function\n", "application with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), we got a mechanism for handling failure pretty\n", "much for free, because [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) is supposed to preserve the context of the\n", "value to which it applies functions. In this case, the context was that\n", "our values were values with failure and so when we applied functions to\n", "such values, the possibility of failure was always taken into account.\n", "\n", "do notation\n", "-----------\n", "\n", "Monads in Haskell are so useful that they got their own special syntax\n", "called `do` notation. We've already encountered `do` notation when we were\n", "doing I/O and there we said that it was for gluing together several I/O\n", "actions into one. Well, as it turns out, `do` notation isn't just for [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO),\n", "but can be used for any monad. Its principle is still the same: gluing\n", "together monadic values in sequence. We're going to take a look at how\n", "`do` notation works and why it's useful.\n", "\n", "Consider this familiar example of monadic application:" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"3!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 3 >>= (\\x -> Just (show x ++ \"!\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Been there, done that. Feeding a monadic value to a function that\n", "returns one, no big deal. Notice how when we do this, `x` becomes `3` inside\n", "the lambda. Once we're inside that lambda, it's just a normal value\n", "rather than a monadic value. Now, what if we had another [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) inside\n", "that function? Check this out:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"3!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 3 >>= (\\x -> Just \"!\" >>= (\\y -> Just (show x ++ y)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah, a nested use of [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-)! In the outermost lambda, we feed `Just \"!\"` to\n", "the lambda `\\y -> Just (show x ++ y)`. Inside this lambda, the `y` becomes\n", "`\"!\"`. `x` is still `3` because we got it from the outer lambda. All this sort\n", "of reminds me of the following expression:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"3!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let x = 3; y = \"!\" in show x ++ y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The main difference between these two is that the values in the former\n", "example are monadic. They're values with a failure context. We can\n", "replace any of them with a failure:" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Nothing >>= (\\x -> Just \"!\" >>= (\\y -> Just (show x ++ y)))" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 3 >>= (\\x -> Nothing >>= (\\y -> Just (show x ++ y)))" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 3 >>= (\\x -> Just \"!\" >>= (\\y -> Nothing))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the first line, feeding a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) to a function naturally results in\n", "a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). In the second line, we feed `Just 3` to a function and the `x`\n", "becomes `3`, but then we feed a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) to the inner lambda and the result\n", "of that is [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), which causes the outer lambda to produce [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) as\n", "well. So this is sort of like assigning values to variables in `let`\n", "expressions, only that the values in question are monadic values.\n", "\n", "To further illustrate this point, let's write this in a script and have\n", "each [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value take up its own line:" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "foo :: Maybe String\n", "foo = Just 3 >>= (\\x ->\n", " Just \"!\" >>= (\\y ->\n", " Just (show x ++ y)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To save us from writing all these annoying lambdas, Haskell gives us `do`\n", "notation. It allows us to write the previous piece of code like this:" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "foo :: Maybe String\n", "foo = do\n", " x <- Just 3\n", " y <- Just \"!\"\n", " Just (show x ++ y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "It would seem as though we've gained the ability to temporarily extract\n", "things from [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values without having to check if the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values are\n", "[`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) values or [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) values at every step. How cool! If any of the\n", "values that we try to extract from are [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), the whole `do` expression\n", "will result in a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). We're yanking out their (possibly existing)\n", "values and letting [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) worry about the context that comes with those\n", "values. It's important to remember that `do` expressions are just\n", "different syntax for chaining monadic values.\n", "\n", "In a `do` expression, every line is a monadic value. To inspect its\n", "result, we use `<-`. If we have a `Maybe String` and we bind it with `<-` to\n", "a variable, that variable will be a [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String), just like when we used [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-)\n", "to feed monadic values to lambdas. The last monadic value in a `do`\n", "expression, like `Just (show x ++ y)` here, can't be used with `<-` to bind\n", "its result, because that wouldn't make sense if we translated the `do`\n", "expression back to a chain of [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) applications. Rather, its result is\n", "the result of the whole glued up monadic value, taking into account the\n", "possible failure of any of the previous ones.\n", "\n", "For instance, examine the following line:" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just 9 >>= (\\x -> Just (x > 8))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because the left parameter of [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) is a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value, the lambda is\n", "applied to `9` and the result is a `Just True`. If we rewrite this in `do`\n", "notation, we get:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "marySue :: Maybe Bool\n", "marySue = do\n", " x <- Just 9\n", " Just (x > 8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we compare these two, it's easy to see why the result of the whole\n", "monadic value is the result of the last monadic value in the `do`\n", "expression with all the previous ones chained into it.\n", "\n", "Our tightwalker's routine can also be expressed with `do` notation.\n", "`landLeft` and `landRight` take a number of birds and a pole and produce a\n", "pole wrapped in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just), unless the tightwalker slips, in which case a\n", "[`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) is produced. We used [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to chain successive steps because\n", "each one relied on the previous one and each one had an added context of\n", "possible failure. Here's two birds landing on the left side, then two\n", "birds landing on the right and then one bird landing on the left:" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "routine :: Maybe Pole\n", "routine = do\n", " start <- return (0,0)\n", " first <- landLeft 2 start\n", " second <- landRight 2 first\n", " landLeft 1 second" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's see if he succeeds:" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (3,2)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "routine" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "He does! Great. When we were doing these routines by explicitly writing\n", "[`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), we usually said something like `return (0,0) >>= landLeft 2`,\n", "because `landLeft 2` is a function that returns a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value. With `do`\n", "expressions however, each line must feature a monadic value. So we\n", "explicitly pass the previous `Pole` to the `landLeft` `landRight` functions.\n", "If we examined the variables to which we bound our [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values, `start`\n", "would be `(0,0)`, first would be `(2,0)` and so on.\n", "\n", "Because `do` expressions are written line by line, they may look like\n", "imperative code to some people. But the thing is, they're just\n", "sequential, as each value in each line relies on the result of the\n", "previous ones, along with their contexts (in this case, whether they\n", "succeeded or failed).\n", "\n", "Again, let's take a look at what this piece of code would look like if\n", "we hadn't used the monadic aspects of [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe):" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "routine :: Maybe Pole\n", "routine =\n", " case Just (0,0) of\n", " Nothing -> Nothing\n", " Just start -> case landLeft 2 start of\n", " Nothing -> Nothing\n", " Just first -> case landRight 2 first of\n", " Nothing -> Nothing\n", " Just second -> landLeft 1 second" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See how in the case of success, the tuple inside `Just (0,0)` becomes\n", "`start`, the result of `landLeft 2 start` becomes [`first`](https://hackage.haskell.org/package/base/docs/Control-Arrow.html#v:first), etc.\n", "\n", "If we want to throw the Pierre a banana peel in `do` notation, we can do\n", "the following:" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "routine :: Maybe Pole\n", "routine = do\n", " start <- return (0,0)\n", " first <- landLeft 2 start\n", " Nothing\n", " second <- landRight 2 first\n", " landLeft 1 second" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we write a line in `do` notation without binding the monadic value\n", "with `<-`, it's just like putting [`>>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62-) after the monadic value whose\n", "result we want to ignore. We sequence the monadic value but we ignore\n", "its result because we don't care what it is and it's prettier than\n", "writing `_ <- Nothing`, which is equivalent to the above.\n", "\n", "When to use `do` notation and when to explicitly use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) is up to you. I\n", "think this example lends itself to explicitly writing [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) because each\n", "step relies specifically on the result of the previous one. With `do`\n", "notation, we had to specifically write on which pole the birds are\n", "landing, but every time we used that came directly before. But still, it\n", "gave us some insight into `do` notation.\n", "\n", "In `do` notation, when we bind monadic values to names, we can utilize\n", "pattern matching, just like in `let` expressions and function parameters.\n", "Here's an example of pattern matching in a `do` expression:" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "justH :: Maybe Char\n", "justH = do\n", " (x:xs) <- Just \"hello\"\n", " return x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We use pattern matching to get the first character of the string `\"hello\"`\n", "and then we present it as the result. So `justH` evaluates to `Just 'h'`.\n", "\n", "What if this pattern matching were to fail? When matching on a pattern\n", "in a function fails, the next pattern is matched. If the matching falls\n", "through all the patterns for a given function, an error is thrown and\n", "our program crashes. On the other hand, failed pattern matching in `let`\n", "expressions results in an error being produced right away, because the\n", "mechanism of falling through patterns isn't present in `let` expressions.\n", "When pattern matching fails in a `do` expression, the [`fail`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fail) function is\n", "called. It's part of the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) type class and it enables failed pattern\n", "matching to result in a failure in the context of the current monad\n", "instead of making our program crash. Its default implementation is this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
fail :: (Monad m) => String -> m a\n",
    "fail msg = error msg
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So by default it does make our program crash, but monads that\n", "incorporate a context of possible failure (like [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)) usually implement\n", "it on their own. For [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), its implemented like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
fail _ = Nothing
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It ignores the error message and makes a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). So when pattern\n", "matching fails in a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value that's written in `do` notation, the whole\n", "value results in a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). This is preferable to having our program\n", "crash. Here's a `do` expression with a pattern that's bound to fail:" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "wopwop :: Maybe Char\n", "wopwop = do\n", " (x:xs) <- Just \"\"\n", " return x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The pattern matching fails, so the effect is the same as if the whole\n", "line with the pattern was replaced with a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). Let's try this out:" ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "wopwop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The failed pattern matching has caused a failure within the context of\n", "our monad instead of causing a program-wide failure, which is pretty\n", "neat.\n", "\n", "\n", "\n", "The list monad\n", "--------------\n", "\n", "\n", "\n", "So far, we've seen how [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values can be viewed as values with a\n", "failure context and how we can incorporate failure handling into our\n", "code by using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to feed them to functions. In this section, we're\n", "going to take a look at how to use the monadic aspects of lists to bring\n", "non-determinism into our code in a clear and readable manner.\n", "\n", "We've already talked about how lists represent non-deterministic values\n", "when they're used as applicatives. A value like `5` is deterministic. It\n", "has only one result and we know exactly what it is. On the other hand, a\n", "value like `[3,8,9]` contains several results, so we can view it as one\n", "value that is actually many values at the same time. Using lists as\n", "applicative functors showcases this non-determinism nicely:" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[10,100,1000,20,200,2000,30,300,3000]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(*) <$> [1,2,3] <*> [10,100,1000]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All the possible combinations of multiplying elements from the left list\n", "with elements from the right list are included in the resulting list.\n", "When dealing with non-determinism, there are many choices that we can\n", "make, so we just try all of them, and so the result is a\n", "non-deterministic value as well, only it has many more results.\n", "\n", "This context of non-determinism translates to monads very nicely. Let's\n", "go ahead and see what the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance for lists looks like:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monad [] where\n",
    "    return x = [x]\n",
    "    xs >>= f = concat (map f xs)\n",
    "    fail _ = []
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) does the same thing as [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure), so we should already be familiar\n", "with [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) for lists. It takes a value and puts it in a minimal default\n", "context that still yields that value. In other words, it makes a list\n", "that has only that one value as its result. This is useful for when we\n", "want to just wrap a normal value into a list so that it can interact\n", "with non-deterministic values.\n", "\n", "To understand how [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) works for lists, it's best if we take a look at\n", "it in action to gain some intuition first. [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) is about taking a value\n", "with a context (a monadic value) and feeding it to a function that takes\n", "a normal value and returns one that has context. If that function just\n", "produced a normal value instead of one with a context, [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) wouldn't be\n", "so useful because after one use, the context would be lost. Anyway,\n", "let's try feeding a non-deterministic value to a function:" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[3,-3,4,-4,5,-5]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[3,4,5] >>= \\x -> [x,-x]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we used [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), the monadic value was fed into the\n", "function while taking care of possible failures. Here, it takes care of\n", "non-determinism for us. `[3,4,5]` is a non-deterministic value and we feed\n", "it into a function that returns a non-deterministic value as well. The\n", "result is also non-deterministic, and it features all the possible\n", "results of taking elements from the list `[3,4,5]` and passing them to the\n", "function `\\x -> [x,-x]`. This function takes a number and produces two\n", "results: one negated and one that's unchanged. So when we use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to\n", "feed this list to the function, every number is negated and also kept\n", "unchanged. The `x` from the lambda takes on every value from the list\n", "that's fed to it.\n", "\n", "To see how this is achieved, we can just follow the implementation.\n", "First, we start off with the list `[3,4,5]`. Then, we map the lambda over\n", "it and the result is the following:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
[[3,-3],[4,-4],[5,-5]]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The lambda is applied to every element and we get a list of lists.\n", "Finally, we just flatten the list and voila! We've applied a\n", "non-deterministic function to a non-deterministic value!\n", "\n", "Non-determinism also includes support for failure. The empty list `[]` is\n", "pretty much the equivalent of [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), because it signifies the absence\n", "of a result. That's why failing is just defined as the empty list. The\n", "error message gets thrown away. Let's play around with lists that fail:" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[] >>= \\x -> [\"bad\",\"mad\",\"rad\"]" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1,2,3] >>= \\x -> []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the first line, an empty list is fed into the lambda. Because the\n", "list has no elements, none of them can be passed to the function and so\n", "the result is an empty list. This is similar to feeding [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) to a\n", "function. In the second line, each element gets passed to the function,\n", "but the element is ignored and the function just returns an empty list.\n", "Because the function fails for every element that goes in it, the result\n", "is a failure.\n", "\n", "Just like with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values, we can chain several lists with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-),\n", "propagating the non-determinism:" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(1,'a'),(1,'b'),(2,'a'),(2,'b')]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1,2] >>= \\n -> ['a','b'] >>= \\ch -> return (n,ch)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "The list `[1,2]` gets bound to `n` and `['a','b']` gets bound to `ch`. Then, we\n", "do `return (n,ch)` (or `[(n,ch)]`), which means taking a pair of `(n,ch)` and\n", "putting it in a default minimal context. In this case, it's making the\n", "smallest possible list that still presents `(n,ch)` as the result and\n", "features as little non-determinism as possible. Its effect on the\n", "context is minimal. What we're saying here is this: for every element in\n", "`[1,2]`, go over every element in `['a','b']` and produce a tuple of one\n", "element from each list.\n", "\n", "Generally speaking, because [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) takes a value and wraps it in a\n", "minimal context, it doesn't have any extra effect (like failing in [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)\n", "or resulting in more non-determinism for lists) but it does present\n", "something as its result.\n", "\n", "> When you have non-deterministic values interacting, you can view their\n", "> computation as a tree where every possible result in a list represents a\n", "> separate branch.\n", "\n", "Here's the previous expression rewritten in `do` notation:" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "listOfTuples :: [(Int,Char)]\n", "listOfTuples = do\n", " n <- [1,2]\n", " ch <- ['a','b']\n", " return (n,ch)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This makes it a bit more obvious that `n` takes on every value from `[1,2]`\n", "and `ch` takes on every value from `['a','b']`. Just like with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), we're\n", "extracting the elements from the monadic values and treating them like\n", "normal values and [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) takes care of the context for us. The context in\n", "this case is non-determinism.\n", "\n", "Using lists with `do` notation really reminds me of something we've seen\n", "before. Check out the following piece of code:" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(1,'a'),(1,'b'),(2,'a'),(2,'b')]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[ (n,ch) | n <- [1,2], ch <- ['a','b'] ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yes! List comprehensions! In our `do` notation example, `n` became every\n", "result from `[1,2]` and for every such result, `ch` was assigned a result\n", "from `['a','b']` and then the final line put `(n,ch)` into a default context\n", "(a singleton list) to present it as the result without introducing any\n", "additional non-determinism. In this list comprehension, the same thing\n", "happened, only we didn't have to write [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) at the end to present\n", "`(n,ch)` as the result because the output part of a list comprehension did\n", "that for us.\n", "\n", "In fact, list comprehensions are just syntactic sugar for using lists as\n", "monads. In the end, list comprehensions and lists in `do` notation\n", "translate to using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to do computations that feature\n", "non-determinism.\n", "\n", "List comprehensions allow us to filter our output. For instance, we can\n", "filter a list of numbers to search only for that numbers whose digits\n", "contain a `7`:" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[7,17,27,37,47]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[ x | x <- [1..50], '7' `elem` show x ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We apply [`show`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:show) to `x` to turn our number into a string and then we check if\n", "the character `'7'` is part of that string. Pretty clever. To see how\n", "filtering in list comprehensions translates to the list monad, we have\n", "to check out the [`guard`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:guard) function and the [`MonadPlus`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#t:MonadPlus) type class. The\n", "[`MonadPlus`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#t:MonadPlus) type class is for monads that can also act as monoids. Here's\n", "its definition:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
class Monad m => MonadPlus m where\n",
    "    mzero :: m a\n",
    "    mplus :: m a -> m a -> m a
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[`mzero`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:mzero) is synonymous to [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty) from the [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) type class and [`mplus`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:mplus)\n", "corresponds to [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend). Because lists are monoids as well as monads,\n", "they can be made an instance of this type class:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance MonadPlus [] where\n",
    "    mzero = []\n",
    "    mplus = (++)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For lists [`mzero`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:mzero) represents a non-deterministic computation that has no\n", "results at all — a failed computation. [`mplus`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:mplus) joins two non-deterministic\n", "values into one. The [`guard`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:guard) function is defined like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
guard :: (MonadPlus m) => Bool -> m ()\n",
    "guard True = return ()\n",
    "guard False = mzero
" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It takes a boolean value and if it's [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True), takes a `()` and puts it in a\n", "minimal default context that still succeeds. Otherwise, it makes a\n", "failed monadic value. Here it is in action:" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just ()" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "guard (5 > 2) :: Maybe ()" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "guard (1 > 2) :: Maybe ()" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[()]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "guard (5 > 2) :: [()]" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "guard (1 > 2) :: [()]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looks interesting, but how is it useful? In the list monad, we use it to\n", "filter out non-deterministic computations. Observe:" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[7,17,27,37,47]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1..50] >>= (\\x -> guard ('7' `elem` show x) >> return x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The result here is the same as the result of our previous list\n", "comprehension. How does [`guard`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:guard) achieve this? Let's first see how [`guard`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:guard)\n", "functions in conjunction with [`>>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62-):" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"cool\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "guard (5 > 2) >> return \"cool\" :: [String]" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "guard (1 > 2) >> return \"cool\" :: [String]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If [`guard`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:guard) succeeds, the result contained within it is an empty tuple. So\n", "then, we use [`>>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62-) to ignore that empty tuple and present something else\n", "as the result. However, if [`guard`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:guard) fails, then so will the [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) later\n", "on, because feeding an empty list to a function with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) always\n", "results in an empty list. A [`guard`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:guard) basically says: if this boolean is\n", "[`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) then produce a failure right here, otherwise make a successful\n", "value that has a dummy result of `()` inside it. All this does is to allow\n", "the computation to continue.\n", "\n", "Here's the previous example rewritten in `do` notation:" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "sevensOnly :: [Int]\n", "sevensOnly = do\n", " x <- [1..50]\n", " guard ('7' `elem` show x)\n", " return x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Had we forgotten to present `x` as the final result by using [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return), the\n", "resulting list would just be a list of empty tuples. Here's this again\n", "in the form of a list comprehension:" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[7,17,27,37,47]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[ x | x <- [1..50], '7' `elem` show x ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So filtering in list comprehensions is the same as using [`guard`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:guard).\n", "\n", "### A knight's quest\n", "\n", "Here's a problem that really lends itself to being solved with\n", "non-determinism. Say you have a chess board and only one knight piece on\n", "it. We want to find out if the knight can reach a certain position in\n", "three moves. We'll just use a pair of numbers to represent the knight's\n", "position on the chess board. The first number will determine the column\n", "he's in and the second number will determine the row.\n", "\n", "\n", "\n", "Let's make a type synonym for the knight's current position on the chess\n", "board:" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type KnightPos = (Int,Int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So let's say that the knight starts at `(6,2)`. Can he get to `(6,1)` in\n", "exactly three moves? Let's see. If we start off at `(6,2)` what's the best\n", "move to make next? I know, how about all of them! We have\n", "non-determinism at our disposal, so instead of picking one move, let's\n", "just pick all of them at once. Here's a function that takes the knight's\n", "position and returns all of its next moves:" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "moveKnight :: KnightPos -> [KnightPos]\n", "moveKnight (c,r) = do\n", " (c',r') <- [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)\n", " ,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)\n", " ]\n", " guard (c' `elem` [1..8] && r' `elem` [1..8])\n", " return (c',r')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The knight can always take one step horizontally or vertically and two\n", "steps horizontally or vertically but its movement has to be both\n", "horizontal and vertical. `(c',r')` takes on every value from the list of\n", "movements and then [`guard`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:guard) makes sure that the new move, `(c',r')` is still\n", "on the board. If it it's not, it produces an empty list, which causes a\n", "failure and `return (c',r')` isn't carried out for that position.\n", "\n", "This function can also be written without the use of lists as a monad,\n", "but we did it here just for kicks. Here is the same function done with\n", "[`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter):" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "moveKnight :: KnightPos -> [KnightPos]\n", "moveKnight (c,r) = filter onBoard\n", " [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)\n", " ,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)\n", " ]\n", " where onBoard (c,r) = c `elem` [1..8] && r `elem` [1..8]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Both of these do the same thing, so pick one that you think looks nicer.\n", "Let's give it a whirl:" ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(8,1),(8,3),(4,1),(4,3),(7,4),(5,4)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "moveKnight (6,2)" ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(6,2),(7,3)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "moveKnight (8,1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Works like a charm! We take one position and we just carry out all the\n", "possible moves at once, so to speak. So now that we have a\n", "non-deterministic next position, we just use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to feed it to\n", "`moveKnight`. Here's a function that takes a position and returns all the\n", "positions that you can reach from it in three moves:" ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "in3 :: KnightPos -> [KnightPos]\n", "in3 start = do\n", " first <- moveKnight start\n", " second <- moveKnight first\n", " moveKnight second" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you pass it `(6,2)`, the resulting list is quite big, because if there\n", "are several ways to reach some position in three moves, it crops up in\n", "the list several times. The above without `do` notation:" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "in3 start = return start >>= moveKnight >>= moveKnight >>= moveKnight" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) once gives us all possible moves from the start and then\n", "when we use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) the second time, for every possible first move, every\n", "possible next move is computed, and the same goes for the last move.\n", "\n", "Putting a value in a default context by applying [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) to it and then\n", "feeding it to a function with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) is the same as just normally\n", "applying the function to that value, but we did it here anyway for\n", "style.\n", "\n", "Now, let's make a function that takes two positions and tells us if you\n", "can get from one to the other in exactly three steps:" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "canReachIn3 :: KnightPos -> KnightPos -> Bool\n", "canReachIn3 start end = end `elem` in3 start" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We generate all the possible positions in three steps and then we see if\n", "the position we're looking for is among them. So let's see if we can get\n", "from `(6,2)` to `(6,1)` in three moves:" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(6,2) `canReachIn3` (6,1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yes! How about from `(6,2)` to `(7,3)`?" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(6,2) `canReachIn3` (7,3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No! As an exercise, you can change this function so that when you can\n", "reach one position from the other, it tells you which moves to take.\n", "Later on, we'll see how to modify this function so that we also pass it\n", "the number of moves to take instead of that number being hardcoded like\n", "it is now.\n", "\n", "Monad laws\n", "----------\n", "\n", "\n", "\n", "Just like applicative functors, and functors before them, monads come\n", "with a few laws that all monad instances must abide by. Just because\n", "something is made an instance of the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) type class doesn't mean that\n", "it's a monad, it just means that it was made an instance of a type\n", "class. For a type to truly be a monad, the monad laws must hold for that\n", "type. These laws allow us to make reasonable assumptions about the type\n", "and its behavior.\n", "\n", "Haskell allows any type to be an instance of any type class as long as\n", "the types check out. It can't check if the monad laws hold for a type\n", "though, so if we're making a new instance of the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) type class, we\n", "have to be reasonably sure that all is well with the monad laws for that\n", "type. We can rely on the types that come with the standard library to\n", "satisfy the laws, but later when we go about making our own monads,\n", "we're going to have to manually check the if the laws hold. But don't\n", "worry, they're not complicated.\n", "\n", "### Left identity\n", "\n", "The first monad law states that if we take a value, put it in a default\n", "context with [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) and then feed it to a function by using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), it's\n", "the same as just taking the value and applying the function to it. To\n", "put it formally:\n", "\n", "- **`return x >>= f`** is the same damn thing as **`f x`**\n", "\n", "If you look at monadic values as values with a context and [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) as\n", "taking a value and putting it in a default minimal context that still\n", "presents that value as its result, it makes sense, because if that\n", "context is really minimal, feeding this monadic value to a function\n", "shouldn't be much different than just applying the function to the\n", "normal value, and indeed it isn't different at all.\n", "\n", "For the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) is defined as [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just). The [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad is all\n", "about possible failure, and if we have a value and want to put it in\n", "such a context, it makes sense that we treat it as a successful\n", "computation because, well, we know what the value is. Here's some [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return)\n", "usage with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe):" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 100003" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return 3 >>= (\\x -> Just (x+100000))" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 100003" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\\x -> Just (x+100000)) 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the list monad [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) puts something in a singleton list. The [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-)\n", "implementation for lists goes over all the values in the list and\n", "applies the function to them, but since there's only one value in a\n", "singleton list, it's the same as applying the function to that value:" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"WoM\",\"WoM\",\"WoM\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return \"WoM\" >>= (\\x -> [x,x,x])" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[\"WoM\",\"WoM\",\"WoM\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\\x -> [x,x,x]) \"WoM\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We said that for [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO), using [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) makes an I/O action that has no\n", "side-effects but just presents a value as its result. So it makes sense\n", "that this law holds for [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) as well.\n", "\n", "### Right identity\n", "\n", "The second law states that if we have a monadic value and we use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-)\n", "to feed it to [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return), the result is our original monadic value.\n", "Formally:\n", "\n", "- **`m >>= return`** is no different than just **`m`**\n", "\n", "This one might be a bit less obvious than the first one, but let's take\n", "a look at why it should hold. When we feed monadic values to functions\n", "by using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), those functions take normal values and [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) monadic\n", "ones. [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) is also one such function, if you consider its type. Like\n", "we said, return puts a value in a minimal context that still presents\n", "that value as its result. This means that, for instance, for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), it\n", "doesn't introduce any failure and for lists, it doesn't introduce any\n", "extra non-determinism. Here's a test run for a few monads:" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just \"move on up\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just \"move on up\" >>= (\\x -> return x)" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1,2,3,4] >>= (\\x -> return x)" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Wah!" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "putStrLn \"Wah!\" >>= (\\x -> return x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we take a closer look at the list example, the implementation for\n", "[`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) is:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
xs >>= f = concat (map f xs)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So when we feed `[1,2,3,4]` to [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return), first [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) gets mapped over\n", "`[1,2,3,4]`, resulting in `[[1],[2],[3],[4]]` and then this gets\n", "concatenated and we have our original list.\n", "\n", "Left identity and right identity are basically laws that describe how\n", "[`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) should behave. It's an important function for making normal\n", "values into monadic ones and it wouldn't be good if the monadic value\n", "that it produced did a lot of other stuff.\n", "\n", "### Associativity\n", "\n", "The final monad law says that when we have a chain of monadic function\n", "applications with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), it shouldn't matter how they're nested.\n", "Formally written:\n", "\n", "- Doing **`(m >>= f) >>= g`** is just like doing **`m >>= (\\x -> f x >>= g)`**\n", "\n", "Hmmm, now what's going on here? We have one monadic value, `m` and two\n", "monadic functions `f` and `g`. When we're doing `(m >>= f) >>= g`, we're\n", "feeding `m` to `f`, which results in a monadic value. Then, we feed that\n", "monadic value to `g`. In the expression `m >>= (\\x -> f x >>= g)`, we\n", "take a monadic value and we feed it to a function that feeds the result\n", "of `f x` to `g`. It's not easy to see how those two are equal, so let's take\n", "a look at an example that makes this equality a bit clearer.\n", "\n", "Remember when we had our tightrope walker Pierre walk a rope while birds\n", "landed on his balancing pole? To simulate birds landing on his balancing\n", "pole, we made a chain of several functions that might produce failure:" ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (2,4)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return (0,0) >>= landRight 2 >>= landLeft 2 >>= landRight 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We started with `Just (0,0)` and then bound that value to the next monadic\n", "function, `landRight 2`. The result of that was another monadic value\n", "which got bound into the next monadic function, and so on. If we were to\n", "explicitly parenthesize this, we'd write:" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (2,4)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "((return (0,0) >>= landRight 2) >>= landLeft 2) >>= landRight 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But we can also write the routine like this:" ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (2,4)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return (0,0) >>= (\\x ->\n", " landRight 2 x >>= (\\y ->\n", " landLeft 2 y >>= (\\z ->\n", " landRight 2 z)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`return (0,0)` is the same as `Just (0,0)` and when we feed it to the\n", "lambda, the `x` becomes `(0,0)`. `landRight` takes a number of birds and a\n", "pole (a tuple of numbers) and that's what it gets passed. This results\n", "in a `Just (0,2)` and when we feed this to the next lambda, `y` is `(0,2)`.\n", "This goes on until the final bird landing produces a `Just (2,4)`, which\n", "is indeed the result of the whole expression.\n", "\n", "So it doesn't matter how you nest feeding values to monadic functions,\n", "what matters is their meaning. Here's another way to look at this law:\n", "consider composing two functions, `f` and `g`. Composing two functions is\n", "implemented like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(.) :: (b -> c) -> (a -> b) -> (a -> c)\n",
    "f . g = (\\x -> f (g x))
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the type of `g` is `a -> b` and the type of `f` is `b -> c`, we arrange\n", "them into a new function which has a type of `a -> c`, so that its\n", "parameter is passed between those functions. Now what if those two\n", "functions were monadic, that is, what if the values they returned were\n", "monadic values? If we had a function of type `a -> m b`, we couldn't just\n", "pass its result to a function of type `b -> m c`, because that function\n", "accepts a normal `b`, not a monadic one. We could however, use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to\n", "make that happen. So by using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), we can compose two monadic\n", "functions:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c)\n",
    "f <=< g = (\\x -> g x >>= f)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So now we can compose two monadic functions:" ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[9,-9,6,-6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let f x = [x,-x]\n", "let g x = [x*3,x*2]\n", "let h = f <=< g\n", "h 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool. So what does that have to do with the associativity law? Well,\n", "when we look at the law as a law of compositions, it states that\n", "**`f <=< (g <=< h)`** should be the same as **`(f <=< g) <=< h`**. This is just\n", "another way of saying that for monads, the nesting of operations\n", "shouldn't matter.\n", "\n", "If we translate the first two laws to use [`<=<`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:-60--61--60-), then the left identity\n", "law states that for every monadic function f, **`f <=< return`** is the same\n", "as writing just **`f`** and the right identity law says that **`return <=< f`** is\n", "also no different from **`f`**.\n", "\n", "This is very similar to how if `f` is a normal function, `(f . g) . h` is\n", "the same as `f . (g . h)`, `f . id` is always the same as `f` and `id . f` is\n", "also just `f`.\n", "\n", "In this chapter, we took a look at the basics of monads and learned how\n", "the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad and the list monad work. In the next chapter, we'll take\n", "a look at a whole bunch of other cool monads and we'll also learn how to\n", "make our own." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/13-for-a-few-monads-more.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "For a Few Monads More\n", "=====================\n", "\n", "\n", "\n", "We've seen how monads can be used to take values with contexts and apply\n", "them to functions and how using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) or `do` notation allows us to focus\n", "on the values themselves while the context gets handled for us.\n", "\n", "We've met the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad and seen how it adds a context of possible\n", "failure to values. We've learned about the list monad and saw how it\n", "lets us easily introduce non-determinism into our programs. We've also\n", "learned how to work in the [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO) monad, even before we knew what a monad\n", "was!\n", "\n", "In this chapter, we're going to learn about a few other monads. We'll\n", "see how they can make our programs clearer by letting us treat all sorts\n", "of values as monadic ones. Exploring a few monads more will also\n", "solidify our intuition for monads.\n", "\n", "The monads that we'll be exploring are all part of the `mtl` package. A\n", "Haskell package is a collection of modules. The `mtl` package comes with\n", "the Haskell Platform, so you probably already have it. To check if you\n", "do, type `ghc-pkg list` in the command-line. This will show which Haskell\n", "packages you have installed and one of them should be `mtl`, followed by a\n", "version number." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Writer? I hardly know her!\n", "--------------------------\n", "\n", "We've loaded our gun with the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad, the list monad and the [`IO`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:IO)\n", "monad. Now let's put the `Writer` monad in the chamber and see what\n", "happens when we fire it!\n", "\n", "Whereas [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) is for values with an added context of failure and the\n", "list is for non-deterministic values, the `Writer` monad is for values\n", "that have another value attached that acts as a sort of log value.\n", "`Writer` allows us to do computations while making sure that all the log\n", "values are combined into one log value that then gets attached to the\n", "result.\n", "\n", "For instance, we might want to equip our values with strings that\n", "explain what's going on, probably for debugging purposes. Consider a\n", "function that takes a number of bandits in a gang and tells us if that's\n", "a big gang or not. That's a very simple function:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "isBigGang :: Int -> Bool\n", "isBigGang x = x > 9" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, what if instead of just giving us a [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) or [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) value, we want it\n", "to also return a log string that says what it did? Well, we just make\n", "that string and return it alongside our [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool):" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "isBigGang :: Int -> (Bool, String)\n", "isBigGang x = (x > 9, \"Compared gang size to 9.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So now instead of just returning a [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool), we return a tuple where the\n", "first component of the tuple is the actual value and the second\n", "component is the string that accompanies that value. There's some added\n", "context to our value now. Let's give this a go:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(False,\"Compared gang size to 9.\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "isBigGang 3" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(True,\"Compared gang size to 9.\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "isBigGang 30" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "So far so good. `isBigGang` takes a normal value and returns a value with\n", "a context. As we've just seen, feeding it a normal value is not a\n", "problem. Now what if we already have a value that has a log string\n", "attached to it, such as `(3, \"Smallish gang.\")`, and we want to feed it to\n", "`isBigGang`? It seems like once again, we're faced with this question: if\n", "we have a function that takes a normal value and returns a value with a\n", "context, how do we take a value with a context and feed it to the\n", "function?\n", "\n", "When we were exploring the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad, we made a function `applyMaybe`,\n", "which took a `Maybe a` value and a function of type `a -> Maybe b` and fed\n", "that `Maybe a` value into the function, even though the function takes a\n", "normal `a` instead of a `Maybe a`. It did this by minding the context that\n", "comes with `Maybe a` values, which is that they are values with possible\n", "failure. But inside the `a -> Maybe b` function, we were able to treat\n", "that value as just a normal value, because `applyMaybe` (which later\n", "became [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-)) took care of checking if it was a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) or a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value.\n", "\n", "In the same vein, let's make a function that takes a value with an\n", "attached log, that is, an `(a,String)` value and a function of type\n", "`a -> (b,String)` and feeds that value into the function. We'll call it\n", "`applyLog`. But because an `(a,String)` value doesn't carry with it a\n", "context of possible failure, but rather a context of an additional log\n", "value, `applyLog` is going to make sure that the log of the original value\n", "isn't lost, but is joined together with the log of the value that\n", "results from the function. Here's the implementation of `applyLog`:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "applyLog :: (a,String) -> (a -> (b,String)) -> (b,String)\n", "applyLog (x,log) f = let (y,newLog) = f x in (y,log ++ newLog)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we have a value with a context and we want to feed it to a\n", "function, we usually try to separate the actual value from the context\n", "and then try to apply the function to the value and then see that the\n", "context is taken care of. In the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad, we checked if the value\n", "was a `Just x` and if it was, we took that `x` and applied the function to\n", "it. In this case, it's very easy to find the actual value, because we're\n", "dealing with a pair where one component is the value and the other a\n", "log. So first we just take the value, which is `x` and we apply the\n", "function `f` to it. We get a pair of `(y,newLog)`, where `y` is the new result\n", "and `newLog` the new log. But if we returned that as the result, the old\n", "log value wouldn't be included in the result, so we return a pair of\n", "`(y,log ++ newLog)`. We use [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) to append the new log to the old one.\n", "\n", "Here's `applyLog` in action:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(False,\"Smallish gang.Compared gang size to 9.\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(3, \"Smallish gang.\") `applyLog` isBigGang" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(True,\"A freaking platoon.Compared gang size to 9.\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(30, \"A freaking platoon.\") `applyLog` isBigGang" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The results are similar to before, only now the number of people in the\n", "gang had its accompanying log and it got included in the result log.\n", "Here are a few more examples of using `applyLog`:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(5,\"Got outlaw name.Applied length.\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\"Tobin\",\"Got outlaw name.\") `applyLog` (\\x -> (length x, \"Applied length.\"))" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(7,\"Got outlaw name.Applied length\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\"Bathcat\",\"Got outlaw name.\") `applyLog` (\\x -> (length x, \"Applied length\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See how inside the lambda, `x` is just a normal string and not a tuple and\n", "how `applyLog` takes care of appending the logs.\n", "\n", "### Monoids to the rescue\n", "\n", "> Be sure you know what [monoids](http://learnyouahaskell.com/functors-applicative-functors-and-monoids#monoids) are at this point! Cheers.\n", "\n", "Right now, `applyLog` takes values of type `(a,String)`, but is there a\n", "reason that the log has to be a [`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String)? It uses [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) to append the logs,\n", "so wouldn't this work on any kind of list, not just a list of\n", "characters? Sure it would. We can go ahead and change its type to this:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "applyLog :: (a,[c]) -> (a -> (b,[c])) -> (b,[c])\n", "applyLog (x,log) f = let (y,newLog) = f x in (y,log ++ newLog)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, the log is a list. The type of values contained in the list has to\n", "be the same for the original list as well as for the list that the\n", "function returns, otherwise we wouldn't be able to use [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) to stick them\n", "together.\n", "\n", "Would this work for bytestrings? There's no reason it shouldn't.\n", "However, the type we have now only works for lists. It seems like we'd\n", "have to make a separate `applyLog` for bytestrings. But wait! Both lists\n", "and bytestrings are monoids. As such, they are both instances of the\n", "[`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) type class, which means that they implement the [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) function.\n", "And for both lists and bytestrings, [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) is for appending. Watch:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[1,2,3] `mappend` [4,5,6]" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "\"chihuahua\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import qualified Data.ByteString as B\n", "\n", "B.pack [99,104,105] `mappend` B.pack [104,117,97,104,117,97]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool! Now our `applyLog` can work for any monoid. We have to change the\n", "type to reflect this, as well as the implementation, because we have to\n", "change [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) to [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend):" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "applyLog :: (Monoid m) => (a,m) -> (a -> (b,m)) -> (b,m)\n", "applyLog (x,log) f = let (y,newLog) = f x in (y,log `mappend` newLog)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because the accompanying value can now be any monoid value, we no longer\n", "have to think of the tuple as a value and a log, but now we can think of\n", "it as a value with an accompanying monoid value. For instance, we can\n", "have a tuple that has an item name and an item price as the monoid\n", "value. We just use the [`Sum`](https://hackage.haskell.org/package/base/docs/Data-Functor-Sum.html) newtype to make sure that the prices get\n", "added as we operate with the items. Here's a function that adds drink to\n", "some cowboy food:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Monoid\n", "\n", "type Food = String\n", "type Price = Sum Int\n", "\n", "addDrink :: Food -> (Food,Price)\n", "addDrink \"beans\" = (\"milk\", Sum 25)\n", "addDrink \"jerky\" = (\"whiskey\", Sum 99)\n", "addDrink _ = (\"beer\", Sum 30)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We use strings to represent foods and an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) in a [`Sum`](https://hackage.haskell.org/package/base/docs/Data-Functor-Sum.html) `newtype` wrapper to\n", "keep track of how many cents something costs. Just a reminder, doing\n", "[`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) with [`Sum`](https://hackage.haskell.org/package/base/docs/Data-Functor-Sum.html) results in the wrapped values getting added together:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Sum {getSum = 12}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Sum 3 `mappend` Sum 9" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `addDrink` function is pretty simple. If we're eating beans, it\n", "returns `\"milk\"` along with `Sum 25`, so 25 cents wrapped in [`Sum`](https://hackage.haskell.org/package/base/docs/Data-Functor-Sum.html). If we're\n", "eating jerky we drink whiskey and if we're eating anything else we drink\n", "beer. Just normally applying this function to a food wouldn't be\n", "terribly interesting right now, but using `applyLog` to feed a food that\n", "comes with a price itself into this function is interesting:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(\"milk\",Sum {getSum = 35})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\"beans\", Sum 10) `applyLog` addDrink" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(\"whiskey\",Sum {getSum = 124})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\"jerky\", Sum 25) `applyLog` addDrink" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(\"beer\",Sum {getSum = 35})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\"dogmeat\", Sum 5) `applyLog` addDrink" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Milk costs `25` cents, but if we eat it with beans that cost `10` cents,\n", "we'll end up paying `35` cents. Now it's clear how the attached value\n", "doesn't always have to be a log, it can be any monoid value and how two\n", "such values are combined into one depends on the monoid. When we were\n", "doing logs, they got appended, but now, the numbers are being added up.\n", "\n", "Because the value that `addDrink` returns is a tuple of type `(Food,Price)`,\n", "we can feed that result to `addDrink` again, so that it tells us what we\n", "should drink along with our drink and how much that will cost us. Let's\n", "give it a shot:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(\"beer\",Sum {getSum = 65})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(\"dogmeat\", Sum 5) `applyLog` addDrink `applyLog` addDrink" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Adding a drink to some dog meat results in a beer and an additional `30`\n", "cents, so `(\"beer\", Sum 35)`. And if we use `applyLog` to feed that to\n", "`addDrink`, we get another beer and the result is `(\"beer\", Sum 65)`.\n", "\n", "### The `Writer` type\n", "\n", "Now that we've seen that a value with an attached monoid acts like a\n", "monadic value, let's examine the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance for types of such\n", "values. The `Control.Monad.Writer` module exports the `Writer w a` type\n", "along with its [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance and some useful functions for dealing with\n", "values of this type.\n", "\n", "First, let's examine the type itself. To attach a monoid to a value, we\n", "just need to put them together in a tuple. The `Writer w a` type is just a\n", "`newtype` wrapper for this. Its definition is very simple:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad.Writer" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
newtype Writer w a = Writer { runWriter :: (a, w) }
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's wrapped in a `newtype` so that it can be made an instance of [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad)\n", "and that its type is separate from a normal tuple. The `a` type parameter\n", "represents the type of the value and the `w` type parameter the type of\n", "the attached monoid value.\n", "\n", "Its [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance is defined like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance (Monoid w) => Monad (Writer w) where\n",
    "    return x = Writer (x, mempty)\n",
    "    (Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "First off, let's examine [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-). Its implementation is essentially the\n", "same as `applyLog`, only now that our tuple is wrapped in the\n", "`Writer newtype`, we have to unwrap it when pattern matching. We take the value `x`\n", "and apply the function `f` to it. This gives us a `Writer w a` value and we\n", "use a `let` expression to pattern match on it. We present `y` as the new\n", "result and use [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) to combine the old monoid value with the new one.\n", "We pack that up with the result value in a tuple and then wrap that with\n", "the `Writer` constructor so that our result is a `Writer` value instead of\n", "just an unwrapped tuple.\n", "\n", "So, what about [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return)? It has to take a value and put it in a default\n", "minimal context that still presents that value as the result. So what\n", "would such a context be for `Writer` values? If we want the accompanying\n", "monoid value to affect other monoid values as little as possible, it\n", "makes sense to use [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty). [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty) is used to present identity monoid\n", "values, such as `\"\"` and `Sum 0` and empty bytestrings. Whenever we use\n", "[`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) between [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty) and some other monoid value, the result is that\n", "other monoid value. So if we use [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) to make a `Writer` value and then\n", "use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to feed that value to a function, the resulting monoid value\n", "will be only what the function returns. Let's use [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) on the number `3`\n", "a bunch of times, only we'll pair it with a different monoid every time:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(3,\"\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runWriter (return 3 :: Writer String Int)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(3,Sum {getSum = 0})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runWriter (return 3 :: Writer (Sum Int) Int)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(3,Product {getProduct = 1})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runWriter (return 3 :: Writer (Product Int) Int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because `Writer` doesn't have a [`Show`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show) instance, we had to use `runWriter` to\n", "convert our `Writer` values to normal tuples that can be shown. For\n", "[`String`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:String), the monoid value is the empty string. With [`Sum`](https://hackage.haskell.org/package/base/docs/Data-Functor-Sum.html), it's `0`, because\n", "if we add 0 to something, that something stays the same. For [`Product`](https://hackage.haskell.org/package/base/docs/Data-Functor-Product.html),\n", "the identity is `1`.\n", "\n", "The `Writer` instance doesn't feature an implementation for [`fail`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fail), so if a\n", "pattern match fails in `do` notation, [`error`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:error) is called.\n", "\n", "### Using do notation with Writer\n", "\n", "Now that we have a [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance, we're free to use `do` notation for\n", "`Writer` values. It's handy for when we have a several `Writer` values and\n", "we want to do stuff with them. Like with other monads, we can treat them\n", "as normal values and the context gets taken for us. In this case, all\n", "the monoid values that come attached get [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend)ed and so are reflected\n", "in the final result. Here's a simple example of using `do` notation with\n", "`Writer` to multiply two numbers:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad.Writer\n", "\n", "logNumber :: Int -> Writer [String] Int\n", "logNumber x = writer (x, [\"Got number: \" ++ show x])\n", "\n", "multWithLog :: Writer [String] Int\n", "multWithLog = do\n", " a <- logNumber 3\n", " b <- logNumber 5\n", " return (a*b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`logNumber` takes a number and makes a `Writer` value out of it. For the\n", "monoid, we use a list of strings and we equip the number with a\n", "singleton list that just says that we have that number. `multWithLog` is a\n", "`Writer` value which multiplies `3` and `5` and makes sure that their attached\n", "logs get included in the final log. We use [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) to present `a*b` as the\n", "result. Because [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) just takes something and puts it in a minimal\n", "context, we can be sure that it won't add anything to the log. Here's\n", "what we see if we run this:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(15,[\"Got number: 3\",\"Got number: 5\"])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runWriter multWithLog" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sometimes we just want some monoid value to be included at some\n", "particular point. For this, the [`tell`](https://hackage.haskell.org/package/base/docs/GHC-IO-Device.html#v:tell) function is useful. It's part of\n", "the `MonadWriter` type class and in the case of `Writer` it takes a monoid\n", "value, like `[\"This is going on\"]` and creates a `Writer` value that\n", "presents the dummy value `()` as its result but has our desired monoid\n", "value attached. When we have a monadic value that has () as its result,\n", "we don't bind it to a variable. Here's `multWithLog` but with some extra\n", "reporting included:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "multWithLog :: Writer [String] Int\n", "multWithLog = do\n", " a <- logNumber 3\n", " b <- logNumber 5\n", " tell [\"Gonna multiply these two\"]\n", " return (a*b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's important that `return (a*b)` is the last line, because the result\n", "of the last line in a `do` expression is the result of the whole `do`\n", "expression. Had we put [`tell`](https://hackage.haskell.org/package/base/docs/GHC-IO-Device.html#v:tell) as the last line, `()` would have been the\n", "result of this `do` expression. We'd lose the result of the\n", "multiplication. However, the log would be the same. Here is this in\n", "action:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(15,[\"Got number: 3\",\"Got number: 5\",\"Gonna multiply these two\"])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runWriter multWithLog" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adding logging to programs\n", "\n", "Euclid's algorithm is an algorithm that takes two numbers and computes\n", "their greatest common divisor. That is, the biggest number that still\n", "divides both of them. Haskell already features the [`gcd`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:gcd) function, which\n", "does exactly this, but let's implement our own and then equip it with\n", "logging capabilities. Here's the normal algorithm:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "gcd' :: Int -> Int -> Int\n", "gcd' a b\n", " | b == 0 = a\n", " | otherwise = gcd' b (a `mod` b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The algorithm is very simple. First, it checks if the second number is\n", "0. If it is, then the result is the first number. If it isn't, then the\n", "result is the greatest common divisor of the second number and the\n", "remainder of dividing the first number with the second one. For\n", "instance, if we want to know what the greatest common divisor of 8 and 3\n", "is, we just follow the algorithm outlined. Because 3 isn't 0, we have to\n", "find the greatest common divisor of 3 and 2 (if we divide 8 by 3, the\n", "remainder is 2). Next, we find the greatest common divisor of 3 and 2. 2\n", "still isn't 0, so now we have have 2 and 1. The second number isn't 0,\n", "so we run the algorithm again for 1 and 0, as dividing 2 by 1 gives us a\n", "remainder of 0. And finally, because the second number is now 0, the\n", "final result is 1. Let's see if our code agrees:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "gcd' 8 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It does. Very good! Now, we want to equip our result with a context, and\n", "the context will be a monoid value that acts as a log. Like before,\n", "we'll use a list of strings as our monoid. So the type of our new `gcd'`\n", "function should be:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
gcd' :: Int -> Int -> Writer [String] Int
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All that's left now is to equip our function with log values. Here's the\n", "code:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad.Writer\n", "\n", "gcd' :: Int -> Int -> Writer [String] Int\n", "gcd' a b\n", " | b == 0 = do\n", " tell [\"Finished with \" ++ show a]\n", " return a\n", " | otherwise = do\n", " tell [show a ++ \" mod \" ++ show b ++ \" = \" ++ show (a `mod` b)]\n", " gcd' b (a `mod` b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This function takes two normal [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) values and returns a `Writer [String] Int`,\n", "that is, an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) that has a log context. In the case where `b` is `0`,\n", "instead of just giving `a` as the result, we use a `do` expression to put\n", "together a `Writer` value as a result. First we use [`tell`](https://hackage.haskell.org/package/base/docs/GHC-IO-Device.html#v:tell) to report that\n", "we're finished and then we use [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) to present `a` as the result of the\n", "`do` expression. Instead of this `do` expression, we could have also written\n", "this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
writer (a, [\"Finished with \" ++ show a])
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, I think the `do` expression is easier to read. Next, we have the\n", "case when `b` isn't `0`. In this case, we log that we're using [`mod`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mod) to figure\n", "out the remainder of dividing `a` and `b`. Then, the second line of the `do`\n", "expression just recursively calls `gcd'`. Remember, `gcd'` now ultimately\n", "returns a `Writer` value, so it's perfectly valid that\n", "``gcd' b (a `mod` b)`` is a line in a `do` expression.\n", "\n", "While it may be kind of useful to trace the execution of this new `gcd'`\n", "by hand to see how the logs get appended, I think it's more insightful\n", "to just look at the big picture and view these as values with a context\n", "and from that gain insight as to what the final result will be.\n", "\n", "Let's try our new `gcd'` out. Its result is a `Writer [String] Int` value\n", "and if we unwrap that from its `newtype`, we get a tuple. The first part\n", "of the tuple is the result. Let's see if it's okay:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fst $ runWriter (gcd' 8 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Good! Now what about the log? Because the log is a list of strings,\n", "let's use `mapM_ putStrLn` to print those strings to the screen:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "8 mod 3 = 2\n", "3 mod 2 = 1\n", "2 mod 1 = 0\n", "Finished with 1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mapM_ putStrLn $ snd $ runWriter (gcd' 8 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I think it's awesome how we were able to change our ordinary algorithm\n", "to one that reports what it does as it goes along just by changing\n", "normal values to monadic values and letting the implementation of [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-)\n", "for `Writer` take care of the logs for us. We can add a logging mechanism\n", "to pretty much any function. We just replace normal values with `Writer`\n", "values where we want and change normal function application to [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) (or\n", "`do` expressions if it increases readability).\n", "\n", "### Inefficient list construction\n", "\n", "When using the `Writer` monad, you have to be careful which monoid to use,\n", "because using lists can sometimes turn out to be very slow. That's\n", "because lists use [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) for [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) and using [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) to add something to the\n", "end of a list is slow if that list is really long.\n", "\n", "In our `gcd'` function, the logging is fast because the list appending\n", "ends up looking like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
a ++ (b ++ (c ++ (d ++ (e ++ f))))
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lists are a data structure that's constructed from left to right, and\n", "this is efficient because we first fully construct the left part of a\n", "list and only then add a longer list on the right. But if we're not\n", "careful, using the `Writer` monad can produce list appending that looks\n", "like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
((((a ++ b) ++ c) ++ d) ++ e) ++ f
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This associates to the left instead of to the right. This is inefficient\n", "because every time it wants to add the right part to the left part, it\n", "has to construct the left part all the way from the beginning!\n", "\n", "The following function works like `gcd'`, only it logs stuff in reverse.\n", "First it produces the log for the rest of the procedure and then adds\n", "the current step to the end of the log." ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad.Writer\n", "\n", "gcdReverse :: Int -> Int -> Writer [String] Int\n", "gcdReverse a b\n", " | b == 0 = do\n", " tell [\"Finished with \" ++ show a]\n", " return a\n", " | otherwise = do\n", " result <- gcdReverse b (a `mod` b)\n", " tell [show a ++ \" mod \" ++ show b ++ \" = \" ++ show (a `mod` b)]\n", " return result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It does the recursion first, and binds its `result` value to result. Then\n", "it adds the current step to the log, but the current step goes at the\n", "end of the log that was produced by the recursion. Finally, it presents\n", "the result of the recursion as the final result. Here it is in action:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Finished with 1\n", "2 mod 1 = 0\n", "3 mod 2 = 1\n", "8 mod 3 = 2" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mapM_ putStrLn $ snd $ runWriter (gcdReverse 8 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's inefficient because it ends up associating the use of [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) to the\n", "left instead of to the right.\n", "\n", "### Difference lists\n", "\n", "\n", "\n", "Because lists can sometimes be inefficient when repeatedly appended in\n", "this manner, it's best to use a data structure that always supports\n", "efficient appending. One such data structure is the difference list. A\n", "difference list is similar to a list, only instead of being a normal\n", "list, it's a function that takes a list and prepends another list to it.\n", "The difference list equivalent of a list like `[1,2,3]` would be the\n", "function `\\xs -> [1,2,3] ++ xs`. A normal empty list is `[]`, whereas an\n", "empty difference list is the function `\\xs -> [] ++ xs`.\n", "\n", "The cool thing about difference lists is that they support efficient\n", "appending. When we append two normal lists with [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-), it has to walk all\n", "the way to the end of the list on the left of [`++`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43--43-) and then stick the\n", "other one there. But what if we take the difference list approach and\n", "represent our lists as functions? Well then, appending two difference\n", "lists can be done like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
f `append` g = \\xs -> f (g xs)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, `f` and `g` are functions that take lists and prepend something to\n", "them. So, for instance, if `f` is the function `(\"dog\"++)` (just another way\n", "of writing `\\xs -> \"dog\" ++ xs`) and `g` the function `(\"meat\"++)`, then\n", "``f `append` g`` makes a new function that's equivalent to the following:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
\\xs -> \"dog\" ++ (\"meat\" ++ xs)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We've appended two difference lists just by making a new function that\n", "first applies one difference list some list and then the other.\n", "\n", "Let's make a `newtype` wrapper for difference lists so that we can easily\n", "give them monoid instances:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "newtype DiffList a = DiffList { getDiffList :: [a] -> [a] }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The type that we wrap is `[a] -> [a]` because a difference list is just a\n", "function that takes a list and returns another. Converting normal lists\n", "to difference lists and vice versa is easy:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "toDiffList :: [a] -> DiffList a\n", "toDiffList xs = DiffList (xs++)\n", "\n", "fromDiffList :: DiffList a -> [a]\n", "fromDiffList (DiffList f) = f []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To make a normal list into a difference list we just do what we did\n", "before and make it a function that prepends it to another list. Because\n", "a difference list is a function that prepends something to another list,\n", "if we just want that something, we apply the function to an empty list!\n", "\n", "Here's the [`Monoid`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monoid) instance:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Semigroup (DiffList a) where\n", " (DiffList f) <> (DiffList g) = DiffList (\\xs -> f (g xs))\n", "instance Monoid (DiffList a) where\n", " mempty = DiffList (\\xs -> [] ++ xs)\n", " mappend = (<>)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice how for lists, [`mempty`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mempty) is just the [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) function and [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) is\n", "actually just function composition. Let's see if this works:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,1,2,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fromDiffList (toDiffList [1,2,3,4] `mappend` toDiffList [1,2,3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tip top! Now we can increase the efficiency of our `gcdReverse` function\n", "by making it use difference lists instead of normal lists:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad.Writer\n", "\n", "gcdReverse :: Int -> Int -> Writer (DiffList String) Int\n", "gcdReverse a b\n", " | b == 0 = do\n", " tell (toDiffList [\"Finished with \" ++ show a])\n", " return a\n", " | otherwise = do\n", " result <- gcdReverse b (a `mod` b)\n", " tell (toDiffList [show a ++ \" mod \" ++ show b ++ \" = \" ++ show (a `mod` b)])\n", " return result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We only had to change the type of the monoid from `[String]` to\n", "`DiffList String` and then when using [`tell`](https://hackage.haskell.org/package/base/docs/GHC-IO-Device.html#v:tell), convert our normal lists into\n", "difference lists with `toDiffList`. Let's see if the log gets assembled\n", "properly:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Finished with 2\n", "8 mod 2 = 0\n", "34 mod 8 = 2\n", "110 mod 34 = 8" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mapM_ putStrLn . fromDiffList . snd . runWriter $ gcdReverse 110 34" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We do `gcdReverse 110 34`, then use `runWriter` to unwrap it from the\n", "`newtype`, then apply [`snd`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:snd) to that to just get the log, then apply\n", "`fromDiffList` to convert it to a normal list and then finally print its\n", "entries to the screen.\n", "\n", "### Comparing Performance\n", "\n", "To get a feel for just how much difference lists may improve your\n", "performance, consider this function that just counts down from some\n", "number to zero, but produces its log in reverse, like `gcdReverse`, so\n", "that the numbers in the log will actually be counted up:" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "finalCountDown :: Int -> Writer (DiffList String) ()\n", "finalCountDown 0 = do\n", " tell (toDiffList [\"0\"])\n", "finalCountDown x = do\n", " finalCountDown (x-1)\n", " tell (toDiffList [show x])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we give it `0`, it just logs it. For any other number, it first counts\n", "down its predecessor to `0` and then appends that number to the log. So if\n", "we apply `finalCountDown` to `100`, the string `\"100\"` will come last in the\n", "log.\n", "\n", "Anyway, if you load this function in GHCi and apply it to a number,\n", "like `5`, you'll see that it quickly starts counting from `0` onwards:" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "0\n", "1\n", "2\n", "3\n", "4\n", "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mapM_ putStrLn . fromDiffList . snd . runWriter $ finalCountDown 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, if we change it to use normal lists instead of difference\n", "lists, like so:" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "finalCountDown :: Int -> Writer [String] ()\n", "finalCountDown 0 = do\n", " tell [\"0\"]\n", "finalCountDown x = do\n", " finalCountDown (x-1)\n", " tell [show x]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then tell GHCi to start counting:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "0\n", "1\n", "2\n", "3\n", "4\n", "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mapM_ putStrLn . snd . runWriter $ finalCountDown 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll see that the counting is really slow.\n", "\n", "Of course, this is not the proper and scientific way to test how fast\n", "our programs are, but we were able to see that in this case, using\n", "difference lists starts producing results right away whereas normal\n", "lists take forever.\n", "\n", "Oh, by the way, the song Final Countdown by Europe is now stuck in your\n", "head. Enjoy!\n", "\n", "Reader? Ugh, not this joke again.\n", "---------------------------------\n", "\n", "\n", "\n", "In the [chapter about\n", "applicatives](#functors-applicative-functors-and-monoids), we saw that\n", "the function type, `(->) r` is an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor). Mapping a function\n", "`f` over a function `g` will make a function that takes the same thing as `g`,\n", "applies `g` to it and then applies `f` to that result. So basically, we're\n", "making a new function that's like `g`, only before returning its result, `f`\n", "gets applied to that result as well. For instance:" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "55" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let f = (*5)\n", "let g = (+3)\n", "(fmap f g) 8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We've also seen that functions are applicative functors. They allow us\n", "to operate on the eventual results of functions as if we already had\n", "their results. Here's an example:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "19" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let f = (+) <$> (*2) <*> (+10)\n", "f 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The expression `(+) <$> (*2) <*> (+10)` makes a function that takes\n", "a number, gives that number to `(*2)` and `(+10)` and then adds together\n", "the results. For instance, if we apply this function to `3`, it applies\n", "both `(*2)` and `(+10)` to `3`, giving `6` and `13`. Then, it calls `(+)` with `6`\n", "and `13` and the result is `19`.\n", "\n", "Not only is the function type `(->) r` a functor and an applicative\n", "functor, but it's also a monad. Just like other monadic values that\n", "we've met so far, a function can also be considered a value with a\n", "context. The context for functions is that that value is not present yet\n", "and that we have to apply that function to something in order to get its\n", "result value.\n", "\n", "Because we're already acquainted with how functions work as functors and\n", "applicative functors, let's dive right in and see what their [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad)\n", "instance looks like. It's located in `Control.Monad.Instances` and it goes\n", "a little something like this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monad ((->) r) where\n",
    "    return x = \\_ -> x\n",
    "    h >>= f = \\w -> f (h w) w
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We've already seen how [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) is implemented for functions, and [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) is\n", "pretty much the same thing as [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure). It takes a value and puts it in a\n", "minimal context that always has that value as its result. And the only\n", "way to make a function that always has a certain value as its result is\n", "to make it completely ignore its parameter.\n", "\n", "The implementation for [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) seems a bit cryptic, but it's really not\n", "all that. When we use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to feed a monadic value to a function, the\n", "result is always a monadic value. So in this case, when we feed a\n", "function to another function, the result is a function as well. That's\n", "why the result starts off as a lambda. All of the implementations of\n", "[`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) so far always somehow isolated the result from the monadic value\n", "and then applied the function `f` to that result. The same thing happens\n", "here. To get the result from a function, we have to apply it to\n", "something, which is why we do `(h w)` here to get the result from the\n", "function and then we apply `f` to that. `f` returns a monadic value, which\n", "is a function in our case, so we apply it to `w` as well.\n", "\n", "If don't get how [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) works at this point, don't worry, because with\n", "examples we'll see how this is a really simple monad. Here's a `do`\n", "expression that utilizes this monad:" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad.Instances\n", "\n", "addStuff :: Int -> Int\n", "addStuff = do\n", " a <- (*2)\n", " b <- (+10)\n", " return (a+b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the same thing as the applicative expression that we wrote\n", "earlier, only now it relies on functions being monads. A `do` expression\n", "always results in a monadic value and this one is no different. The\n", "result of this monadic value is a function. What happens here is that it\n", "takes a number and then `(*2)` gets applied to that number and the result\n", "becomes `a`. `(+10)` is applied to the same number that `(*2)` got applied to\n", "and the result becomes `b`. [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return), like in other monads, doesn't have any\n", "other effect but to make a monadic value that presents some result. This\n", "presents `a+b` as the result of this function. If we test it out, we get\n", "the same result as before:" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "19" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "addStuff 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Both `(*2)` and `(+10)` get applied to the number `3` in this case.\n", "`return (a+b)` does as well, but it ignores it and always presents `a+b` as the\n", "result. For this reason, the function monad is also called the reader\n", "monad. All the functions read from a common source. To illustrate this\n", "even better, we can rewrite `addStuff` like so:" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "addStuff :: Int -> Int\n", "addStuff x = let\n", " a = (*2) x\n", " b = (+10) x\n", " in a+b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that the reader monad allows us to treat functions as values with\n", "a context. We can act as if we already know what the functions will\n", "return. It does this by gluing functions together into one function and\n", "then giving that function's parameter to all of the functions that it\n", "was glued from. So if we have a lot of functions that are all just\n", "missing one parameter and they'd eventually be applied to the same\n", "thing, we can use the reader monad to sort of extract their future\n", "results and the [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) implementation will make sure that it all works\n", "out.\n", "\n", "Tasteful stateful computations\n", "------------------------------\n", "\n", "\n", "\n", "Haskell is a pure language and because of that, our programs are made of\n", "functions that can't change any global state or variables, they can only\n", "do some computations and return them results. This restriction actually\n", "makes it easier to think about our programs, as it frees us from\n", "worrying what every variable's value is at some point in time. However,\n", "some problems are inherently stateful in that they rely on some state\n", "that changes over time. While such problems aren't a problem for\n", "Haskell, they can be a bit tedious to model sometimes. That's why\n", "Haskell features a thing called the state monad, which makes dealing\n", "with stateful problems a breeze while still keeping everything nice and\n", "pure.\n", "\n", "[When we were dealing with random numbers](http://learnyouahaskell.com/input-and-output#randomness),\n", "we dealt with functions that took a random generator as a parameter and\n", "returned a random number and a new random generator. If we wanted to\n", "generate several random numbers, we always had to use the random\n", "generator that a previous function returned along with its result. When\n", "making a function that takes a `StdGen` and tosses a coin three times\n", "based on that generator, we had to do this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
threeCoins :: StdGen -> (Bool, Bool, Bool)\n",
    "threeCoins gen =\n",
    "    let (firstCoin, newGen) = random gen\n",
    "        (secondCoin, newGen') = random newGen\n",
    "        (thirdCoin, newGen'') = random newGen'\n",
    "    in  (firstCoin, secondCoin, thirdCoin)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It took a generator `gen` and then `random gen` returned a [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) value along\n", "with a new generator. To throw the second coin, we used the new\n", "generator, and so on. In most other languages, we wouldn't have to\n", "return a new generator along with a random number. We could just modify\n", "the existing one! But since Haskell is pure, we can't do that, so we had\n", "to take some state, make a result from it and a new state and then use\n", "that new state to generate new results.\n", "\n", "You'd think that to avoid manually dealing with stateful computations in\n", "this way, we'd have to give up the purity of Haskell. Well, we don't\n", "have to, since there exist a special little monad called the state monad\n", "which handles all this state business for us and without giving up any\n", "of the purity that makes Haskell programming so cool.\n", "\n", "So, to help us understand this concept of stateful computations better,\n", "let's go ahead and give them a type. We'll say that a stateful\n", "computation is a function that takes some state and returns a value\n", "along with some new state. That function would have the following type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
s -> (a,s)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`s` is the type of the state and `a` the result of the stateful\n", "computations.\n", "\n", "> Assignment in most other languages could be thought of as a stateful\n", "> computation. For instance, when we do `x = 5` in an imperative language,\n", "> it will usually assign the value `5` to the variable `x` and it will also\n", "> have the value `5` as an expression. If you look at that functionally, you\n", "> could look at it as a function that takes a state (that is, all the\n", "> variables that have been assigned previously) and returns a result (in\n", "> this case `5`) and a new state, which would be all the previous variable\n", "> mappings plus the newly assigned variable.\n", "\n", "This stateful computation, a function that takes a state and returns a\n", "result and a new state, can be thought of as a value with a context as\n", "well. The actual value is the result, whereas the context is that we\n", "have to provide some initial state to actually get that result and that\n", "apart from getting a result we also get a new state.\n", "\n", "### Stacks and stones\n", "\n", "Say we want to model operating a stack. You have a stack of things one\n", "on top of another and you can either push stuff on top of that stack or\n", "you can take stuff off the top of the stack. When you're putting an item\n", "on top of the stack we say that you're pushing it to the stack and when\n", "you're taking stuff off the top we say that you're popping it. If you\n", "want to something that's at the bottom of the stack, you have to pop\n", "everything that's above it.\n", "\n", "We'll use a list to represent our stack and the head of the list will be\n", "the top of the stack. To help us with our task, we'll make two\n", "functions: `pop` and `push`. `pop` will take a stack, pop one item and return\n", "that item as the result and also return a new stack, without that item.\n", "`push` will take an item and a stack and then push that item onto the\n", "stack. It will return `()` as its result, along with a new stack. Here\n", "goes:" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type Stack = [Int]\n", "\n", "pop :: Stack -> (Int,Stack)\n", "pop (x:xs) = (x,xs)\n", "\n", "push :: Int -> Stack -> ((),Stack)\n", "push a xs = ((),a:xs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We used `()` as the result when pushing to the stack because pushing an\n", "item onto the stack doesn't have any important result value, its main\n", "job is to change the stack. Notice how we just apply the first parameter\n", "of `push`, we get a stateful computation. `pop` is already a stateful\n", "computation because of its type.\n", "\n", "Let's write a small piece of code to simulate a stack using these\n", "functions. We'll take a stack, push 3 to it and then pop two items, just\n", "for kicks. Here it is:" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "stackManip :: Stack -> (Int, Stack)\n", "stackManip stack = let\n", " ((),newStack1) = push 3 stack\n", " (a ,newStack2) = pop newStack1\n", " in pop newStack2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We take a `stack` and then we do `push 3 stack`, which results in a tuple.\n", "The first part of the tuple is a `()` and the second is a new stack and we\n", "call it `newStack1`. Then, we pop a number from `newStack1`, which results\n", "in a number `a` (which is the `3`) that we pushed and a new stack which we\n", "call `newStack2`. Then, we pop a number off `newStack2` and we get a number\n", "that's `b` and a `newStack3`. We return a tuple with that number and that\n", "stack. Let's try it out:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(5,[8,2,1])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "stackManip [5,8,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool, the result is `5` and the new stack is `[8,2,1]`. Notice how\n", "`stackManip` is itself a stateful computation. We've taken a bunch of\n", "stateful computations and we've sort of glued them together. Hmm, sounds\n", "familiar.\n", "\n", "The above code for `stackManip` is kind of tedious since we're manually\n", "giving the state to every stateful computation and storing it and then\n", "giving it to the next one. Wouldn't it be cooler if, instead of giving\n", "the stack manually to each function, we could write something like this:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "stackManip = do\n", " push 3\n", " a <- pop\n", " pop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well, using the state monad will allow us to do exactly this. With it,\n", "we will be able to take stateful computations like these and use them\n", "without having to manage the state manually.\n", "\n", "### The State monad\n", "\n", "The `Control.Monad.State` module provides a `newtype` that wraps stateful\n", "computations. Here's its definition:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
newtype State s a = State { runState :: s -> (a,s) }
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `State s a` is a stateful computation that manipulates a state of type `s`\n", "and has a result of type `a`.\n", "\n", "Now that we've seen what stateful computations are about and how they\n", "can even be thought of as values with contexts, let's check out their\n", "[`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monad (State s) where\n",
    "    return x = State $ \\s -> (x,s)\n",
    "    (State h) >>= f = State $ \\s -> let (a, newState) = h s\n",
    "                                        (State g) = f a\n",
    "                                    in  g newState
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's take a gander at [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) first. Our aim with [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) is to take a\n", "value and make a stateful computation that always has that value as its\n", "result. That's why we just make a lambda `\\s -> (x,s)`. We always\n", "present `x` as the result of the stateful computation and the state is\n", "kept unchanged, because [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) has to put a value in a minimal context.\n", "So [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) will make a stateful computation that presents a certain value\n", "as the result and keeps the state unchanged.\n", "\n", "\n", "\n", "What about [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-)? Well, the result of feeding a stateful computation to\n", "a function with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) has to be a stateful computation, right? So we\n", "start off with the `State` `newtype` wrapper and then we type out a lambda.\n", "This lambda will be our new stateful computation. But what goes on in\n", "it? Well, we somehow have to extract the result value from the first\n", "stateful computation. Because we're in a stateful computation right now,\n", "we can give the stateful computation `h` our current state `s`, which\n", "results in a pair of result and a new state: `(a, newState)`. Every time\n", "so far when we were implementing [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), once we had the extracted the\n", "result from the monadic value, we applied the function `f` to it to get\n", "the new monadic value. In `Writer`, after doing that and getting the new\n", "monadic value, we still had to make sure that the context was taken care\n", "of by [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend)ing the old monoid value with the new one. Here, we do `f a`\n", "and we get a new stateful computation `g`. Now that we have a new stateful\n", "computation and a new state (goes by the name of `newState`) we just apply\n", "that stateful computation `g` to the `newState`. The result is a tuple of\n", "final result and final state!\n", "\n", "So with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), we kind of glue two stateful computations together, only\n", "the second one is hidden inside a function that takes the previous one's\n", "result. Because `pop` and `push` are already stateful computations, it's\n", "easy to wrap them into a `State` wrapper. Watch:" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad.State\n", "\n", "pop :: State Stack Int\n", "pop = state $ \\(x:xs) -> (x,xs)\n", "\n", "push :: Int -> State Stack ()\n", "push a = state $ \\xs -> ((),a:xs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`pop` is already a stateful computation and `push` takes an [`Int`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Int) and returns\n", "a stateful computation. Now we can rewrite our previous example of\n", "pushing `3` onto the stack and then popping two numbers off like this:" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Control.Monad.State\n", "\n", "stackManip :: State Stack Int\n", "stackManip = do\n", " push 3\n", " a <- pop\n", " pop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See how we've glued a push and two pops into one stateful computation?\n", "When we unwrap it from its `newtype` wrapper we get a function to which we\n", "can provide some initial state:" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(5,[8,2,1])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runState stackManip [5,8,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We didn't have to bind the second `pop` to `a` because we didn't use that `a`\n", "at all. So we could have written it like this:" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "stackManip :: State Stack Int\n", "stackManip = do\n", " push 3\n", " pop\n", " pop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty cool. But what if we want to do this: pop one number off the\n", "stack and then if that number is `5` we just put it back onto the stack\n", "and stop but if it isn't `5`, we push `3` and `8` back on? Well, here's the\n", "code:" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "stackStuff :: State Stack ()\n", "stackStuff = do\n", " a <- pop\n", " if a == 5\n", " then push 5\n", " else do\n", " push 3\n", " push 8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is quite straightforward. Let's run it with an initial stack." ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "((),[8,3,0,2,1,0])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runState stackStuff [9,0,2,1,0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, `do` expressions result in monadic values and with the `State`\n", "monad, a single `do` expression is also a stateful function. Because\n", "`stackManip` and `stackStuff` are ordinary stateful computations, we can\n", "glue them together to produce further stateful computations." ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "moreStack :: State Stack ()\n", "moreStack = do\n", " a <- stackManip\n", " if a == 100\n", " then stackStuff\n", " else return ()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the result of `stackManip` on the current stack is `100`, we run\n", "`stackStuff`, otherwise we do nothing. `return ()` just keeps the state as\n", "it is and does nothing.\n", "\n", "The `Control.Monad.State` module provides a type class that's called\n", "`MonadState` and it features two pretty useful functions, namely [`get`](https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html#v:get) and\n", "`put`. For `State`, the [`get`](https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html#v:get) function is implemented like this:" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "get = state $ \\s -> (s,s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So it just takes the current state and presents it as the result. The\n", "`put` function takes some state and makes a stateful function that\n", "replaces the current state with it:" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "put newState = state $ \\s -> ((),newState)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So with these, we can see what the current stack is or we can replace it\n", "with a whole other stack. Like so:" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "stackyStack :: State Stack ()\n", "stackyStack = do\n", " stackNow <- get\n", " if stackNow == [1,2,3]\n", " then put [8,3,1]\n", " else put [9,2,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's worth examining what the type of [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) would be if it only worked\n", "for `State` values:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(>>=) :: State s a -> (a -> State s b) -> State s b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See how the type of the state `s` stays the same but the type of the\n", "result can change from `a` to `b`? This means that we can glue together\n", "several stateful computations whose results are of different types but\n", "the type of the state has to stay the same. Now why is that? Well, for\n", "instance, for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) has this type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It makes sense that the monad itself, [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), doesn't change. It wouldn't\n", "make sense to use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) between two different monads. Well, for the\n", "state monad, the monad is actually `State s`, so if that `s` was different,\n", "we'd be using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) between two different monads.\n", "\n", "### Randomness and the state monad\n", "\n", "At the beginning of this section, we saw how generating numbers can\n", "sometimes be awkward because every random function takes a generator and\n", "returns a random number along with a new generator, which must then be\n", "used instead of the old one if we want to generate another random\n", "number. The state monad makes dealing with this a lot easier.\n", "\n", "The `random` function from `System.Random` has the following type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
random :: (RandomGen g, Random a) => g -> (a, g)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Meaning it takes a random generator and produces a random number along\n", "with a new generator. We can see that it's a stateful computation, so we\n", "can wrap it in the `State` `newtype` constructor and then use it as a\n", "monadic value so that passing of the state gets handled for us:" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Random\n", "import Control.Monad.State\n", "\n", "randomSt :: (RandomGen g, Random a) => State g a\n", "randomSt = state random" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So now if we want to throw three coins ([`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) is tails, [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) is heads)\n", "we just do the following:" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import System.Random\n", "import Control.Monad.State\n", "\n", "threeCoins :: State StdGen (Bool,Bool,Bool)\n", "threeCoins = do\n", " a <- randomSt\n", " b <- randomSt\n", " c <- randomSt\n", " return (a,b,c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`threeCoins` is now a stateful computations and after taking an initial\n", "random generator, it passes it to the first `randomSt`, which produces a\n", "number and a new generator, which gets passed to the next one and so on.\n", "We use `return (a,b,c)` to present `(a,b,c)` as the result without changing\n", "the most recent generator. Let's give this a go:" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "((True,False,True),StdGen {unStdGen = SMGen 17473681083660280586 3174492301114349737})" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runState threeCoins (mkStdGen 33)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nice. Doing these sort of things that require some state to be kept in\n", "between steps just became much less of a hassle!\n", "\n", "Error error on the wall\n", "-----------------------\n", "\n", "We know by now that [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) is used to add a context of possible failure\n", "to values. A value can be a `Just something` or a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). However useful\n", "it may be, when we have a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), all we know is that there was some\n", "sort of failure, but there's no way to cram some more info in there\n", "telling us what kind of failure it was or why it failed.\n", "\n", "The `Either e a` type on the other hand, allows us to incorporate a\n", "context of possible failure to our values while also being able to\n", "attach values to the failure, so that they can describe what went wrong\n", "or provide some other useful info regarding the failure. An `Either e a`\n", "value can either be a [`Right`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Right) value, signifying the right answer and a\n", "success, or it can be a [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) value, signifying failure. For instance:" ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Right 4 :: forall {b} {a}. Num b => Either a b" ], "text/plain": [ "Right 4 :: forall {b} {a}. Num b => Either a b" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Right 4" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/html": [ "Left \"out of cheese error\" :: forall {b}. Either String b" ], "text/plain": [ "Left \"out of cheese error\" :: forall {b}. Either String b" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t Left \"out of cheese error\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is pretty much just an enhanced [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe), so it makes sense for it to\n", "be a monad, because it can also be viewed as a value with an added\n", "context of possible failure, only now there's a value attached when\n", "there's an error as well.\n", "\n", "Its [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance is similar to that of [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) and it can be found in\n", "[`Data.Either`](https://hackage.haskell.org/package/base/docs/Data-Either.html#t:Either):" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
instance Monad (Either e) where\n",
    "    return x = Right x\n",
    "    Right x >>= f = f x\n",
    "    Left err >>= f = Left err
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return), as always, takes a value and puts it in a default minimal\n", "context. It wraps our value in the [`Right`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Right) constructor because we're using\n", "[`Right`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Right) to represent a successful computation where a result is present.\n", "This is a lot like [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) for [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe).\n", "\n", "The [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) examines two possible cases: a [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) and a [`Right`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Right). In the case\n", "of a [`Right`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Right), the function `f` is applied to the value inside it, similar to\n", "how in the case of a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just), the function is just applied to its contents.\n", "In the case of an error, the [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) value is kept, along with its\n", "contents, which describe the failure." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Anyway, here are a few examples of usage:" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Left \"boom\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Left \"boom\" >>= \\x -> return (x+1)" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Left \"no way!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Right 100 >>= \\x -> Left \"no way!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we use [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to feed a [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) value to a function, the function is\n", "ignored and an identical [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) value is returned. When we feed a [`Right`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Right)\n", "value to a function, the function gets applied to what's on the inside,\n", "but in this case that function produced a [`Left`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Left) value anyway!" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" }, "tags": [] }, "outputs": [ { "data": { "text/plain": [ "Right 103" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Right 3 >>= \\x -> return (x + 100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using this monad is very similar to using\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) as a monad. In the previous chapter, we used the monadic aspects\n", "of [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) to simulate birds landing on the balancing pole of a tightrope\n", "walker. As an exercise, you can rewrite that with the error monad so\n", "that when the tightrope walker slips and falls, we remember how many\n", "birds were on each side of the pole when he fell.\n", "\n", "Some useful monadic functions\n", "-----------------------------\n", "\n", "In this section, we're going to explore a few functions that either\n", "operate on monadic values or return monadic values as their results (or\n", "both!). Such functions are usually referred to as monadic functions.\n", "While some of them will be brand new, others will be monadic\n", "counterparts of functions that we already know, like [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) and [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl).\n", "Let's see what they are then!\n", "\n", "### liftM and friends\n", "\n", "\n", "\n", "When we started our journey to the top of Monad Mountain, we first\n", "looked at functors, which are for things that can be mapped over. Then,\n", "we learned about improved functors called applicative functors, which\n", "allowed us to apply normal functions between several applicative values\n", "as well as to take a normal value and put it in some default context.\n", "Finally, we introduced monads as improved applicative functors, which\n", "added the ability for these values with context to somehow be fed into\n", "normal functions.\n", "\n", "So every monad is an applicative functor and every applicative functor\n", "is a functor. The [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) type class has a class constraint such\n", "that our type has to be an instance of [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) before we can make it an\n", "instance of [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative). But even though [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) should have the same\n", "constraint for [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative), as every monad is an applicative functor, it\n", "doesn't, because the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) type class was introduced to Haskell way\n", "before [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative).\n", "\n", "But even though every monad is a functor, we don't have to rely on it\n", "having a [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) instance because of the [`liftM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:liftM) function. [`liftM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:liftM) takes a\n", "function and a monadic value and maps it over the monadic value. So it's\n", "pretty much the same thing as [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap)! This is [`liftM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:liftM)'s type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
liftM :: (Monad m) => (a -> b) -> m a -> m b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And this is the type of [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap):" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
fmap :: (Functor f) => (a -> b) -> f a -> f b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) and [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instances for a type obey the functor and monad\n", "laws, these two amount to the same thing (and all the monads that we've\n", "met so far obey both). This is kind of like [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) and [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) do the same\n", "thing, only one has an [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) class constraint whereas the other\n", "has a [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) one. Let's try [`liftM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:liftM) out:" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 24" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "liftM (*3) (Just 8)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 24" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap (*3) (Just 8)" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(False,\"chickpeas\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runWriter $ liftM not $ writer (True, \"chickpeas\")" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(False,\"chickpeas\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runWriter $ fmap not $ writer (True, \"chickpeas\")" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(101,[2,3,4])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runState (liftM (+100) pop) [1,2,3,4]" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(101,[2,3,4])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runState (fmap (+100) pop) [1,2,3,4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We already know quite well how [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) works with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values. And [`liftM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:liftM)\n", "does the same thing. For `Writer` values, the function is mapped over the\n", "first component of the tuple, which is the result. Doing [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) or [`liftM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:liftM)\n", "over a stateful computation results in another stateful computation,\n", "only its eventual result is modified by the supplied function. Had we\n", "not mapped `(+100)` over `pop` in this case before running it, it would have\n", "returned `(1,[2,3,4])`.\n", "\n", "This is how [`liftM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:liftM) is implemented:" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "liftM :: (Monad m) => (a -> b) -> m a -> m b\n", "liftM f m = m >>= (\\x -> return (f x))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or with `do` notation:" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "liftM :: (Monad m) => (a -> b) -> m a -> m b\n", "liftM f m = do\n", " x <- m\n", " return (f x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We feed the monadic value `m` into the function and then we apply the\n", "function `f` to its result before putting it back into a default context.\n", "Because of the monad laws, this is guaranteed not to change the context,\n", "only the result that the monadic value presents. We see that [`liftM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:liftM) is\n", "implemented without referencing the [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) type class at all. This\n", "means that we can implement [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) (or [`liftM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:liftM), whatever you want to call\n", "it) just by using the goodies that monads offer us. Because of this, we\n", "can conclude that monads are stronger than just regular old functors.\n", "\n", "The [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) type class allows us to apply functions between values\n", "with contexts as if they were normal values. Like this:" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 8" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(+) <$> Just 3 <*> Just 5" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(+) <$> Just 3 <*> Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using this applicative style makes things pretty easy. [`<$>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--36--62-) is just\n", "[`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) and [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) is a function from the [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) type class that has\n", "the following type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So it's kind of like [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap), only the function itself is in a context. We\n", "have to somehow extract it from the context and map it over the `f a`\n", "value and then assemble the context back together. Because all functions\n", "are curried in Haskell by default, we can use the combination of [`<$>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--36--62-)\n", "and [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) to apply functions that take several parameters between\n", "applicative values.\n", "\n", "Anyway, it turns out that just like [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap), [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) can also be implemented\n", "by using only what the [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) type class give us. The [`ap`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:ap) function is\n", "basically [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-), only it has a [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) constraint instead of an\n", "[`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) one. Here's its definition:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
ap :: (Monad m) => m (a -> b) -> m a -> m b\n",
    "ap mf m = do\n",
    "    f <- mf\n",
    "    x <- m\n",
    "    return (f x)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`mf` is a monadic value whose result is a function. Because the function\n", "is in a context as well as the value, we get the function from the\n", "context and call it `f`, then get the value and call that `x` and then\n", "finally apply the function to the value and present that as a result.\n", "Here's a quick demonstration:" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 7" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just (+3) <*> Just 4" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 7" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "Just (+3) `ap` Just 4" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[11,12,12,13,13,14]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[(+1),(+2),(+3)] <*> [10,11]" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[11,12,12,13,13,14]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[(+1),(+2),(+3)] `ap` [10,11]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we see that monads are stronger than applicatives as well, because\n", "we can use the functions from [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) to implement the ones for\n", "[`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative). In fact, many times when a type is found to be a monad,\n", "people first write up a [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance and then make an [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative)\n", "instance by just saying that [`pure`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:pure) is [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) and [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) is [`ap`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:ap). Similarly,\n", "if you already have a [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance for something, you can give it a\n", "[`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) instance just saying that [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) is [`liftM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:liftM).\n", "\n", "The `liftA2` function is a convenience function for applying a function\n", "between two applicative values. It's defined simply like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c\n",
    "liftA2 f x y = f <$> x <*> y
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `liftM2` function does the same thing, only it has a [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) constraint.\n", "There also exist `liftM3` and `liftM4` and `liftM5`.\n", "\n", "We saw how monads are stronger than applicatives and functors and how\n", "even though all monads are functors and applicative functors, they don't\n", "necessarily have [`Functor`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Functor) and [`Applicative`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Applicative) instances, so we examined the\n", "monadic equivalents of the functions that functors and applicative\n", "functors use.\n", "\n", "### The join function\n", "\n", "Here's some food for thought: if the result of one monadic value is\n", "another monadic value i.e. if one monadic value is nested inside the\n", "other, can you flatten them to just a single normal monadic value? Like,\n", "if we have `Just (Just 9)`, can we make that into `Just 9`? It turns out\n", "that any nested monadic value can be flattened and that this is actually\n", "a property unique to monads. For this, the [`join`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:join) function exists. Its\n", "type is this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
join :: (Monad m) => m (m a) -> m a
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So it takes a monadic value within a monadic value and gives us just a\n", "monadic value, so it sort of flattens it. Here it is with some [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)\n", "values:" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 9" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "join (Just (Just 9))" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "join (Just Nothing)" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "join Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first line has a successful computation as a result of a successful\n", "computation, so they're both just joined into one big successful\n", "computation. The second line features a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) as a result of a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just)\n", "value. Whenever we were dealing with [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values before and we wanted\n", "to combine several of them into one, be it with [`<*>`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-60--42--62-) or [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), they\n", "all had to be [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) values for the result to be a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) value. If there\n", "was any failure along the way, the result was a failure and the same\n", "thing happens here. In the third line, we try to flatten what is from\n", "the onset a failure, so the result is a failure as well.\n", "\n", "Flattening lists is pretty intuitive:" ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5,6]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "join [[1,2,3],[4,5,6]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, for lists, [`join`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:join) is just [`concat`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:concat). To flatten a `Writer`\n", "value whose result is a `Writer` value itself, we have to [`mappend`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:mappend) the\n", "monoid value." ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(1,\"bbbaaa\")" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runWriter $ join (writer (writer (1,\"aaa\"),\"bbb\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The outer monoid value `\"bbb\"` comes first and then to it `\"aaa\"` is\n", "appended. Intuitively speaking, when you want to examine what the result\n", "of a `Writer` value is, you have to write its monoid value to the log\n", "first and only then can you examine what it has inside.\n", "\n", "Flattening [`Either`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Either) values is very similar to flattening [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values:" ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Right 9" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "join (Right (Right 9)) :: Either String Int" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Left \"error\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "join (Right (Left \"error\")) :: Either String Int" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Left \"error\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "join (Left \"error\") :: Either String Int" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we apply [`join`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:join) to a stateful computation whose result is a stateful\n", "computation, the result is a stateful computation that first runs the\n", "outer stateful computation and then the resulting one. Watch:" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "((),[10,1,2,0,0,0])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "runState (join (state $ \\s -> (push 10,1:2:s))) [0,0,0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The lambda here takes a state and puts `2` and `1` onto the stack and\n", "presents `push 10` as its result. So when this whole thing is flattened\n", "with [`join`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:join) and then run, it first puts `2` and `1` onto the stack and then\n", "`push 10` gets carried out, pushing a `10` on to the top.\n", "\n", "The implementation for [`join`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:join) is as follows:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
join :: (Monad m) => m (m a) -> m a\n",
    "join mm = do\n",
    "    m <- mm\n",
    "    m
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because the result of `mm` is a monadic value, we get that result and then\n", "just put it on a line of its own because it's a monadic value. The trick\n", "here is that when we do `m <- mm`, the context of the monad in which we\n", "are in gets taken care of. That's why, for instance, [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) values result\n", "in [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) values only if the outer and inner values are both [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) values.\n", "Here's what this would look like if the `mm` value was set in advance to\n", "`Just (Just 8)`:" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "joinedMaybes :: Maybe Int\n", "joinedMaybes = do\n", " m <- Just (Just 8)\n", " m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Perhaps the most interesting thing about [`join`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:join) is that for every monad,\n", "feeding a monadic value to a function with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) is the same thing as\n", "just mapping that function over the value and then using [`join`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:join) to flatten\n", "the resulting nested monadic value! In other words, `m >>= f` is always\n", "the same thing as `join (fmap f m)`! It makes sense when you think about\n", "it. With [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), we're always thinking about how to feed a monadic value\n", "to a function that takes a normal value but returns a monadic value. If\n", "we just map that function over the monadic value, we have a monadic\n", "value inside a monadic value. For instance, say we have `Just 9` and the\n", "function `\\x -> Just (x+1)`. If we map this function over `Just 9`, we're\n", "left with `Just (Just 10)`.\n", "\n", "The fact that `m >>= f` always equals `join (fmap f m)` is very useful if\n", "we're making our own [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance for some type because it's often\n", "easier to figure out how we would flatten a nested monadic value than\n", "figuring out how to implement [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-).\n", "\n", "### filterM\n", "\n", "The [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) function is pretty much the bread of Haskell programming ([`map`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:map)\n", "being the butter). It takes a predicate and a list to filter out and\n", "then returns a new list where only the elements that satisfy the\n", "predicate are kept. Its type is this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
filter :: (a -> Bool) -> [a] -> [a]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The predicate takes an element of the list and returns a [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) value.\n", "Now, what if the [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) value that it returned was actually a monadic\n", "value? Whoa! That is, what if it came with a context? Could that work?\n", "For instance, what if every [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) or a [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) value that the predicate\n", "produced also had an accompanying monoid value, like\n", "`[\"Accepted the number 5\"]` or `[\"3 is too small\"]`? That sounds like it could work. If\n", "that were the case, we'd expect the resulting list to also come with a\n", "log of all the log values that were produced along the way. So if the\n", "[`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) that the predicate returned came with a context, we'd expect the\n", "final resulting list to have some context attached as well, otherwise\n", "the context that each [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool) came with would be lost.\n", "\n", "The [`filterM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:filterM) function from `Control.Monad` does just what we want! Its type\n", "is this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The predicate returns a monadic value whose result is a [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool), but\n", "because it's a monadic value, its context can be anything from a\n", "possible failure to non-determinism and more! To ensure that the context\n", "is reflected in the final result, the result is also a monadic value.\n", "\n", "Let's take a list and only keep those values that are smaller than 4. To\n", "start, we'll just use the regular [`filter`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:filter) function:" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "filter (\\x -> x < 4) [9,1,5,2,10,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's pretty easy. Now, let's make a predicate that, aside from\n", "presenting a [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) or [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) result, also provides a log of what it did.\n", "Of course, we'll be using the `Writer` monad for this:" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "keepSmall :: Int -> Writer [String] Bool\n", "keepSmall x\n", " | x < 4 = do\n", " tell [\"Keeping \" ++ show x]\n", " return True\n", " | otherwise = do\n", " tell [show x ++ \" is too large, throwing it away\"]\n", " return False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead of just returning a [`Bool`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Bool), this function returns a\n", "`Writer [String] Bool`. It's a monadic predicate. Sounds fancy, doesn't it? If\n", "the number is smaller than `4` we report that we're keeping it and then\n", "`return True`.\n", "\n", "Now, let's give it to [`filterM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:filterM) along with a list. Because the predicate\n", "returns a `Writer` value, the resulting list will also be a `Writer` value." ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[1,2,3]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fst $ runWriter $ filterM keepSmall [9,1,5,2,10,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Examining the result of the resulting `Writer` value, we see that\n", "everything is in order. Now, let's print the log and see what we got:" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "9 is too large, throwing it away\n", "Keeping 1\n", "5 is too large, throwing it away\n", "Keeping 2\n", "10 is too large, throwing it away\n", "Keeping 3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mapM_ putStrLn $ snd $ runWriter $ filterM keepSmall [9,1,5,2,10,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Awesome. So just by providing a monadic predicate to [`filterM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:filterM), we were\n", "able to filter a list while taking advantage of the monadic context that\n", "we used.\n", "\n", "A very cool Haskell trick is using [`filterM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:filterM) to get the powerset of a list\n", "(if we think of them as sets for now). The powerset of some set is a set\n", "of all subsets of that set. So if we have a set like `[1,2,3]`, its\n", "powerset would include the following sets:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
[1,2,3]\n",
    "[1,2]\n",
    "[1,3]\n",
    "[1]\n",
    "[2,3]\n",
    "[2]\n",
    "[3]\n",
    "[]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In other words, getting a powerset is like getting all the combinations\n", "of keeping and throwing out elements from a set. `[2,3]` is like the\n", "original set, only we excluded the number `1`.\n", "\n", "To make a function that returns a powerset of some list, we're going to\n", "rely on non-determinism. We take the list `[1,2,3]` and then look at the\n", "first element, which is `1` and we ask ourselves: should we keep it or\n", "drop it? Well, we'd like to do both actually. So we are going to filter\n", "a list and we'll use a predicate that non-deterministically both keeps\n", "and drops every element from the list. Here's our `powerset` function:" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "powerset :: [a] -> [[a]]\n", "powerset xs = filterM (\\x -> [True, False]) xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wait, that's it? Yup. We choose to drop and keep every element,\n", "regardless of what that element is. We have a non-deterministic\n", "predicate, so the resulting list will also be a non-deterministic value\n", "and will thus be a list of lists. Let's give this a go:" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[[1,2,3],[1,2],[1,3],[1],[2,3],[2],[3],[]]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "powerset [1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This takes a bit of thinking to wrap your head around, but if you just\n", "consider lists as non-deterministic values that don't know what to be so\n", "they just decide to be everything at once, it's a bit easier.\n", "\n", "### foldM\n", "\n", "The monadic counterpart to [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl) is [`foldM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:foldM). If you remember your folds\n", "from the [folds section](http://learnyouahaskell.com/higher-order-functions#folds), you know that [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl) takes a binary\n", "function, a starting accumulator and a list to fold up and then folds it\n", "from the left into a single value by using the binary function. [`foldM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:foldM)\n", "does the same thing, except it takes a binary function that produces a\n", "monadic value and folds the list up with that. Unsurprisingly, the\n", "resulting value is also monadic. The type of [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl) is this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
foldl :: (a -> b -> a) -> a -> [b] -> a
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whereas [`foldM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:foldM) has the following type:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The value that the binary function returns is monadic and so the result\n", "of the whole fold is monadic as well. Let's sum a list of numbers with a\n", "fold:" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "14" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldl (\\acc x -> acc + x) 0 [2,8,3,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The starting accumulator is `0` and then `2` gets added to the accumulator,\n", "resulting in a new accumulator that has a value of `2`. `8` gets added to\n", "this accumulator resulting in an accumulator of `10` and so on and when we\n", "reach the end, the final accumulator is the result.\n", "\n", "Now what if we wanted to sum a list of numbers but with the added\n", "condition that if any number is greater than `9` in the list, the whole\n", "thing fails? It would make sense to use a binary function that checks if\n", "the current number is greater than `9` and if it is, fails, and if it\n", "isn't, continues on its merry way. Because of this added possibility of\n", "failure, let's make our binary function return a [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) accumulator\n", "instead of a normal one. Here's the binary function:" ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "binSmalls :: Int -> Int -> Maybe Int\n", "binSmalls acc x\n", " | x > 9 = Nothing\n", " | otherwise = Just (acc + x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because our binary function is now a monadic function, we can't use it\n", "with the normal [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl), but we have to use [`foldM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:foldM). Here goes:" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 14" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldM binSmalls 0 [2,8,3,1]" ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldM binSmalls 0 [2,11,3,1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Excellent! Because one number in the list was greater than `9`, the whole\n", "thing resulted in a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). Folding with a binary function that returns\n", "a `Writer` value is cool as well because then you log whatever you want as\n", "your fold goes along its way.\n", "\n", "### Making a safe RPN calculator\n", "\n", "\n", "\n", "When we were solving the problem of [implementing a RPN\n", "calculator](http://learnyouahaskell.com/functionally-solving-problems#reverse-polish-notation-calculator), we noted that it worked\n", "fine as long as the input that it got made sense. But if something went\n", "wrong, it caused our whole program to crash. Now that we know how to\n", "take some code that we have and make it monadic, let's take our RPN\n", "calculator and add error handling to it by taking advantage of the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe)\n", "monad.\n", "\n", "We implemented our RPN calculator by taking a string like `\"1 3 + 2 *\"`,\n", "breaking it up into words to get something like `[\"1\",\"3\",\"+\",\"2\",\"*\"]`\n", "and then folding over that list by starting out with an empty stack and\n", "then using a binary folding function that adds numbers to the stack or\n", "manipulates numbers on the top of the stack to add them together and\n", "divide them and such.\n", "\n", "This was the main body of our function:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
import Data.List\n",
    "\n",
    "solveRPN :: String -> Double\n",
    "solveRPN = head . foldl foldingFunction [] . words
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We made the expression into a list of strings, folded over it with our\n", "folding function and then when we were left with just one item in the\n", "stack, we returned that item as the answer. This was the folding\n", "function:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
foldingFunction :: [Double] -> String -> [Double]\n",
    "foldingFunction (x:y:ys) \"*\" = (x * y):ys\n",
    "foldingFunction (x:y:ys) \"+\" = (x + y):ys\n",
    "foldingFunction (x:y:ys) \"-\" = (y - x):ys\n",
    "foldingFunction xs numberString = read numberString:xs
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The accumulator of the fold was a stack, which we represented with a\n", "list of [`Double`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Double) values. As the folding function went over the RPN\n", "expression, if the current item was an operator, it took two items off\n", "the top of the stack, applied the operator between them and then put the\n", "result back on the stack. If the current item was a string that\n", "represented a number, it converted that string into an actual number and\n", "returned a new stack that was like the old one, except with that number\n", "pushed to the top.\n", "\n", "Let's first make our folding function capable of graceful failure. Its\n", "type is going to change from what it is now to this:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
foldingFunction :: [Double] -> String -> Maybe [Double]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So it will either return [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) a new stack or it will fail with [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "\n", "The [`reads`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reads) function is like [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read), only it returns a list with a single\n", "element in case of a successful read. If it fails to read something,\n", "then it returns an empty list. Apart from returning the value that it\n", "read, it also returns the part of the string that it didn't consume.\n", "We're going to say that it always has to consume the full input to work\n", "and make it into a [`readMaybe`](https://hackage.haskell.org/package/base/docs/Text-Read.html#v:readMaybe) function for convenience. Here it is:" ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "readMaybe :: (Read a) => String -> Maybe a\n", "readMaybe st = case reads st of [(x,\"\")] -> Just x\n", " _ -> Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Testing it out:" ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "readMaybe \"1\" :: Maybe Int" ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "readMaybe \"GO TO HELL\" :: Maybe Int" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Okay, it seems to work. So, let's make our folding function into a\n", "monadic function that can fail:" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "foldingFunction :: [Double] -> String -> Maybe [Double]\n", "foldingFunction (x:y:ys) \"*\" = return ((x * y):ys)\n", "foldingFunction (x:y:ys) \"+\" = return ((x + y):ys)\n", "foldingFunction (x:y:ys) \"-\" = return ((y - x):ys)\n", "foldingFunction xs numberString = liftM (:xs) (readMaybe numberString)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first three cases are like the old ones, except the new stack gets\n", "wrapped in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) (we used [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) here to do this, but we could have\n", "written [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) just as well). In the last case, we do\n", "`readMaybe numberString` and then we map `(:xs)` over it. So if the stack `xs` is\n", "`[1.0,2.0]` and `readMaybe numberString` results in a `Just 3.0`, the result\n", "is `Just [3.0,1.0,2.0]`. If `readMaybe numberString` results in a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing)\n", "then the result is [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing). Let's try out the folding function by\n", "itself:" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just [6.0]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldingFunction [3,2] \"*\"" ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just [-1.0]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldingFunction [3,2] \"-\"" ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldingFunction [] \"*\"" ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just [1.0]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldingFunction [] \"1\"" ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foldingFunction [] \"1 wawawawa\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It looks like it's working! And now it's time for the new and improved\n", "`solveRPN`. Here it is ladies and gents!" ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.List\n", "\n", "solveRPN :: String -> Maybe Double\n", "solveRPN st = do\n", " [result] <- foldM foldingFunction [] (words st)\n", " return result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just like before, we take the string and make it into a list of words.\n", "Then, we do a fold, starting with the empty stack, only instead of doing\n", "a normal [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl), we do a [`foldM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:foldM). The result of that [`foldM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:foldM) should be a\n", "[`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value that contains a list (that's our final stack) and that list\n", "should have only one value. We use a `do` expression to get that value and\n", "we call it `result`. In case the [`foldM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:foldM) returns a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), the whole thing\n", "will be a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing), because that's how [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) works. Also notice that we\n", "pattern match in the `do` expression, so if the list has more than one\n", "value or none at all, the pattern match fails and a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing) is produced.\n", "In the last line we just do `return result` to present the result of the\n", "RPN calculation as the result of the final [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) value.\n", "\n", "Let's give it a shot:" ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 6.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"1 2 * 4 +\"" ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 30.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"1 2 * 4 + 5 *\"" ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"1 2 * 4\"" ] }, { "cell_type": "code", "execution_count": 120, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "solveRPN \"1 8 wharglbllargh\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first failure happens because the final stack isn't a list with one\n", "element in it and so the pattern matching in the `do` expression fails.\n", "The second failure happens because [`readMaybe`](https://hackage.haskell.org/package/base/docs/Text-Read.html#v:readMaybe) returns a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "\n", "### Composing monadic functions\n", "\n", "When we were learning about the monad laws, we said that the [`<=<`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:-60--61--60-)\n", "function is just like composition, only instead of working for normal\n", "functions like `a -> b`, it works for monadic functions like `a -> m b`.\n", "For instance:" ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "401" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let f = (+1) . (*100)\n", "f 4" ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just 401" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let g = (\\x -> return (x+1)) <=< (\\x -> return (x*100))\n", "Just 4 >>= g" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this example we first composed two normal functions, applied the\n", "resulting function to `4` and then we composed two monadic functions and\n", "fed `Just 4` to the resulting function with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-).\n", "\n", "If we have a bunch of functions in a list, we can compose them one all\n", "into one big function by just using [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) as the starting accumulator and\n", "the [`.`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:.) function as the binary function. Here's an example:" ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "201" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let f = foldr (.) id [(+1),(*100),(+1)]\n", "f 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function `f` takes a number and then adds `1` to it, multiplies the\n", "result by `100` and then adds `1` to that. Anyway, we can compose monadic\n", "functions in the same way, only instead of normal composition we use [`<=<`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:-60--61--60-)\n", "and instead of [`id`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:id) we use [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return). We don't have to use a [`foldM`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:foldM) over a\n", "[`foldr`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldr) or anything because the [`<=<`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:-60--61--60-) function makes sure that composition\n", "happens in a monadic fashion.\n", "\n", "When we were getting to know the list monad in the [previous\n", "chapter](http://learnyouahaskell.com/a-fistful-of-monads#the-list-monad), we used it to figure out\n", "if a knight can go from one position on a chessboard to another in\n", "exactly three moves. We had a function called `moveKnight` which took the\n", "knight's position on the board and returned all the possible moves that\n", "he can make next. Then, to generate all the possible positions that he\n", "can have after taking three moves, we made the following function:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
in3 start = return start >>= moveKnight >>= moveKnight >>= moveKnight
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And to check if he can go from `start` to `end` in three moves, we did the\n", "following:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
canReachIn3 :: KnightPos -> KnightPos -> Bool\n",
    "canReachIn3 start end = end `elem` in3 start
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using monadic function composition, we can make a function like `in3`,\n", "only instead of generating all the positions that the knight can have\n", "after making three moves, we can do it for an arbitrary number of moves.\n", "If you look at in3, we see that we used `moveKnight` three times and each\n", "time we used [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to feed it all the possible previous positions. So\n", "now, let's make it more general. Here's how to do it:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
import Data.List\n",
    "\n",
    "inMany :: Int -> KnightPos -> [KnightPos]\n",
    "inMany x start = return start >>= foldr (<=<) return (replicate x moveKnight)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we use [`replicate`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:replicate) to make a list that contains `x` copies of the\n", "function `moveKnight`. Then, we monadically compose all those functions\n", "into one, which gives us a function that takes a starting position and\n", "non-deterministically moves the knight `x` times. Then, we just make the\n", "starting position into a singleton list with [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) and feed it to the\n", "function.\n", "\n", "Now, we can change our `canReachIn3` function to be more general as well:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
canReachIn :: Int -> KnightPos -> KnightPos -> Bool\n",
    "canReachIn x start end = end `elem` inMany x start
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Making monads\n", "-------------\n", "\n", "\n", "\n", "In this section, we're going to look at an example of how a type gets\n", "made, identified as a monad and then given the appropriate [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad)\n", "instance. We don't usually set out to make a monad with the sole purpose\n", "of making a monad. Instead, we usually make a type whose purpose is\n", "to model an aspect of some problem and then later on if we see that the\n", "type represents a value with a context and can act like a monad, we give\n", "it a [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance.\n", "\n", "As we've seen, lists are used to represent non-deterministic values. A\n", "list like `[3,5,9]` can be viewed as a single non-deterministic value that\n", "just can't decide what it's going to be. When we feed a list into a\n", "function with [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), it just makes all the possible choices of taking an\n", "element from the list and applying the function to it and then presents\n", "those results in a list as well.\n", "\n", "If we look at the list `[3,5,9]` as the numbers `3`, `5` and `9` occurring at\n", "once, we might notice that there's no info regarding the probability\n", "that each of those numbers occurs. What if we wanted to model a\n", "non-deterministic value like `[3,5,9]`, but we wanted to express that `3`\n", "has a 50% chance of happening and `5` and `9` both have a 25% chance of\n", "happening? Let's try and make this happen!\n", "\n", "Let's say that every item in the list comes with another value, a\n", "probability of it happening. It might make sense to present this like\n", "this then:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
[(3,0.5),(5,0.25),(9,0.25)]
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In mathematics, probabilities aren't usually expressed in percentages,\n", "but rather in real numbers between a 0 and 1. A 0 means that there's no\n", "chance in hell for something to happen and a 1 means that it's happening\n", "for sure. Floating point numbers can get real messy real fast because\n", "they tend to lose precision, so Haskell offers us a data type for\n", "rational numbers that doesn't lose precision. That type is called\n", "[`Rational`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Rational) and it lives in `Data.Ratio`. To make a [`Rational`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Rational), we write it as\n", "if it were a fraction. The numerator and the denominator are separated\n", "by a [`%`](https://hackage.haskell.org/package/base/docs/Data-Ratio.html#v:-37-). Here are a few examples:" ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Ratio" ] }, { "cell_type": "code", "execution_count": 125, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1 % 4" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "1%4" ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "1 % 1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "1%2 + 1%2" ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "19 % 12" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "1%3 + 5%4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first line is just one quarter. In the second line we add two halves\n", "to get a whole and in the third line we add one third with five quarters\n", "and get nineteen twelfths. So let'use throw out our floating points and\n", "use [`Rational`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Rational) for our probabilities:" ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(3,1 % 2),(5,1 % 4),(9,1 % 4)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "[(3,1%2),(5,1%4),(9,1%4)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Okay, so `3` has a one out of two chance of happening while `5` and `9` will\n", "happen one time out of four. Pretty neat.\n", "\n", "We took lists and we added some extra context to them, so this\n", "represents values with contexts too. Before we go any further, let's\n", "wrap this into a `newtype` because something tells me we'll be making some\n", "instances." ] }, { "cell_type": "code", "execution_count": 129, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.Ratio\n", "\n", "newtype Prob a = Prob { getProb :: [(a,Rational)] } deriving Show" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alright. Is this a functor? Well, the list is a functor, so this should\n", "probably be a functor as well, because we just added some stuff to the\n", "list. When we map a function over a list, we apply it to each element.\n", "Here, we'll apply it to each element as well, only we'll leave the\n", "probabilities as they are. Let's make an instance:" ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Functor Prob where\n", " fmap f (Prob xs) = Prob $ map (\\(x,p) -> (f x,p)) xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We unwrap it from the `newtype` with pattern matching, apply the function\n", "`f` to the values while keeping the probabilities as they are and then\n", "wrap it back up. Let's see if it works:" ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Prob {getProb = [(-3,1 % 2),(-5,1 % 4),(-9,1 % 4)]}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fmap negate (Prob [(3,1%2),(5,1%4),(9,1%4)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another thing to note is that the probabilities should always add up to\n", "`1`. If those are all the things that can happen, it doesn't make sense\n", "for the sum of their probabilities to be anything other than `1`. A coin\n", "that lands tails 75% of the time and heads 50% of the time seems like it\n", "could only work in some other strange universe.\n", "\n", "Now the big question, is this a monad? Given how the list is a monad,\n", "this looks like it should be a monad as well. First, let's think about\n", "[`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return). How does it work for lists? It takes a value and puts it in a\n", "singleton list. What about here? Well, since it's supposed to be a\n", "default minimal context, it should also make a singleton list. What\n", "about the probability? Well, `return x` is supposed to make a monadic\n", "value that always presents `x` as its result, so it doesn't make sense for\n", "the probability to be `0`. If it always has to present it as its result,\n", "the probability should be `1`!\n", "\n", "What about [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-)? Seems kind of tricky, so let's make use of the fact\n", "that `m >>= f` always equals `join (fmap f m)` for monads and think about\n", "how we would flatten a probability list of probability lists. As an\n", "example, let's consider this list where there's a 25% chance that\n", "exactly one of `'a'` or `'b'` will happen. Both `'a'` and `'b'` are equally\n", "likely to occur. Also, there's a 75% chance that exactly one of `'c'` or\n", "`'d'` will happen. `'c'` and `'d'` are also equally likely to happen. Here's a\n", "picture of a probability list that models this scenario:\n", "\n", "\n", "\n", "What are the chances for each of these letters to occur? If we were to\n", "draw this as just four boxes, each with a probability, what would those\n", "probabilities be? To find out, all we have to do is multiply each\n", "probability with all of probabilities that it contains. `'a'` would occur\n", "one time out of eight, as would `'b'`, because if we multiply one half by\n", "one quarter we get one eighth. `'c'` would happen three times out of eight\n", "because three quarters multiplied by one half is three eighths. `'d'`\n", "would also happen three times out of eight. If we sum all the\n", "probabilities, they still add up to one.\n", "\n", "Here's this situation expressed as a probability list:" ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "thisSituation :: Prob (Prob Char)\n", "thisSituation = Prob\n", " [( Prob [('a',1%2),('b',1%2)] , 1%4 )\n", " ,( Prob [('c',1%2),('d',1%2)] , 3%4)\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that its type is `Prob (Prob Char)`. So now that we've figured out\n", "how to flatten a nested probability list, all we have to do is write the\n", "code for this and then we can write [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) simply as `join (fmap f m)` and\n", "we have ourselves a monad! So here's `flatten`, which we'll use because\n", "the name [`join`](https://hackage.haskell.org/package/base/docs/Control-Monad.html#v:join) is already taken:" ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "flatten :: Prob (Prob a) -> Prob a\n", "flatten (Prob xs) = Prob $ concat $ map multAll xs\n", " where multAll (Prob innerxs,p) = map (\\(x,r) -> (x,p*r)) innerxs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function `multAll` takes a tuple of probability list and a probability\n", "`p` that comes with it and then multiplies every inner probability with `p`,\n", "returning a list of pairs of items and probabilities. We map `multAll`\n", "over each pair in our nested probability list and then we just flatten\n", "the resulting nested list.\n", "\n", "Now we have all that we need, we can write a [`Monad`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Monad) instance!" ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Applicative Prob" ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "instance Monad Prob where\n", " return x = Prob [(x,1%1)]\n", " m >>= f = flatten (fmap f m)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Because we already did all the hard work, the instance is very simple.\n", "\n", "It's also important to check if the monad laws hold for the monad that\n", "we just made. The first one says that `return x >>= f` should be equal\n", "to `f x`. A rigorous proof would be rather tedious, but we can see that if\n", "we put a value in a default context with [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) and then [`fmap`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:fmap) a function\n", "over that and flatten the resulting probability list, every probability\n", "that results from the function would be multiplied by the `1%1`\n", "probability that we made with [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return), so it wouldn't affect the context.\n", "The reasoning for `m >>= return` being equal to just `m` is similar. The\n", "third law states that `f <=< (g <=< h)` should be the same as\n", "`(f <=< g) <=< h`. This one holds as well, because it holds for the list monad\n", "which forms the basis of the probability monad and because\n", "multiplication is associative. `1%2 * (1%3 * 1%5)` is equal to\n", "`(1%2 * 1%3) * 1%5`.\n", "\n", "Now that we have a monad, what can we do with it? Well, it can help us\n", "do calculations with probabilities. We can treat probabilistic events as\n", "values with contexts and the probability monad will make sure that those\n", "probabilities get reflected in the probabilities of the final result.\n", "\n", "Say we have two normal coins and one loaded coin that gets tails an\n", "astounding nine times out of ten and heads only one time out of ten. If\n", "we throw all the coins at once, what are the odds of all of them landing\n", "tails? First, let's make probability values for a normal coin flip and\n", "for a loaded one:" ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Coin = Heads | Tails deriving (Show, Eq)\n", "\n", "coin :: Prob Coin\n", "coin = Prob [(Heads,1%2),(Tails,1%2)]\n", "\n", "loadedCoin :: Prob Coin\n", "loadedCoin = Prob [(Heads,1%10),(Tails,9%10)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And finally, the coin throwing action:" ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.List (all)\n", "\n", "flipThree :: Prob Bool\n", "flipThree = do\n", " a <- coin\n", " b <- coin\n", " c <- loadedCoin\n", " return (all (==Tails) [a,b,c])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Giving it a go, we see that the odds of all three landing tails are not\n", "that good, despite cheating with our loaded coin:" ] }, { "cell_type": "code", "execution_count": 138, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "[(False,1 % 40),(False,9 % 40),(False,1 % 40),(False,9 % 40),(False,1 % 40),(False,9 % 40),(False,1 % 40),(True,9 % 40)]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getProb flipThree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All three of them will land tails nine times out of forty, which is less\n", "than 25%. We see that our monad doesn't know how to join all of the\n", "[`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False) outcomes where all coins don't land tails into one outcome. That's\n", "not a big problem, since writing a function to put all the same outcomes\n", "into one outcome is pretty easy and is left as an exercise to the reader\n", "(you!)\n", "\n", "In this section, we went from having a question (what if lists also\n", "carried information about probability?) to making a type, recognizing a\n", "monad and finally making an instance and doing something with it. I\n", "think that's quite fetching! By now, we should have a pretty good grasp\n", "on monads and what they're about." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook/14-zippers.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Zippers\n", "=======\n", "\n", "\n", "\n", "While Haskell's purity comes with a whole bunch of benefits, it makes us\n", "tackle some problems differently than we would in impure languages.\n", "Because of referential transparency, one value is as good as another in\n", "Haskell if it represents the same thing.\n", "\n", "So if we have a tree full of fives (high-fives, maybe?) and we want to\n", "change one of them into a six, we have to have some way of knowing\n", "exactly which five in our tree we want to change. We have to know where\n", "it is in our tree. In impure languages, we could just note where in our\n", "memory the five is located and change that. But in Haskell, one five is\n", "as good as another, so we can't discriminate based on where in our\n", "memory they are. We also can't really *change* anything; when we say\n", "that we change a tree, we actually mean that we take a tree and return a\n", "new one that's similar to the original tree, but slightly different.\n", "\n", "One thing we can do is to remember a path from the root of the tree to\n", "the element that we want to change. We could say, take this tree, go\n", "left, go right and then left again and change the element that's there.\n", "While this works, it can be inefficient. If we want to later change an\n", "element that's near the element that we previously changed, we have to\n", "walk all the way from the root of the tree to our element again!\n", "\n", "In this chapter, we'll see how we can take some data structure and focus\n", "on a part of it in a way that makes changing its elements easy and\n", "walking around it efficient. Nice!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> __Jupyter Note:__ We'll turn off the [automatic linting for IHaskell](https://github.com/IHaskell/IHaskell/wiki#opt-no-lint) first." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ ":opt no-lint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Taking a walk\n", "-------------\n", "\n", "Like we've learned in biology class, there are many different kinds of\n", "trees, so let's pick a seed that we will use to plant ours. Here it is:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So our tree is either empty or it's a node that has an element and two\n", "sub-trees. Here's a fine example of such a tree, which I give to you,\n", "the reader, for free!" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "freeTree :: Tree Char\n", "freeTree =\n", " Node 'P'\n", " (Node 'O'\n", " (Node 'L'\n", " (Node 'N' Empty Empty)\n", " (Node 'T' Empty Empty)\n", " )\n", " (Node 'Y'\n", " (Node 'S' Empty Empty)\n", " (Node 'A' Empty Empty)\n", " )\n", " )\n", " (Node 'L'\n", " (Node 'W'\n", " (Node 'C' Empty Empty)\n", " (Node 'R' Empty Empty)\n", " )\n", " (Node 'A'\n", " (Node 'A' Empty Empty)\n", " (Node 'C' Empty Empty)\n", " )\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And here's this tree represented graphically:\n", "\n", "\n", "\n", "Notice that `W` in the tree there? Say we want to change it into a `P`. How\n", "would we go about doing that? Well, one way would be to pattern match on\n", "our tree until we find the element that's located by first going right\n", "and then left and changing said element. Here's the code for this:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "changeToP :: Tree Char -> Tree Char\n", "changeToP (Node x l (Node y (Node _ m n) r)) = Node x l (Node y (Node 'P' m n) r)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yuck! Not only is this rather ugly, it's also kind of confusing. What\n", "happens here? Well, we pattern match on our tree and name its root\n", "element `x` (that's becomes the `'P'` in the root) and its left sub-tree `l`.\n", "Instead of giving a name to its right sub-tree, we further pattern match\n", "on it. We continue this pattern matching until we reach the sub-tree\n", "whose root is our `'W'`. Once we've done this, we rebuild the tree, only\n", "the sub-tree that contained the 'W' at its root now has a `'P'`.\n", "\n", "Is there a better way of doing this? How about we make our function take\n", "a tree along with a list of directions. The directions will be either `L`\n", "or [`R`](https://hackage.haskell.org/package/base/docs/GHC-Generics.html#t:R), representing left and right respectively, and we'll change the\n", "element that we arrive at if we follow the supplied directions. Here it\n", "is:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Direction = L | R deriving (Show)\n", "type Directions = [Direction]\n", "\n", "changeToP :: Directions-> Tree Char -> Tree Char\n", "changeToP (L:ds) (Node x l r) = Node x (changeToP ds l) r\n", "changeToP (R:ds) (Node x l r) = Node x l (changeToP ds r)\n", "changeToP [] (Node _ l r) = Node 'P' l r" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the first element in the our list of directions is `L`, we construct a\n", "new tree that's like the old tree, only its left sub-tree has an element\n", "changed to `'P'`. When we recursively call `changeToP`, we give it only the\n", "tail of the list of directions, because we already took a left. We do\n", "the same thing in the case of an [`R`](https://hackage.haskell.org/package/base/docs/GHC-Generics.html#t:R). If the list of directions is empty,\n", "that means that we're at our destination, so we return a tree that's\n", "like the one supplied, only it has `'P'` as its root element.\n", "\n", "To avoid printing out the whole tree, let's make a function that takes a\n", "list of directions and tells us what the element at the destination is:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "elemAt :: Directions -> Tree a -> a\n", "elemAt (L:ds) (Node _ l _) = elemAt ds l\n", "elemAt (R:ds) (Node _ _ r) = elemAt ds r\n", "elemAt [] (Node x _ _) = x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This function is actually quite similar to `changeToP`, only instead of\n", "remembering stuff along the way and reconstructing the tree, it ignores\n", "everything except its destination. Here we change the `'W'` to a `'P'` and\n", "see if the change in our new tree sticks:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "'P'" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let newTree = changeToP [R,L] freeTree\n", "elemAt [R,L] newTree" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nice, this seems to work. In these functions, the list of directions\n", "acts as a sort of *focus*, because it pinpoints one exact sub-tree from\n", "our tree. A direction list of `[R]` focuses on the sub-tree that's right\n", "of the root, for example. An empty direction list focuses on the main\n", "tree itself.\n", "\n", "While this technique may seem cool, it can be rather inefficient,\n", "especially if we want to repeatedly change elements. Say we have a\n", "really huge tree and a long direction list that points to some element\n", "all the way at the bottom of the tree. We use the direction list to take\n", "a walk along the tree and change an element at the bottom. If we want to\n", "change another element that's close to the element that we've just\n", "changed, we have to start from the root of the tree and walk all the way\n", "to the bottom again! What a drag.\n", "\n", "In the next section, we'll find a better way of focusing on a sub-tree,\n", "one that allows us to efficiently switch focus to sub-trees that are\n", "nearby.\n", "\n", "A trail of breadcrumbs\n", "----------------------\n", "\n", "\n", "\n", "Okay, so for focusing on a sub-tree, we want something better than just\n", "a list of directions that we always follow from the root of our tree.\n", "Would it help if we start at the root of the tree and move either left\n", "or right one step at a time and sort of leave breadcrumbs? That is, when\n", "we go left, we remember that we went left and when we go right, we\n", "remember that we went right. Sure, we can try that.\n", "\n", "To represent our breadcrumbs, we'll also use a list of `Direction` (which\n", "is either `L` or [`R`](https://hackage.haskell.org/package/base/docs/GHC-Generics.html#t:R)), only instead of calling it `Directions`, we'll call it\n", "`Breadcrumbs` , because our directions will now be reversed since we're\n", "leaving them as we go down our tree:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type Breadcrumbs = [Direction]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here's a function that takes a tree and some breadcrumbs and moves to\n", "the left sub-tree while adding `L` to the head of the list that represents\n", "our breadcrumbs:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "goLeft :: (Tree a, Breadcrumbs) -> (Tree a, Breadcrumbs)\n", "goLeft (Node _ l _, bs) = (l, L:bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We ignore the element at the root and the right sub-tree and just return\n", "the left sub-tree along with the old breadcrumbs with `L` as the head.\n", "Here's a function to go right:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "goRight :: (Tree a, Breadcrumbs) -> (Tree a, Breadcrumbs)\n", "goRight (Node _ _ r, bs) = (r, R:bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It works the same way. Let's use these functions to take our `freeTree`\n", "and go right and then left:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "goLeft (goRight (freeTree, []))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Okay, so now we have a tree that has `'W'` in its root and `'C'` in the root\n", "of its left sub-tree and `'R'` in the root of its right sub-tree. The\n", "breadcrumbs are `[L,R]`, because we first went right and then left.\n", "\n", "To make walking along our tree clearer, we can use the `-:` function that\n", "we defined like so:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "x -: f = f x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Which allows us to apply functions to values by first writing the value,\n", "then writing a `-:` and then the function. So instead of\n", "`goRight (freeTree, [])`, we can write `(freeTree, []) -: goRight`. Using this, we\n", "can rewrite the above so that it's more apparent that we're first going\n", "right and then left:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(freeTree, []) -: goRight -: goLeft" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Going back up\n", "\n", "What if we now want to go back up in our tree? From our breadcrumbs we\n", "know that the current tree is the left sub-tree of its parent and that\n", "it is the right sub-tree of its parent, but that's it. They don't tell\n", "us enough about the parent of the current sub-tree for us to be able to\n", "go up in the tree. It would seem that apart from the direction that we\n", "took, a single breadcrumb should also contain all other data that we\n", "need to go back up. In this case, that's the element in the parent tree\n", "along with its right sub-tree.\n", "\n", "In general, a single breadcrumb should contain all the data needed to\n", "reconstruct the parent node. So it should have the information from all\n", "the paths that we didn't take and it should also know the direction that\n", "we did take, but it must not contain the sub-tree that we're currently\n", "focusing on. That's because we already have that sub-tree in the first\n", "component of the tuple, so if we also had it in the breadcrumbs, we'd\n", "have duplicate information.\n", "\n", "Let's modify our breadcrumbs so that they also contain information about\n", "everything that we previously ignored when moving left and right.\n", "Instead of `Direction`, we'll make a new data type:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data Crumb a = LeftCrumb a (Tree a) | RightCrumb a (Tree a) deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, instead of just `L`, we have a `LeftCrumb` that also contains the\n", "element in the node that we moved from and the right tree that we didn't\n", "visit. Instead of [`R`](https://hackage.haskell.org/package/base/docs/GHC-Generics.html#t:R), we have `RightCrumb`, which contains the element in\n", "the node that we moved from and the left tree that we didn't visit.\n", "\n", "These breadcrumbs now contain all the data needed to recreate the tree\n", "that we walked through. So instead of just being normal bread crumbs,\n", "they're now more like floppy disks that we leave as we go along, because\n", "they contain a lot more information than just the direction that we\n", "took.\n", "\n", "In essence, every breadcrumb is now like a tree node with a hole in it.\n", "When we move deeper into a tree, the breadcrumb carries all the\n", "information that the node that we moved away from carried *except* the\n", "sub-tree that we chose to focus on. It also has to note where the hole\n", "is. In the case of a `LeftCrumb`, we know that we moved left, so the\n", "sub-tree that's missing is the left one.\n", "\n", "Let's also change our `Breadcrumbs` type synonym to reflect this:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type Breadcrumbs a = [Crumb a]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next up, we have to modify the `goLeft` and `goRight` functions to store\n", "information about the paths that we didn't take in our breadcrumbs,\n", "instead of ignoring that information like they did before. Here's\n", "`goLeft`:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "goLeft :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)\n", "goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can see that it's very similar to our previous `goLeft`, only instead\n", "of just adding a `L` to the head of our list of breadcrumbs, we add a\n", "`LeftCrumb` to signify that we went left and we equip our `LeftCrumb` with\n", "the element in the node that we moved from (that's the `x`) and the right\n", "sub-tree that we chose not to visit.\n", "\n", "Note that this function assumes that the current tree that's under focus\n", "isn't `Empty`. An empty tree doesn't have any sub-trees, so if we try to\n", "go left from an empty tree, an error will occur because the pattern\n", "match on `Node` won't succeed and there's no pattern that takes care of\n", "`Empty`.\n", "\n", "`goRight` is similar:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "goRight :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)\n", "goRight (Node x l r, bs) = (r, RightCrumb x l:bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We were previously able to go left and right. What we've gotten now is\n", "the ability to actualy go back up by remembering stuff about the parent\n", "nodes and the paths that we didn't visit. Here's the `goUp` function:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "goUp :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)\n", "goUp (t, LeftCrumb x r:bs) = (Node x t r, bs)\n", "goUp (t, RightCrumb x l:bs) = (Node x l t, bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "We're focusing on the tree `t` and we check what the latest `Crumb` is. If\n", "it's a `LeftCrumb`, then we construct a new tree where our tree `t` is the\n", "left sub-tree and we use the information about the right sub-tree that\n", "we didn't visit and the element to fill out the rest of the `Node`.\n", "Because we moved back so to speak and picked up the last breadcrumb to\n", "recreate with it the parent tree, the new list of breadcrumbs doesn't\n", "contain it.\n", "\n", "Note that this function causes an error if we're already at the top of a\n", "tree and we want to move up. Later on, we'll use the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad to\n", "represent possible failure when moving focus.\n", "\n", "With a pair of `Tree a` and `Breadcrumbs a`, we have all the information to\n", "rebuild the whole tree and we also have a focus on a sub-tree. This\n", "scheme also enables us to easily move up, left and right. Such a pair\n", "that contains a focused part of a data structure and its surroundings is\n", "called a zipper, because moving our focus up and down the data structure\n", "resembles the operation of a zipper on a regular pair of pants. So it's\n", "cool to make a type synonym as such:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type Zipper a = (Tree a, Breadcrumbs a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I'd prefer naming the type synonym `Focus` because that makes it clearer\n", "that we're focusing on a part of a data structure, but the term zipper\n", "is more widely used to describe such a setup, so we'll stick with\n", "`Zipper`.\n", "\n", "### Manipulating trees under focus\n", "\n", "Now that we can move up and down, let's make a function that modifies\n", "the element in the root of the sub-tree that the zipper is focusing on:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "modify :: (a -> a) -> Zipper a -> Zipper a\n", "modify f (Node x l r, bs) = (Node (f x) l r, bs)\n", "modify f (Empty, bs) = (Empty, bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we're focusing on a node, we modify its root element with the\n", "function `f`. If we're focusing on an empty tree, we leave it as it is.\n", "Now we can start off with a tree, move to anywhere we want and modify an\n", "element, all while keeping focus on that element so that we can easily\n", "move further up or down. An example:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let newFocus = modify (\\_ -> 'P') (goRight (goLeft (freeTree,[])))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We go left, then right and then modify the root element by replacing it\n", "with a `'P'`. This reads even better if we use `-:`:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let newFocus = (freeTree,[]) -: goLeft -: goRight -: modify (\\_ -> 'P')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can then move up if we want and replace an element with a mysterious\n", "`'X'`:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let newFocus2 = modify (\\_ -> 'X') (goUp newFocus)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or if we wrote it with `-:`:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let newFocus2 = newFocus -: goUp -: modify (\\_ -> 'X')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Moving up is easy because the breadcrumbs that we leave form the part of\n", "the data structure that we're not focusing on, but it's inverted, sort\n", "of like turning a sock inside out. That's why when we want to move up,\n", "we don't have to start from the root and make our way down, but we just\n", "take the top of our inverted tree, thereby uninverting a part of it and\n", "adding it to our focus.\n", "\n", "Each node has two sub-trees, even if those sub-trees are empty trees. So\n", "if we're focusing on an empty sub-tree, one thing we can do is to\n", "replace it with a non-empty subtree, thus attaching a tree to a leaf\n", "node. The code for this is simple:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "attach :: Tree a -> Zipper a -> Zipper a\n", "attach t (_, bs) = (t, bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We take a tree and a zipper and return a new zipper that has its focus\n", "replaced with the supplied tree. Not only can we extend trees this way\n", "by replacing empty sub-trees with new trees, we can also replace whole\n", "existing sub-trees. Let's attach a tree to the far left of our `freeTree`:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let farLeft = (freeTree,[]) -: goLeft -: goLeft -: goLeft -: goLeft\n", "let newFocus = farLeft -: attach (Node 'Z' Empty Empty)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`newFocus` is now focused on the tree that we just attached and the rest\n", "of the tree lies inverted in the breadcrumbs. If we were to use `goUp` to\n", "walk all the way to the top of the tree, it would be the same tree as\n", "`freeTree` but with an additional `'Z'` on its far left.\n", "\n", "### I'm going straight to the top, oh yeah, up where the air is fresh and clean!\n", "\n", "Making a function that walks all the way to the top of the tree,\n", "regardless of what we're focusing on, is really easy. Here it is:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "topMost :: Zipper a -> Zipper a\n", "topMost (t,[]) = (t,[])\n", "topMost z = topMost (goUp z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If our trail of beefed up breadcrumbs is empty, this means that we're\n", "already at the root of our tree, so we just return the current focus.\n", "Otherwise, we go up to get the focus of the parent node and then\n", "recursively apply `topMost` to that. So now we can walk around our tree,\n", "going left and right and up, applying `modify` and `attach` as we go along\n", "and then when we're done with our modifications, we use `topMost` to focus\n", "on the root of our tree and see the changes that we've done in proper\n", "perspective.\n", "\n", "Focusing on lists\n", "-----------------\n", "\n", "Zippers can be used with pretty much any data structure, so it's no\n", "surprise that they can be used to focus on sub-lists of lists. After\n", "all, lists are pretty much like trees, only where a node in a tree has\n", "an element (or not) and several sub-trees, a node in a list has an\n", "element and only a single sub-list. When we [implemented our own\n", "lists](http://learnyouahaskell.com/making-our-own-types-and-typeclasses#recursive-data-structures),\n", "we defined our data type like so:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Contrast this with our definition of our binary tree and it's easy to\n", "see how lists can be viewed as trees where each node has only one\n", "sub-tree.\n", "\n", "A list like `[1,2,3]` can be written as `1:2:3:[]`. It consists of the head\n", "of the list, which is `1` and then the list's tail, which is `2:3:[]`. In\n", "turn, `2:3:[]` also has a head, which is `2` and a tail, which is `3:[]`. With\n", "`3:[]`, the `3` is the head and the tail is the empty list `[]`.\n", "\n", "Let's make a zipper for lists. To change the focus on sub-lists of a\n", "list, we move either forward or back (whereas with trees we moved either\n", "up or left or right). The focused part will be a sub-tree and along with\n", "that we'll leave breadcrumbs as we move forward. Now what would a single\n", "breadcrumb for a list consist of? When we were dealing with binary\n", "trees, we said that a breadcrumb has to hold the element in the root of\n", "the parent node along with all the sub-trees that we didn't choose. It\n", "also had to remember if we went left or right. So, it had to have all\n", "the information that a node has except for the sub-tree that we chose to\n", "focus on.\n", "\n", "Lists are simpler than trees, so we don't have to remember if we went\n", "left or right, because there's only one way to go deeper into a list.\n", "Because there's only one sub-tree to each node, we don't have to\n", "remember the paths that we didn't take either. It seems that all we have\n", "to remember is the previous element. If we have a list like `[3,4,5]` and\n", "we know that the previous element was `2`, we can go back by just putting\n", "that element at the head of our list, getting `[2,3,4,5]`.\n", "\n", "Because a single breadcrumb here is just the element, we don't really\n", "have to put it inside a data type, like we did when we made the `Crumb`\n", "data type for tree zippers:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type ListZipper a = ([a],[a])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first list represents the list that we're focusing on and the second\n", "list is the list of breadcrumbs. Let's make functions that go forward\n", "and back into lists:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "goForward :: ListZipper a -> ListZipper a\n", "goForward (x:xs, bs) = (xs, x:bs)\n", "\n", "goBack :: ListZipper a -> ListZipper a\n", "goBack (xs, b:bs) = (b:xs, bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we're going forward, we focus on the tail of the current list and\n", "leave the head element as a breadcrumb. When we're moving backwards, we\n", "take the latest breadcrumb and put it at the beginning of the list.\n", "\n", "Here are these two functions in action:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let xs = [1,2,3,4]" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "([2,3,4],[1])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "goForward (xs,[])" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "([3,4],[2,1])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "goForward ([2,3,4],[1])" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "([4],[3,2,1])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "goForward ([3,4],[2,1])" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "([3,4],[2,1])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "goBack ([4],[3,2,1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that the breadcrumbs in the case of lists are nothing more but a\n", "reversed part of our list. The element that we move away from always\n", "goes into the head of the breadcrumbs, so it's easy to move back by just\n", "taking that element from the head of the breadcrumbs and making it the\n", "head of our focus.\n", "\n", "This also makes it easier to see why we call this a zipper, because this\n", "really looks like the slider of a zipper moving up and down.\n", "\n", "If you were making a text editor, you could use a list of strings to\n", "represent the lines of text that are currently opened and you could then\n", "use a zipper so that you know which line the cursor is currently focused\n", "on. By using a zipper, it would also make it easier to insert new lines\n", "anywhere in the text or delete existing ones.\n", "\n", "A very simple file system\n", "-------------------------\n", "\n", "Now that we know how zippers work, let's use trees to represent a very\n", "simple file system and then make a zipper for that file system, which\n", "will allow us to move between folders, just like we usually do when\n", "jumping around our file system.\n", "\n", "If we take a simplistic view of the average hierarchical file system, we\n", "see that it's mostly made up of files and folders. Files are units of\n", "data and come with a name, whereas folders are used to organize those\n", "files and can contain files or other folders. So let's say that an item\n", "in a file system is either a file, which comes with a name and some\n", "data, or a folder, which has a name and then a bunch of items that are\n", "either files or folders themselves. Here's a data type for this and some\n", "type synonyms so we know what's what:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type Name = String\n", "type Data = String\n", "data FSItem = File Name Data | Folder Name [FSItem] deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A file comes with two strings, which represent its name and the data it\n", "holds. A folder comes with a string that is its name and a list of\n", "items. If that list is empty, then we have an empty folder.\n", "\n", "Here's a folder with some files and sub-folders:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "myDisk :: FSItem\n", "myDisk =\n", " Folder \"root\"\n", " [ File \"goat_yelling_like_man.wmv\" \"baaaaaa\"\n", " , File \"pope_time.avi\" \"god bless\"\n", " , Folder \"pics\"\n", " [ File \"ape_throwing_up.jpg\" \"bleargh\"\n", " , File \"watermelon_smash.gif\" \"smash!!\"\n", " , File \"skull_man(scary).bmp\" \"Yikes!\"\n", " ]\n", " , File \"dijon_poupon.doc\" \"best mustard\"\n", " , Folder \"programs\"\n", " [ File \"fartwizard.exe\" \"10gotofart\"\n", " , File \"owl_bandit.dmg\" \"mov eax, h00t\"\n", " , File \"not_a_virus.exe\" \"really not a virus\"\n", " , Folder \"source code\"\n", " [ File \"best_hs_prog.hs\" \"main = print (fix error)\"\n", " , File \"random.hs\" \"main = print 4\"\n", " ]\n", " ]\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's actually what my disk contains right now.\n", "\n", "### A zipper for our file system\n", "\n", "\n", "\n", "Now that we have a file system, all we need is a zipper so we can zip\n", "and zoom around it and add, modify and remove files as well as folders.\n", "Like with binary trees and lists, we're going to be leaving breadcrumbs\n", "that contain info about all the stuff that we chose not to visit. Like\n", "we said, a single breadcrumb should be kind of like a node, only it\n", "should contain everything except the sub-tree that we're currently\n", "focusing on. It should also note where the hole is so that once we move\n", "back up, we can plug our previous focus into the hole.\n", "\n", "In this case, a breadcrumb should be like a folder, only it should be\n", "missing the folder that we currently chose. Why not like a file, you\n", "ask? Well, because once we're focusing on a file, we can't move deeper\n", "into the file system, so it doesn't make sense to leave a breadcrumb\n", "that says that we came from a file. A file is sort of like an empty\n", "tree.\n", "\n", "If we're focusing on the folder `\"root\"` and we then focus on the file\n", "`\"dijon_poupon.doc\"`, what should the breadcrumb that we leave look like?\n", "Well, it should contain the name of its parent folder along with the\n", "items that come before the file that we're focusing on and the items\n", "that come after it. So all we need is a `Name` and two lists of items. By\n", "keeping separate lists for the items that come before the item that\n", "we're focusing and for the items that come after it, we know exactly\n", "where to place it once we move back up. So this way, we know where the\n", "hole is.\n", "\n", "Here's our breadcrumb type for the file system:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "data FSCrumb = FSCrumb Name [FSItem] [FSItem] deriving (Show)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And here's a type synonym for our zipper:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "type FSZipper = (FSItem, [FSCrumb])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Going back up in the hierarchy is very simple. We just take the latest\n", "breadcrumb and assemble a new focus from the current focus and\n", "breadcrumb. Like so:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "fsUp :: FSZipper -> FSZipper\n", "fsUp (item, FSCrumb name ls rs:bs) = (Folder name (ls ++ [item] ++ rs), bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because our breadcrumb knew what the parent folder's name was, as well\n", "as the items that came before our focused item in the folder (that's `ls`)\n", "and the ones that came after (that's `rs`), moving up was easy.\n", "\n", "How about going deeper into the file system? If we're in the `\"root\"` and\n", "we want to focus on `\"dijon_poupon.doc\"`, the breadcrumb that we leave is\n", "going to include the name `\"root\"` along with the items that precede\n", "`\"dijon_poupon.doc\"` and the ones that come after it.\n", "\n", "Here's a function that, given a name, focuses on a file of folder that's\n", "located in the current focused folder:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "import Data.List (break)\n", "\n", "fsTo :: Name -> FSZipper -> FSZipper\n", "fsTo name (Folder folderName items, bs) =\n", " let (ls, item:rs) = break (nameIs name) items\n", " in (item, FSCrumb folderName ls rs:bs)\n", "\n", "nameIs :: Name -> FSItem -> Bool\n", "nameIs name (Folder folderName _) = name == folderName\n", "nameIs name (File fileName _) = name == fileName" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`fsTo` takes a `Name` and a `FSZipper` and returns a new `FSZipper` that focuses\n", "on the file with the given name. That file has to be in the current\n", "focused folder. This function doesn't search all over the place, it just\n", "looks at the current folder.\n", "\n", "\n", "\n", "First we use [`break`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:break) to break the list of items in a folder into those\n", "that precede the file that we're searching for and those that come after\n", "it. If you remember, [`break`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:break) takes a predicate and a list and returns a\n", "pair of lists. The first list in the pair holds items for which the\n", "predicate returns [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False). Then, once the predicate returns [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) for an\n", "item, it places that item and the rest of the list in the second item of\n", "the pair. We made an auxiliary function called `nameIs` that takes a name\n", "and a file system item and returns [`True`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:True) if the names match.\n", "\n", "So now, `ls` is a list that contains the items that precede the `item` that\n", "we're searching for, item is that very item and `rs` is the list of items\n", "that come after it in its folder. Now that we have this, we just present\n", "the item that we got from [`break`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:break) as the focus and build a breadcrumb that\n", "has all the data it needs.\n", "\n", "Note that if the name we're looking for isn't in the folder, the pattern\n", "`item:rs` will try to match on an empty list and we'll get an error. Also,\n", "if our current focus isn't a folder at all but a file, we get an error\n", "as well and the program crashes.\n", "\n", "Now we can move up and down our file system. Let's start at the root and\n", "walk to the file `\"skull_man(scary).bmp\"`:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let newFocus = (myDisk,[]) -: fsTo \"pics\" -: fsTo \"skull_man(scary).bmp\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`newFocus` is now a zipper that's focused on the `\"skull_man(scary).bmp\"`\n", "file. Let's get the first component of the zipper (the focus itself) and\n", "see if that's really true:" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "File \"skull_man(scary).bmp\" \"Yikes!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fst newFocus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's move up and then focus on its neighboring file\n", "`\"watermelon_smash.gif\"`:" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "File \"watermelon_smash.gif\" \"smash!!\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "let newFocus2 = newFocus -: fsUp -: fsTo \"watermelon_smash.gif\"\n", "fst newFocus2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Manipulating our file system\n", "\n", "Now that we know how to navigate our file system, manipulating it is\n", "easy. Here's a function that renames the currently focused file or\n", "folder:" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "fsRename :: Name -> FSZipper -> FSZipper\n", "fsRename newName (Folder name items, bs) = (Folder newName items, bs)\n", "fsRename newName (File name dat, bs) = (File newName dat, bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can rename our `\"pics\"` folder to `\"cspi\"`:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let newFocus = (myDisk,[]) -: fsTo \"pics\" -: fsRename \"cspi\" -: fsUp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We descended to the `\"pics\"` folder, renamed it and then moved back up.\n", "\n", "How about a function that makes a new item in the current folder?\n", "Behold:" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "fsNewFile :: FSItem -> FSZipper -> FSZipper\n", "fsNewFile item (Folder folderName items, bs) =\n", " (Folder folderName (item:items), bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Easy as pie. Note that this would crash if we tried to add an item but\n", "weren't focusing on a folder, but were focusing on a file instead.\n", "\n", "Let's add a file to our `\"pics\"` folder and then move back up to the root:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let newFocus = (myDisk,[]) -: fsTo \"pics\" -: fsNewFile (File \"heh.jpg\" \"lol\") -: fsUp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What's really cool about all this is that when we modify our file\n", "system, it doesn't actually modify it in place but it returns a whole\n", "new file system. That way, we have access to our old file system (in\n", "this case, `myDisk`) as well as the new one (the first component of\n", "`newFocus`). So by using zippers, we get versioning for free, meaning that\n", "we can always refer to older versions of data structures even after\n", "we've changed them, so to speak. This isn't unique to zippers, but is a\n", "property of Haskell because its data structures are immutable. With\n", "zippers however, we get the ability to easily and efficiently walk\n", "around our data structures, so the persistence of Haskell's data\n", "structures really begins to shine.\n", "\n", "Watch your step\n", "---------------\n", "\n", "So far, while walking through our data structures, whether they were\n", "binary trees, lists or file systems, we didn't really care if we took a\n", "step too far and fell off. For instance, our `goLeft` function takes a\n", "zipper of a binary tree and moves the focus to its left sub-tree:" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "goLeft :: Zipper a -> Zipper a\n", "goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "But what if the tree we're stepping off from is an empty tree? That is,\n", "what if it's not a `Node`, but an `Empty`? In this case, we'd get a runtime\n", "error because the pattern match would fail and we have made no pattern\n", "to handle an empty tree, which doesn't have any sub-trees at all. So\n", "far, we just assumed that we'd never try to focus on the left sub-tree\n", "of an empty tree as its left sub-tree doesn't exist at all. But going to\n", "the left sub-tree of an empty tree doesn't make much sense, and so far\n", "we've just conveniently ignored this.\n", "\n", "Or what if we were already at the root of some tree and didn't have any\n", "breadcrumbs but still tried to move up? The same thing would happen. It\n", "seems that when using zippers, any step could be our last (cue ominous\n", "music). In other words, any move can result in a success, but it can\n", "also result in a failure. Does that remind you of something? Of course,\n", "monads! More specifically, the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad which adds a context of\n", "possible failure to normal values.\n", "\n", "So let's use the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad to add a context of possible failure to our\n", "movements. We're going to take the functions that work on our binary\n", "tree zipper and we're going to make them into monadic functions. First,\n", "let's take care of possible failure in `goLeft` and `goRight`. So far, the\n", "failure of functions that could fail was always reflected in their\n", "result, and this time is no different. So here are `goLeft` and `goRight`\n", "with an added possibility of failure:" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "goLeft :: Zipper a -> Maybe (Zipper a)\n", "goLeft (Node x l r, bs) = Just (l, LeftCrumb x r:bs)\n", "goLeft (Empty, _) = Nothing\n", "\n", "goRight :: Zipper a -> Maybe (Zipper a)\n", "goRight (Node x l r, bs) = Just (r, RightCrumb x l:bs)\n", "goRight (Empty, _) = Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool, now if we try to take a step to the left of an empty tree, we get\n", "a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing)!" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "goLeft (Empty, [])" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (Empty,[LeftCrumb 'A' Empty])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "goLeft (Node 'A' Empty Empty, [])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looks good! How about going up? The problem before happened if we tried\n", "to go up but we didn't have any more breadcrumbs, which meant that we\n", "were already in the root of the tree. This is the `goUp` function that\n", "throws an error if we don't keep within the bounds of our tree:" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "goUp :: Zipper a -> Zipper a\n", "goUp (t, LeftCrumb x r:bs) = (Node x t r, bs)\n", "goUp (t, RightCrumb x l:bs) = (Node x l t, bs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's modify it to fail gracefully:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "goUp :: Zipper a -> Maybe (Zipper a)\n", "goUp (t, LeftCrumb x r:bs) = Just (Node x t r, bs)\n", "goUp (t, RightCrumb x l:bs) = Just (Node x l t, bs)\n", "goUp (_, []) = Nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we have breadcrumbs, everything is okay and we return a successful\n", "new focus, but if we don't, then we return a failure.\n", "\n", "Before, these functions took zippers and returned zippers, which meant\n", "that we could chain them like this to walk around:" ] }, { "cell_type": "markdown", "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "source": [ "
let newFocus = (freeTree,[]) -: goLeft -: goRight
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But now, instead of returning `Zipper a`, they return `Maybe (Zipper a)`, so\n", "chaining functions like this won't work. We had a similar problem when\n", "we were [dealing with our tightrope\n", "walker](http://learnyouahaskell.com/a-fistful-of-monads#walk-the-line) in the chapter about monads.\n", "He also walked one step at a time and each of his steps could result in\n", "failure because a bunch of birds could land on one side of his balancing\n", "pole and make him fall.\n", "\n", "Now, the joke's on us because we're the ones doing the walking, and\n", "we're traversing a labyrinth of our own devising. Luckily, we can learn\n", "from the tightrope walker and just do what he did, which is to exchange\n", "normal function application for using [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-), which takes a value with a\n", "context (in our case, the `Maybe (Zipper a)`, which has a context of\n", "possible failure) and feeds it into a function while making sure that\n", "the context is taken care of. So just like our tightrope walker, we're\n", "going to trade in all our `-:` operators for [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-). Alright, we can chain\n", "our functions again! Watch:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [], "source": [ "let coolTree = Node 1 Empty (Node 3 Empty Empty)" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (Node 3 Empty Empty,[RightCrumb 1 Empty])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return (coolTree,[]) >>= goRight" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Just (Empty,[RightCrumb 3 Empty,RightCrumb 1 Empty])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return (coolTree,[]) >>= goRight >>= goRight" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "attributes": { "classes": [ "haskell:hs" ], "id": "", "name": "\"code\"" } }, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "return (coolTree,[]) >>= goRight >>= goRight >>= goRight" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We used [`return`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:return) to put a zipper in a [`Just`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Just) and then used [`>>=`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-) to feed\n", "that to our `goRight` function. First, we made a tree that has on its left\n", "an empty sub-tree and on its right a node that has two empty sub-trees.\n", "When we try to go right once, the result is a success, because the\n", "operation makes sense. Going right twice is okay too; we end up with the\n", "focus on an empty sub-tree. But going right three times wouldn't make\n", "sense, because we can't go to the right of an empty sub-tree, which is\n", "why the result is a [`Nothing`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:Nothing).\n", "\n", "Now we've equipped our trees with a safety-net that will catch us should\n", "we fall off. Wow, I nailed this metaphor.\n", "\n", "Our file system also has a lot of cases where an operation could fail,\n", "such as trying to focus on a file or folder that doesn't exist. As an\n", "exercise, you can equip our file system with functions that fail\n", "gracefully by using the [`Maybe`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Maybe) monad." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.2.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook_extra/WidgetChart.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Demonstration combining Widgets with Charts" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3f106d34-dd2d-4817-a566-dd53574c5d3e", "version_major": 2, "version_minor": 0 } }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3afa14b2-cc8c-4660-a758-8bd1e2f5ea38", "version_major": 2, "version_minor": 0 } }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import Graphics.Rendering.Chart\n", "import IHaskell.Display.Widgets\n", "import IHaskell.Display\n", "import Data.Default.Class\n", "import Control.Lens\n", "\n", "f = sin -- The function which we will plot\n", "domain = (0.0, 10.0) -- The domain of the function which we will plot\n", "\n", "grain = (snd domain - fst domain) / 100.0\n", "\n", "numericIntegral [] = 0\n", "numericIntegral [_] = 0\n", "numericIntegral ((x1,y1):(x2,y2):ps) = \n", " ((x2 - x1) * ((y1 + y2) / 2.0)) + numericIntegral ((x2,y2):ps)\n", "\n", "sliderRange <- mkFloatRangeSlider\n", "setField sliderRange MinFloat (fst domain)\n", "setField sliderRange MaxFloat (snd domain)\n", "setField sliderRange StepFloat (Just grain)\n", "setField sliderRange FloatPairValue (fst domain, snd domain)\n", "\n", "outChart <- mkOutput\n", "\n", "drawChart = do \n", " (rangeMin, rangeMax) <- getField sliderRange FloatPairValue\n", " let \n", " pts = [(x, f x) | x <- [fst domain, grain .. snd domain]] \n", " ptsRange = takeWhile ((<= rangeMax) . fst) $ dropWhile ((< rangeMin) . fst) pts\n", " layout = def & layout_plots .~\n", " [ toPlot $ def & plot_lines_values .~ [pts]\n", " , toPlot $ def & plot_fillbetween_values .~ [(x,(0.0,y)) | (x,y) <- ptsRange]\n", " ]\n", " displayCaption <- display $ plain \n", " $ \"Area under the curve = \" <> show (numericIntegral ptsRange)\n", " displayChart <- display $ toRenderable layout\n", " setField outChart Outputs [OutputData displayCaption, OutputData displayChart]\n", " \n", "setField sliderRange ChangeHandler drawChart\n", "\n", "sliderRange\n", "outChart\n", "drawChart" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "8.10.4" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook_extra/WidgetDiagram.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Demonstration combining Widgets with Diagrams" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's attach a slider widget to one parameter of the *Apollonian gasket* from the `diagrams-contrib` package.\n", "\n", "https://hackage.haskell.org/package/diagrams-contrib/docs/Diagrams-TwoD-Apollonian.html" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3a836ed3-872d-43a8-9572-34987eb47d71", "version_major": 2, "version_minor": 0 } }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "631d997a-191f-4dc9-8566-4a3a3cb32921", "version_major": 2, "version_minor": 0 } }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import Diagrams.Prelude\n", "import Diagrams.TwoD.Apollonian\n", "import IHaskell.Display.Widgets\n", "\n", "sliderApollo <- mkFloatSlider\n", "setField sliderApollo MinFloat 4.0\n", "setField sliderApollo MaxFloat 9.0\n", "setField sliderApollo StepFloat (Just 0.1)\n", "setField sliderApollo FloatValue 5.0\n", "\n", "outApollo <- mkOutput\n", "\n", "drawApollo = getField sliderApollo FloatValue >>= \\val ->\n", " replaceOutput outApollo $ diagram $ apollonianGasket 0.01 2 4 val\n", "\n", "setField sliderApollo ChangeHandler drawApollo\n", "\n", "sliderApollo\n", "outApollo\n", "drawApollo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Draw a cat tail diagram, controlled by a slider widget." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "9e58e9f2-6403-4b3b-88a1-90928bd8474c", "version_major": 2, "version_minor": 0 } }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "727cfef5-8275-4563-b26e-5a6371bd95c6", "version_major": 2, "version_minor": 0 } }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":extension BlockArguments\n", "import Data.Foldable\n", "import Diagrams.Prelude\n", "import Diagrams.TwoD.Path.Turtle\n", "import IHaskell.Display.Widgets\n", "\n", "sliderCat <- mkFloatSlider\n", "setField sliderCat MinFloat (-100.0)\n", "setField sliderCat MaxFloat 100.0\n", "setField sliderCat StepFloat (Just 1.0)\n", "setField sliderCat FloatValue 0.0\n", "\n", "outCat <- mkOutput\n", "\n", "drawCat = do\n", " val <- getField sliderCat FloatValue\n", " replaceOutput outCat $ diagram $ rotate ((-90.0) @@ deg) $ sizeDiagram $ drawTurtle do\n", " setPenWidth 4\n", " for_ [1.0,2.0..30.0] \\i -> do\n", " forward 10.0\n", " left (val * i / 100.0)\n", " where \n", " sizeDiagram = withEnvelope (fromPoints [mkP2 0.0 (-180.0), mkP2 300.0 180.0]) \n", "\n", "setField sliderCat ChangeHandler drawCat\n", "\n", "sliderCat\n", "outCat\n", "drawCat" ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "8.10.4" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: notebook_extra/WidgetRevival.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 💙 ihaskell-widgets Revival Celebration Notebook 💙\n", "\n", "This demonstration notebook is to celebrate the successful completion of [David Davó Laviña’s Google Summer of Code 2021 project](https://summerofcode.withgoogle.com/projects/#5497023849037824) to revive [*ihaskell-widgets*](https://github.com/gibiansky/IHaskell/tree/master/ihaskell-display/ihaskell-widgets). 🎉\n", "\n", "Here is [David Davó Laviña’s report on __Fixing IHaskell-Widgets__](https://gsoc21.ddavo.me/).\n", "\n", "For David’s *ihaskell-widgets* example notebooks and tutorials, see the `/ihaskell_examples/ihaskell-widgets` directory.\n", "\n", "For David’s other work, see [ddavo.me English](https://ddavo.me/en), [ddavo.me Español](https://ddavo.me/es), [ddavo.me 日本語](https://ddavo.me/jp).\n", "\n", "Many experienced Haskell programmers tried and failed to revive the *ihaskell-widgets* display extension for the last three years, and we’re grateful to David for this successful project.\n", "\n", "— James Brock, Tokyo 2021\n", "\n", "
\n", "\n", "Click to launch this notebook on Binder cloud service (takes a minute to launch): \n", "\n", "[IHaskell on Github](https://github.com/gibiansky/IHaskell) | [This notebook on Github](https://github.com/jamesdbrock/learn-you-a-haskell-notebook/blob/master/notebook_extra/WidgetRevival.ipynb) | [*Learn You a Haskell for Great Good!* Jupyter adaptation](https://github.com/jamesdbrock/learn-you-a-haskell-notebook) \n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Demonstration combining Widgets with Charts\n", "\n", "This is a demonstration of how to combine [*ihaskell-widgets*](https://github.com/gibiansky/IHaskell/tree/master/ihaskell-display/ihaskell-widgets) with [*ihaskell-charts*](https://github.com/gibiansky/IHaskell/tree/master/ihaskell-display/ihaskell-charts) for [*Chart*](https://hackage.haskell.org/package/Chart).\n", "\n", "It renders an *ihaskell-widgets* `FloatRangeSlider` which allows you to select the beginning and end points of a numeric integral area under a function. The *ihaskell-charts* output renders the area under the function as a *Chart*.\n", "\n", "See the [*Chart* wiki](https://github.com/timbod7/haskell-chart/wiki) for other possibilities." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import Graphics.Rendering.Chart\n", "import IHaskell.Display.Widgets\n", "import IHaskell.Display\n", "import Data.Default.Class\n", "import Control.Lens\n", "\n", "f x = sin x * (x + 5.0) -- The function which we will plot\n", "domain = (0.0, 10.0) -- The domain of the function which we will plot\n", "\n", "grain = (snd domain - fst domain) / 100.0\n", "\n", "integralTrapezoid ((x1,y1):(x2,y2):ps) = \n", " ((x2 - x1) * ((y1 + y2) / 2.0)) + integralTrapezoid ((x2,y2):ps)\n", "integralTrapezoid _ = 0\n", "\n", "sliderRange <- mkFloatRangeSlider\n", "setField @MinFloat sliderRange (fst domain)\n", "setField @MaxFloat sliderRange (snd domain)\n", "setField @StepFloat sliderRange (Just grain)\n", "setField @FloatPairValue sliderRange (fst domain, snd domain)\n", "\n", "outChart <- mkOutput\n", "\n", "drawChart = do \n", " (rangeMin, rangeMax) <- getField @FloatPairValue sliderRange\n", " let \n", " pts = [(x, f x) | x <- [fst domain, fst domain + grain .. snd domain]] \n", " ptsRange = [(x,y) | (x,y) <- pts, x >= rangeMin, x <= rangeMax]\n", " displayCaption <- display $ plain \n", " $ \"Area under the curve = \" <> show (integralTrapezoid ptsRange)\n", " displayChart <- display $ toRenderable $ def & layout_plots .~\n", " [ toPlot $ def & plot_lines_values .~ [pts]\n", " , toPlot $ def & plot_fillbetween_values .~ [(x,(0.0,y)) | (x,y) <- ptsRange]\n", " ]\n", " setField @Outputs outChart [OutputData displayCaption, OutputData displayChart]\n", " \n", "setField @ChangeHandler sliderRange drawChart\n", "\n", "sliderRange\n", "outChart\n", "drawChart" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Demonstration combining Widgets with Diagrams\n", "\n", "This is a demonstration of how to combine [*ihaskell-widgets*](https://github.com/gibiansky/IHaskell/tree/master/ihaskell-display/ihaskell-widgets) with [*ihaskell-diagrams*](https://github.com/gibiansky/IHaskell/tree/master/ihaskell-display/ihaskell-diagrams) for [*diagrams*](https://hackage.haskell.org/package/diagrams).\n", "\n", "It renders a `FloatSlider` widget and uses the widget value as one parameter of a rendering of the [__Apollonian gasket__](https://hackage.haskell.org/package/diagrams-contrib/docs/Diagrams-TwoD-Apollonian.html) from the [*diagrams-contrib*](https://hackage.haskell.org/package/diagrams-contrib) package.\n", "\n", "See the [*Diagrams* project website](https://archives.haskell.org/projects.haskell.org/diagrams/) for other possibilities." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import Diagrams.Prelude\n", "import Diagrams.TwoD.Apollonian\n", "import IHaskell.Display.Widgets\n", "\n", "sliderApollo <- mkFloatSlider\n", "setField @MinFloat sliderApollo 3.0\n", "setField @MaxFloat sliderApollo 9.0\n", "setField @StepFloat sliderApollo (Just 0.1)\n", "setField @FloatValue sliderApollo 6.0\n", "\n", "outApollo <- mkOutput\n", "\n", "drawApollo = do\n", " val <- getField @FloatValue sliderApollo\n", " replaceOutput outApollo $ diagram $ apollonianGasket 0.01 2 4 val\n", "\n", "setField @ChangeHandler sliderApollo drawApollo\n", "\n", "sliderApollo\n", "outApollo\n", "drawApollo" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.8.4" } }, "nbformat": 4, "nbformat_minor": 4 }