├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── README.screenshot.png ├── anchordoc ├── LICENSE ├── README.md ├── Setup.hs ├── anchordoc.cabal ├── notebooks.anchordoc.sh ├── src │ └── Main.hs └── stack.yaml ├── docker-compose.yml ├── flake.lock ├── flake.nix ├── kernels.nix ├── kernels └── haskell.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 └── img │ ├── 60sdude.png │ ├── accordion.png │ ├── alien.png │ ├── almostzipper.png │ ├── angeleyes.png │ ├── arguments.png │ ├── asstronaut.png │ ├── baby.png │ ├── badge.png │ ├── balloondog.png │ ├── banana.png │ ├── bear.png │ ├── bigtree.png │ ├── binarytree.png │ ├── bird.png │ ├── boat.png │ ├── bomb.png │ ├── bonus.png │ ├── box.png │ ├── brain.png │ ├── bread.png │ ├── buddha.png │ ├── cactus.png │ ├── calculator.png │ ├── case.png │ ├── caveman.png │ ├── centaur.png │ ├── chainchomp.png │ ├── chess.png │ ├── chicken.png │ ├── classes.png │ ├── clint.png │ ├── composition.png │ ├── concatmap.png │ ├── cool.png │ ├── cover.png │ ├── cow.png │ ├── cowboy.png │ ├── curry.png │ ├── deadcat.png │ ├── dognap.png │ ├── dollar.png │ ├── edd.png │ ├── file.png │ ├── foldl.png │ ├── frogtor.png │ ├── functor.png │ ├── fx.png │ ├── gob.png │ ├── guards.png │ ├── guycar.png │ ├── helloworld.png │ ├── holy.png │ ├── jackofdiamonds.png │ ├── jazzb.png │ ├── judgedog.png │ ├── justice.png │ ├── kermit.png │ ├── kid.png │ ├── knight.png │ ├── krakatoa.png │ ├── lamb.png │ ├── lambda.png │ ├── lazy.png │ ├── legochar.png │ ├── legolists.png │ ├── legomap.png │ ├── legosets.png │ ├── letitbe.png │ ├── lifter.png │ ├── list.png │ ├── listmonster.png │ ├── luggage.png │ ├── lyah.jpg │ ├── making_modules.png │ ├── maoi.png │ ├── map.png │ ├── maxs.png │ ├── meekrat.png │ ├── miner.png │ ├── modules.png │ ├── newsplash.png │ ├── notes.png │ ├── origami.png │ ├── owld.png │ ├── painter.png │ ├── pattern.png │ ├── picard.png │ ├── pierre.png │ ├── pirateship.png │ ├── police.png │ ├── pollywantsa.png │ ├── present.png │ ├── prob.png │ ├── puppy.png │ ├── pythag.png │ ├── quickman.png │ ├── quicksort.png │ ├── random.png │ ├── record.png │ ├── recursion.png │ ├── revolver.png │ ├── ride.png │ ├── ringring.png │ ├── roads.png │ ├── roads_simple.png │ ├── rpn.png │ ├── salad.png │ ├── setnotation.png │ ├── shamrock.png │ ├── smug.png │ ├── smugpig.png │ ├── socialmediapreview.jpg │ ├── spearhead.png │ ├── spongedisk.png │ ├── startingout.png │ ├── streams.png │ ├── sun.png │ ├── texas.png │ ├── thefonz.png │ ├── timber.png │ ├── tipi.png │ ├── trafficlight.png │ ├── tuco.png │ ├── tuple.png │ ├── tur2.png │ ├── typefoo.png │ ├── washmachine.png │ ├── whale.png │ ├── wolf.png │ ├── yesno.png │ └── yeti.png └── notebook_extra ├── WidgetChart.ipynb ├── WidgetDiagram.ipynb └── WidgetRevival.ipynb /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/ihaskell/ihaskell-notebook:master 2 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.194.0/containers/docker-existing-dockerfile 3 | { 4 | "name": "IHaskell Community Docker Stack", 5 | 6 | // Sets the run context to one level up instead of the .devcontainer folder. 7 | "context": "..", 8 | 9 | // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. 10 | "dockerFile": "./Dockerfile", 11 | 12 | // Set *default* container specific settings.json values on container create. 13 | "settings": {}, 14 | 15 | // Add the IDs of extensions you want installed when the container is created. 16 | "extensions": [ 17 | "ms-toolsai.jupyter", 18 | "haskell.haskell", 19 | "justusadam.language-haskell", 20 | // A popup dialog pesters us to install the Python extension if it's not installed. 21 | // I think this is needed to render errors. 22 | "ms-python.python" 23 | ] 24 | 25 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 26 | // "forwardPorts": [], 27 | 28 | // Uncomment the next line to run commands after the container is created - for example installing curl. 29 | // "postCreateCommand": "apt-get update && apt-get install -y curl", 30 | 31 | // Uncomment when using a ptrace-based debugger like C++, Go, and Rust 32 | // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], 33 | 34 | // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. 35 | // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], 36 | 37 | // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. 38 | // "remoteUser": "vscode" 39 | } 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | notebook/*.txt 2 | notebook/Geometry.dyn_hi 3 | notebook/Geometry.dyn_o 4 | notebook/Geometry.hi 5 | notebook/Geometry.hs 6 | notebook/Geometry.o 7 | notebook/Geometry/ 8 | */.ipynb_checkpoints 9 | .jupyter/ 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for mybinder.org 2 | # 3 | # Test this Dockerfile: 4 | # 5 | # docker build -t learn-you-a-haskell . 6 | # docker run --rm -p 8888:8888 --name learn-you-a-haskell learn-you-a-haskell:latest jupyter lab --ServerApp.token='' 7 | # 8 | 9 | FROM ghcr.io/ihaskell/ihaskell-notebook:master@sha256:3619fe7a14c8c17e196760377ef3500a1f8db32c9c51ce83c41b4e213a97f1bf 10 | 11 | USER root 12 | 13 | RUN mkdir /home/$NB_USER/learn_you_a_haskell 14 | COPY notebook/*.ipynb /home/$NB_USER/learn_you_a_haskell/ 15 | COPY notebook/img /home/$NB_USER/learn_you_a_haskell/img 16 | RUN chown --recursive $NB_UID:users /home/$NB_USER/learn_you_a_haskell 17 | 18 | ARG EXAMPLES_PATH=/home/$NB_USER/ihaskell_examples 19 | COPY notebook_extra/WidgetRevival.ipynb $EXAMPLES_PATH/ 20 | RUN chown $NB_UID:users $EXAMPLES_PATH/WidgetRevival.ipynb 21 | 22 | USER $NB_UID 23 | 24 | ENV JUPYTER_ENABLE_LAB=yes 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | https://creativecommons.org/licenses/by-nc-sa/3.0/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learn You a Haskell for Great Good! 2 | 3 | ## Read the book right now 4 | 5 | ### Read the book method 1: [mybinder.org](https://mybinder.org/) 6 | 7 | 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) 8 | (Usually takes a minute to launch) 9 | 10 | ### Read the book method 2: [Nix run](https://determinate.systems/posts/nix-run) 11 | 12 | ```console 13 | nix run github:IHaskell/learn-you-a-haskell-notebook 14 | ``` 15 | 16 | --- 17 | 18 | ![Screenshot](README.screenshot.png) 19 | 20 | This is a Jupyter notebook adaptation of the book [__*Learn You a Haskell for Great Good!*__](http://learnyouahaskell.com/) by Miran Lipovača. 21 | 22 | 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. 23 | 24 | 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). 25 | 26 | Each chapter of the book is one `.ipynb` Jupyter notebook file. See the list of chapter files on the left sidebar in JupyterLab. 27 | 28 | 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. 29 | 30 | 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/). 31 | 32 | Thanks also to Paul Vorbach for . 33 | 34 | # How to run on your local computer in Visual Studio Code 35 | 36 | Visual Studio Code has a 37 | [Notebook UI](https://code.visualstudio.com/docs/datascience/jupyter-notebooks) 38 | which you can use to run Jupyter kernels instead of the browser-based 39 | JupyterLab or classic Jupyter UI. 40 | 41 | Clone this repository with `git` and `cd` into the cloned directory 42 | 43 | ```bash 44 | git clone https://github.com/IHaskell/learn-you-a-haskell-notebook.git 45 | cd learn-you-a-haskell-notebook 46 | code . 47 | ``` 48 | 49 | There is a Docker 50 | [Visual Studio Code devcontainer](https://github.com/microsoft/vscode-dev-containers) 51 | configuration in the [`.devcontainer`](.devcontainer) directory. 52 | 53 | When you open Visual Studio Code by running `code .`, a pop-up dialog will 54 | tell you that “Folder contains a Dev Container configuration file. Reopen folder 55 | to develop in a container.” Click the button labelled __Reopen in Container__. 56 | 57 | You will then be able to open the `.ipynb` chapter files in the `notebook` 58 | directory. 59 | 60 | # How to run on your local computer in JupyterLab in a web browser 61 | 62 | Clone this repository with `git` and `cd` into the cloned directory 63 | 64 | ```bash 65 | git clone https://github.com/IHaskell/learn-you-a-haskell-notebook.git 66 | cd learn-you-a-haskell-notebook 67 | ``` 68 | 69 | Then here are three options for running. 70 | 71 | 1. `docker` 72 | 73 | ```bash 74 | 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='' 75 | ``` 76 | 77 | then open [http://localhost:8888](http://localhost:8888) to read the book. 78 | 79 | 2. `podman` 80 | 81 | ```bash 82 | 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='' 83 | ``` 84 | 85 | then open [http://localhost:8888](http://localhost:8888) to read the book. 86 | 87 | 3. [NixOS](https://nixos.org/) flake 88 | 89 | ``` 90 | nix run . -- --notebook-dir notebook 91 | ``` 92 | 93 | then the book will open in a browser automatically. If it doesn’t, open the “running at” link. 94 | 95 | # How to edit notebooks 96 | 97 | 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, 98 | 99 | 1. Maximize the browser window to full screen width, because cell horizontal scroll bar visibility gets saved in the notebook. 100 | 101 | 2. Run -> Restart kernel and Run All Cells... 102 | 103 | 3. Save the notebook. 104 | 105 | # ihaskell-notebook 106 | 107 | For more information about the IHaskell Docker image, see 108 | 109 | -------------------------------------------------------------------------------- /README.screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/README.screenshot.png -------------------------------------------------------------------------------- /anchordoc/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright James D. Brock (c) 2019 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of James D. Brock nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /anchordoc/README.md: -------------------------------------------------------------------------------- 1 | # anchordoc 2 | -------------------------------------------------------------------------------- /anchordoc/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /anchordoc/anchordoc.cabal: -------------------------------------------------------------------------------- 1 | name: anchordoc 2 | version: 0.1.0.0 3 | -- synopsis: 4 | -- description: 5 | homepage: https://github.com/jamesdbrock/anchordoc#readme 6 | license: BSD3 7 | license-file: LICENSE 8 | author: James D. Brock 9 | maintainer: jamesbrock@gmail.com 10 | copyright: James D. Brock 11 | category: personal 12 | build-type: Simple 13 | cabal-version: >=1.10 14 | extra-source-files: README.md 15 | 16 | executable anchordoc 17 | hs-source-dirs: src 18 | main-is: Main.hs 19 | default-language: Haskell2010 20 | build-depends: base >= 4.7 && < 5 21 | , megaparsec 22 | , split 23 | , wreq 24 | , aeson 25 | , text 26 | , lens 27 | , lens-aeson 28 | , transformers 29 | , replace-megaparsec 30 | -------------------------------------------------------------------------------- /anchordoc/notebooks.anchordoc.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | for n in $(ls ../notebook/*.ipynb); do 4 | echo $n " ..." 5 | stack exec anchordoc < $n > ../notebook/tmp 6 | mv ../notebook/tmp $n 7 | echo "done" 8 | done 9 | -------------------------------------------------------------------------------- /anchordoc/src/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | {-# LANGUAGE LambdaCase #-} 4 | {-# LANGUAGE TypeFamilies #-} 5 | {-# LANGUAGE FlexibleContexts #-} 6 | {-# LANGUAGE ViewPatterns #-} 7 | 8 | module Main where 9 | 10 | import Network.Wreq (getWith, responseBody, param, defaults) 11 | import Control.Lens ((^..), (&), (.~)) 12 | import Data.Aeson.Lens 13 | import Data.Text (unpack, pack) 14 | import Text.Megaparsec as Mp 15 | import Replace.Megaparsec 16 | 17 | import Data.Void 18 | import Data.Maybe 19 | import Data.Proxy 20 | import Control.Monad 21 | 22 | type Parser = Parsec Void String 23 | 24 | main :: IO () 25 | main = do 26 | getContents >>= streamEditT pattern hoogleReplace >>= putStr 27 | where 28 | pattern = match $ eitherP doubleBacktickMask backtickSymbol 29 | hoogleReplace (orig, Left _) = return orig -- masked by doubleBacktickMask 30 | hoogleReplace (orig, Right (_, _, "=", _, _)) = return orig -- not acually a symbol 31 | hoogleReplace (orig, Right (tickPrefix, tickOpen, symbol, tickClose, tickSuffix)) = do 32 | 33 | -- https://github.com/ndmitchell/hoogle/blob/master/docs/API.md#json-api 34 | -- 35 | -- Query looks like this: 36 | -- https://hoogle.haskell.org/?mode=json&hoogle=head&scope=package:base&start=1&count=1 37 | -- 38 | -- Query looks like this: 39 | -- https://hoogle.haskell.org/?mode=json&hoogle=%3E%3E%3D&scope=package:base&start=1&count=1 40 | 41 | hoogleResult <- flip getWith 42 | "https://hoogle.haskell.org" 43 | $ defaults 44 | & param "mode" .~ ["json"] 45 | & param "hoogle" .~ [pack symbol] -- will be url encoded 46 | & param "scope" .~ ["package:base"] 47 | & param "start" .~ ["1"] 48 | & param "count" .~ ["1"] 49 | 50 | -- Result looks like this: 51 | -- [ 52 | -- { 53 | -- "url": "https://hackage.haskell.org/package/base/docs/Prelude.html#v:head", 54 | -- "module": { 55 | -- "url": "https://hackage.haskell.org/package/base/docs/Prelude.html", 56 | -- "name": "Prelude" 57 | -- }, 58 | -- "package": { 59 | -- "url": "https://hackage.haskell.org/package/base", 60 | -- "name": "base" 61 | -- }, 62 | -- "item": "head :: [a] -> a", 63 | -- "type": "", 64 | -- "docs": "Extract the first element of a list, which must be non-empty.\n" 65 | -- } 66 | -- ] 67 | -- 68 | -- Result looks like this: 69 | -- [ 70 | -- { 71 | -- "url": "https://hackage.haskell.org/package/base/docs/Prelude.html#v:-62--62--61-", 72 | -- "module": { 73 | -- "url": "https://hackage.haskell.org/package/base/docs/Prelude.html", 74 | -- "name": "Prelude" 75 | -- }, 76 | -- "package": { 77 | -- "url": "https://hackage.haskell.org/package/base", 78 | -- "name": "base" 79 | -- }, 80 | -- "item": "(>>=) :: forall a b . Monad m => m a -> (a -> m b) -> m b", 81 | -- "type": "", 82 | -- "docs": "Sequentially compose two actions, passing any value produced by the\nfirst as an argument to the second.\n" 83 | -- } 84 | -- ] 85 | 86 | let hoogleReturnItem = fmap unpack $ listToMaybe $ hoogleResult ^.. responseBody . nth 0 . key "item" . _String 87 | 88 | -- If hoogle returns a documentation URL, and the same symbol 89 | -- that was queried, then substitute a markdown link 90 | case 91 | ( maybe False (==symbol) $ parseMaybe (hoogleReturnItemSymbol :: Parser String) =<< hoogleReturnItem 92 | , listToMaybe $ hoogleResult ^.. responseBody . nth 0 . key "url" . _String 93 | ) of 94 | (True, Just docUrl) -> 95 | -- Construct a Markdown link with the documentation URL 96 | return 97 | $ tickPrefix 98 | ++ "[" 99 | ++ tickOpen 100 | ++ symbol 101 | ++ tickClose 102 | ++ "](" 103 | ++ unpack docUrl 104 | ++ ")" 105 | ++ tickSuffix 106 | _ -> return orig 107 | 108 | hoogleReturnItemSymbol :: Parser String 109 | hoogleReturnItemSymbol = do 110 | void $ manyTill anySingle $ chunk "" 111 | s <- fmap (tokensToChunk (Proxy::Proxy String)) 112 | $ someTill anySingle $ chunk "" 113 | void $ takeRest -- must consume all input for parseMaybe success 114 | return $ streamEdit (chunk "<") (const "<") 115 | $ streamEdit (chunk ">") (const ">") 116 | $ streamEdit (chunk "&") (const "&") s 117 | 118 | -- exclude special Markdown backtick escape like ``92 `div` 10``. 119 | doubleBacktickMask = do 120 | void $ chunk "``" 121 | void $ manyTill anySingle $ chunk "``" 122 | -- we have to succeed and return something or 'sepCap' will backtrack 123 | 124 | -- Parse something that looks like a symbol from Prelude in backticks. 125 | backtickSymbol = do 126 | tickPrefix <- noneOf ("[" :: String) 127 | tickOpen <- chunk "`" 128 | symbol <- Mp.some $ Mp.oneOf $ ['a'..'z'] ++ ['A'..'Z'] ++ "|&?%^*#~.<>+=-$/:'" 129 | tickClose <- chunk "`" 130 | tickSuffix <- noneOf ("]" :: String) 131 | return ([tickPrefix], tickOpen, symbol, tickClose, [tickSuffix]) 132 | 133 | -- TODO: 134 | -- 135 | -- cases that don't work: 136 | -- infix backtick functions like `elem` in code blocks. we're going to 137 | -- need to only search in "cell-type":"markdown". 138 | -- 139 | -- We need to parse only markdown cells, like: 140 | -- { 141 | -- "cell_type": "markdown", 142 | -- "metadata": {}, 143 | -- "source": [ 144 | -- "__`repeat`__ takes an element and produces an infinite list of just that\n", 145 | -- "element. It's like cycling a list with only one element." 146 | -- ] 147 | -- }, 148 | -- markdownCell :: Parser () 149 | -- markdownCell = do 150 | -- chunk "{" 151 | -- space 152 | -- chunk "\"cell_type\": \"markdown\"," 153 | -- space 154 | -- chunk "\"metadata\": {}," 155 | -- space 156 | -- chunk "\"source\": [" 157 | -- space 158 | -- chunk "]" >> newline 159 | -- return () 160 | 161 | -------------------------------------------------------------------------------- /anchordoc/stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: nightly-2019-09-13 2 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | 3 | services: 4 | ihaskell-notebook: 5 | build: . 6 | ports: 7 | - "8888:8888" 8 | volumes: 9 | - ./notebook:/home/jovyan/work 10 | restart: always 11 | environment: 12 | JUPYTER_ENABLE_LAB: "yes" 13 | JUPYTER_TOKEN: x 14 | 15 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "IHaskell": { 4 | "inputs": { 5 | "flake-utils": "flake-utils", 6 | "hls": "hls", 7 | "nix-filter": "nix-filter", 8 | "nixpkgs24_11": "nixpkgs24_11", 9 | "nixpkgsMaster": "nixpkgsMaster" 10 | }, 11 | "locked": { 12 | "lastModified": 1746628981, 13 | "narHash": "sha256-IaV7XyrmanvJhg4zugrgrPjyaXF8WDj4XrBC2lxhXvc=", 14 | "owner": "IHaskell", 15 | "repo": "IHaskell", 16 | "rev": "fefb187c617b958458244a3919200d5f091a6f6e", 17 | "type": "github" 18 | }, 19 | "original": { 20 | "owner": "IHaskell", 21 | "repo": "IHaskell", 22 | "type": "github" 23 | } 24 | }, 25 | "flake-compat": { 26 | "flake": false, 27 | "locked": { 28 | "lastModified": 1733328505, 29 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", 30 | "owner": "edolstra", 31 | "repo": "flake-compat", 32 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", 33 | "type": "github" 34 | }, 35 | "original": { 36 | "owner": "edolstra", 37 | "repo": "flake-compat", 38 | "type": "github" 39 | } 40 | }, 41 | "flake-utils": { 42 | "inputs": { 43 | "systems": "systems" 44 | }, 45 | "locked": { 46 | "lastModified": 1731533236, 47 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 48 | "owner": "numtide", 49 | "repo": "flake-utils", 50 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 51 | "type": "github" 52 | }, 53 | "original": { 54 | "owner": "numtide", 55 | "repo": "flake-utils", 56 | "type": "github" 57 | } 58 | }, 59 | "flake-utils_2": { 60 | "locked": { 61 | "lastModified": 1676283394, 62 | "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", 63 | "owner": "numtide", 64 | "repo": "flake-utils", 65 | "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073", 66 | "type": "github" 67 | }, 68 | "original": { 69 | "owner": "numtide", 70 | "repo": "flake-utils", 71 | "type": "github" 72 | } 73 | }, 74 | "hls": { 75 | "inputs": { 76 | "flake-compat": "flake-compat", 77 | "flake-utils": [ 78 | "IHaskell", 79 | "flake-utils" 80 | ], 81 | "nixpkgs": "nixpkgs" 82 | }, 83 | "locked": { 84 | "lastModified": 1745762042, 85 | "narHash": "sha256-q0i+5CUzSrRY5idecKsx6EqpxTh3mkKNlGKCR6D3JoE=", 86 | "owner": "haskell", 87 | "repo": "haskell-language-server", 88 | "rev": "173b5a7fbfb92c3c6a3375be100b662226aad852", 89 | "type": "github" 90 | }, 91 | "original": { 92 | "owner": "haskell", 93 | "repo": "haskell-language-server", 94 | "type": "github" 95 | } 96 | }, 97 | "nix-filter": { 98 | "locked": { 99 | "lastModified": 1731533336, 100 | "narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=", 101 | "owner": "numtide", 102 | "repo": "nix-filter", 103 | "rev": "f7653272fd234696ae94229839a99b73c9ab7de0", 104 | "type": "github" 105 | }, 106 | "original": { 107 | "owner": "numtide", 108 | "repo": "nix-filter", 109 | "type": "github" 110 | } 111 | }, 112 | "nixpkgs": { 113 | "locked": { 114 | "lastModified": 1739019272, 115 | "narHash": "sha256-7Fu7oazPoYCbDzb9k8D/DdbKrC3aU1zlnc39Y8jy/s8=", 116 | "owner": "NixOS", 117 | "repo": "nixpkgs", 118 | "rev": "fa35a3c8e17a3de613240fea68f876e5b4896aec", 119 | "type": "github" 120 | }, 121 | "original": { 122 | "owner": "NixOS", 123 | "ref": "nixpkgs-unstable", 124 | "repo": "nixpkgs", 125 | "type": "github" 126 | } 127 | }, 128 | "nixpkgs24_11": { 129 | "locked": { 130 | "lastModified": 1746301764, 131 | "narHash": "sha256-5odz+NZszRya//Zd0P8h+sIwOnV35qJi+73f4I+iv1M=", 132 | "owner": "NixOS", 133 | "repo": "nixpkgs", 134 | "rev": "537ee98218704e21ea465251de512ab6bbb9012e", 135 | "type": "github" 136 | }, 137 | "original": { 138 | "owner": "NixOS", 139 | "ref": "release-24.11", 140 | "repo": "nixpkgs", 141 | "type": "github" 142 | } 143 | }, 144 | "nixpkgsMaster": { 145 | "locked": { 146 | "lastModified": 1746325182, 147 | "narHash": "sha256-pqy0L71v6zLA9Yduoc4jef/TXteKv6Icz+Dok8fNx00=", 148 | "owner": "NixOS", 149 | "repo": "nixpkgs", 150 | "rev": "6a81767ccacfe438ca3b2c6d5ceed4163def4f4f", 151 | "type": "github" 152 | }, 153 | "original": { 154 | "owner": "NixOS", 155 | "ref": "master", 156 | "repo": "nixpkgs", 157 | "type": "github" 158 | } 159 | }, 160 | "root": { 161 | "inputs": { 162 | "IHaskell": "IHaskell", 163 | "flake-utils": "flake-utils_2" 164 | } 165 | }, 166 | "systems": { 167 | "locked": { 168 | "lastModified": 1681028828, 169 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 170 | "owner": "nix-systems", 171 | "repo": "default", 172 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 173 | "type": "github" 174 | }, 175 | "original": { 176 | "owner": "nix-systems", 177 | "repo": "default", 178 | "type": "github" 179 | } 180 | } 181 | }, 182 | "root": "root", 183 | "version": 7 184 | } 185 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Learn You a Haskell for Great Good! Jupyter adaptation"; 3 | 4 | inputs.flake-utils.url = "github:numtide/flake-utils"; 5 | inputs.IHaskell.url = "github:IHaskell/IHaskell"; 6 | 7 | nixConfig = { 8 | extra-substituters = [ "https://ihaskell.cachix.org" ]; 9 | extra-trusted-public-keys = [ "ihaskell.cachix.org-1:WoIvex/Ft/++sjYW3ntqPUL3jDGXIKDpX60pC8d5VLM="]; 10 | }; 11 | 12 | outputs = { 13 | self, 14 | flake-utils, 15 | IHaskell, 16 | ... 17 | } @ inputs: 18 | flake-utils.lib.eachDefaultSystem (system: 19 | let 20 | pkgs = IHaskell.inputs.nixpkgs24_11.legacyPackages.${system}; 21 | 22 | ihaskell-env = IHaskell.packages.${system}.ihaskell-env-display-ghc98; 23 | notebook-lyah = pkgs.stdenv.mkDerivation { 24 | name = "notebook-lyah"; 25 | src = ./notebook; 26 | phases = [ "unpackPhase" "installPhase" ]; 27 | installPhase = '' 28 | mkdir -p $out 29 | cp -r $src/* $out/ 30 | ''; 31 | }; 32 | in rec { 33 | packages = { 34 | inherit ihaskell-env notebook-lyah; 35 | }; 36 | apps = { 37 | default = 38 | let 39 | script = pkgs.writeShellApplication { 40 | name = "jupyter-lab-lyah"; 41 | runtimeInputs = [ ihaskell-env notebook-lyah ]; 42 | text = "jupyter lab --notebook-dir=${notebook-lyah} ${notebook-lyah}/00-preface.ipynb"; 43 | }; 44 | in 45 | { 46 | type = "app"; 47 | program = "${script}/bin/jupyter-lab-lyah"; 48 | }; 49 | }; 50 | } 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /kernels.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | # kernel.python.minimal = { 3 | # enable = true; 4 | # }; 5 | kernel.haskell."1" = { 6 | enable = true; 7 | displayName = "Haskell"; 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /kernels/haskell.nix: -------------------------------------------------------------------------------- 1 | { 2 | name, 3 | availableKernels, 4 | extraArgs, 5 | }: 6 | availableKernels.haskell { 7 | inherit name; 8 | inherit (extraArgs) pkgs; 9 | displayName = "Haskell"; 10 | } 11 | -------------------------------------------------------------------------------- /notebook/00-preface.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | "\n", 9 | "Preface to the Jupyter notebook\n", 10 | "===============================\n", 11 | "\n", 12 | "This is a Jupyter notebook adaptation of the book [__*Learn You a Haskell for Great Good!*__](http://learnyouahaskell.com/) by Miran Lipovača.\n", 13 | "\n", 14 | "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", 15 | "\n", 16 | "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", 17 | "\n", 18 | "Each chapter of the book is one `.ipynb` Jupyter notebook file. See the list of chapter files on the left sidebar in JupyterLab.\n", 19 | "\n", 20 | "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", 21 | " \n", 22 | "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", 23 | "\n", 24 | "\n", 25 | "\n", 26 | "Please fork or contribute or ★ or report bugs on GitHub: \n", 27 | "\n", 28 | "
\n", 29 | "James Brock, Tokyo 2019\n", 30 | "
" 31 | ] 32 | } 33 | ], 34 | "metadata": { 35 | "kernelspec": { 36 | "display_name": "Haskell", 37 | "language": "haskell", 38 | "name": "haskell" 39 | }, 40 | "language_info": { 41 | "codemirror_mode": "ihaskell", 42 | "file_extension": ".hs", 43 | "mimetype": "text/x-haskell", 44 | "name": "haskell", 45 | "pygments_lexer": "Haskell", 46 | "version": "9.2.5" 47 | } 48 | }, 49 | "nbformat": 4, 50 | "nbformat_minor": 4 51 | } 52 | -------------------------------------------------------------------------------- /notebook/01-introduction.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Introduction\n", 8 | "============\n", 9 | "\n", 10 | "About this tutorial\n", 11 | "-------------------\n", 12 | "\n", 13 | "Welcome to __Learn You a Haskell for Great Good__! If you're reading this,\n", 14 | "chances are you want to learn Haskell. Well, you've come to the right\n", 15 | "place, but let's talk about this tutorial a bit first.\n", 16 | "\n", 17 | "I decided to write this because I wanted to solidify my own knowledge of\n", 18 | "Haskell and because I thought I could help people new to Haskell learn\n", 19 | "it from my perspective. There are quite a few tutorials on Haskell\n", 20 | "floating around on the internet. When I was starting out in Haskell, I\n", 21 | "didn't learn from just one resource. The way I learned it was by reading\n", 22 | "several different tutorials and articles because each explained\n", 23 | "something in a different way than the other did. By going through\n", 24 | "several resources, I was able put together the pieces and it all just\n", 25 | "came falling into place. So this is an attempt at adding another useful\n", 26 | "resource for learning Haskell so you have a bigger chance of finding one\n", 27 | "you like.\n", 28 | "\n", 29 | "\n", 30 | "\n", 31 | "This tutorial is aimed at people who have experience in imperative\n", 32 | "programming languages (C, C++, Java, Python …) but haven't programmed in\n", 33 | "a functional language before (Haskell, ML, OCaml …). Although I bet that\n", 34 | "even if you don't have any significant programming experience, a smart\n", 35 | "person such as yourself will be able to follow along and learn Haskell.\n", 36 | "\n", 37 | "The channel \\#haskell on the freenode network is a great place to ask\n", 38 | "questions if you're feeling stuck. People there are extremely nice,\n", 39 | "patient and understanding to newbies.\n", 40 | "\n", 41 | "I failed to learn Haskell approximately 2 times before finally grasping\n", 42 | "it because it all just seemed too weird to me and I didn't get it. But\n", 43 | "then once it just \"clicked\" and after getting over that initial hurdle,\n", 44 | "it was pretty much smooth sailing. I guess what I'm trying to say is:\n", 45 | "Haskell is great and if you're interested in programming you should\n", 46 | "really learn it even if it seems weird at first. Learning Haskell is\n", 47 | "much like learning to program for the first time — it's fun! It forces\n", 48 | "you to think differently, which brings us to the next section …\n", 49 | "\n", 50 | "So what's Haskell?\n", 51 | "------------------\n", 52 | "\n", 53 | " Haskell is a __purely\n", 54 | "functional programming language__. In imperative languages you get things\n", 55 | "done by giving the computer a sequence of tasks and then it executes\n", 56 | "them. While executing them, it can change state. For instance, you set\n", 57 | "variable `a` to 5 and then do some stuff and then set it to something\n", 58 | "else. You have control flow structures for doing some action several\n", 59 | "times. In purely functional programming you don't tell the computer what\n", 60 | "to do as such but rather you tell it what stuff *is*. The factorial of a\n", 61 | "number is the product of all the numbers from 1 to that number, the sum\n", 62 | "of a list of numbers is the first number plus the sum of all the other\n", 63 | "numbers, and so on. You express that in the form of functions. You also\n", 64 | "can't set a variable to something and then set it to something else\n", 65 | "later. If you say that a is 5, you can't say it's something else later\n", 66 | "because you just said it was 5. What are you, some kind of liar? So in\n", 67 | "purely functional languages, a function has no side-effects. The only\n", 68 | "thing a function can do is calculate something and return it as a\n", 69 | "result. At first, this seems kind of limiting but it actually has some\n", 70 | "very nice consequences: if a function is called twice with the same\n", 71 | "parameters, it's guaranteed to return the same result. That's called\n", 72 | "referential transparency and not only does it allow the compiler to\n", 73 | "reason about the program's behavior, but it also allows you to easily\n", 74 | "deduce (and even prove) that a function is correct and then build more\n", 75 | "complex functions by gluing simple functions together.\n", 76 | "\n", 77 | " Haskell is __lazy__. That\n", 78 | "means that unless specifically told otherwise, Haskell won't execute\n", 79 | "functions and calculate things until it's really forced to show you a\n", 80 | "result. That goes well with referential transparency and it allows you\n", 81 | "to think of programs as a series of __transformations on data__. It also\n", 82 | "allows cool things such as infinite data structures. Say you have an\n", 83 | "immutable list of numbers `xs = [1,2,3,4,5,6,7,8]` and a function `doubleMe`\n", 84 | "which multiplies every element by 2 and then returns a new list. If we\n", 85 | "wanted to multiply our list by 8 in an imperative language and did\n", 86 | "`doubleMe(doubleMe(doubleMe(xs)))`, it would probably pass through the\n", 87 | "list once and make a copy and then return it. Then it would pass through\n", 88 | "the list another two times and return the result. In a lazy language,\n", 89 | "calling `doubleMe` on a list without forcing it to show you the result\n", 90 | "ends up in the program sort of telling you \"Yeah yeah, I'll do it\n", 91 | "later!\". But once you want to see the result, the first `doubleMe` tells\n", 92 | "the second one it wants the result, now! The second one says that to the\n", 93 | "third one and the third one reluctantly gives back a doubled 1, which is\n", 94 | "a 2. The second one receives that and gives back 4 to the first one. The\n", 95 | "first one sees that and tells you the first element is 8. So it only\n", 96 | "does one pass through the list and only when you really need it. That\n", 97 | "way when you want something from a lazy language you can just take some\n", 98 | "initial data and efficiently transform and mend it so it resembles what\n", 99 | "you want at the end.\n", 100 | "\n", 101 | " Haskell is __statically\n", 102 | "typed__. When you compile your program, the compiler knows which piece of\n", 103 | "code is a number, which is a string and so on. That means that a lot of\n", 104 | "possible errors are caught at compile time. If you try to add together a\n", 105 | "number and a string, the compiler will whine at you. Haskell uses a very\n", 106 | "good type system that has __type inference__. That means that you don't\n", 107 | "have to explicitly label every piece of code with a type because the\n", 108 | "type system can intelligently figure out a lot about it. If you say `a =\n", 109 | "5 + 4`, you don't have to tell Haskell that `a` is a number, it can figure\n", 110 | "that out by itself. Type inference also allows your code to be more\n", 111 | "general. If a function you make takes two parameters and adds them\n", 112 | "together and you don't explicitly state their type, the function will\n", 113 | "work on any two parameters that act like numbers.\n", 114 | "\n", 115 | "Haskell is __elegant and concise__. Because it uses a lot of high level\n", 116 | "concepts, Haskell programs are usually shorter than their imperative\n", 117 | "equivalents. And shorter programs are easier to maintain than longer\n", 118 | "ones and have less bugs.\n", 119 | "\n", 120 | "Haskell was made by some __really smart guys__ (with PhDs). Work on\n", 121 | "Haskell began in 1987 when a committee of researchers got together to\n", 122 | "design a kick-ass language. In 2003 the Haskell Report was published,\n", 123 | "which defines a stable version of the language." 124 | ] 125 | } 126 | ], 127 | "metadata": { 128 | "kernelspec": { 129 | "display_name": "Haskell", 130 | "language": "haskell", 131 | "name": "haskell" 132 | }, 133 | "language_info": { 134 | "codemirror_mode": "ihaskell", 135 | "file_extension": ".hs", 136 | "mimetype": "text/x-haskell", 137 | "name": "haskell", 138 | "pygments_lexer": "Haskell", 139 | "version": "9.2.5" 140 | } 141 | }, 142 | "nbformat": 4, 143 | "nbformat_minor": 4 144 | } 145 | -------------------------------------------------------------------------------- /notebook/05-recursion.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Recursion\n", 8 | "=========\n", 9 | "\n", 10 | "\n", 11 | "\n", 12 | "Hello recursion!\n", 13 | "----------------\n", 14 | "\n", 15 | "\n", 16 | "\n", 17 | "We mention recursion briefly in the previous chapter. In this chapter,\n", 18 | "we'll take a closer look at recursion, why it's important to Haskell and\n", 19 | "how we can work out very concise and elegant solutions to problems by\n", 20 | "thinking recursively.\n", 21 | "\n", 22 | "If you still don't know what recursion is, read this sentence. Haha!\n", 23 | "Just kidding! Recursion is actually a way of defining functions in which\n", 24 | "the function is applied inside its own definition. Definitions in\n", 25 | "mathematics are often given recursively. For instance, the Fibonacci\n", 26 | "sequence is defined recursively. First, we define the first two\n", 27 | "Fibonacci numbers non-recursively. We say that $F(0) = 0$ and\n", 28 | "$F(1) = 1$, meaning that the 0th and 1st Fibonacci numbers are 0 and 1,\n", 29 | "respectively. Then we say that for any other natural number, that\n", 30 | "Fibonacci number is the sum of the previous two Fibonacci numbers. So\n", 31 | "$F(n) = F(n-1) + F(n-2)$. That way, $F(3)$ is $F(2) + F(1)$, which is\n", 32 | "$(F(1) + F(0)) + F(1)$. Because we've now come down to only\n", 33 | "non-recursively defined Fibonacci numbers, we can safely say that $F(3)$\n", 34 | "is 2. Having an element or two in a recursion definition defined\n", 35 | "non-recursively (like $F(0)$ and $F(1)$ here) is also called the __edge\n", 36 | "condition__ and is important if you want your recursive function to\n", 37 | "terminate. If we hadn't defined $F(0)$ and $F(1)$ non recursively, you'd\n", 38 | "never get a solution any number because you'd reach 0 and then you'd go\n", 39 | "into negative numbers. All of a sudden, you'd be saying that $F(-2000)$\n", 40 | "is $F(-2001) + F(-2002)$ and there still wouldn't be an end in sight!\n", 41 | "\n", 42 | "Recursion is important to Haskell because unlike imperative languages,\n", 43 | "you do computations in Haskell by declaring what something *is* instead\n", 44 | "of declaring *how* you get it. That's why there are no while loops or\n", 45 | "for loops in Haskell and instead we many times have to use recursion to\n", 46 | "declare what something is.\n", 47 | "\n", 48 | "Maximum awesome\n", 49 | "---------------\n", 50 | "\n", 51 | "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", 52 | "instances of the [`Ord`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Ord) typeclass) and returns the biggest of them. Think\n", 53 | "about how you'd implement that in an imperative fashion. You'd probably\n", 54 | "set up a variable to hold the maximum value so far and then you'd loop\n", 55 | "through the elements of a list and if an element is bigger than then the\n", 56 | "current maximum value, you'd replace it with that element. The maximum\n", 57 | "value that remains at the end is the result. Whew! That's quite a lot of\n", 58 | "words to describe such a simple algorithm!\n", 59 | "\n", 60 | "Now let's see how we'd define it recursively. We could first set up an\n", 61 | "edge condition and say that the maximum of a singleton list is equal to\n", 62 | "the only element in it. Then we can say that the maximum of a longer\n", 63 | "list is the head if the head is bigger than the maximum of the tail. If\n", 64 | "the maximum of the tail is bigger, well, then it's the maximum of the\n", 65 | "tail. That's it! Now let's implement that in Haskell." 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": 1, 71 | "metadata": { 72 | "attributes": { 73 | "classes": [ 74 | "haskell:hs" 75 | ], 76 | "id": "", 77 | "name": "\"code\"" 78 | } 79 | }, 80 | "outputs": [], 81 | "source": [ 82 | "maximum' :: (Ord a) => [a] -> a\n", 83 | "maximum' [] = error \"maximum of empty list\"\n", 84 | "maximum' [x] = x\n", 85 | "maximum' (x:xs)\n", 86 | " | x > maxTail = x\n", 87 | " | otherwise = maxTail\n", 88 | " where maxTail = maximum' xs" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "As you can see, pattern matching goes great with recursion! Most\n", 96 | "imperative languages don't have pattern matching so you have to make a\n", 97 | "lot of if else statements to test for edge conditions. Here, we simply\n", 98 | "put them out as patterns. So the first edge condition says that if the\n", 99 | "list is empty, crash! Makes sense because what's the maximum of an empty\n", 100 | "list? I don't know. The second pattern also lays out an edge condition.\n", 101 | "It says that if it's the singleton list, just give back the only\n", 102 | "element.\n", 103 | "\n", 104 | "Now the third pattern is where the action happens. We use pattern\n", 105 | "matching to split a list into a head and a tail. This is a very common\n", 106 | "idiom when doing recursion with lists, so get used to it. We use a\n", 107 | "*where* binding to define `maxTail` as the maximum of the rest of the\n", 108 | "list. Then we check if the head is greater than the maximum of the rest\n", 109 | "of the list. If it is, we return the head. Otherwise, we return the\n", 110 | "maximum of the rest of the list.\n", 111 | "\n", 112 | "Let's take an example list of numbers and check out how this would work\n", 113 | "on them: `[2,5,1]`. If we call `maximum'` on that, the first two patterns\n", 114 | "won't match. The third one will and the list is split into `2` and `[5,1]`.\n", 115 | "The *where* clause wants to know the maximum of `[5,1]`, so we follow that\n", 116 | "route. It matches the third pattern again and `[5,1]` is split into `5` and\n", 117 | "`[1]`. Again, the `where` clause wants to know the maximum of `[1]`. Because\n", 118 | "that's the edge condition, it returns `1`. Finally! So going up one step,\n", 119 | "comparing `5` to the maximum of `[1]` (which is `1`), we obviously get back `5`.\n", 120 | "So now we know that the maximum of `[5,1]` is `5`. We go up one step again\n", 121 | "where we had `2` and `[5,1]`. Comparing `2` with the maximum of `[5,1]`, which\n", 122 | "is `5`, we choose `5`.\n", 123 | "\n", 124 | "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", 125 | "remember, [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max) is a function that takes two numbers and returns the\n", 126 | "bigger of them. Here's how we could rewrite `maximum'` by using [`max`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:max):" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 2, 132 | "metadata": { 133 | "attributes": { 134 | "classes": [ 135 | "haskell:hs" 136 | ], 137 | "id": "", 138 | "name": "\"code\"" 139 | } 140 | }, 141 | "outputs": [], 142 | "source": [ 143 | "maximum' :: (Ord a) => [a] -> a\n", 144 | "maximum' [] = error \"maximum of empty list\"\n", 145 | "maximum' [x] = x\n", 146 | "maximum' (x:xs) = max x (maximum' xs)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": {}, 152 | "source": [ 153 | "How's that for elegant! In essence, the maximum of a list is the max of\n", 154 | "the first element and the maximum of the tail.\n", 155 | "\n", 156 | "\n", 157 | "\n", 158 | "A few more recursive functions\n", 159 | "------------------------------\n", 160 | "\n", 161 | "Now that we know how to generally think recursively, let's implement a\n", 162 | "few functions using recursion. First off, we'll implement [`replicate`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:replicate).\n", 163 | "[`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", 164 | "several repetitions of the same element. For instance, `replicate 3 5`\n", 165 | "returns `[5,5,5]`. Let's think about the edge condition. My guess is that\n", 166 | "the edge condition is 0 or less. If we try to replicate something zero\n", 167 | "times, it should return an empty list. Also for negative numbers,\n", 168 | "because it doesn't really make sense." 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 3, 174 | "metadata": { 175 | "attributes": { 176 | "classes": [ 177 | "haskell:hs" 178 | ], 179 | "id": "", 180 | "name": "\"code\"" 181 | } 182 | }, 183 | "outputs": [], 184 | "source": [ 185 | "replicate' :: (Num i, Ord i) => i -> a -> [a]\n", 186 | "replicate' n x\n", 187 | " | n <= 0 = []\n", 188 | " | otherwise = x:replicate' (n-1) x" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "We used guards here instead of patterns because we're testing for a\n", 196 | "boolean condition. If `n` is less than or equal to 0, return an empty\n", 197 | "list. Otherwise return a list that has `x` as the first element and then `x`\n", 198 | "replicated n-1 times as the tail. Eventually, the `(n-1)` part will cause\n", 199 | "our function to reach the edge condition.\n", 200 | "\n", 201 | "> __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", 202 | "> for a number doesn't really have to adhere to an ordering. So that's why\n", 203 | "> 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", 204 | "> addition or subtraction and also comparison.\n", 205 | "\n", 206 | "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", 207 | "from a list. For instance, `take 3 [5,4,3,2,1]` will return `[5,4,3]`. If we\n", 208 | "try to take 0 or less elements from a list, we get an empty list. Also\n", 209 | "if we try to take anything from an empty list, we get an empty list.\n", 210 | "Notice that those are two edge conditions right there. So let's write\n", 211 | "that out:" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": 4, 217 | "metadata": { 218 | "attributes": { 219 | "classes": [ 220 | "haskell:hs" 221 | ], 222 | "id": "", 223 | "name": "\"code\"" 224 | } 225 | }, 226 | "outputs": [], 227 | "source": [ 228 | "take' :: (Num i, Ord i) => i -> [a] -> [a]\n", 229 | "take' n _\n", 230 | " | n <= 0 = []\n", 231 | "take' _ [] = []\n", 232 | "take' n (x:xs) = x : take' (n-1) xs" 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": {}, 238 | "source": [ 239 | "\n", 240 | "\n", 241 | "The first pattern specifies that if we try to take a 0 or negative\n", 242 | "number of elements, we get an empty list. Notice that we're using `_` to\n", 243 | "match the list because we don't really care what it is in this case.\n", 244 | "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", 245 | "means that if `n` turns out to be more than 0, the matching will fall\n", 246 | "through to the next pattern. The second pattern indicates that if we try\n", 247 | "to take anything from an empty list, we get an empty list. The third\n", 248 | "pattern breaks the list into a head and a tail. And then we state that\n", 249 | "taking `n` elements from a list equals a list that has `x` as the head and\n", 250 | "then a list that takes `n-1` elements from the tail as a tail. Try using a\n", 251 | "piece of paper to write down how the evaluation would look like if we\n", 252 | "try to take, say, 3 from `[4,3,2,1]`.\n", 253 | "\n", 254 | "[`reverse`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:reverse) simply reverses a list. Think about the edge condition. What is\n", 255 | "it? Come on ... it's the empty list! An empty list reversed equals the\n", 256 | "empty list itself. O-kay. What about the rest of it? Well, you could say\n", 257 | "that if we split a list to a head and a tail, the reversed list is equal\n", 258 | "to the reversed tail and then the head at the end." 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": 5, 264 | "metadata": { 265 | "attributes": { 266 | "classes": [ 267 | "haskell:hs" 268 | ], 269 | "id": "", 270 | "name": "\"code\"" 271 | } 272 | }, 273 | "outputs": [], 274 | "source": [ 275 | "reverse' :: [a] -> [a]\n", 276 | "reverse' [] = []\n", 277 | "reverse' (x:xs) = reverse' xs ++ [x]" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": [ 284 | "There we go!\n", 285 | "\n", 286 | "Because Haskell supports infinite lists, our recursion doesn't really\n", 287 | "have to have an edge condition. But if it doesn't have it, it will\n", 288 | "either keep churning at something infinitely or produce an infinite data\n", 289 | "structure, like an infinite list. The good thing about infinite lists\n", 290 | "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", 291 | "and returns an infinite list that just has that element. A recursive\n", 292 | "implementation of that is really easy, watch." 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 6, 298 | "metadata": { 299 | "attributes": { 300 | "classes": [ 301 | "haskell:hs" 302 | ], 303 | "id": "", 304 | "name": "\"code\"" 305 | } 306 | }, 307 | "outputs": [], 308 | "source": [ 309 | "repeat' :: a -> [a]\n", 310 | "repeat' x = x:repeat' x" 311 | ] 312 | }, 313 | { 314 | "cell_type": "markdown", 315 | "metadata": {}, 316 | "source": [ 317 | "Calling `repeat 3` will give us a list that starts with `3` and then has an\n", 318 | "infinite amount of 3's as a tail. So calling `repeat 3` would evaluate\n", 319 | "like `3:repeat 3`, which is `3:(3:repeat 3)`, which is `3:(3:(3:repeat 3))`,\n", 320 | "etc. `repeat 3` will never finish evaluating, whereas `take 5 (repeat 3)`\n", 321 | "will give us a list of five 3's. So essentially it's like doing\n", 322 | "`replicate 5 3`.\n", 323 | "\n", 324 | "[`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", 325 | "`[(1,2),(2,3)]`, because it truncates the longer list to match the length\n", 326 | "of the shorter one. How about if we zip something with an empty list?\n", 327 | "Well, we get an empty list back then. So there's our edge condition.\n", 328 | "However, [`zip`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:zip) takes two lists as parameters, so there are actually two\n", 329 | "edge conditions." 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": 7, 335 | "metadata": { 336 | "attributes": { 337 | "classes": [ 338 | "haskell:hs" 339 | ], 340 | "id": "", 341 | "name": "\"code\"" 342 | } 343 | }, 344 | "outputs": [], 345 | "source": [ 346 | "zip' :: [a] -> [b] -> [(a,b)]\n", 347 | "zip' _ [] = []\n", 348 | "zip' [] _ = []\n", 349 | "zip' (x:xs) (y:ys) = (x,y):zip' xs ys" 350 | ] 351 | }, 352 | { 353 | "cell_type": "markdown", 354 | "metadata": {}, 355 | "source": [ 356 | "First two patterns say that if the first list or second list is empty,\n", 357 | "we get an empty list. The third one says that two lists zipped are equal\n", 358 | "to pairing up their heads and then tacking on the zipped tails. Zipping\n", 359 | "`[1,2,3]` and `['a','b']` will eventually try to zip `[3]` with `[]`. The edge\n", 360 | "condition patterns kick in and so the result is `(1,'a'):(2,'b'):[]`,\n", 361 | "which is exactly the same as `[(1,'a'),(2,'b')]`.\n", 362 | "\n", 363 | "Let's implement one more standard library function — [`elem`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem). It takes an\n", 364 | "element and a list and sees if that element is in the list. The edge\n", 365 | "condition, as is most of the times with lists, is the empty list. We\n", 366 | "know that an empty list contains no elements, so it certainly doesn't\n", 367 | "have the droids we're looking for." 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "execution_count": 8, 373 | "metadata": { 374 | "attributes": { 375 | "classes": [ 376 | "haskell:hs" 377 | ], 378 | "id": "", 379 | "name": "\"code\"" 380 | } 381 | }, 382 | "outputs": [], 383 | "source": [ 384 | "elem' :: (Eq a) => a -> [a] -> Bool\n", 385 | "elem' a [] = False\n", 386 | "elem' a (x:xs)\n", 387 | " | a == x = True\n", 388 | " | otherwise = a `elem'` xs" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": {}, 394 | "source": [ 395 | "Pretty simple and expected. If the head isn't the element then we check\n", 396 | "the tail. If we reach an empty list, the result is [`False`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:False).\n", 397 | "\n", 398 | "Quick, sort!\n", 399 | "------------\n", 400 | "\n", 401 | "We have a list of items that can be sorted. Their type is an instance of\n", 402 | "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", 403 | "algorithm for sorting called quicksort. It's a very clever way of sorting\n", 404 | "items. While it takes upwards of 10 lines to implement quicksort in\n", 405 | "imperative languages, the implementation is much shorter and elegant in\n", 406 | "Haskell. Quicksort has become a sort of poster child for Haskell.\n", 407 | "Therefore, let's implement it here, even though implementing quicksort\n", 408 | "in Haskell is considered really cheesy because everyone does it to\n", 409 | "showcase how elegant Haskell is.\n", 410 | "\n", 411 | "\n", 412 | "\n", 413 | "So, the type signature is going to be\n", 414 | "`quicksort :: (Ord a) => [a] -> [a]`.\n", 415 | "No surprises there. The edge condition? Empty list, as is expected.\n", 416 | "A sorted empty list is an empty list. Now here comes the main algorithm:\n", 417 | "__a sorted list is a list that has all the values smaller than (or equal\n", 418 | "to) the head of the list in front (and those values are sorted), then\n", 419 | "comes the head of the list in the middle and then come all the values\n", 420 | "that are bigger than the head (they're also sorted).__ Notice that we\n", 421 | "said *sorted* two times in this definition, so we'll probably have to\n", 422 | "make the recursive call twice! Also notice that we defined it using the\n", 423 | "verb *is* to define the algorithm instead of saying *do this, do that,\n", 424 | "then do that ...*. That's the beauty of functional programming! How are\n", 425 | "we going to filter the list so that we get only the elements smaller\n", 426 | "than the head of our list and only elements that are bigger? List\n", 427 | "comprehensions. So, let's dive in and define this function." 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": 9, 433 | "metadata": { 434 | "attributes": { 435 | "classes": [ 436 | "haskell:hs" 437 | ], 438 | "id": "", 439 | "name": "\"code\"" 440 | } 441 | }, 442 | "outputs": [], 443 | "source": [ 444 | "quicksort :: (Ord a) => [a] -> [a]\n", 445 | "quicksort [] = []\n", 446 | "quicksort (x:xs) =\n", 447 | " let smallerSorted = quicksort [a | a <- xs, a <= x]\n", 448 | " biggerSorted = quicksort [a | a <- xs, a > x]\n", 449 | " in smallerSorted ++ [x] ++ biggerSorted" 450 | ] 451 | }, 452 | { 453 | "cell_type": "markdown", 454 | "metadata": {}, 455 | "source": [ 456 | "Let's give it a small test run to see if it appears to behave correctly." 457 | ] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "execution_count": 10, 462 | "metadata": { 463 | "attributes": { 464 | "classes": [ 465 | "haskell:ghci" 466 | ], 467 | "id": "", 468 | "name": "\"code\"" 469 | } 470 | }, 471 | "outputs": [ 472 | { 473 | "data": { 474 | "text/plain": [ 475 | "[1,2,2,3,3,4,4,5,6,7,8,9,10]" 476 | ] 477 | }, 478 | "metadata": {}, 479 | "output_type": "display_data" 480 | } 481 | ], 482 | "source": [ 483 | "quicksort [10,2,5,3,1,6,7,4,2,3,4,8,9]" 484 | ] 485 | }, 486 | { 487 | "cell_type": "code", 488 | "execution_count": 11, 489 | "metadata": { 490 | "attributes": { 491 | "classes": [ 492 | "haskell:ghci" 493 | ], 494 | "id": "", 495 | "name": "\"code\"" 496 | } 497 | }, 498 | "outputs": [ 499 | { 500 | "data": { 501 | "text/plain": [ 502 | "\" abcdeeefghhijklmnoooopqrrsttuuvwxyz\"" 503 | ] 504 | }, 505 | "metadata": {}, 506 | "output_type": "display_data" 507 | } 508 | ], 509 | "source": [ 510 | "quicksort \"the quick brown fox jumps over the lazy dog\"" 511 | ] 512 | }, 513 | { 514 | "cell_type": "markdown", 515 | "metadata": {}, 516 | "source": [ 517 | "Booyah! That's what I'm talking about! So if we have, say\n", 518 | "`[5,1,9,4,6,7,3]` and we want to sort it, this algorithm will first take\n", 519 | "the head, which is `5` and then put it in the middle of two lists that are\n", 520 | "smaller and bigger than it. So at one point, you'll have\n", 521 | "`[1,4,3] ++ [5] ++ [9,6,7]`. We know that once the list is sorted completely,\n", 522 | "the number `5` will stay in the fourth place since there are 3 numbers lower\n", 523 | "than it and 3 numbers higher than it. Now, if we sort `[1,4,3]` and `[9,6,7]`, we\n", 524 | "have a sorted list! We sort the two lists using the same function.\n", 525 | "Eventually, we'll break it up so much that we reach empty lists and an\n", 526 | "empty list is already sorted in a way, by virtue of being empty. Here's\n", 527 | "an illustration:\n", 528 | "\n", 529 | "\n", 530 | "\n", 531 | "An element that is in place and won't move anymore is represented in\n", 532 | "orange. If you read them from left to right, you'll see the sorted list.\n", 533 | "Although we chose to compare all the elements to the heads, we could\n", 534 | "have used any element to compare against. In quicksort, an element that\n", 535 | "you compare against is called a pivot. They're in green here. We chose\n", 536 | "the head because it's easy to get by pattern matching. The elements that\n", 537 | "are smaller than the pivot are light green and elements larger than the\n", 538 | "pivot are dark green. The yellowish gradient thing represents an\n", 539 | "application of quicksort.\n", 540 | "\n", 541 | "Thinking recursively\n", 542 | "--------------------\n", 543 | "\n", 544 | "We did quite a bit of recursion so far and as you've probably noticed,\n", 545 | "there's a pattern here. Usually you define an edge case and then you\n", 546 | "define a function that does something between some element and the\n", 547 | "function applied to the rest. It doesn't matter if it's a list, a tree\n", 548 | "or any other data structure. A sum is the first element of a list plus\n", 549 | "the sum of the rest of the list. A product of a list is the first\n", 550 | "element of the list times the product of the rest of the list. The\n", 551 | "length of a list is one plus the length of the tail of the list.\n", 552 | "Ekcetera, ekcetera ...\n", 553 | "\n", 554 | "\n", 555 | "\n", 556 | "Of course, these also have edge cases. Usually the edge case is some\n", 557 | "scenario where a recursive application doesn't make sense. When dealing\n", 558 | "with lists, the edge case is most often the empty list. If you're\n", 559 | "dealing with trees, the edge case is usually a node that doesn't have\n", 560 | "any children.\n", 561 | "\n", 562 | "It's similar when you're dealing with numbers recursively. Usually it\n", 563 | "has to do with some number and the function applied to that number\n", 564 | "modified. We did the factorial function earlier and it's the product of\n", 565 | "a number and the factorial of that number minus one. Such a recursive\n", 566 | "application doesn't make sense with zero, because factorials are defined\n", 567 | "only for positive integers. Often the edge case value turns out to be an\n", 568 | "identity. The identity for multiplication is 1 because if you multiply\n", 569 | "something by 1, you get that something back. Also when doing sums of\n", 570 | "lists, we define the sum of an empty list as 0 and 0 is the identity for\n", 571 | "addition. In quicksort, the edge case is the empty list and the identity\n", 572 | "is also the empty list, because if you add an empty list to a list, you\n", 573 | "just get the original list back.\n", 574 | "\n", 575 | "So when trying to think of a recursive way to solve a problem, try to\n", 576 | "think of when a recursive solution doesn't apply and see if you can use\n", 577 | "that as an edge case, think about identities and think about whether\n", 578 | "you'll break apart the parameters of the function (for instance, lists\n", 579 | "are usually broken into a head and a tail via pattern matching) and on\n", 580 | "which part you'll use the recursive call." 581 | ] 582 | } 583 | ], 584 | "metadata": { 585 | "kernelspec": { 586 | "display_name": "Haskell", 587 | "language": "haskell", 588 | "name": "haskell" 589 | }, 590 | "language_info": { 591 | "codemirror_mode": "ihaskell", 592 | "file_extension": ".hs", 593 | "mimetype": "text/x-haskell", 594 | "name": "haskell", 595 | "pygments_lexer": "Haskell", 596 | "version": "9.2.5" 597 | } 598 | }, 599 | "nbformat": 4, 600 | "nbformat_minor": 4 601 | } 602 | -------------------------------------------------------------------------------- /notebook/10-functionally-solving-problems.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Functionally Solving Problems\n", 8 | "=============================\n", 9 | "\n", 10 | "In this chapter, we'll take a look at a few interesting problems and how\n", 11 | "to think functionally to solve them as elegantly as possible. We\n", 12 | "probably won't be introducing any new concepts, we'll just be flexing\n", 13 | "our newly acquired Haskell muscles and practicing our coding skills.\n", 14 | "Each section will present a different problem. First we'll describe the\n", 15 | "problem, then we'll try and find out what the best (or least worst) way\n", 16 | "of solving it is." 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": { 23 | "attributes": { 24 | "classes": [ 25 | "haskell:hs" 26 | ], 27 | "id": "", 28 | "name": "\"code\"" 29 | } 30 | }, 31 | "outputs": [], 32 | "source": [ 33 | ":opt no-lint" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "\n", 41 | "\n", 42 | "Reverse Polish notation calculator\n", 43 | "----------------------------------\n", 44 | "\n", 45 | "Usually when we write mathematical expressions in school, we write them\n", 46 | "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", 47 | "[`-`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-45-) are infix operators, just like the infix functions we met in Haskell\n", 48 | "([`+`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:-43-), `` `elem` ``, etc.). This makes it handy because we, as humans, can\n", 49 | "parse it easily in our minds by looking at such an expression. The\n", 50 | "downside to it is that we have to use parentheses to denote precedence.\n", 51 | "\n", 52 | "[Reverse Polish\n", 53 | "notation](http://en.wikipedia.org/wiki/Reverse_Polish_notation) is\n", 54 | "another way of writing down mathematical expressions. Initially it looks\n", 55 | "a bit weird, but it's actually pretty easy to understand and use because\n", 56 | "there's no need for parentheses and it's very easy to punch into a\n", 57 | "calculator. While most modern calculators use infix notation, some\n", 58 | "people still swear by RPN calculators. This is what the previous infix\n", 59 | "expression looks like in RPN: `10 4 3 + 2 * -`. How do we calculate what\n", 60 | "the result of that is? Well, think of a stack. You go over the\n", 61 | "expression from left to right. Every time a number is encountered, push\n", 62 | "it on to the stack. When we encounter an operator, take the two numbers\n", 63 | "that are on top of the stack (we also say that we *pop* them), use the\n", 64 | "operator and those two and then push the resulting number back onto the\n", 65 | "stack. When you reach the end of the expression, you should be left with\n", 66 | "a single number if the expression was well-formed and that number\n", 67 | "represents the result.\n", 68 | "\n", 69 | "\n", 70 | "\n", 71 | "Let's go over the expression `10 4 3 + 2 * -` together! First we push `10`\n", 72 | "on to the stack and the stack is now `10`. The next item is `4`, so we push\n", 73 | "it to the stack as well. The stack is now `10, 4`. We do the same with `3`\n", 74 | "and the stack is now `10, 4, 3`. And now, we encounter an operator, namely\n", 75 | "[`+`](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", 76 | "`10`), add those numbers together and push that result to the stack. The\n", 77 | "stack is now `10, 7`. We push `2` to the stack, the stack for now is\n", 78 | "`10, 7, 2`. We've encountered an operator again, so let's pop `7` and `2` off the\n", 79 | "stack, multiply them and push that result to the stack. Multiplying `7`\n", 80 | "and `2` produces a `14`, so the stack we have now is `10, 14`. Finally,\n", 81 | "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", 82 | "push that back. The number on the stack is now `-4` and because there are\n", 83 | "no more numbers or operators in our expression, that's our result!\n", 84 | "\n", 85 | "Now that we know how we'd calculate any RPN expression by hand, let's\n", 86 | "think about how we could make a Haskell function that takes as its\n", 87 | "parameter a string that contains a RPN expression, like\n", 88 | "`\"10 4 3 + 2 * -\"` and gives us back its result.\n", 89 | "\n", 90 | "What would the type of that function be? We want it to take a string as\n", 91 | "a parameter and produce a number as its result. So it will probably be\n", 92 | "something like `solveRPN :: (Num a) => String -> a`.\n", 93 | "\n", 94 | "> __Protip:__ it really helps to first think what the type declaration of a\n", 95 | "> function should be before concerning ourselves with the implementation\n", 96 | "> and then write it down. In Haskell, a function's type declaration tells\n", 97 | "> us a whole lot about the function, due to the very strong type system.\n", 98 | "\n", 99 | "\n", 100 | "\n", 101 | "Cool. When implementing a solution to a problem in Haskell, it's also\n", 102 | "good to think back on how you did it by hand and maybe try to see if you\n", 103 | "can gain any insight from that. Here we see that we treated every number\n", 104 | "or operator that was separated by a space as a single item. So it might\n", 105 | "help us if we start by breaking a string like\n", 106 | "`\"10 4 3 + 2 * -\"` into a\n", 107 | "list of items like `[\"10\",\"4\",\"3\",\"+\",\"2\",\"*\",\"-\"]`.\n", 108 | "\n", 109 | "Next up, what did we do with that list of items in our head? We went\n", 110 | "over it from left to right and kept a stack as we did that. Does the\n", 111 | "previous sentence remind you of anything? Remember, in the section about\n", 112 | "[folds](http://learnyouahaskell.com/higher-order-functions#folds), we said that pretty much any\n", 113 | "function where you traverse a list from left to right or right to left\n", 114 | "one element by element and build up (accumulate) some result (whether\n", 115 | "it's a number, a list, a stack, whatever) can be implemented with a\n", 116 | "fold.\n", 117 | "\n", 118 | "In this case, we're going to use a left fold, because we go over the\n", 119 | "list from left to right. The accumulator value will be our stack and\n", 120 | "hence, the result from the fold will also be a stack, only as we've\n", 121 | "seen, it will only have one item.\n", 122 | "\n", 123 | "One more thing to think about is, well, how are we going to represent\n", 124 | "the stack? I propose we use a list. Also I propose that we keep the top\n", 125 | "of our stack at the head of the list. That's because adding to the head\n", 126 | "(beginning) of a list is much faster than adding to the end of it. So if\n", 127 | "we have a stack of, say, `10, 4, 3`, we'll represent that as the list\n", 128 | "`[3,4,10]`.\n", 129 | "\n", 130 | "Now we have enough information to roughly sketch our function. It's\n", 131 | "going to take a string, like, `\"10 4 3 + 2 * -\"` and break it down into a\n", 132 | "list of items by using [`words`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:words) to get `[\"10\",\"4\",\"3\",\"+\",\"2\",\"*\",\"-\"]`.\n", 133 | "Next, we'll do a left fold over that list and end up with a stack that\n", 134 | "has a single item, so `[-4]`. We take that single item out of the list and\n", 135 | "that's our final result!\n", 136 | "\n", 137 | "So here's a sketch of that function:" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": { 143 | "attributes": { 144 | "classes": [ 145 | "haskell:hs" 146 | ], 147 | "id": "", 148 | "name": "\"code\"" 149 | } 150 | }, 151 | "source": [ 152 | "
import Data.List\n",
 153 |     "\n",
 154 |     "solveRPN :: (Num a) => String -> a\n",
 155 |     "solveRPN expression = head (foldl foldingFunction [] (words expression))\n",
 156 |     "    where   foldingFunction stack item = ...
" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "We take the expression and turn it into a list of items. Then we fold\n", 164 | "over that list of items with the folding function. Mind the `[]`, which\n", 165 | "represents the starting accumulator. The accumulator is our stack, so `[]`\n", 166 | "represents an empty stack, which is what we start with. After getting\n", 167 | "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", 168 | "item out and then we apply [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read).\n", 169 | "\n", 170 | "So all that's left now is to implement a folding function that will take\n", 171 | "a stack, like `[4,10]`, and an item, like `\"3\"` and return a new stack\n", 172 | "`[3,4,10]`. If the stack is `[4,10]` and the item `\"*\"`, then it will have to\n", 173 | "return `[40]`. But before that, let's turn our function into [point-free\n", 174 | "style](http://learnyouahaskell.com/higher-order-functions#composition) because it has a lot of\n", 175 | "parentheses that are kind of freaking me out:" 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": { 181 | "attributes": { 182 | "classes": [ 183 | "haskell:hs" 184 | ], 185 | "id": "", 186 | "name": "\"code\"" 187 | } 188 | }, 189 | "source": [ 190 | "
import Data.List\n",
 191 |     "\n",
 192 |     "solveRPN :: (Num a) => String -> a\n",
 193 |     "solveRPN = head . foldl foldingFunction [] . words\n",
 194 |     "    where   foldingFunction stack item = ...
" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": {}, 200 | "source": [ 201 | "Ah, there we go. Much better. So, the folding function will take a stack\n", 202 | "and an item and return a new stack. We'll use pattern matching to get\n", 203 | "the top items of a stack and to pattern match against operators like\n", 204 | "`\"*\"` and `\"-\"`." 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 2, 210 | "metadata": { 211 | "attributes": { 212 | "classes": [ 213 | "haskell:hs" 214 | ], 215 | "id": "", 216 | "name": "\"code\"" 217 | } 218 | }, 219 | "outputs": [], 220 | "source": [ 221 | "solveRPN :: (Num a, Read a) => String -> a\n", 222 | "solveRPN = head . foldl foldingFunction [] . words\n", 223 | " where foldingFunction (x:y:ys) \"*\" = (x * y):ys\n", 224 | " foldingFunction (x:y:ys) \"+\" = (x + y):ys\n", 225 | " foldingFunction (x:y:ys) \"-\" = (y - x):ys\n", 226 | " foldingFunction xs numberString = read numberString:xs" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": {}, 232 | "source": [ 233 | "We laid this out as four patterns. The patterns will be tried from top\n", 234 | "to bottom. First the folding function will see if the current item is\n", 235 | "`\"*\"`. If it is, then it will take a list like `[3,4,9,3]` and call its\n", 236 | "first two elements `x` and `y` respectively. So in this case, `x` would be `3`\n", 237 | "and `y` would be `4`. `ys` would be `[9,3]`. It will return a list that's just\n", 238 | "like `ys`, only it has `x` and `y` multiplied as its head. So with this we pop\n", 239 | "the two topmost numbers off the stack, multiply them and push the result\n", 240 | "back on to the stack. If the item is not `\"*\"`, the pattern matching will\n", 241 | "fall through and `\"+\"` will be checked, and so on.\n", 242 | "\n", 243 | "If the item is none of the operators, then we assume it's a string that\n", 244 | "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", 245 | "to get a number from it and return the previous stack but with that\n", 246 | "number pushed to the top.\n", 247 | "\n", 248 | "And that's it! Also noticed that we added an extra class constraint of\n", 249 | "`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", 250 | "to get the number. So this declaration means that the result can be of\n", 251 | "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", 252 | "etc.).\n", 253 | "\n", 254 | "For the list of items `[\"2\",\"3\",\"+\"]`, our function will start folding\n", 255 | "from the left. The initial stack will be `[]`. It will call the folding\n", 256 | "function with `[]` as the stack (accumulator) and `\"2\"` as the item. Because\n", 257 | "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", 258 | "beginning of `[]`. So the new stack is now `[2]` and the folding function\n", 259 | "will be called with `[2]` as the stack and `[\"3\"]` as the item, producing a\n", 260 | "new stack of `[3,2]`. Then, it's called for the third time with `[3,2]` as\n", 261 | "the stack and `\"+\"` as the item. This causes these two numbers to be\n", 262 | "popped off the stack, added together and pushed back. The final stack is\n", 263 | "`[5]`, which is the number that we return.\n", 264 | "\n", 265 | "Let's play around with our function:" 266 | ] 267 | }, 268 | { 269 | "cell_type": "code", 270 | "execution_count": 3, 271 | "metadata": { 272 | "attributes": { 273 | "classes": [ 274 | "haskell:hs" 275 | ], 276 | "id": "", 277 | "name": "\"code\"" 278 | } 279 | }, 280 | "outputs": [ 281 | { 282 | "data": { 283 | "text/plain": [ 284 | "-4" 285 | ] 286 | }, 287 | "metadata": {}, 288 | "output_type": "display_data" 289 | } 290 | ], 291 | "source": [ 292 | "solveRPN \"10 4 3 + 2 * -\"" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 4, 298 | "metadata": { 299 | "attributes": { 300 | "classes": [ 301 | "haskell:hs" 302 | ], 303 | "id": "", 304 | "name": "\"code\"" 305 | } 306 | }, 307 | "outputs": [ 308 | { 309 | "data": { 310 | "text/plain": [ 311 | "5" 312 | ] 313 | }, 314 | "metadata": {}, 315 | "output_type": "display_data" 316 | } 317 | ], 318 | "source": [ 319 | "solveRPN \"2 3 +\"" 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": 5, 325 | "metadata": { 326 | "attributes": { 327 | "classes": [ 328 | "haskell:hs" 329 | ], 330 | "id": "", 331 | "name": "\"code\"" 332 | } 333 | }, 334 | "outputs": [ 335 | { 336 | "data": { 337 | "text/plain": [ 338 | "-3947" 339 | ] 340 | }, 341 | "metadata": {}, 342 | "output_type": "display_data" 343 | } 344 | ], 345 | "source": [ 346 | "solveRPN \"90 34 12 33 55 66 + * - +\"" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": 6, 352 | "metadata": { 353 | "attributes": { 354 | "classes": [ 355 | "haskell:hs" 356 | ], 357 | "id": "", 358 | "name": "\"code\"" 359 | } 360 | }, 361 | "outputs": [ 362 | { 363 | "data": { 364 | "text/plain": [ 365 | "4037" 366 | ] 367 | }, 368 | "metadata": {}, 369 | "output_type": "display_data" 370 | } 371 | ], 372 | "source": [ 373 | "solveRPN \"90 34 12 33 55 66 + * - + -\"" 374 | ] 375 | }, 376 | { 377 | "cell_type": "code", 378 | "execution_count": 7, 379 | "metadata": { 380 | "attributes": { 381 | "classes": [ 382 | "haskell:hs" 383 | ], 384 | "id": "", 385 | "name": "\"code\"" 386 | } 387 | }, 388 | "outputs": [ 389 | { 390 | "data": { 391 | "text/plain": [ 392 | "4037" 393 | ] 394 | }, 395 | "metadata": {}, 396 | "output_type": "display_data" 397 | } 398 | ], 399 | "source": [ 400 | "solveRPN \"90 34 12 33 55 66 + * - + -\"" 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": 8, 406 | "metadata": { 407 | "attributes": { 408 | "classes": [ 409 | "haskell:hs" 410 | ], 411 | "id": "", 412 | "name": "\"code\"" 413 | } 414 | }, 415 | "outputs": [ 416 | { 417 | "data": { 418 | "text/plain": [ 419 | "87" 420 | ] 421 | }, 422 | "metadata": {}, 423 | "output_type": "display_data" 424 | } 425 | ], 426 | "source": [ 427 | "solveRPN \"90 3 -\"" 428 | ] 429 | }, 430 | { 431 | "cell_type": "markdown", 432 | "metadata": {}, 433 | "source": [ 434 | "Cool, it works! One nice thing about this function is that it can be\n", 435 | "easily modified to support various other operators. They don't even have\n", 436 | "to be binary operators. For instance, we can make an operator `\"log\"` that\n", 437 | "just pops one number off the stack and pushes back its logarithm. We can\n", 438 | "also make a ternary operators that pop three numbers off the stack and\n", 439 | "push back a result or operators like `\"sum\"` which pop off all the numbers\n", 440 | "and push back their sum.\n", 441 | "\n", 442 | "Let's modify our function to take a few more operators. For simplicity's\n", 443 | "sake, we'll change its type declaration so that it returns a number of\n", 444 | "type [`Float`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Float)." 445 | ] 446 | }, 447 | { 448 | "cell_type": "code", 449 | "execution_count": 9, 450 | "metadata": { 451 | "attributes": { 452 | "classes": [ 453 | "haskell:hs" 454 | ], 455 | "id": "", 456 | "name": "\"code\"" 457 | } 458 | }, 459 | "outputs": [], 460 | "source": [ 461 | "import Data.List\n", 462 | "\n", 463 | "solveRPN :: String -> Float\n", 464 | "solveRPN = head . foldl foldingFunction [] . words\n", 465 | " where foldingFunction (x:y:ys) \"*\" = (x * y):ys\n", 466 | " foldingFunction (x:y:ys) \"+\" = (x + y):ys\n", 467 | " foldingFunction (x:y:ys) \"-\" = (y - x):ys\n", 468 | " foldingFunction (x:y:ys) \"/\" = (y / x):ys\n", 469 | " foldingFunction (x:y:ys) \"^\" = (y ** x):ys\n", 470 | " foldingFunction (x:xs) \"ln\" = log x:xs\n", 471 | " foldingFunction xs \"sum\" = [sum xs]\n", 472 | " foldingFunction xs numberString = read numberString:xs" 473 | ] 474 | }, 475 | { 476 | "cell_type": "markdown", 477 | "metadata": {}, 478 | "source": [ 479 | "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", 480 | "exponentiation. With the logarithm operator, we just pattern match\n", 481 | "against a single element and the rest of the stack because we only need\n", 482 | "one element to perform its natural logarithm. With the sum operator, we\n", 483 | "just return a stack that has only one element, which is the sum of the\n", 484 | "stack so far." 485 | ] 486 | }, 487 | { 488 | "cell_type": "code", 489 | "execution_count": 10, 490 | "metadata": { 491 | "attributes": { 492 | "classes": [ 493 | "haskell:hs" 494 | ], 495 | "id": "", 496 | "name": "\"code\"" 497 | } 498 | }, 499 | "outputs": [ 500 | { 501 | "data": { 502 | "text/plain": [ 503 | "0.9932518" 504 | ] 505 | }, 506 | "metadata": {}, 507 | "output_type": "display_data" 508 | } 509 | ], 510 | "source": [ 511 | "solveRPN \"2.7 ln\"" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 11, 517 | "metadata": { 518 | "attributes": { 519 | "classes": [ 520 | "haskell:hs" 521 | ], 522 | "id": "", 523 | "name": "\"code\"" 524 | } 525 | }, 526 | "outputs": [ 527 | { 528 | "data": { 529 | "text/plain": [ 530 | "10.0" 531 | ] 532 | }, 533 | "metadata": {}, 534 | "output_type": "display_data" 535 | } 536 | ], 537 | "source": [ 538 | "solveRPN \"10 10 10 10 sum 4 /\"" 539 | ] 540 | }, 541 | { 542 | "cell_type": "code", 543 | "execution_count": 12, 544 | "metadata": { 545 | "attributes": { 546 | "classes": [ 547 | "haskell:hs" 548 | ], 549 | "id": "", 550 | "name": "\"code\"" 551 | } 552 | }, 553 | "outputs": [ 554 | { 555 | "data": { 556 | "text/plain": [ 557 | "12.5" 558 | ] 559 | }, 560 | "metadata": {}, 561 | "output_type": "display_data" 562 | } 563 | ], 564 | "source": [ 565 | "solveRPN \"10 10 10 10 10 sum 4 /\"" 566 | ] 567 | }, 568 | { 569 | "cell_type": "code", 570 | "execution_count": 13, 571 | "metadata": { 572 | "attributes": { 573 | "classes": [ 574 | "haskell:hs" 575 | ], 576 | "id": "", 577 | "name": "\"code\"" 578 | } 579 | }, 580 | "outputs": [ 581 | { 582 | "data": { 583 | "text/plain": [ 584 | "100.0" 585 | ] 586 | }, 587 | "metadata": {}, 588 | "output_type": "display_data" 589 | } 590 | ], 591 | "source": [ 592 | "solveRPN \"10 2 ^\"" 593 | ] 594 | }, 595 | { 596 | "cell_type": "markdown", 597 | "metadata": {}, 598 | "source": [ 599 | "Notice that we can include floating point numbers in our expression\n", 600 | "because [`read`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:read) knows how to read them." 601 | ] 602 | }, 603 | { 604 | "cell_type": "code", 605 | "execution_count": 14, 606 | "metadata": { 607 | "attributes": { 608 | "classes": [ 609 | "haskell:hs" 610 | ], 611 | "id": "", 612 | "name": "\"code\"" 613 | } 614 | }, 615 | "outputs": [ 616 | { 617 | "data": { 618 | "text/plain": [ 619 | "6.575903" 620 | ] 621 | }, 622 | "metadata": {}, 623 | "output_type": "display_data" 624 | } 625 | ], 626 | "source": [ 627 | "solveRPN \"43.2425 0.5 ^\"" 628 | ] 629 | }, 630 | { 631 | "cell_type": "markdown", 632 | "metadata": {}, 633 | "source": [ 634 | "I think that making a function that can calculate arbitrary floating\n", 635 | "point RPN expressions and has the option to be easily extended in 10\n", 636 | "lines is pretty awesome.\n", 637 | "\n", 638 | "One thing to note about this function is that it's not really fault\n", 639 | "tolerant. When given input that doesn't make sense, it will just crash\n", 640 | "everything. We'll make a fault tolerant version of this with a type\n", 641 | "declaration of `solveRPN :: String -> Maybe Float` once we get to know\n", 642 | "monads (they're not scary, trust me!). We could make one right now, but\n", 643 | "it would be a bit tedious because it would involve a lot of checking for\n", 644 | "[`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", 645 | "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", 646 | "successful or not.\n", 647 | "\n", 648 | "Heathrow to London\n", 649 | "------------------\n", 650 | "\n", 651 | "Our next problem is this: your plane has just landed in England and you\n", 652 | "rent a car. You have a meeting really soon and you have to get from\n", 653 | "Heathrow Airport to London as fast as you can (but safely!).\n", 654 | "\n", 655 | "There are two main roads going from Heathrow to London and there's a\n", 656 | "number of regional roads crossing them. It takes you a fixed amount of\n", 657 | "time to travel from one crossroads to another. It's up to you to find\n", 658 | "the optimal path to take so that you get to London as fast as you can!\n", 659 | "You start on the left side and can either cross to the other main road\n", 660 | "or go forward.\n", 661 | "\n", 662 | "\n", 663 | "\n", 664 | "As you can see in the picture, the shortest path from Heathrow to London\n", 665 | "in this case is to start on main road B, cross over, go forward on A,\n", 666 | "cross over again and then go forward twice on B. If we take this path,\n", 667 | "it takes us 75 minutes. Had we chosen any other path, it would take more\n", 668 | "than that.\n", 669 | "\n", 670 | "Our job is to make a program that takes input that represents a road\n", 671 | "system and print out what the shortest path across it is. Here's what\n", 672 | "the input would look like for this case:" 673 | ] 674 | }, 675 | { 676 | "cell_type": "markdown", 677 | "metadata": { 678 | "attributes": { 679 | "classes": [ 680 | "plain" 681 | ], 682 | "id": "", 683 | "name": "\"code\"" 684 | } 685 | }, 686 | "source": [ 687 | "
50\n",
 688 |     "10\n",
 689 |     "30\n",
 690 |     "5\n",
 691 |     "90\n",
 692 |     "20\n",
 693 |     "40\n",
 694 |     "2\n",
 695 |     "25\n",
 696 |     "10\n",
 697 |     "8\n",
 698 |     "0
" 699 | ] 700 | }, 701 | { 702 | "cell_type": "markdown", 703 | "metadata": {}, 704 | "source": [ 705 | "To mentally parse the input file, read it in threes and mentally split\n", 706 | "the road system into sections. Each section is comprised of a road A,\n", 707 | "road B and a crossing road. To have it neatly fit into threes, we say\n", 708 | "that there's a last crossing section that takes 0 minutes to drive over.\n", 709 | "That's because we don't care where we arrive in London, as long as we're\n", 710 | "in London.\n", 711 | "\n", 712 | "Just like we did when solving the RPN calculator problem, we're going to\n", 713 | "solve this problem in three steps:\n", 714 | "\n", 715 | "- Forget Haskell for a minute and think about how we'd solve the\n", 716 | " problem by hand\n", 717 | "- Think about how we're going to represent our data in Haskell\n", 718 | "- Figure out how to operate on that data in Haskell so that we produce\n", 719 | " at a solution\n", 720 | "\n", 721 | "In the RPN calculator section, we first figured out that when\n", 722 | "calculating an expression by hand, we'd keep a sort of stack in our\n", 723 | "minds and then go over the expression one item at a time. We decided to\n", 724 | "use a list of strings to represent our expression. Finally, we used a\n", 725 | "left fold to walk oevr the list of strings while keeping a stack to\n", 726 | "produce a solution.\n", 727 | "\n", 728 | "Okay, so how would we figure out the shortest path from Heathrow to\n", 729 | "London by hand? Well, we can just sort of look at the whole picture and\n", 730 | "try to guess what the shortest path is and hopefully we'll make a guess\n", 731 | "that's right. That solution works for very small inputs, but what if we\n", 732 | "have a road that has 10,000 sections? Yikes! We also won't be able to\n", 733 | "say for certain that our solution is the optimal one, we can just sort\n", 734 | "of say that we're pretty sure.\n", 735 | "\n", 736 | "That's not a good solution then. Here's a simplified picture of our road\n", 737 | "system:\n", 738 | "\n", 739 | "\n", 740 | "\n", 741 | "Alright, can you figure out what the shortest path to the first\n", 742 | "crossroads (the first blue dot on A, marked *A1*) on road A is? That's\n", 743 | "pretty trivial. We just see if it's shorter to go directly forward on A\n", 744 | "or if it's shorter to go forward on B and then cross over. Obviously,\n", 745 | "it's cheaper to go forward via B and then cross over because that takes\n", 746 | "40 minutes, whereas going directly via A takes 50 minutes. What about\n", 747 | "crossroads *B1*? Same thing. We see that it's a lot cheaper to just go\n", 748 | "directly via B (incurring a cost of 10 minutes), because going via A and\n", 749 | "then crossing over would take us a whole 80 minutes!\n", 750 | "\n", 751 | "Now we know what the cheapest path to *A1* is (go via B and then cross\n", 752 | "over, so we'll say that's `B, C` with a cost of 40) and we know what the\n", 753 | "cheapest path to *B1* is (go directly via B, so that's just `B`, going at\n", 754 | "10). Does this knowledge help us at all if we want to know the cheapest\n", 755 | "path to the next crossroads on both main roads? Gee golly, it sure does!\n", 756 | "\n", 757 | "Let's see what the shortest path to *A2* would be. To get to *A2*, we'll\n", 758 | "either go directly to *A2* from *A1* or we'll go forward from *B1* and\n", 759 | "then cross over (remember, we can only move forward or cross to the\n", 760 | "other side). And because we know the cost to *A1* and *B1*, we can\n", 761 | "easily figure out what the best path to *A2* is. It costs 40 to get to\n", 762 | "*A1* and then 5 to get from *A1* to *A2*, so that's `B, C, A` for a cost\n", 763 | "of 45. It costs only 10 to get to *B1*, but then it would take an\n", 764 | "additional 110 minutes to go to *B2* and then cross over! So obviously,\n", 765 | "the cheapest path to *A2* is `B, C, A`. In the same way, the cheapest way\n", 766 | "to *B2* is to go forward from *A1* and then cross over.\n", 767 | "\n", 768 | "> __Maybe you're asking yourself__: but what about getting to *A2* by first\n", 769 | "> crossing over at *B1* and then going on forward? Well, we already\n", 770 | "> covered crossing from *B1* to *A1* when we were looking for the best way\n", 771 | "> to *A1*, so we don't have to take that into account in the next step as\n", 772 | "> well.\n", 773 | "\n", 774 | "Now that we have the best path to *A2* and *B2*, we can repeat this\n", 775 | "indefinitely until we reach the end. Once we've gotten the best paths\n", 776 | "for *A4* and *B4*, the one that's cheaper is the optimal path!\n", 777 | "\n", 778 | "So in essence, for the second section, we just repeat the step we did at\n", 779 | "first, only we take into account what the previous best paths on A and\n", 780 | "B. We could say that we also took into account the best paths on A and\n", 781 | "on B in the first step, only they were both empty paths with a cost of\n", 782 | "0.\n", 783 | "\n", 784 | "Here's a summary. To get the bast path from Heathrow to London, we do\n", 785 | "this: first we see what the best path to the next crossroads on main\n", 786 | "road A is. The two options are going directly forward or starting at the\n", 787 | "opposite road, going forward and then crossing over. We remember the\n", 788 | "cost and the path. We use the same method to see what the best path to\n", 789 | "the next crossroads on main road B is and remember that. Then, we see if\n", 790 | "the path to the next crossroads on A is cheaper if we go from the\n", 791 | "previous A crossroads or if we go from the previous B crossroads and\n", 792 | "then cross over. We remember the cheaper path and then we do the same\n", 793 | "for the crossroads opposite of it. We do this for every section until we\n", 794 | "reach the end. Once we've reached the end, the cheapest of the two paths\n", 795 | "that we have is our optimal path!\n", 796 | "\n", 797 | "So in essence, we keep one shortest path on the A road and one shortest\n", 798 | "path on the B road and when we reach the end, the shorter of those two\n", 799 | "is our path. We now know how to figure out the shortest path by hand. If\n", 800 | "you had enough time, paper and pencils, you could figure out the\n", 801 | "shortest path through a road system with any number of sections.\n", 802 | "\n", 803 | "Next step! How do we represent this road system with Haskell's data\n", 804 | "types? One way is to think of the starting points and crossroads as\n", 805 | "nodes of a graph that point to other crossroads. If we imagine that the\n", 806 | "starting points actually point to each other with a road that has a\n", 807 | "length of one, we see that every crossroads (or node) points to the node\n", 808 | "on the other side and also to the next one on its side. Except for the\n", 809 | "last nodes, they just point to the other side." 810 | ] 811 | }, 812 | { 813 | "cell_type": "code", 814 | "execution_count": 15, 815 | "metadata": { 816 | "attributes": { 817 | "classes": [ 818 | "haskell:hs" 819 | ], 820 | "id": "", 821 | "name": "\"code\"" 822 | } 823 | }, 824 | "outputs": [], 825 | "source": [ 826 | "data Node = Node Road Road | EndNode Road\n", 827 | "data Road = Road Int Node" 828 | ] 829 | }, 830 | { 831 | "cell_type": "markdown", 832 | "metadata": {}, 833 | "source": [ 834 | "A node is either a normal node and has information about the road that\n", 835 | "leads to the other main road and the road that leads to the next node or\n", 836 | "an end node, which only has information about the road to the other main\n", 837 | "road. A road keeps information about how long it is and which node it\n", 838 | "points to. For instance, the first part of the road on the A main road\n", 839 | "would be `Road 50 a1` where `a1` would be a node `Node x y`, where `x` and `y` are\n", 840 | "roads that point to *B1* and *A2*.\n", 841 | "\n", 842 | "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", 843 | "Each node has a road part that point to the opposite road, but only\n", 844 | "those nodes that aren't the end ones have road parts that point forward." 845 | ] 846 | }, 847 | { 848 | "cell_type": "code", 849 | "execution_count": 16, 850 | "metadata": { 851 | "attributes": { 852 | "classes": [ 853 | "haskell:hs" 854 | ], 855 | "id": "", 856 | "name": "\"code\"" 857 | } 858 | }, 859 | "outputs": [], 860 | "source": [ 861 | "data Node = Node Road (Maybe Road)\n", 862 | "data Road = Road Int Node" 863 | ] 864 | }, 865 | { 866 | "cell_type": "markdown", 867 | "metadata": {}, 868 | "source": [ 869 | "This is an alright way to represent the road system in Haskell and we\n", 870 | "could certainly solve this problem with it, but maybe we could come up\n", 871 | "with something simpler? If we think back to our solution by hand, we\n", 872 | "always just checked the lengths of three road parts at once: the road\n", 873 | "part on the A road, its opposite part on the B road and part C, which\n", 874 | "touches those two parts and connects them. When we were looking for the\n", 875 | "shortest path to *A1* and *B1*, we only had to deal with the lengths of\n", 876 | "the first three parts, which have lengths of 50, 10 and 30. We'll call\n", 877 | "that one section. So the road system that we use for this example can be\n", 878 | "easily represented as four sections: `50, 10, 30`, `5, 90, 20`, `40, 2, 25`,\n", 879 | "and `10, 8, 0`.\n", 880 | "\n", 881 | "It's always good to keep our data types as simple as possible, although\n", 882 | "not any simpler!" 883 | ] 884 | }, 885 | { 886 | "cell_type": "code", 887 | "execution_count": 17, 888 | "metadata": { 889 | "attributes": { 890 | "classes": [ 891 | "haskell:hs" 892 | ], 893 | "id": "", 894 | "name": "\"code\"" 895 | } 896 | }, 897 | "outputs": [], 898 | "source": [ 899 | "data Section = Section { getA :: Int, getB :: Int, getC :: Int } deriving (Show)\n", 900 | "type RoadSystem = [Section]" 901 | ] 902 | }, 903 | { 904 | "cell_type": "markdown", 905 | "metadata": {}, 906 | "source": [ 907 | "This is pretty much perfect! It's as simple as it goes and I have a\n", 908 | "feeling it'll work perfectly for implementing our solution. `Section` is a\n", 909 | "simple algebraic data type that holds three integers for the lengths of\n", 910 | "its three road parts. We introduce a type synonym as well, saying that\n", 911 | "`RoadSystem` is a list of sections.\n", 912 | "\n", 913 | "> We could also use a triple of `(Int, Int, Int)` to represent a road\n", 914 | "> section. Using tuples instead of making your own algebraic data types is\n", 915 | "> good for some small localized stuff, but it's usually better to make a\n", 916 | "> new type for things like this. It gives the type system more information\n", 917 | "> about what's what. We can use `(Int, Int, Int)` to represent a road\n", 918 | "> section or a vector in 3D space and we can operate on those two, but\n", 919 | "> that allows us to mix them up. If we use `Section` and `Vector` data types,\n", 920 | "> then we can't accidentally add a vector to a section of a road system.\n", 921 | "\n", 922 | "Our road system from Heathrow to London can now be represented like\n", 923 | "this:" 924 | ] 925 | }, 926 | { 927 | "cell_type": "code", 928 | "execution_count": 18, 929 | "metadata": { 930 | "attributes": { 931 | "classes": [ 932 | "haskell:hs" 933 | ], 934 | "id": "", 935 | "name": "\"code\"" 936 | } 937 | }, 938 | "outputs": [], 939 | "source": [ 940 | "heathrowToLondon :: RoadSystem\n", 941 | "heathrowToLondon = [Section 50 10 30, Section 5 90 20, Section 40 2 25, Section 10 8 0]" 942 | ] 943 | }, 944 | { 945 | "cell_type": "markdown", 946 | "metadata": {}, 947 | "source": [ 948 | "All we need to do now is to implement the solution that we came up with\n", 949 | "previously in Haskell. What should the type declaration for a function\n", 950 | "that calculates a shortest path for any given road system be? It should\n", 951 | "take a road system as a parameter and return a path. We'll represent a\n", 952 | "path as a list as well. Let's introduce a `Label` type that's just an\n", 953 | "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`." 954 | ] 955 | }, 956 | { 957 | "cell_type": "code", 958 | "execution_count": 19, 959 | "metadata": { 960 | "attributes": { 961 | "classes": [ 962 | "haskell:hs" 963 | ], 964 | "id": "", 965 | "name": "\"code\"" 966 | } 967 | }, 968 | "outputs": [], 969 | "source": [ 970 | "data Label = A | B | C deriving (Show)\n", 971 | "type Path = [(Label, Int)]" 972 | ] 973 | }, 974 | { 975 | "cell_type": "markdown", 976 | "metadata": {}, 977 | "source": [ 978 | "Our function, we'll call it `optimalPath` should thus have a type\n", 979 | "declaration of `optimalPath :: RoadSystem -> Path`. If called with the\n", 980 | "road system `heathrowToLondon`, it should return the following path:" 981 | ] 982 | }, 983 | { 984 | "cell_type": "markdown", 985 | "metadata": { 986 | "attributes": { 987 | "classes": [ 988 | "haskell:hs" 989 | ], 990 | "id": "", 991 | "name": "\"code\"" 992 | } 993 | }, 994 | "source": [ 995 | "
[(B,10),(C,30),(A,5),(C,20),(B,2),(B,8)]
" 996 | ] 997 | }, 998 | { 999 | "cell_type": "markdown", 1000 | "metadata": {}, 1001 | "source": [ 1002 | "We're going to have to walk over the list with the sections from left to\n", 1003 | "right and keep the optimal path on A and optimal path on B as we go\n", 1004 | "along. We'll accumulate the best path as we walk over the list, left to\n", 1005 | "right. What does that sound like? Ding, ding, ding! That's right, A LEFT\n", 1006 | "FOLD!\n", 1007 | "\n", 1008 | "When doing the solution by hand, there was a step that we repeated over\n", 1009 | "and over again. It involved checking the optimal paths on A and B so far\n", 1010 | "and the current section to produce the new optimal paths on A and B. For\n", 1011 | "instance, at the beginning the optimal paths were `[]` and `[]` for A and B\n", 1012 | "respectively. We examined the section `Section 50 10 30` and concluded\n", 1013 | "that the new optimal path to *A1* is `[(B,10),(C,30)]` and the optimal\n", 1014 | "path to *B1* is `[(B,10)]`. If you look at this step as a function, it\n", 1015 | "takes a pair of paths and a section and produces a new pair of paths.\n", 1016 | "The type is `(Path, Path) -> Section -> (Path, Path)`. Let's go ahead\n", 1017 | "and implement this function, because it's bound to be useful.\n", 1018 | "\n", 1019 | "> __Hint:__ it will be useful because\n", 1020 | "> `(Path, Path) -> Section -> (Path, Path)`\n", 1021 | "> can be used as the binary function for a left fold, which has to\n", 1022 | "> have a type of `a -> b -> a`" 1023 | ] 1024 | }, 1025 | { 1026 | "cell_type": "code", 1027 | "execution_count": 20, 1028 | "metadata": { 1029 | "attributes": { 1030 | "classes": [ 1031 | "haskell:hs" 1032 | ], 1033 | "id": "", 1034 | "name": "\"code\"" 1035 | } 1036 | }, 1037 | "outputs": [], 1038 | "source": [ 1039 | "roadStep :: (Path, Path) -> Section -> (Path, Path)\n", 1040 | "roadStep (pathA, pathB) (Section a b c) =\n", 1041 | " let priceA = sum $ map snd pathA\n", 1042 | " priceB = sum $ map snd pathB\n", 1043 | " forwardPriceToA = priceA + a\n", 1044 | " crossPriceToA = priceB + b + c\n", 1045 | " forwardPriceToB = priceB + b\n", 1046 | " crossPriceToB = priceA + a + c\n", 1047 | " newPathToA = if forwardPriceToA <= crossPriceToA\n", 1048 | " then (A,a):pathA\n", 1049 | " else (C,c):(B,b):pathB\n", 1050 | " newPathToB = if forwardPriceToB <= crossPriceToB\n", 1051 | " then (B,b):pathB\n", 1052 | " else (C,c):(A,a):pathA\n", 1053 | " in (newPathToA, newPathToB)" 1054 | ] 1055 | }, 1056 | { 1057 | "cell_type": "markdown", 1058 | "metadata": {}, 1059 | "source": [ 1060 | "\n", 1061 | "\n", 1062 | "What's going on here? First, calculate the optimal price on road A based\n", 1063 | "on the best so far on A and we do the same for B. We do\n", 1064 | "`sum $ map snd pathA`, so if `pathA` is something like\n", 1065 | "`[(A,100),(C,20)]`, `priceA` becomes\n", 1066 | "`120`. `forwardPriceToA` is the price that we would pay if we went to the\n", 1067 | "next crossroads on A if we went there directly from the previous\n", 1068 | "crossroads on A. It equals the best price to our previous A, plus the\n", 1069 | "length of the A part of the current section. `crossPriceToA` is the price\n", 1070 | "that we would pay if we went to the next A by going forward from the\n", 1071 | "previous B and then crossing over. It's the best price to the previous B\n", 1072 | "so far plus the B length of the section plus the C length of the\n", 1073 | "section. We determine `forwardPriceToB` and `crossPriceToB` in the same\n", 1074 | "manner.\n", 1075 | "\n", 1076 | "Now that we know what the best way to A and B is, we just need to make\n", 1077 | "the new paths to A and B based on that. If it's cheaper to go to A by\n", 1078 | "just going forwards, we set `newPathToA` to be `(A,a):pathA`. Basically we\n", 1079 | "prepend the `Label` `A` and the section length `a` to the optimal path path on\n", 1080 | "A so far. Basically, we say that the best path to the next A crossroads\n", 1081 | "is the path to the previous A crossroads and then one section forward\n", 1082 | "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", 1083 | "we prepend instead of doing `pathA ++ [(A,a)]`? Well, adding an element to\n", 1084 | "the beginning of a list (also known as consing) is much faster than\n", 1085 | "adding it to the end. This means that the path will be the wrong way\n", 1086 | "around once we fold over a list with this function, but it's easy to\n", 1087 | "reverse the list later. If it's cheaper to get to the next A crossroads\n", 1088 | "by going forward from road B and then crossing over, then `newPathToA` is\n", 1089 | "the old path to B that then goes forward and crosses to A. We do the\n", 1090 | "same thing for `newPathToB`, only everything's mirrored.\n", 1091 | "\n", 1092 | "Finally, we return `newPathToA` and `newPathToB` in a pair.\n", 1093 | "\n", 1094 | "Let's run this function on the first section of `heathrowToLondon`.\n", 1095 | "Because it's the first section, the best paths on A and B parameter will\n", 1096 | "be a pair of empty lists." 1097 | ] 1098 | }, 1099 | { 1100 | "cell_type": "code", 1101 | "execution_count": 21, 1102 | "metadata": { 1103 | "attributes": { 1104 | "classes": [ 1105 | "haskell:hs" 1106 | ], 1107 | "id": "", 1108 | "name": "\"code\"" 1109 | } 1110 | }, 1111 | "outputs": [ 1112 | { 1113 | "data": { 1114 | "text/plain": [ 1115 | "([(C,30),(B,10)],[(B,10)])" 1116 | ] 1117 | }, 1118 | "metadata": {}, 1119 | "output_type": "display_data" 1120 | } 1121 | ], 1122 | "source": [ 1123 | "roadStep ([], []) (head heathrowToLondon)" 1124 | ] 1125 | }, 1126 | { 1127 | "cell_type": "markdown", 1128 | "metadata": {}, 1129 | "source": [ 1130 | "Remember, the paths are reversed, so read them from right to left. From\n", 1131 | "this we can read that the best path to the next A is to start on B and\n", 1132 | "then cross over to A and that the best path to the next B is to just go\n", 1133 | "directly forward from the starting point at B.\n", 1134 | "\n", 1135 | "> __Optimization tip:__ when we do `priceA = sum $ map snd pathA`, we're\n", 1136 | "> calculating the price from the path on every step. We wouldn't have to\n", 1137 | "> do that if we implemented `roadStep` as a\n", 1138 | "> `(Path, Path, Int, Int) -> Section -> (Path, Path, Int, Int)`\n", 1139 | "> function where the integers represent\n", 1140 | "> the best price on A and B.\n", 1141 | "\n", 1142 | "Now that we have a function that takes a pair of paths and a section and\n", 1143 | "produces a new optimal path, we can just easily do a left fold over a\n", 1144 | "list of sections. `roadStep` is called with `([],[])` and the first section\n", 1145 | "and returns a pair of optimal paths to that section. Then, it's called\n", 1146 | "with that pair of paths and the next section and so on. When we've\n", 1147 | "walked over all the sections, we're left with a pair of optimal paths\n", 1148 | "and the shorter of them is our answer. With this in mind, we can\n", 1149 | "implement `optimalPath`." 1150 | ] 1151 | }, 1152 | { 1153 | "cell_type": "code", 1154 | "execution_count": 22, 1155 | "metadata": { 1156 | "attributes": { 1157 | "classes": [ 1158 | "haskell:hs" 1159 | ], 1160 | "id": "", 1161 | "name": "\"code\"" 1162 | } 1163 | }, 1164 | "outputs": [], 1165 | "source": [ 1166 | "optimalPath :: RoadSystem -> Path\n", 1167 | "optimalPath roadSystem =\n", 1168 | " let (bestAPath, bestBPath) = foldl roadStep ([],[]) roadSystem\n", 1169 | " in if sum (map snd bestAPath) <= sum (map snd bestBPath)\n", 1170 | " then reverse bestAPath\n", 1171 | " else reverse bestBPath" 1172 | ] 1173 | }, 1174 | { 1175 | "cell_type": "markdown", 1176 | "metadata": {}, 1177 | "source": [ 1178 | "We left fold over `roadSystem` (remember, it's a list of sections) with\n", 1179 | "the starting accumulator being a pair of empty paths. The result of that\n", 1180 | "fold is a pair of paths, so we pattern match on the pair to get the\n", 1181 | "paths themselves. Then, we check which one of these was cheaper and\n", 1182 | "return it. Before returning it, we also reverse it, because the optimal\n", 1183 | "paths so far were reversed due to us choosing consing over appending.\n", 1184 | "\n", 1185 | "Let's test this!" 1186 | ] 1187 | }, 1188 | { 1189 | "cell_type": "code", 1190 | "execution_count": 23, 1191 | "metadata": { 1192 | "attributes": { 1193 | "classes": [ 1194 | "haskell:hs" 1195 | ], 1196 | "id": "", 1197 | "name": "\"code\"" 1198 | } 1199 | }, 1200 | "outputs": [ 1201 | { 1202 | "data": { 1203 | "text/plain": [ 1204 | "[(B,10),(C,30),(A,5),(C,20),(B,2),(B,8),(C,0)]" 1205 | ] 1206 | }, 1207 | "metadata": {}, 1208 | "output_type": "display_data" 1209 | } 1210 | ], 1211 | "source": [ 1212 | "optimalPath heathrowToLondon" 1213 | ] 1214 | }, 1215 | { 1216 | "cell_type": "markdown", 1217 | "metadata": {}, 1218 | "source": [ 1219 | "This is the result that we were supposed to get! Awesome! It differs\n", 1220 | "from our expected result a bit because there's a step `(C,0)` at the end,\n", 1221 | "which means that we cross over to the other road once we're in London,\n", 1222 | "but because that crossing doesn't cost anything, this is still the\n", 1223 | "correct result.\n", 1224 | "\n", 1225 | "We have the function that finds an optimal path based on, now we just\n", 1226 | "have to read a textual representation of a road system from the standard\n", 1227 | "input, convert it into a type of `RoadSystem`, run that through our\n", 1228 | "`optimalPath` function and print the path.\n", 1229 | "\n", 1230 | "First off, let's make a function that takes a list and splits it into\n", 1231 | "groups of the same size. We'll call it `groupsOf`. For a parameter of\n", 1232 | "`[1..10]`, `groupsOf 3` should return `[[1,2,3],[4,5,6],[7,8,9],[10]]`." 1233 | ] 1234 | }, 1235 | { 1236 | "cell_type": "code", 1237 | "execution_count": 24, 1238 | "metadata": { 1239 | "attributes": { 1240 | "classes": [ 1241 | "haskell:hs" 1242 | ], 1243 | "id": "", 1244 | "name": "\"code\"" 1245 | } 1246 | }, 1247 | "outputs": [], 1248 | "source": [ 1249 | "groupsOf :: Int -> [a] -> [[a]]\n", 1250 | "groupsOf 0 _ = undefined\n", 1251 | "groupsOf _ [] = []\n", 1252 | "groupsOf n xs = take n xs : groupsOf n (drop n xs)" 1253 | ] 1254 | }, 1255 | { 1256 | "cell_type": "markdown", 1257 | "metadata": {}, 1258 | "source": [ 1259 | "A standard recursive function. For an `xs` of `[1..10]` and an `n` of `3`, this\n", 1260 | "equals `[1,2,3] : groupsOf 3 [4,5,6,7,8,9,10]`. When the recursion is\n", 1261 | "done, we get our list in groups of three. And here's our `main` function,\n", 1262 | "which reads from the standard input, makes a `RoadSystem` out of it and\n", 1263 | "prints out the shortest path:" 1264 | ] 1265 | }, 1266 | { 1267 | "cell_type": "code", 1268 | "execution_count": 25, 1269 | "metadata": { 1270 | "attributes": { 1271 | "classes": [ 1272 | "haskell:hs" 1273 | ], 1274 | "id": "", 1275 | "name": "\"code\"" 1276 | } 1277 | }, 1278 | "outputs": [], 1279 | "source": [ 1280 | "import Data.List\n", 1281 | "\n", 1282 | "main contents = do\n", 1283 | " let threes = groupsOf 3 (map read $ lines contents)\n", 1284 | " roadSystem = map (\\[a,b,c] -> Section a b c) threes\n", 1285 | " path = optimalPath roadSystem\n", 1286 | " pathString = concat $ map (show . fst) path\n", 1287 | " pathPrice = sum $ map snd path\n", 1288 | " putStrLn $ \"The best path to take is: \" ++ pathString\n", 1289 | " putStrLn $ \"The price is: \" ++ show pathPrice" 1290 | ] 1291 | }, 1292 | { 1293 | "cell_type": "markdown", 1294 | "metadata": {}, 1295 | "source": [ 1296 | "First, we get all the contents from the standard input. Then, we call\n", 1297 | "[`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", 1298 | "`[\"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", 1299 | "of numbers. We call `groupsOf 3` on it so that we turn it to a list of\n", 1300 | "lists of length 3. We map the lambda (`\\[a,b,c] -> Section a b c`) over\n", 1301 | "that list of lists. As you can see, the lambda just takes a list of\n", 1302 | "length 3 and turns it into a section. So `roadSystem` is now our system of\n", 1303 | "roads and it even has the correct type, namely `RoadSystem` (or\n", 1304 | "`[Section]`). We call `optimalPath` with that and then get the path and the\n", 1305 | "price in a nice textual representation and print it out.\n", 1306 | "\n", 1307 | "We save the following text" 1308 | ] 1309 | }, 1310 | { 1311 | "cell_type": "code", 1312 | "execution_count": 26, 1313 | "metadata": { 1314 | "attributes": { 1315 | "classes": [ 1316 | "plain" 1317 | ], 1318 | "id": "", 1319 | "name": "\"code\"" 1320 | } 1321 | }, 1322 | "outputs": [], 1323 | "source": [ 1324 | "writeFile \"paths.txt\" $ unlines $ map show\n", 1325 | " [ 50\n", 1326 | " , 10\n", 1327 | " , 30\n", 1328 | " , 5\n", 1329 | " , 90\n", 1330 | " , 20\n", 1331 | " , 40\n", 1332 | " , 2\n", 1333 | " , 25\n", 1334 | " , 10\n", 1335 | " , 8\n", 1336 | " , 0\n", 1337 | " ]" 1338 | ] 1339 | }, 1340 | { 1341 | "cell_type": "markdown", 1342 | "metadata": {}, 1343 | "source": [ 1344 | "in a file called `paths.txt` and then feed it to our program." 1345 | ] 1346 | }, 1347 | { 1348 | "cell_type": "code", 1349 | "execution_count": 27, 1350 | "metadata": { 1351 | "attributes": { 1352 | "classes": [ 1353 | "plain" 1354 | ], 1355 | "id": "", 1356 | "name": "\"code\"" 1357 | } 1358 | }, 1359 | "outputs": [ 1360 | { 1361 | "data": { 1362 | "text/plain": [ 1363 | "The best path to take is: BCACBBC\n", 1364 | "The price is: 75" 1365 | ] 1366 | }, 1367 | "metadata": {}, 1368 | "output_type": "display_data" 1369 | } 1370 | ], 1371 | "source": [ 1372 | "readFile \"paths.txt\" >>= main" 1373 | ] 1374 | }, 1375 | { 1376 | "cell_type": "markdown", 1377 | "metadata": {}, 1378 | "source": [ 1379 | "Works like a charm! You can use your knowledge of the `Data.Random` module\n", 1380 | "to generate a much longer system of roads, which you can then feed to\n", 1381 | "what we just wrote. If you get stack overflows, try using `foldl'` instead\n", 1382 | "of [`foldl`](https://hackage.haskell.org/package/base/docs/Prelude.html#v:foldl), because `foldl'` is strict." 1383 | ] 1384 | } 1385 | ], 1386 | "metadata": { 1387 | "kernelspec": { 1388 | "display_name": "Haskell", 1389 | "language": "haskell", 1390 | "name": "haskell" 1391 | }, 1392 | "language_info": { 1393 | "codemirror_mode": "ihaskell", 1394 | "file_extension": ".hs", 1395 | "mimetype": "text/x-haskell", 1396 | "name": "haskell", 1397 | "pygments_lexer": "Haskell", 1398 | "version": "9.2.5" 1399 | } 1400 | }, 1401 | "nbformat": 4, 1402 | "nbformat_minor": 4 1403 | } 1404 | -------------------------------------------------------------------------------- /notebook/img/60sdude.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/60sdude.png -------------------------------------------------------------------------------- /notebook/img/accordion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/accordion.png -------------------------------------------------------------------------------- /notebook/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/alien.png -------------------------------------------------------------------------------- /notebook/img/almostzipper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/almostzipper.png -------------------------------------------------------------------------------- /notebook/img/angeleyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/angeleyes.png -------------------------------------------------------------------------------- /notebook/img/arguments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/arguments.png -------------------------------------------------------------------------------- /notebook/img/asstronaut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/asstronaut.png -------------------------------------------------------------------------------- /notebook/img/baby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/baby.png -------------------------------------------------------------------------------- /notebook/img/badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/badge.png -------------------------------------------------------------------------------- /notebook/img/balloondog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/balloondog.png -------------------------------------------------------------------------------- /notebook/img/banana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/banana.png -------------------------------------------------------------------------------- /notebook/img/bear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/bear.png -------------------------------------------------------------------------------- /notebook/img/bigtree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/bigtree.png -------------------------------------------------------------------------------- /notebook/img/binarytree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/binarytree.png -------------------------------------------------------------------------------- /notebook/img/bird.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/bird.png -------------------------------------------------------------------------------- /notebook/img/boat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/boat.png -------------------------------------------------------------------------------- /notebook/img/bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/bomb.png -------------------------------------------------------------------------------- /notebook/img/bonus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/bonus.png -------------------------------------------------------------------------------- /notebook/img/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/box.png -------------------------------------------------------------------------------- /notebook/img/brain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/brain.png -------------------------------------------------------------------------------- /notebook/img/bread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/bread.png -------------------------------------------------------------------------------- /notebook/img/buddha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/buddha.png -------------------------------------------------------------------------------- /notebook/img/cactus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/cactus.png -------------------------------------------------------------------------------- /notebook/img/calculator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/calculator.png -------------------------------------------------------------------------------- /notebook/img/case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/case.png -------------------------------------------------------------------------------- /notebook/img/caveman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/caveman.png -------------------------------------------------------------------------------- /notebook/img/centaur.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/centaur.png -------------------------------------------------------------------------------- /notebook/img/chainchomp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/chainchomp.png -------------------------------------------------------------------------------- /notebook/img/chess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/chess.png -------------------------------------------------------------------------------- /notebook/img/chicken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/chicken.png -------------------------------------------------------------------------------- /notebook/img/classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/classes.png -------------------------------------------------------------------------------- /notebook/img/clint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/clint.png -------------------------------------------------------------------------------- /notebook/img/composition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/composition.png -------------------------------------------------------------------------------- /notebook/img/concatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/concatmap.png -------------------------------------------------------------------------------- /notebook/img/cool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/cool.png -------------------------------------------------------------------------------- /notebook/img/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/cover.png -------------------------------------------------------------------------------- /notebook/img/cow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/cow.png -------------------------------------------------------------------------------- /notebook/img/cowboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/cowboy.png -------------------------------------------------------------------------------- /notebook/img/curry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/curry.png -------------------------------------------------------------------------------- /notebook/img/deadcat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/deadcat.png -------------------------------------------------------------------------------- /notebook/img/dognap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/dognap.png -------------------------------------------------------------------------------- /notebook/img/dollar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/dollar.png -------------------------------------------------------------------------------- /notebook/img/edd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/edd.png -------------------------------------------------------------------------------- /notebook/img/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/file.png -------------------------------------------------------------------------------- /notebook/img/foldl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/foldl.png -------------------------------------------------------------------------------- /notebook/img/frogtor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/frogtor.png -------------------------------------------------------------------------------- /notebook/img/functor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/functor.png -------------------------------------------------------------------------------- /notebook/img/fx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/fx.png -------------------------------------------------------------------------------- /notebook/img/gob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/gob.png -------------------------------------------------------------------------------- /notebook/img/guards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/guards.png -------------------------------------------------------------------------------- /notebook/img/guycar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/guycar.png -------------------------------------------------------------------------------- /notebook/img/helloworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/helloworld.png -------------------------------------------------------------------------------- /notebook/img/holy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/holy.png -------------------------------------------------------------------------------- /notebook/img/jackofdiamonds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/jackofdiamonds.png -------------------------------------------------------------------------------- /notebook/img/jazzb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/jazzb.png -------------------------------------------------------------------------------- /notebook/img/judgedog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/judgedog.png -------------------------------------------------------------------------------- /notebook/img/justice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/justice.png -------------------------------------------------------------------------------- /notebook/img/kermit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/kermit.png -------------------------------------------------------------------------------- /notebook/img/kid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/kid.png -------------------------------------------------------------------------------- /notebook/img/knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/knight.png -------------------------------------------------------------------------------- /notebook/img/krakatoa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/krakatoa.png -------------------------------------------------------------------------------- /notebook/img/lamb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/lamb.png -------------------------------------------------------------------------------- /notebook/img/lambda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/lambda.png -------------------------------------------------------------------------------- /notebook/img/lazy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/lazy.png -------------------------------------------------------------------------------- /notebook/img/legochar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/legochar.png -------------------------------------------------------------------------------- /notebook/img/legolists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/legolists.png -------------------------------------------------------------------------------- /notebook/img/legomap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/legomap.png -------------------------------------------------------------------------------- /notebook/img/legosets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/legosets.png -------------------------------------------------------------------------------- /notebook/img/letitbe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/letitbe.png -------------------------------------------------------------------------------- /notebook/img/lifter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/lifter.png -------------------------------------------------------------------------------- /notebook/img/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/list.png -------------------------------------------------------------------------------- /notebook/img/listmonster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/listmonster.png -------------------------------------------------------------------------------- /notebook/img/luggage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/luggage.png -------------------------------------------------------------------------------- /notebook/img/lyah.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/lyah.jpg -------------------------------------------------------------------------------- /notebook/img/making_modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/making_modules.png -------------------------------------------------------------------------------- /notebook/img/maoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/maoi.png -------------------------------------------------------------------------------- /notebook/img/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/map.png -------------------------------------------------------------------------------- /notebook/img/maxs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/maxs.png -------------------------------------------------------------------------------- /notebook/img/meekrat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/meekrat.png -------------------------------------------------------------------------------- /notebook/img/miner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/miner.png -------------------------------------------------------------------------------- /notebook/img/modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/modules.png -------------------------------------------------------------------------------- /notebook/img/newsplash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/newsplash.png -------------------------------------------------------------------------------- /notebook/img/notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/notes.png -------------------------------------------------------------------------------- /notebook/img/origami.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/origami.png -------------------------------------------------------------------------------- /notebook/img/owld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/owld.png -------------------------------------------------------------------------------- /notebook/img/painter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/painter.png -------------------------------------------------------------------------------- /notebook/img/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/pattern.png -------------------------------------------------------------------------------- /notebook/img/picard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/picard.png -------------------------------------------------------------------------------- /notebook/img/pierre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/pierre.png -------------------------------------------------------------------------------- /notebook/img/pirateship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/pirateship.png -------------------------------------------------------------------------------- /notebook/img/police.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/police.png -------------------------------------------------------------------------------- /notebook/img/pollywantsa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/pollywantsa.png -------------------------------------------------------------------------------- /notebook/img/present.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/present.png -------------------------------------------------------------------------------- /notebook/img/prob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/prob.png -------------------------------------------------------------------------------- /notebook/img/puppy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/puppy.png -------------------------------------------------------------------------------- /notebook/img/pythag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/pythag.png -------------------------------------------------------------------------------- /notebook/img/quickman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/quickman.png -------------------------------------------------------------------------------- /notebook/img/quicksort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/quicksort.png -------------------------------------------------------------------------------- /notebook/img/random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/random.png -------------------------------------------------------------------------------- /notebook/img/record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/record.png -------------------------------------------------------------------------------- /notebook/img/recursion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/recursion.png -------------------------------------------------------------------------------- /notebook/img/revolver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/revolver.png -------------------------------------------------------------------------------- /notebook/img/ride.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/ride.png -------------------------------------------------------------------------------- /notebook/img/ringring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/ringring.png -------------------------------------------------------------------------------- /notebook/img/roads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/roads.png -------------------------------------------------------------------------------- /notebook/img/roads_simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/roads_simple.png -------------------------------------------------------------------------------- /notebook/img/rpn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/rpn.png -------------------------------------------------------------------------------- /notebook/img/salad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/salad.png -------------------------------------------------------------------------------- /notebook/img/setnotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/setnotation.png -------------------------------------------------------------------------------- /notebook/img/shamrock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/shamrock.png -------------------------------------------------------------------------------- /notebook/img/smug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/smug.png -------------------------------------------------------------------------------- /notebook/img/smugpig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/smugpig.png -------------------------------------------------------------------------------- /notebook/img/socialmediapreview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/socialmediapreview.jpg -------------------------------------------------------------------------------- /notebook/img/spearhead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/spearhead.png -------------------------------------------------------------------------------- /notebook/img/spongedisk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/spongedisk.png -------------------------------------------------------------------------------- /notebook/img/startingout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/startingout.png -------------------------------------------------------------------------------- /notebook/img/streams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/streams.png -------------------------------------------------------------------------------- /notebook/img/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/sun.png -------------------------------------------------------------------------------- /notebook/img/texas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/texas.png -------------------------------------------------------------------------------- /notebook/img/thefonz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/thefonz.png -------------------------------------------------------------------------------- /notebook/img/timber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/timber.png -------------------------------------------------------------------------------- /notebook/img/tipi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/tipi.png -------------------------------------------------------------------------------- /notebook/img/trafficlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/trafficlight.png -------------------------------------------------------------------------------- /notebook/img/tuco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/tuco.png -------------------------------------------------------------------------------- /notebook/img/tuple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/tuple.png -------------------------------------------------------------------------------- /notebook/img/tur2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/tur2.png -------------------------------------------------------------------------------- /notebook/img/typefoo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/typefoo.png -------------------------------------------------------------------------------- /notebook/img/washmachine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/washmachine.png -------------------------------------------------------------------------------- /notebook/img/whale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/whale.png -------------------------------------------------------------------------------- /notebook/img/wolf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/wolf.png -------------------------------------------------------------------------------- /notebook/img/yesno.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/yesno.png -------------------------------------------------------------------------------- /notebook/img/yeti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IHaskell/learn-you-a-haskell-notebook/9b90efe7fd66efa87bff881bc19adcf34bb217b1/notebook/img/yeti.png -------------------------------------------------------------------------------- /notebook_extra/WidgetChart.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Demonstration combining Widgets with Charts" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "data": { 17 | "application/vnd.jupyter.widget-view+json": { 18 | "model_id": "3f106d34-dd2d-4817-a566-dd53574c5d3e", 19 | "version_major": 2, 20 | "version_minor": 0 21 | } 22 | }, 23 | "metadata": {}, 24 | "output_type": "display_data" 25 | }, 26 | { 27 | "data": { 28 | "application/vnd.jupyter.widget-view+json": { 29 | "model_id": "3afa14b2-cc8c-4660-a758-8bd1e2f5ea38", 30 | "version_major": 2, 31 | "version_minor": 0 32 | } 33 | }, 34 | "metadata": {}, 35 | "output_type": "display_data" 36 | } 37 | ], 38 | "source": [ 39 | "import Graphics.Rendering.Chart\n", 40 | "import IHaskell.Display.Widgets\n", 41 | "import IHaskell.Display\n", 42 | "import Data.Default.Class\n", 43 | "import Control.Lens\n", 44 | "\n", 45 | "f = sin -- The function which we will plot\n", 46 | "domain = (0.0, 10.0) -- The domain of the function which we will plot\n", 47 | "\n", 48 | "grain = (snd domain - fst domain) / 100.0\n", 49 | "\n", 50 | "numericIntegral [] = 0\n", 51 | "numericIntegral [_] = 0\n", 52 | "numericIntegral ((x1,y1):(x2,y2):ps) = \n", 53 | " ((x2 - x1) * ((y1 + y2) / 2.0)) + numericIntegral ((x2,y2):ps)\n", 54 | "\n", 55 | "sliderRange <- mkFloatRangeSlider\n", 56 | "setField sliderRange MinFloat (fst domain)\n", 57 | "setField sliderRange MaxFloat (snd domain)\n", 58 | "setField sliderRange StepFloat (Just grain)\n", 59 | "setField sliderRange FloatPairValue (fst domain, snd domain)\n", 60 | "\n", 61 | "outChart <- mkOutput\n", 62 | "\n", 63 | "drawChart = do \n", 64 | " (rangeMin, rangeMax) <- getField sliderRange FloatPairValue\n", 65 | " let \n", 66 | " pts = [(x, f x) | x <- [fst domain, grain .. snd domain]] \n", 67 | " ptsRange = takeWhile ((<= rangeMax) . fst) $ dropWhile ((< rangeMin) . fst) pts\n", 68 | " layout = def & layout_plots .~\n", 69 | " [ toPlot $ def & plot_lines_values .~ [pts]\n", 70 | " , toPlot $ def & plot_fillbetween_values .~ [(x,(0.0,y)) | (x,y) <- ptsRange]\n", 71 | " ]\n", 72 | " displayCaption <- display $ plain \n", 73 | " $ \"Area under the curve = \" <> show (numericIntegral ptsRange)\n", 74 | " displayChart <- display $ toRenderable layout\n", 75 | " setField outChart Outputs [OutputData displayCaption, OutputData displayChart]\n", 76 | " \n", 77 | "setField sliderRange ChangeHandler drawChart\n", 78 | "\n", 79 | "sliderRange\n", 80 | "outChart\n", 81 | "drawChart" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [] 90 | } 91 | ], 92 | "metadata": { 93 | "kernelspec": { 94 | "display_name": "Haskell", 95 | "language": "haskell", 96 | "name": "haskell" 97 | }, 98 | "language_info": { 99 | "codemirror_mode": "ihaskell", 100 | "file_extension": ".hs", 101 | "mimetype": "text/x-haskell", 102 | "name": "haskell", 103 | "pygments_lexer": "Haskell", 104 | "version": "8.10.4" 105 | } 106 | }, 107 | "nbformat": 4, 108 | "nbformat_minor": 4 109 | } 110 | -------------------------------------------------------------------------------- /notebook_extra/WidgetDiagram.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Demonstration combining Widgets with Diagrams" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Let's attach a slider widget to one parameter of the *Apollonian gasket* from the `diagrams-contrib` package.\n", 15 | "\n", 16 | "https://hackage.haskell.org/package/diagrams-contrib/docs/Diagrams-TwoD-Apollonian.html" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [ 24 | { 25 | "data": { 26 | "application/vnd.jupyter.widget-view+json": { 27 | "model_id": "3a836ed3-872d-43a8-9572-34987eb47d71", 28 | "version_major": 2, 29 | "version_minor": 0 30 | } 31 | }, 32 | "metadata": {}, 33 | "output_type": "display_data" 34 | }, 35 | { 36 | "data": { 37 | "application/vnd.jupyter.widget-view+json": { 38 | "model_id": "631d997a-191f-4dc9-8566-4a3a3cb32921", 39 | "version_major": 2, 40 | "version_minor": 0 41 | } 42 | }, 43 | "metadata": {}, 44 | "output_type": "display_data" 45 | } 46 | ], 47 | "source": [ 48 | "import Diagrams.Prelude\n", 49 | "import Diagrams.TwoD.Apollonian\n", 50 | "import IHaskell.Display.Widgets\n", 51 | "\n", 52 | "sliderApollo <- mkFloatSlider\n", 53 | "setField sliderApollo MinFloat 4.0\n", 54 | "setField sliderApollo MaxFloat 9.0\n", 55 | "setField sliderApollo StepFloat (Just 0.1)\n", 56 | "setField sliderApollo FloatValue 5.0\n", 57 | "\n", 58 | "outApollo <- mkOutput\n", 59 | "\n", 60 | "drawApollo = getField sliderApollo FloatValue >>= \\val ->\n", 61 | " replaceOutput outApollo $ diagram $ apollonianGasket 0.01 2 4 val\n", 62 | "\n", 63 | "setField sliderApollo ChangeHandler drawApollo\n", 64 | "\n", 65 | "sliderApollo\n", 66 | "outApollo\n", 67 | "drawApollo" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "Draw a cat tail diagram, controlled by a slider widget." 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 2, 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "data": { 84 | "application/vnd.jupyter.widget-view+json": { 85 | "model_id": "9e58e9f2-6403-4b3b-88a1-90928bd8474c", 86 | "version_major": 2, 87 | "version_minor": 0 88 | } 89 | }, 90 | "metadata": {}, 91 | "output_type": "display_data" 92 | }, 93 | { 94 | "data": { 95 | "application/vnd.jupyter.widget-view+json": { 96 | "model_id": "727cfef5-8275-4563-b26e-5a6371bd95c6", 97 | "version_major": 2, 98 | "version_minor": 0 99 | } 100 | }, 101 | "metadata": {}, 102 | "output_type": "display_data" 103 | } 104 | ], 105 | "source": [ 106 | ":extension BlockArguments\n", 107 | "import Data.Foldable\n", 108 | "import Diagrams.Prelude\n", 109 | "import Diagrams.TwoD.Path.Turtle\n", 110 | "import IHaskell.Display.Widgets\n", 111 | "\n", 112 | "sliderCat <- mkFloatSlider\n", 113 | "setField sliderCat MinFloat (-100.0)\n", 114 | "setField sliderCat MaxFloat 100.0\n", 115 | "setField sliderCat StepFloat (Just 1.0)\n", 116 | "setField sliderCat FloatValue 0.0\n", 117 | "\n", 118 | "outCat <- mkOutput\n", 119 | "\n", 120 | "drawCat = do\n", 121 | " val <- getField sliderCat FloatValue\n", 122 | " replaceOutput outCat $ diagram $ rotate ((-90.0) @@ deg) $ sizeDiagram $ drawTurtle do\n", 123 | " setPenWidth 4\n", 124 | " for_ [1.0,2.0..30.0] \\i -> do\n", 125 | " forward 10.0\n", 126 | " left (val * i / 100.0)\n", 127 | " where \n", 128 | " sizeDiagram = withEnvelope (fromPoints [mkP2 0.0 (-180.0), mkP2 300.0 180.0]) \n", 129 | "\n", 130 | "setField sliderCat ChangeHandler drawCat\n", 131 | "\n", 132 | "sliderCat\n", 133 | "outCat\n", 134 | "drawCat" 135 | ] 136 | } 137 | ], 138 | "metadata": { 139 | "kernelspec": { 140 | "display_name": "Haskell", 141 | "language": "haskell", 142 | "name": "haskell" 143 | }, 144 | "language_info": { 145 | "codemirror_mode": "ihaskell", 146 | "file_extension": ".hs", 147 | "mimetype": "text/x-haskell", 148 | "name": "haskell", 149 | "pygments_lexer": "Haskell", 150 | "version": "8.10.4" 151 | } 152 | }, 153 | "nbformat": 4, 154 | "nbformat_minor": 4 155 | } 156 | -------------------------------------------------------------------------------- /notebook_extra/WidgetRevival.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 💙 ihaskell-widgets Revival Celebration Notebook 💙\n", 8 | "\n", 9 | "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", 10 | "\n", 11 | "Here is [David Davó Laviña’s report on __Fixing IHaskell-Widgets__](https://gsoc21.ddavo.me/).\n", 12 | "\n", 13 | "For David’s *ihaskell-widgets* example notebooks and tutorials, see the `/ihaskell_examples/ihaskell-widgets` directory.\n", 14 | "\n", 15 | "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", 16 | "\n", 17 | "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", 18 | "\n", 19 | "— James Brock, Tokyo 2021\n", 20 | "\n", 21 | "
\n", 22 | "\n", 23 | "Click to launch this notebook on Binder cloud service (takes a minute to launch): \n", 24 | "\n", 25 | "[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", 26 | "\n", 27 | "
" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "## Demonstration combining Widgets with Charts\n", 35 | "\n", 36 | "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", 37 | "\n", 38 | "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", 39 | "\n", 40 | "See the [*Chart* wiki](https://github.com/timbod7/haskell-chart/wiki) for other possibilities." 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "import Graphics.Rendering.Chart\n", 50 | "import IHaskell.Display.Widgets\n", 51 | "import IHaskell.Display\n", 52 | "import Data.Default.Class\n", 53 | "import Control.Lens\n", 54 | "\n", 55 | "f x = sin x * (x + 5.0) -- The function which we will plot\n", 56 | "domain = (0.0, 10.0) -- The domain of the function which we will plot\n", 57 | "\n", 58 | "grain = (snd domain - fst domain) / 100.0\n", 59 | "\n", 60 | "integralTrapezoid ((x1,y1):(x2,y2):ps) = \n", 61 | " ((x2 - x1) * ((y1 + y2) / 2.0)) + integralTrapezoid ((x2,y2):ps)\n", 62 | "integralTrapezoid _ = 0\n", 63 | "\n", 64 | "sliderRange <- mkFloatRangeSlider\n", 65 | "setField @MinFloat sliderRange (fst domain)\n", 66 | "setField @MaxFloat sliderRange (snd domain)\n", 67 | "setField @StepFloat sliderRange (Just grain)\n", 68 | "setField @FloatPairValue sliderRange (fst domain, snd domain)\n", 69 | "\n", 70 | "outChart <- mkOutput\n", 71 | "\n", 72 | "drawChart = do \n", 73 | " (rangeMin, rangeMax) <- getField @FloatPairValue sliderRange\n", 74 | " let \n", 75 | " pts = [(x, f x) | x <- [fst domain, fst domain + grain .. snd domain]] \n", 76 | " ptsRange = [(x,y) | (x,y) <- pts, x >= rangeMin, x <= rangeMax]\n", 77 | " displayCaption <- display $ plain \n", 78 | " $ \"Area under the curve = \" <> show (integralTrapezoid ptsRange)\n", 79 | " displayChart <- display $ toRenderable $ def & layout_plots .~\n", 80 | " [ toPlot $ def & plot_lines_values .~ [pts]\n", 81 | " , toPlot $ def & plot_fillbetween_values .~ [(x,(0.0,y)) | (x,y) <- ptsRange]\n", 82 | " ]\n", 83 | " setField @Outputs outChart [OutputData displayCaption, OutputData displayChart]\n", 84 | " \n", 85 | "setField @ChangeHandler sliderRange drawChart\n", 86 | "\n", 87 | "sliderRange\n", 88 | "outChart\n", 89 | "drawChart" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "## Demonstration combining Widgets with Diagrams\n", 97 | "\n", 98 | "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", 99 | "\n", 100 | "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", 101 | "\n", 102 | "See the [*Diagrams* project website](https://archives.haskell.org/projects.haskell.org/diagrams/) for other possibilities." 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "import Diagrams.Prelude\n", 112 | "import Diagrams.TwoD.Apollonian\n", 113 | "import IHaskell.Display.Widgets\n", 114 | "\n", 115 | "sliderApollo <- mkFloatSlider\n", 116 | "setField @MinFloat sliderApollo 3.0\n", 117 | "setField @MaxFloat sliderApollo 9.0\n", 118 | "setField @StepFloat sliderApollo (Just 0.1)\n", 119 | "setField @FloatValue sliderApollo 6.0\n", 120 | "\n", 121 | "outApollo <- mkOutput\n", 122 | "\n", 123 | "drawApollo = do\n", 124 | " val <- getField @FloatValue sliderApollo\n", 125 | " replaceOutput outApollo $ diagram $ apollonianGasket 0.01 2 4 val\n", 126 | "\n", 127 | "setField @ChangeHandler sliderApollo drawApollo\n", 128 | "\n", 129 | "sliderApollo\n", 130 | "outApollo\n", 131 | "drawApollo" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [] 140 | } 141 | ], 142 | "metadata": { 143 | "kernelspec": { 144 | "display_name": "Haskell", 145 | "language": "haskell", 146 | "name": "haskell" 147 | }, 148 | "language_info": { 149 | "codemirror_mode": "ihaskell", 150 | "file_extension": ".hs", 151 | "mimetype": "text/x-haskell", 152 | "name": "haskell", 153 | "pygments_lexer": "Haskell", 154 | "version": "9.8.4" 155 | } 156 | }, 157 | "nbformat": 4, 158 | "nbformat_minor": 4 159 | } 160 | --------------------------------------------------------------------------------