{"id":13681346,"url":"https://github.com/alpacaaa/zero-bs-haskell","last_synced_at":"2026-02-09T02:08:25.696Z","repository":{"id":55326241,"uuid":"170578652","full_name":"alpacaaa/zero-bs-haskell","owner":"alpacaaa","description":"Learn Haskell, with tiny lessons.","archived":false,"fork":false,"pushed_at":"2021-07-13T09:16:38.000Z","size":772,"stargazers_count":564,"open_issues_count":4,"forks_count":23,"subscribers_count":22,"default_branch":"master","last_synced_at":"2025-11-07T02:03:42.906Z","etag":null,"topics":["burritos","haskell","learn"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alpacaaa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-13T20:58:46.000Z","updated_at":"2025-11-03T07:28:14.000Z","dependencies_parsed_at":"2022-08-14T21:11:48.453Z","dependency_job_id":null,"html_url":"https://github.com/alpacaaa/zero-bs-haskell","commit_stats":null,"previous_names":["alpacaaa/zero-bullshit-haskell"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/alpacaaa/zero-bs-haskell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpacaaa%2Fzero-bs-haskell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpacaaa%2Fzero-bs-haskell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpacaaa%2Fzero-bs-haskell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpacaaa%2Fzero-bs-haskell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alpacaaa","download_url":"https://codeload.github.com/alpacaaa/zero-bs-haskell/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpacaaa%2Fzero-bs-haskell/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29254307,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-09T01:52:29.835Z","status":"online","status_checked_at":"2026-02-09T02:00:09.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["burritos","haskell","learn"],"created_at":"2024-08-02T13:01:29.701Z","updated_at":"2026-02-09T02:08:25.665Z","avatar_url":"https://github.com/alpacaaa.png","language":"Haskell","readme":"\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Zero Bullshit Haskell\" src=\"exercises/test/logo.png\" width=\"480\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Let's learn Haskell. For real this time. :rocket:\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.youtube.com/channel/UCJiwOqQi88UZCe8w7lwV4gw\"\u003e\u003cimg alt=\"Zero Bullshit Haskell Youtube channel\" src=\"https://img.shields.io/badge/Youtube-channel-ff0000.svg?style=flat\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://alpacaaa.github.io/zero-bullshit-haskell/docs/\"\u003e\u003cimg alt=\"Zero Bullshit Haddock documentation\" src=\"https://img.shields.io/badge/docs-zero--bullshit-5E5184.svg?style=flat\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\n\u0026nbsp;\n\n\n# Table of Contents\n\n- [Introduction](#toc-introduction)\n  - [What's Zero Bullshit Haskell](#toc-what-is-it)\n  - [Exercises](#toc-exercises)\n  - [Scope](#toc-scope)\n\n- [Install Haskell in two minutes](#toc-install)\n  - [The need for `stack`](#toc-need-stack)\n  - [A word on `cabal`](#toc-word-cabal)\n  - [Doing things with `stack`](#toc-things-stack)\n\n- [What are types and why you should care](#toc-types)\n  - [Using and abusing the type system](#toc-abusing-types)\n  - [Show me the types](#toc-show-types)\n  - [Types are your friends](#toc-types-friends)\n\n- [Local dev environment and first exercise](#toc-solve-exercise)\n  - [Getting ready](#toc-exercise-ready)\n  - [Your first lines of Haskell](#toc-first-lines)\n  - [Completing Exercise #01](#toc-completing-ex01)\n\n- [Standard library and importing modules](#toc-imports-std-lib) \n  - [The Prelude](#toc-prelude)\n  - [Qualified imports will keep you sane](#toc-qualify-imports)\n\n- [How to debug Haskell code](#toc-debug-haskell)\n  - [Everything is an expression](#toc-everything-expression)\n  - [But... side effects?!](#toc-debug-side-effects)\n\n- [Encoding and decoding JSON](#toc-encoding-decoding-json)\n  - [A brief interlude on instances](#toc-interlude-instances)\n  - [An even shorter interlude on constraints](#toc-interlude-constraints)\n  - [`FromJSON` and `ToJSON`](#toc-fromjson-tojson)\n\n- [Dealing with mutable state](#toc-dealing-state)\n  - [Modeling state the functional way](#toc-modeling-state)\n  - [A slight detour in OTP land](#toc-detour-otp)\n  - [Let the runtime handle state for you](#toc-runtime-handle)\n\n- [Install libraries from Hackage/Stackage](#toc-hackage-stackage)\n  - [Fun times](#toc-fun-times)\n  - [Hackage](#toc-hackage)\n  - [Stackage](#toc-stackage)\n  - [Packages that are not on Stackage](#toc-packages-not-stackage)\n\u0026nbsp;\n\n\n# Exercises index\n\n#### Beginner\n- [Exercise #01 - Static String](https://alpacaaa.github.io/zero-bullshit-haskell/Ex01)\n- [Exercise #02 - Echo](https://alpacaaa.github.io/zero-bullshit-haskell/Ex02)\n- [Exercise #03 - Case match](https://alpacaaa.github.io/zero-bullshit-haskell/Ex03)\n- [Exercise #04 - String manipulation](https://alpacaaa.github.io/zero-bullshit-haskell/Ex04)\n- [Exercise #05 - On/Off switch](https://alpacaaa.github.io/zero-bullshit-haskell/Ex05)\n- [Exercise #06 - Counter](https://alpacaaa.github.io/zero-bullshit-haskell/Ex06)\n- [Exercise #07 - ShoppingCart](https://alpacaaa.github.io/zero-bullshit-haskell/Ex07)\n- [Exercise #08 - ShoppingCart V2](https://alpacaaa.github.io/zero-bullshit-haskell/Ex08)\n\n_work in progress_\n\n\u0026nbsp;\n\n\n# \u003ca id=\"toc-introduction\"\u003e\u003c/a\u003eIntroduction\n\nHaskell is a beautiful language.\n\nIt's not an easy language to learn though. Especially if you have years of experience in object oriented/imperative programming, getting into Haskell can be a bit of a pain.\n\nThis is my attempt at rectifying that. I want more and more people to learn about functional programming and Haskell in particular.\n\n### \u003ca id=\"toc-what-is-it\"\u003e\u003c/a\u003eWhat's Zero Bullshit Haskell\n\nThe goal is to have a learning resource that is simple and digestable, with a great deal of hands on exercises. Most Haskell resources tend to be super dense with a lot of theory — I want to approach things going the other way. I want to draw as many parallels as I can with the Javascript world because that's what most people are familiar with these days and **introduce concepts and theory only when necessary**.\n\nI want you to get the bare minimum amount of information to get going with Haskell and complete the first few exercises. As you get more comfortable, further reading material will introduce new topics that will help you solve the problem at hand while polishing your existing solutions.\n\nAll the nasty and scary jargon is pushed as further as possible and presented only when absolutely necessary. If you complete all the exercises, you will have gained enough intuition beyond certain patterns that it'll only be a matter of naming them properly. This will help you strenghten your fp knowledge and understanding of all the beautiful concepts behind Haskell — but none of that will be smashed in your face from the get go. I know how challenging and overwhelming it can be for a beginner.\n\n### \u003ca id=\"toc-exercises\"\u003e\u003c/a\u003eExercises\n\nExercises revolve around creating a webserver. It can be a very very simple webserver that just outputs `hello`, all the way to a concrete REST api that can handle state and connect to external services. We'll get there.\n\nYou can fiddle with the exercises with very little effort, as they come with a test runner that runs in the browser and hits `http://localhost:7879` — where your Haskell server will be listening. That's right, no need to install any extra bullshit. All you need to do is go to the exercises website and start firing some AJAX requests. Read [Local dev environment and first exercise](#toc-solve-exercise).\n\nPerhpaps most importantly, all exercises have a **reference node.js implementation**. We'll discuss how each exercise could be solved using `express`, what the pitfalls of that implementation might be and how functional programming can help us in writing more robust code. Have a peak at [Exercise #01](https://alpacaaa.github.io/zero-bullshit-haskell/Ex01) to get an idea of how it works.\n\nWe're going to be using a Haskell [library](https://alpacaaa.github.io/zero-bullshit-haskell/docs) that is very similar in spirit and simplicity to `express`. In fact, I wrote it specifically for this project and I made sure it's devoid of any bullshit whatsoever. It's not production ready of course — we will transition to a more robust and widely used library later on.\n\n### \u003ca id=\"toc-scope\"\u003e\u003c/a\u003eScope\n\nThis is not a Haskell tutorial.\n\nYou will not learn how to define types, functions or any of the syntax. Don't worry though! I think it's _manageable_ to figure this stuff out on your own. For instance, going through [Learn Haskell in Y Minutes](https://learnxinyminutes.com/docs/haskell/) a couple of times should be enough to get you started (note there's some bullshit at the end: ignore all the scary words, just play around with some of the examples).\n\nWith that being said, I'd like to have some content that covers the basics as well, eventually. Let me know if you'd like to see that happen. \n\nZero Bullshit Haskell is about **overcoming the fear of types and start writing real Haskell code** that is simple to understand and doesn't look scary.\n\n\u0026nbsp;\n\n\n# \u003ca id=\"toc-install\"\u003e\u003c/a\u003eInstall Haskell in two minutes\n\nThis is the first step, and it's just a `curl` away, but first a bit of clarification.\n\nYou can't really _install_ Haskell as much as you can't _install_ Javascript.\n\nWhat you want to do, is install a Haskell compiler. The Haskell compiler that everybody uses is **GHC**: The Glorious Haskell Compiler. See that? It's called glorious, so it must be good.\n\nGHC is like the Node.js of Haskell. For reasons we don't really care about, using GHC directly is a bit nasty. What we want is a tool that manages a Haskell project for us and that tool is called `stack`.\n\nInstall `stack` now and keep reading.\n\n```bash\ncurl -sSL https://get.haskellstack.org/ | sh\n```\n\n### \u003ca id=\"toc-need-stack\"\u003e\u003c/a\u003eThe need for `stack`\n\nYou can think of `stack` as `npm` or `yarn` — it's effectively solving a lot of the same problems. If you've already installed some version of GHC on your system, don't worry. Everytime you initialize a stack project, it will download and configure an appropriate version of GHC for you to use, so that you're not _stuck_ with a global install. Yes, it's nice like that.\n\n### \u003ca id=\"toc-word-cabal\"\u003e\u003c/a\u003eA word on `cabal`\n\nThere was a time when `stack` didn't exist and people used a tool called `cabal`. Those people experienced the so called _cabal hell_ and eventually created `stack` out of frustration. Recently, `cabal` has gotten a make over and it has gained back some of its original userbase.\n\nGiven that we have no idea what we're doing, we'll stick with `stack` because there is really nothing wrong with it and works great for what we need to accomplish here.\n\nUnder the hood, stack still uses `cabal`. You might also find a `.cabal` file in your projects. Leave that file alone (it's auto generated) and forget about `cabal`.\n\n### \u003ca id=\"toc-things-stack\"\u003e\u003c/a\u003eDoing things with `stack`\n\nThere are two commands that you're going to use all the time. To be honest, they are basically all you need.\n\n* `stack build --file-watch`  \n  Watch `.hs` files for changes and rebuild the project\n* `stack run`  \n  Build (if necessary) and run the project\n\n\u0026nbsp;\n\n\n# \u003ca id=\"toc-types\"\u003e\u003c/a\u003eWhat are types and why you should care\n\nIf you've read a Haskell blog post, joined some functional programming chat or attended a conference talk, chances are you experienced how much **people like to talk about types**. That's fine, once you get familiar with this stuff, you grow a weird and obsessive interest in pushing the _type system_, which is the set of rules that GHC follows to check that your program is correct.\n\nBut if you're like me, you don't.\n\n### \u003ca id=\"toc-abusing-types\"\u003e\u003c/a\u003eUsing and abusing the type system\n\nThere is a fine line between making sure something is absolutely correct but unreadable/impossible to understand, and something that is less _safe_ (that is, has less guarantees at compile time) but is an actual piece of code that makes sense.\n\nI personally don't get much excited about crazy types, but I still get to write Haskell for a living.\n\nTo me, writing simple Haskell that leverages only the absolute minimum amount of core features that are available in the language, is a joy to write, maintain and coming back to. I don't care about _fancy types_ and you won't find any in this series. This is as practical as it gets and there are fantastic resources out there when and if you'll want to get into that stuff. (Couple of personal suggestions: [i-am-tom/haskell-exercises](\u003chttps://github.com/i-am-tom/haskell-exercises\u003e) and [Thinking with Types](\u003chttps://leanpub.com/thinking-with-types\u003e))\n\n### \u003ca id=\"toc-show-types\"\u003e\u003c/a\u003eShow me the types\n\nHere are a few sample types. You should familiarize yourself with all the different ways you can define a type.\n\n```haskell\ndata Color\n  = Blue | Orange | Green | Yellow\n\nfavoriteColor :: Color\nfavoriteColor = Green\n```\n\nA `Color` can be one of `Blue`, `Orange`, `Green` or `Yellow`.\n\n`favoriteColor` is of type `Color` and has value `Green`.\n\n```haskell\ndata Person\n  = Person\n      { name :: String\n      , age :: Int\n      , hair :: Color\n      }\n\nbob :: Person\nbob = Person\n        { name = \"bob\"\n        , age = 97\n        , hair = Orange\n        }\n```\n\nA `Person` is a record. `bob` is of type `Person` .\n\n```haskell\ndata Product\n  = Book String Int\n  | Movie String\n\noneBook :: Product\noneBook = Book \"Some book title\" 293\n\noneMovie :: Product\noneMovie = Movie \"Some movie title\"\n```\n\nA `Product` can either be a `Book`, which is made of a title of type `String` and the number of pages of type `Int`. Or it can be a `Movie`, which only has a title of type `String`.\n\nWe should use types to help us, so a better way of modelling this could be:\n\n```haskell\ndata BookTitle\n  = BookTitle String\n\ndata Pages\n  = Pages Int\n\ndata MovieTitle\n  = MovieTitle String\n\ndata Product\n  = Book BookTitle Pages\n  | Movie MovieTitle\n  \noneBook :: Product\noneBook = Book (BookTitle \"Some book title\") (Pages 293)\n\noneMovie :: Product\noneMovie = Movie (MovieTitle \"Some movie title\")\n```\n\n### \u003ca id=\"toc-types-friends\"\u003e\u003c/a\u003eTypes are your friends\n\nYou model your problem/domain through types.\n\nThe first thing you should do before writing any actual implementation of your program is **figuring out your types**. If you get your types right, the implementation will follow naturally and GHC will be able to help you along the way because it knows exactly which types it's expecting. Your job is then to just fill in the holes.\n\nThe more you can prove about the correctness of your program at compile time, the less bugs you'll run into in production. Types allow you to keep the smallest context possible in your head and let GHC figure out what's going on. That's an invaluable asset when you want to reason about your program.\n\nType safety is all about that. We're making our code _safer_ through types. You can write some pretty complex and weird code through fancy types but that's bullshit, especially at this stage, so we're not getting into any of that.\n\nA well modeled but simple type is just as useful. **Get your types right and the implementation will follow**. This is one of the main reasons Haskell is so appealing.\n\n\u0026nbsp;\n\n\n# \u003ca id=\"toc-solve-exercise\"\u003e\u003c/a\u003eLocal dev environment and first exercise\n\nAll right, let's get down to business!\n\nMake sure you have `stack` installed and create a new project — name it however you want.\n\n```bash\nstack new types-arouse-me\n```\n\nNow go into that folder and build. It might take a few minutes the first time.\n\n```bash\nstack build\n```\n\nWhile it's building, crack open `stack.yaml` in your freshly created project and tell it where to find the `zero-bullshit` library. This is the library you're going to need to complete the exercises. It's similar in spirit to `express` and `sinatra` in that it's super basic. I wrote it specifically for the Zero Bullshit Haskell project and believe me when I say I kept all the bullshit at bay!\n\n```yaml\n# stack.yaml\n\nextra-deps:\n- github: alpacaaa/zero-bullshit-haskell\n  commit: master\n  subdirs:\n    - library\n```\n\n(Yes, we're pulling from Github. This library might end up on Hackage some day).\n\nThen open `package.yaml` to require the package as a dependency.\n\n```yaml\n# package.yaml (recall this is basically package.json)\n\n# Turn on some useful extensions while we're here!\n# These should really be on by default. They just make\n# the language nicer, don't worry about them.\ndefault-extensions:\n- OverloadedStrings\n- DeriveGeneric\n- DeriveAnyClass\n\ndependencies:\n- base # feel free to omit the version constraint\n- zero-bullshit\n```\n\n### \u003ca id=\"toc-exercise-ready\"\u003e\u003c/a\u003e Getting ready\n\nLet's now take a peek at the first exercise.\n\n[Exercise #01 - Static String](https://alpacaaa.github.io/zero-bullshit-haskell/Ex01)\n\nI built all exercises so that they have an integration test that you can run straight from your browser. Head over to the [Exercise #01](https://alpacaaa.github.io/zero-bullshit-haskell/Ex01) page to see the `mocha` test runner ready to go. When you run the tests, they will hit `localhost:7879` which is where your server is going to be listening.\n\nObviously we have nothing running locally yet, so you should see a message saying that your server isn't running.\n\nThat's absolutely fine, we'll fix it in a minute. First, we want to run our vanilla stack-generated Haskell project just to check what's going on.\n\n```haskell\nstack run\n\u003e someFunc\n```\n\nWhat's this `someFunc` business? It's just a poorly worded hello world message. But more importantly, we have compiled and ran a Haskell application!\n\n\n### \u003ca id=\"toc-first-lines\"\u003e\u003c/a\u003e Let's write some Haskell\n\nThere are currently two files in our project `app/Main.hs` and `src/Lib.hs`.\n\nMost of the times, you'll be editing stuff in the `src` folder — that's where 99% of your code should be. `app` is just for executables, which are thin entrypoints to your application (we have a single executable right now).\n\nIf you open `src/Lib.hs` you'll see this:\n\n```haskell\nmodule Lib\n    ( someFunc\n    ) where\n\nsomeFunc :: IO ()\nsomeFunc = putStrLn \"someFunc\"\n```\n\nThe very last line is the one we care about and hopefully it makes sense —  we're just printing some text to the console.\n\nThe first thing we want to do is import the `Zero.Server` module from the `zero-bullshit` package.\n\n```haskell\nimport qualified Zero.Server as Server\n```\n\nAnd then we want to have our server listen for requests, even though it isn't able to handle any at the moment. So we end up with something like this:\n\n```haskell\nmodule Lib where\n\nimport qualified Zero.Server as Server\n\nsomeFunc :: IO ()\nsomeFunc = Server.startServer []\n```\n\nNow, give `stack run` another go and visit the integration tests page again. If your code is compiling and running correctly, the warning message should have disappeared!\n\nThat's right, the test runner has detected that our server is up and it's ready to run the tests. Let's give it a go, it will fail miserably.\n\n\n### \u003ca id=\"toc-completing-ex01\"\u003e\u003c/a\u003e Completing the first exercise\n\nThe test is making a `GET` request to `/hello` and is expecting a response with body `hello`. That shouldn't be too hard to implement. The main ingredients are `simpleHandler` and `stringResponse`.\n\n**I highly encourage you to [watch the video](https://www.youtube.com/watch?v=Agp0qP96780) where I go through this** to understand what's going on. I'm pasting the solution here for reference (you'll find solution for other exercises linked in each exercise page).\n\n```haskell\nhelloHandler :: Server.Request -\u003e Server.Response\nhelloHandler _\n  = Server.stringResponse \"hello\"\n\n-- Change `someFunc` to `run` because we're not savages.\n-- You'll have to update this in app/Main.hs as well.\nrun :: IO ()\nrun\n  = Server.startServer\n      [ Server.simpleHandler Server.GET \"/hello\" helloHandler\n      ]\n```\n\nExecute `stack run` again and give the test another go, it should work.\n\nCongratulation on getting this far!\nDon't worry if you weren't able to follow along this exercise or any of the following ones.\nI plan to record videos where I go through each exercise **in detail**, uncovering new concepts\nand clearing up what we're doing differently compared to the node.js implementation. With time, you'll realize\nhow many things are uncomfortable/dangerous in the Javascript world, although I must admit you probably think Haskell\nis the funny one right now. Stick around, it will be worth it!\n\n\u0026nbsp;\n\n\n# \u003ca id=\"toc-imports-std-lib\"\u003e\u003c/a\u003e Standard library and importing modules\n\nImagine you've just invented a new programming language. Now you need a standard library — a set of functions, types and a bit of blackbox magic baked into the compiler for developers to use.\n\nStandard libraries are very hard to get right the first time. Once you make a bad decision, it's extremely difficult to **fix or deprecate** it because _everyone_ is using that thing. Effectively, all programming languages I've learned over the years have a somewhat broken standard library. So it shouldn't come as a surprise that the Haskell's one isn't great either, but it's not that bad.\n\n### \u003ca id=\"toc-prelude\"\u003e\u003c/a\u003e The Prelude\n\nThe standard library in Haskell is called `Prelude`.\n\nIt comes with the [base](http://hackage.haskell.org/package/base) package (you might have seen it listed as a dependency in your project, that's what it is). Anything in the `Prelude` module is imported by default, meaning you don't need an explicit `import` statement. Note that there are other modules in `base` and those do need importing (for example `Data.Maybe`). It might seem there's a lot of stuff in `base` — it is indeed a chunky package. But the day to day stuff you'll need is only a subset.\n\nWhile not perfect, the stock Prelude will do just fine for our purposes. The main thing you have to watch out for is partial functions. In case of errors, these functions throw exceptions when they really shouldn't be. When you go through the exercises, you'll find warnings against usage of these functions and alternative safer implementations.\n\n### \u003ca id=\"toc-qualify-imports\"\u003e\u003c/a\u003e Qualified imports will keep you sane\n\nIt's often a good idea to _qualify_ your imports.\n\n```haskell\nimport qualified Zero.Server as Server\n-- In Javascript, this would be the equivalent of:\n-- import * as express from 'express'\n```\n\nThis way, you're not polluting the scope with a ton of functions and types. Most importantly, you'll always be able to tell where a function or type is coming from.\n\nSometimes it's handy to import specific types, or widely used functions, in which case it makes sense to use an unqualified import. But be careful, one of the things I struggled the most with as a beginner (among other things obviously) was having too many unqualified imports (it's just super confusing).\n\n```haskell\n-- The `Text` type and the `fromMaybe` function are super common.\n-- It makes sense to import them unqualified.\n\nimport Data.Text (Text)\nimport Data.Maybe (fromMaybe)\n\nexample :: Text\nexample = fromMaybe ...\n\n\n-- Next, the qualified version.\n-- `Text.Text` is really uncomfortable to type over and over again and it just adds noise.\n-- This is one of the instances where I would stick with unqualified imports.\n\nimport qualified Data.Text as Text\nimport qualified Data.Maybe as Maybe\n\nexample :: Text.Text\nexample = Maybe.fromMaybe ...\n\n```\n\n\u0026nbsp;\n\n\n# \u003ca id=\"toc-debug-haskell\"\u003e\u003c/a\u003eHow to debug Haskell code\n\nTLDR;  the closest equivalent to `console.log` is `traceShowId`.\n\n```haskell\nimport qualified Debug.Trace as Debug\n\n{-\n  In JS we would write:\n  const sum = (a, b) =\u003e {\n    console.log(a)\n    console.log(b)\n    return a + b\n  }\n-}\n\nsum :: Int -\u003e Int -\u003e Int\nsum a b\n  = (Debug.traceShowId a) + (Debug.traceShowId b)\n\n-- \u003e\u003e\u003e sum 1 2\n-- 1\n-- 2\n```\n\nNot quite the same is it? That's because everything in Haskell is an expression. (Note that the `Debug.Trace` module is part of the `base` package, so there's nothing extra to install)\n\n\n\n### \u003ca id=\"toc-everything-expression\"\u003e\u003c/a\u003eEverything is an expression\n\nIn Haskell there are no statements. At no point you can just say \"do this\" — your program is built as **expressions composed together**. So it gets tricky because `console.log` is very much a statement that you can basically drop anywhere and it works. But we have no such thing in Haskell so how can we work around that?\n\nThe trick is to \"wrap\" the value you want to log with any of the `trace` functions. The idea is that the `trace` function will print something to the console and give back the original value unchanged. This is cool because our expression **evaluates to the same result** whether it's wrapped in a `trace` function or not. In other words, these two implementations of `sum` are equivalent:\n\n```haskell\nsum :: Int -\u003e Int -\u003e Int\nsum a b\n  = a + b\n\nsumWithLog :: Int -\u003e Int -\u003e Int\nsumWithLog a b\n  = (Debug.traceShowId a) + (Debug.traceShowId b)\n```\n\nThey produce exactly the same result. In fact, the type signature for `traceShowId` is\n\n```haskell\ntraceShowId :: Show a =\u003e a -\u003e a\n-- Ignore the `Show a =\u003e` bit, we haven't really seen that yet.\n```\nWhich means that whatever we give it (an `Int` in the `sum` function above) will be given back to us unchanged. Effectively, whether the `trace` function is there or not makes no difference!\n\nLet's look at another example.\n\n```haskell\n{-\n  In JS\n\n  const sumItems = (items) =\u003e {\n    const reducer = (acc, item) =\u003e {\n      console.log(item)\n      return acc + item\n    }\n\n    const sum = items.reduce(reducer, 0)\n    console.log(\"the sum is \" + sum)\n    return sum\n  }\n\n  sumItems([1,2,3,4])\n-}\n\nsumItems :: [Int] -\u003e Int\nsumItems items\n  = Debug.trace (\"the sum is \" ++ show sum) sum\n\n  -- trace :: String -\u003e a -\u003e a\n  -- print the first argument (String) and return the second argument (a)\n  where\n    reducer item acc\n      = acc + (Debug.traceShowId item)\n    sum\n      = foldl reducer 0 items\n```\n\nI appreciate this might look a bit unfamiliar, but if you apply the \"wrapping\" logic it will make sense. As many other things in Haskell, it will take you some time getting used to. The trick is to realize that everything is an expression in Haskell and we have to work with this constraint to fit an extremely imperative concept (\"log this, log that\") into a purely functional language.\n\n\n\n### \u003ca id=\"toc-debug-side-effects\"\u003e\u003c/a\u003eBut... side effects?!\n\nYou might not have realized it but: printing to the console is a _side effect_! How come none of the `trace` functions (all right, excluding `traceIO`) have `IO` in their signature??\n\nThe `putStrLn` function, which is used to print to the console, does correctly express in the type that it's _impure_ and some side effects will occur.\n\n```haskell\nputStrLn :: String -\u003e IO ()\n```\n\nHowever, `trace` functions don't and that's because they are not type safe **on purpose**.\n\nRemember that these functions are meant to be used to debug code, and most of the code we write will hopefully be pure. Let's pretend for a moment that `trace` functions _did_ run in `IO`.\n\n```haskell\nfictionalTrace :: String -\u003e a -\u003e IO a\n\nsum :: Int -\u003e Int -\u003e Int\nsum a b\n  = a + b\n\n-- If we now wanted to log something in the `sum`\n-- function, we would have to modify its signature!\n\nsumWithLog :: Int -\u003e Int -\u003e IO Int\nsumWithLog a b\n  = fictionalTrace (\"the sum is\" ++ show sum) sum\n  where\n    sum = a + b\n```\n\nThis would be nuts. Not only you would have to edit your function everytime you wanted to log something, but all the places _where that function is used_ will need to change as well! You would soon go insane and sure enough just write _all of your functions_ in `IO`, just to avoid the pain.\n\nThat's not the code we want to write! After all, having `IO` everywhere wouldn't be much different than writing Javascript, because we would have lost the ability to **distinguish pure functions from impure ones**.\n\nThe takeaway is: yes, `trace` functions are a bit weird because they have side effects (printing to the console) but they don't run in `IO`. However that's on purpose, to make it more ergonomic for us developers in need of some sweet logging when fixing a bug.\n\nFor proper application logging you wouldn't use `trace` functions, just as much as you wouldn't use `console.log`. But that's a topic for another time.\n\n\n\n\u0026nbsp;\n\n\n# \u003ca id=\"toc-encoding-decoding-json\"\u003e\u003c/a\u003e Encoding and decoding JSON\n\nWe haven't mentioned JSON so far, but we better start looking into it so we can deal with the Real World. Going from JSON (decoding) and to JSON (encoding) in Haskell is not as easy as in Javascript, where everything is untyped. In Javascript we can just `JSON.parse()` and hope for the best, but we get no guarantees as to what we're actually getting back. Indeed, we hardly get any guarantees _at all_ — that's why we're learning Haskell after all!\n\nBefore we go any further, we need to quickly introduce a couple of things: _instances_ and _constraints_.\n\n### \u003ca id=\"toc-interlude-instances\"\u003e\u003c/a\u003e A brief interlude on instances\n\nYou might have seen type definitions followed by `deriving` , for example:\n\n```haskell\ndata Person\n  = Person\n      { name :: String\n      , age :: Int\n      }\n  deriving (Eq)\n\nbob :: Person\nbob = Person \"bob\" 69\n\nalice :: Person\nalice = Person \"alice\" 99\n\n-- We can use the equality operator only because\n-- `Person` has an `Eq` instance\n-- (which we derived through `deriving`)\nsamePerson\n  = if bob == alice\n      then \"Weird, they're the same\"\n      else \"Duh, of course they're different\"\n```\n\nHere, we're defining a new data type `Person`. However, the definition alone gives us a pretty limited type, because we wouldn't even be able to compare (`==`) two different `Person`s by default! So if we want to _enhance_ our type with equality, we need to tell the compiler that we'd like to derive an _instance_ for `Eq`, so that comparing `bob` and `alice` becomes possible.\n\nNote that **the term _instance_ in OOP means something different**. In Haskell, an instance is just a way to describe what a type _is_ or _can do_. In this case, the `Person` type supports equality.\n\nAll you need to understand is that we can derive instances for our types so that we can do more useful things with them.\n\n### \u003ca id=\"toc-interlude-constraints\"\u003e\u003c/a\u003e An even shorter interlude on constraints\n\nInstances alone wouldn't be that interesting — they become very useful when paired with constraints. You can _constraint_ the usage of a function so that a type parameter _must satisfy certain conditions_. Constraints are declared on the left of the fat arrow (`=\u003e`). For example:\n\n```haskell\n-- You can only call this function IF the type has an `Eq` instance and a `Show` instance.\n-- (`Show` is for turning values into Strings.)\ncompareAndPrint :: (Eq a, Show a) =\u003e a -\u003e a -\u003e String\ncompareAndPrint p1 p2\n  = if p1 == p2\n      then \"They are the same person! \" \u003c\u003e (show p1)\n      else \"Not the same person. You gave me\" \u003c\u003e (show p1) \u003c\u003e \" and \" \u003c\u003e (show p2)\n\n-- If we then derive `Show` as well as `Eq`,\n-- the `Person` type will satisfy all the requirements of `compareAndPrint`.\ndata Person\n  = Person\n      { name :: String\n      , age :: Int\n      }\n  deriving (Eq, Show)\n\n-- \u003e\u003e\u003e compareAndPrint bob bob\n-- \"They are the same person! Person {name = \\\"bob\\\", age = 69}\"\n```\n\nAgain, we can't rely on the `Person` type being showable by default, we need to derive a `Show` instance. And here's the super interesting thing: `compareAndPrint` can only work with *types that can be compared and shown*.\n\n`(Eq a, Show a)` are constraints. They are necessary if we want to use `==` and `show` in our functions. An helpful way of looking at constraints is to read the full type signature in English:\n\n```haskell\ncompareAndPrint :: (Eq a, Show a) =\u003e a -\u003e a -\u003e String\n```\n\n\u003e If you want to use `compareAndPrint` , you have to give me two values of type `a`. But `a` can't be _any_ type! I'm looking for a type that has _at least_ (it can have more) instances for `Eq` and `Show`.\n\nIf that's clear, you have all you need to understand how JSON encoding/decoding works!\n\nSide note, types in Prelude have been defined with common instances. So, for example, you could call `compareAndPrint` with `Int` and `String` because both satisfy the `Eq` and `Show` constraints.\n\n```haskell\ncompareAndPrint 1 1\ncompareAndPrint \"yes\" \"no\"\ncompareAndPrint True True\n```\n\n\n\n### \u003ca id=\"toc-fromjson-tojson\"\u003e\u003c/a\u003e `FromJSON` and `ToJSON`\n\nHow do we turn the `Person` type into JSON and how do we read a JSON value back into a `Person`? Easy, we just derive some more instances!\n\nFirst of all, we need to install the `aeson` package (it's the Haskell package to deal with JSON basically). As you know, installing a package means opening `package.yaml`  and adding a new dependency.\n\n```yaml\n# package.yaml\n\ndependencies:\n- aeson # get this bad boy in!\n- base\n- zero-bullshit\n```\n\nNow we can derive `FromJSON` and `ToJSON` on our `Person` type!\n\n```haskell\nimport GHC.Generics (Generic)\nimport qualified Data.Aeson as Aeson\nimport qualified Zero.Server as Server\n\ndata Person\n  = Person\n      { name :: String\n      , age :: Int\n      }\n  deriving (Eq, Generic, Aeson.FromJSON, Aeson.ToJSON)\n\n-- Notice the `Generic` instance.\n-- It's required when you want to derive `FromJSON` and/or `ToJSON`.\n```\n\nDope. How do we use them in practice? There are two functions available in the `Zero.Server` module that you'll end up using quite a lot: `decodeJson` and `jsonResponse`.\n\n\u0026nbsp;\n\n```haskell\njsonResponse :: ToJSON a =\u003e a -\u003e Response\n```\n\n\u003e If you give me a type `a` that can be turned into JSON, then I'll produce a `Response` with the JSON representation of that type.\n\n\u0026nbsp;\n\n```haskell\ndecodeJson :: FromJSON a =\u003e String -\u003e Either String a\n```\n\n\u003e If you give me a type `a` that can be parsed from a `String` (ie. the JSON encoded value), then I'll either give you an error (in case the JSON is invalid) or a value of type `a`.\n\n\n\nIn practice, if our server gets a request with some JSON encoded body, we can try to parse it into the type we expect.\n\n```haskell\nmyHandler :: Server.Request -\u003e Server.Response\nmyHandler req\n  = Server.stringResponse result\n  where\n    body\n      = Server.requestBody req\n    result\n      = case Server.decodeJson body of\n          Left err -\u003e \"Failed to decode request body as a Person. It must be something else\"\n          Right p  -\u003e \"Yay! We have a person named: \" \u003c\u003e (name p)\n```\n\n\u003e **Side note**: where is that `name` function coming from? Recall that when you define a record in Haskell (`Person` in this case), one accessor function per field name gets automatically created as well. `name` and `age` are functions in scope here and we can use them to get values out of a record of type `Person`.\n\nAnd, if we have a `Person` that we want to send as JSON, we can just do that (`jsonResponse` will also set the proper `Content-type` header).\n\n```haskell\nbob :: Person\nbob = Person \"bob\" 69\n\nmyHandler :: Request -\u003e Response\nmyHandler req\n  = jsonResponse bob\n```\n\n\n\nThe first few Zero Bullshit Haskell exercises don't deal with JSON at all, but given that exercises are about writing webservers, then you need to understand how to work with JSON. [Exercise #07](https://alpacaaa.github.io/zero-bullshit-haskell/Ex07) is all about putting what we just discussed into practice. If something isn't clear, join the discussion in the relevant issue!\n\n\n\n\u0026nbsp;\n\n# \u003ca id=\"toc-dealing-state\"\u003e\u003c/a\u003eDealing with mutable state\n\n\nMutable state is one of the most common sources of bugs I regularly faced over the years. In Haskell everything is immutable (which is super nice) but sometimes we have to deal with mutable state nonetheless. How do we do that?\n\n### \u003ca id=\"toc-modeling-state\"\u003e\u003c/a\u003eModeling state the functional way\n\nIn Javascript, we can just define a global variable and get away with it. For example, here's how we could implement a counter that gets increased with every request.\n\n```javascript\nvar mutableCounter = 0\n\napp.post('/increase-counter', (req, res) =\u003e {\n  mutableCounter += 1\n  res.send('Current count: ' + mutableCounter)\n})\n```\n\nWe could still define a _global_ `mutableCounter` value in Haskell, but there wouldn't be any way to update it.\n\n```haskell\nmutableCounter :: Int\nmutableCounter = 0\n\ncounterHandler :: Server.Request -\u003e Server.Response\ncounterHandler _ = ???\n-- mutableCounter = mutableCounter + 1\n-- No way the compiler would allow that!\n```\n\nOk fine, GHC always has to ruin the party. Let's try to model a change of state as a function then. So far, our `counterHandler`  just takes a `Request` and returns a `Response`. A good first step would be adding the current counter value as an input to the function, so that we can use it!\n\n```haskell\ncounterHandler :: Int -\u003e Server.Request -\u003e Server.Response\ncounterHandler currentCount _\n  = Server.stringResponse $ \"Current count: \" ++ show (currentCount + 1)\n```\n\nThis is fine, but `currentCount` is still an immutable value, so we can't do anything with it. Sure we can now return the current value to the client, but we can't update it.\n\nWhat if we could let the server **manage state for us** instead?\nRight now, our handler only returns a `Response`, but there's no reason why we couldn't return _extra information_. In fact, we can tell the server that along with the `Response`, we now also return an updated version of the state! The server will _somehow_ take that new value and do the actual mutation, we're not concerned with the details. The impure state update is hidden in library code and we can keep on writing nice pure functions. Turns out we don't really need mutable variables after all. :)\n\n```haskell\ncounterHandler :: Int -\u003e Server.Request -\u003e (Int, Server.Response)\ncounterHandler currentCount _\n  = (newCount, response)\n  where\n    newCount\n      = currentCount + 1\n    response\n      = Server.stringResponse (\"Current count: \" ++ show newCount)\n```\n\n\u003e Note how we now return a _tuple_ with two elements (the new state, the `Response`) instead of just the `Response`.\n\nThis is fantastic. All of our values are still immutable, we're just using **functions to model state updates**. The great thing about this is that `counterHandler` is still **super easy to test**. You feed it some state and check if the state you get out is the correct one. Have you ever written a test for a function that dealt with global mutable state (like in the JS example above)? I did, and then proceeded to learn Haskell.\n\n\n### \u003ca id=\"toc-detour-otp\"\u003e\u003c/a\u003eA slight detour in OTP land\n\n[OTP](http://erlang.org/doc/system_architecture_intro/sys_arch_intro.html) is the set of indispensable libraries that you use to build Erlang/Elixir programs. Erlang is a functional language with immutable variables and, as you can imagine, wouldn't allow you to mutate any state.\n\nOne of the core abstraction in Erlang is the `gen_server`. A `gen_server` allows you to model a server by providing _callbacks_ that hook into the server lifecycle, so that you can implement your logic there.\n\nHere's how the callback to handle requests looks like:\n\n```erlang\n handle_call(Request, From, State)\n```\n\nIt takes a `Request`, the process id of who's making the request and the **current state** of the server. What do you think the return value is?\n\n```erlang\n{reply, Reply, State1}\n```\n\nYou guessed it, once again the trusty tuple!\n\nAlong with the Response (`Reply` in this case) we also return an updated version of the state, here referred to as `State1` to indicate it's going to be different than `State`.\n\n\n\n### \u003ca id=\"toc-runtime-handle\"\u003e\u003c/a\u003eLet the runtime handle state for you\n\nWe almost have all we need to build stateful request handlers. Let's drop some type signatures to understand what we're missing. In the `zero-bullshit` package, we create stateful handlers with the function `statefulHandler`.\n\n```haskell\nsimpleHandler\n  :: Method\n  -\u003e String\n  -\u003e (Request -\u003e Response)\n  -\u003e Handler\n\nstatefulHandler\n  :: Method\n  -\u003e String\n  -\u003e (state -\u003e Request -\u003e (state, Response))\n  -\u003e StatefulHandler state\n```\n\nThis signature is similar to the familiar `simpleHandler` that we've used up until now. We know what `Method` and `String` are, so let's ignore those. The callback is interesting though (the function between parens). Note also how the return types are different. We wouldn't be able to pass a `StatefulHandler` directly to `startServer`, which only accepts `Handler`.\n\nLet's bring back `counterHandler` and compare it to the callback we have here.\n\n```haskell\n                 (state -\u003e Request -\u003e (state, Response))\ncounterHandler :: Int   -\u003e Request -\u003e (Int,   Response)\n```\n\nWait, it's exactly the same thing! `state` is what we call a _type variable_. We have seen a few of those already, when decoding JSON for example. We could have used `a` instead of `state` and it would have worked just the same, but it's good to be explicit with type variables when you can.\n\nSo `state` is a type, any type for that matter, it's not _constrained_ in any way so you can use whatever you want (we're using an `Int` here).\n\nThe last bit is `StatefulHandler state`. You might be wondering why `state` is there. Why can't our type just be `StatefulHandler`? The reason is we want to track the type of `state` an handler is compatible with. This is to **make sure we don't mix handlers** that work with `Int`s with handlers that work with `String`s for example. If it's still confusing, keep on reading and remember that `state` is just a type variable (meaning it can be replaced by `Int`, `String`, `Person` whichever type you want).\n\n---\n\nHow do we get an `Handler` from a `StatefulHandler state` so that we can pass it to `startServer`? We use `handlersWithState`.\n\n```haskell\nhandlersWithState :: state -\u003e [StatefulHandler state] -\u003e Handler\n```\n\nThe server needs to know what the initial state is going to be, so that's the first argument we need to pass to `handlersWithState`. And then we can just pass a list of `StatefulHandler` provided that **they all work with the same `state` type**. That's why we need the type variable!\n\n\u003e `handlersWithState` is a _polymorphic_ function. It works with any type of `state`. Do you want `Int`? That's fine. Want `String`? Fine as well. But it **must match** the type of the `StatefulHandler`s you provide.\n\u003e\n\u003e We can use type variables in type constructors to carry _extra information_ at the type level. In this case, `StatefulHandler state` carries more information than just `StatefulHandler` because we can talk about the `state` as well. The better you model your types, the more guarantees you get at compile time. You're _reducing_ the number of invalid programs that the compiler will accept and get free errors (at compile time) when your types don't line up.\n\u003e\n\u003e That's where a type system is incredibly useful. If our program doesn't make sense at the type level, we're not going to be able to compile it and have bugs sneak into production code.\n\nPutting it all together, we can use our `counterHandler` like this:\n\n```haskell\ninitialState :: Int\ninitialState = 0\n\ncounterHandler :: Int -\u003e Server.Request -\u003e (Int, Server.Response)\ncounterHandler currentCount\n  = (newCount, response)\n  where\n    newCount\n      = currentCount + 1\n    response\n      = Server.simpleHandler (\"Current count: \" ++ show newCount)\n\nmain :: IO ()\nmain\n  = Server.startServer\n      [ Server.handlersWithState initialState [counterHandler]\n      ]\n```\n\nThis is how we **model state in a functional way**.\n\nAt the end of the day, there's still going to be some mutable variable that gets updated under the hood, but the benefit of using this approach is we only get to deal with the **nice api on top**. When writing a `gen_server` in Erlang you don't have to think about how to manage state, everything is taken care of, just supply a callback. The same goes for the `zero-bullshit` server, you just have to pick a `state` of your liking, handle the `Request` and return an updated version of the `state` together with a `Response`.\n\n---\n\n# \u003ca id=\"toc-hackage-stackage\"\u003e\u003c/a\u003eInstall libraries from Hackage/Stackage\n\nHaskell can be confusing. Or rather, the Haskell ecosystem can get pretty confusing. Turns out installing a library is super easy nonetheless, but it's worth going through it real quick.\n\n### \u003ca id=\"toc-hackage\"\u003e\u003c/a\u003eHackage\n\nIn the beginning, it was Hackage. Hackage is just a website where users upload packages, pretty much as you would do with npmjs.com. You then list your dependencies in `package.yml` (which, you guessed it, is basically `package.json` in node.js) and you're good to go.\n\n### \u003ca id=\"toc-fun-times\"\u003e\u003c/a\u003eFun times\n\nHave you ever looked inside the `node_modules` folder?\n\nIn node.js, dependencies that have other dependencies are installed _within_ the dependency. You end up with `node_modules` nested into other `node_modules`. That's because a dependency can exist at **different** versions even in the **same** project.\n\nWhether that's good or not I'll leave it to you to decide (hint, it's a bit shit). The important bit is that this is not how GHC works. GHC expects a specific package to exist only **once** at a **fixed** version (within the same project). This means that if two packages share a dependency, they'll have to agree on which version to use.\n\nNow imagine 10 or 15 packages all depending on the same package, but with different version constraints. Dependency resolution becomes impossible. Welcome to *cabal hell*.\n\n### \u003ca id=\"toc-stackage\"\u003e\u003c/a\u003eStackage\n\nRemember how we talked about the nice people behind the build tool `stack` — well, who could have possibly invented *stackage*?\n\nThat's right, it's them striking again. (shout out FP Complete).\n\nTheir solution to the dependency problem (which, by the way, is in no way specific to Haskell, other languages have exactly the same problem) was to _curate_ a set of packages that are known to work together and put them into a package set.\n\nIn other words, the job of working out which packages build together (and most importantly, at which version) is taken care of.\n\nThis is massively useful! It means when you need to install a package, you just open `package.yml` and add a new entry under the `dependencies` field **without** pinning a specific version (unless you have a very good reason to).\n\n### \u003ca id=\"toc-packages-not-stackage\"\u003e\u003c/a\u003ePackages that are not on Stackage\n\nFirst of all, is Hackage deprecated? Absolutely not, Hackage is still the source for all the packages in Stackage. And I'm just used to the way documentation looks on the Hackage website that I spend pretty much all of my time there and zero time on the Stackage one.\n\nBut it may happen that a package on Hackage is not present on Stackage. In fact, a lot of packages (either forgotten, never updated or not deemed production ready) can be found on Hackage but never make it to Stackage. It could also happen that a package was at some point in a Stackage package set, but because it failed to build or couldn't be maintained anymore, it got dropped in a more recent package set.\n\nIn a nutshell, you should be able to find everything you need, especially in the beginning. When you'll find yourself needing a package that's only available on Hackage, `stack` will warn you and will provide instructions on how to tweak your `stack.yml` file accordingly. It's super painless and working with a package set is really really nice.\n","funding_links":[],"categories":["Haskell","\u003ca name=\"Haskell\"\u003e\u003c/a\u003eHaskell"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falpacaaa%2Fzero-bs-haskell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falpacaaa%2Fzero-bs-haskell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falpacaaa%2Fzero-bs-haskell/lists"}