deft/HWP.org
2018-01-16 22:14:50 +01:00

34 KiB
Raw Blame History

Haskell for the working programmer

THIS IS A WORK IN PROGRESS

TODO Introduction

This is somehow a follow-up from Learn Haskell Fast and Hard. Which was more about being able to play with Haskell than to work with it.

This is also an experiment. I'm not sure if it will be as positive as I hope. This book try to be a good resource to learn Haskell but to speed up the learning in the first part I'll skip the explanation about why Haskell does things the way it does. As a consequence if you don't keep in mind that there is very good reasons to make some things way more difficult in Haskell than in other languages you might miss the real reason.

Also don't forget in the beginning you might only see what is more difficult or harder to achieve in Haskell. But for each thing harder keep in mind that there are very difficult things in other languages that are solved extremely easily in Haskell. And I personnally believe the things Haskell make easier are essential to reach the best balance between speed, elegance, safety and pragmatism with regards to any programming language I ever used before.

So this book might be a bit raw. And in fact not really "fun" unfortunately. But it should be efficient.

This book is aimed to be one of the fastest way to learn how to be productive with Haskell.

Know that there still will be a very long road ahead once this book will be finished to master Haskell. That should be ok.

Modern computing has unfortunately less to do with algorithmic than to create a mashup of libs and external APIs. So while learning all the details of Haskell can seems like an impossible challenge. Learning the necessary skills to be productive shouldn't be that hard.

What does this book will talk about.

  1. Having a clean and stable dev environment
  2. Basic Introduction to the language
  3. Professional Project developement workflow
  4. Make command line program
  5. Use external libraries
  6. Handle the filesystem
  7. Handle a few DBs
  8. Make a basic REST API

What does "working programmer" stand for?

Being able to:

  • create a new working program from scratch,
  • work with the filesystem (read/write files/directories),
  • work with BDD (SQLite, PostgresSQL, MongoDB, etc…),
  • work with network (send/receive HTTP request),
  • make a REST API,
  • write test for your application,
  • to deploy your application

This is more about being an user, consumer from the Haskell community than being an active contributor. Hopefully the gap won't be hard to pass from user to contributor. So I'll write a minimal chapter about how to write your own library and publish it for other developpers.

Prerequiste

The target audience I'm writting this book for is software developpers.

You should:

  • be familiar with some programming language,
  • be familiar with command line in a shell,
  • know how to editing text files (I try to focus on generic editors like emacs, vim, etc…),
  • know the basic usage of git

If you don't know that, your journey with this book might be a bit difficult but I'll do my best to not make it impossible.

Opinionated

Keep in mind that Haskell has a very active and open ecosystem. And the language itself let you make very different choices to the fundamentals.

This book is very opinionated, because I wanted to be efficient in learning fast for some specific kind of personalities.

It might not be for you. One of my goal is to shortcicuit some classic learning detour.

For a lot of decisions I generally make only one choice. I'll try to talk about the other choices and it will be your duty to explore other choices after you completed this book to decide which is the one that has your preference.

Also note that this book was written in the past. And as I said Haskell ecosystem evolve very fast. And some choices which are an evidence today might be deprecated in a few months from now.

Typically there are many different and concurrent web frameworks, db libs, etc..

TODO A Word about Haskell philosophy

One Haskell main characteristic is that it tends to make the right/most secure choice by default.

A very simple example is that it is generally harder to write unsafe code than to write safe and pure code.

Also one of the reason I think Haskell is percieved as hard to learn by many people is that you generally need to ingest a lot of concepts before being able to be productive.

Install a dev environment (about 30 minutes)

Installing a dev environment should hopefully be the most boring part of this book. But this is a necessary price to pay to really get why Haskell is considered so great by people using it.

Working environment

A thing to note is the distinction between learning a language for personal interrest for some personal project and learning with the goal to achieve a "product" with some hard deadline.

So for example, it can be nice to understand the language by playing inside a REPL. That will be very almost not used in this book as the goal is not to really gain a deeper knowledge but perhaps to be able to "use" the language.

The problem I try to solve in this book is to make you a professional "user" of Haskell more than a "contributor" to Haskell. While I encourage everybody to gain deeper understanding on the internals of Haskell this is not the primary goal of this book.

What I mean by professional "user" is that you should have the following features at your disposal:

  • DCVS
  • Generated documentation
  • Tests (Unit tests, Generative tests, Integration tests, etc…)
  • Benchmark
  • Continuous Integration
  • Continuous Deployment

Choices:

  • Raw: get GHC and cabal exectuable and work with that. Too long and manual
  • Nix: this is really great because it's like a super make that can deal with external dependencies. Certainly the best best in the long term.
  • Stack: fast to install focused on being user friendly. Has a lot of easy to use features like:

    • integration with docker that will make it easy to cross-compile.
    • integration with nix
    • easy to deal with many private repositories
    • good professional starting templates

Stack

I recommend stack. But there are many different method to install Haskell. Stack should be simple and straight to the point.

If thing haven't changed sint the book is written it could be installed with:

curl -sSL https://get.haskellstack.org/ | sh

git

You should have git installed.

Stack template

Before starting to write your first line of code. Let's create a project with a sane and modern file organisation.

I made a stack templates largely inspired by tasty-travis template. It will provide a bootstrap for organizing your application with tests, benchmarks and continuous integration.

This template provide a file organisation for your projects.

Mainly do jump into programmin you could theoretically just download the binary of the main Haskell compiler GHC to your compiler and compile each file with ghc myfile.hs. But let's face it. It's not suitable for real project which need more informations about it.

So let's start with a sane professional organisation for your files.

stack new myproject https://git.io/vbpej

After that, this should generate a new `myproject` directory with the following files:

> tree
.
├── CHANGELOG.md
├── LICENSE
├── README.md
├── Setup.hs
├── myproject.cabal
├── package.yaml
├── src
│   └── Lib.hs
├── src-benchmark
│   └── Main.hs
├── src-doctest
│   └── Main.hs
├── src-exe
│   └── Main.hs
├── src-test
│   └── Main.hs
├── stack.yaml
└── tutorial.md

5 directories, 13 files

Most of your source code should be in the src directory. Generally src-exe should be a minimal code that could handle the main function to start your application. We'll talk about other part later in the book but most other file should be quite straightforward.

Editor

You should check any of the supported editor here:

https://github.com/rainbyte/haskell-ide-chart#the-chart-with-a-link-to-each-plug-in

I personnaly use spacemacs with the haskell layer because it comes with battery included. If you're not used to vim keybindings I believe it is easy to switch to more classical editor keybindings easily.

Even if I don't have a strong opinion on the editor you should choose. It should at least be easy to support the Haskell tooling, like intero or ghc-mod. Because it's one of the best part of Haskell.

For example without any configuration I have the following features:

  • I see errors, warn and even code hints while I'm typing my code.
  • very good code completion
  • auto styling of my source code and be able to change the style of my entire buffer
  • be able to get the type of the expression under my cursor
  • be able to add the type of a top level declaration
  • be able to launch a repl easily loading the current code of the file I'm currently editing

And many other nice features.

Note that in the past I had some problem with ghc-mod during upgrades while intero was mostly a no problem story.

It is also useful to have hoogle and hayoo, which are search engine focused on Haskell.

Spacemacs

So if you want to choose spacemacs:

  1. Install a recent emacs
  2. git clone https://github.com/syl20bnr/spacemacs ~/.emacs.d
  3. Launch emacs
  4. Edit your ~/.spacemacs file to add to the layer list:
  haskell
  (auto-completion :variables
                      auto-completion-enable-help-tooltip t
                      auto-completion-enable-short-usage t)

If you're not used to vim keybinding and it is too much to handle for you. I think you can change the value of dotspacemacs-editing-style from 'vim to 'hybrid or 'emacs in the .spacemacs file.

It should be good now.

Conclusion

First you can congratulate yourself to have installed all the prerequiste to have a great working development environment.

I know it was already a lot of boring tasks to perform before being able to write any line of code. But I promise it will be worth it. Going this route you'll short circuited a lot of detours.

By starting with this template, you won't use the classic prelude. It's quite a strong opinionated move. Because many classic function will be overwritten by safer/more generic one.

So be prepared that the actual learning route is jumping other classical learning steps you can find in other learning resources. Don't worry I'll do my best to make the jump as natural as possible.

TODO Working like in any other language / Learning with examples

We'll start by example first and all notion will be introduced as they appear. If you find confident you could feel free to skip some descriptions and explanations.

Basics Project 1: Guessing Game

Init the project

☞ As a first projet a lot of new concept will be introduced. Don't be discouraged by that.

> stack new guess https://git.io/vbpej
> cd guess

Edit the file src-exe/Main.hs

The file contains:

import Protolude

import Lib (inc)

main :: IO ()
main = print (inc 41)

To compile it do a

> stack build
> stack exec -- guess-exe
42

So that program print 42 and stop.

Let's go line by line about what is occuring here.

  • import Protolude: Haskell is a language, but you need to start with some useful definition for you. And Protolude is a prelude. That mean that it provides a list of useful definitions for you. By default you don't need to import manually the default prelude. But the more you work with Haskell the more you feel the need to write your own prelude. Protolude is a sane professional starting point.
  • import Lib (inc): That line means that we import the function inc from another module named Lib The module correspond to the file src/Lib.hs. No need to look into it now.
  • main :: IO (): This is the declaration of the type of the main function. The main function will be the function called when you launch your application. Excatly like in C. The type is IO (). It means that main will make some interaction with the system and will return nothing.
  • main = print . inc $ (41 :: Int)

For that line there are in fact a lot of things going on.

We define the function main. Lets compare this notation with other programming languages:

C:

void main () {
  ...
}

javascript:

function main() {
    ...
}

python:

def main:
    ...

LISP:

(define foo () ...)

Clojure:

(defn main [] ...)

Now take a look at the content:

main = print (inc 41)

☞ A very important syntax detail; function application is done with a simple space. So foo bar means you apply the function foo to the parameter bar. And by default the priority is on the left. So: foo bar baz is equivalent to (foo bar) baz.

While that notation is quite simpler it can take some time to be used to it and to parse it naturally.

So here we first call inc on 41 wich is 42. Then we print it to the standard output.

And that's it.

Print and read things

Now let's modify the code of main to print things. First comment the import line for Lib. Haskell comment are -- till the end of the line or {- .... -} for multiline comments. Without this comment you'll get a warning that this import is unused. And by default we compile using -Werror flag to GHC which tell that the compilation should fail also on warnings as well as on errors.

The default template tend to be a professional environment and has more restrictions in order to maximize confidence in quality.

import Protolude

-- import Lib (inc)

main = putText "Hello, world!"

Simple and natural. Now let's ask your name.

main = do
  putText "What is your name?"
  name <- getLine
  putText ("Hello " <> name <> "!")

We can try that in the REPL (GHCI). You shold be able to start it from your editor. For example in spacemacs I can load the current buffer (open file) in the REPL with SPC m s b.

You could also start the repl in a terminal with stack ghci And then load the module with :l src-exe/Main. The :l is a shortcut for :load.

> main
What is your name?
Yann
Hello Yann!

OK simple enough.

But let's take a moment to understand a bit more what's going on.

We started with the do keyword. It's a syntactical sugar that helps in combining multiple lines easily. Let's take a look at the type of each part.

putText :: Text -> IO ()

It means that putText is a function that take a Text as parameter and return an IO (). Mainly IO () simply means, it will return () (nothing) while doing some IO or border effect. The border effect here being, writing the text to the standard output.

putText "What is your name?" :: IO ()

So yes this line make an IO but returns nothing significant.

name <- getLine

The function getLine will read from standard input and provide the line read and send the value as a Text. If you look at the type of getLine you have:

getLine :: IO Text

And that means that to be able to retrieve and manipulate the Text returned by in an "IO context" you can use the <- notation. So in the code the type of name is Text

More generally if foo :: IO a then when you write

do
  x <- foo :: IO a

Then the type of x is a.

Finally the last line:

  putText ("Hello " <> name <> "!")

putText take a Text as argument so: ("Hello " <> name <> "!") :: Text.

So (<>) is the infix operator equivalent to the function mappend. Here are equivalent way to write the same thing:

"Hello" <> name <> "!"
"Hello" `mappend` name `mappend` "!"

mappend "Hello" (mappend name "!")
(<>) "Hello" ((<>) name "!")

So in Haskell if your function contains chars it will be a prefix function. If your function contains special chars then it is considered to be an infix operator.

You can use your function as infix if you put "`" around it name. And you can make your operator prefix if you put it inside parentheses.

So you should have remarqued a pattern here. Which is really important. Each line of a do bloc has a type of IO a.

main = do
  putText "What is your name?"      :: IO ()
  name <- getLine                   :: IO Text
  putText ("Hello " <> name <> "!") :: IO ()

So whenever you have an error message try to think about the type of your expression.

Another very important aspect to notice. The type of "Hello " <> name <> "!" is Text not IO Text. This is because this expression can be evaluated purely. Without any side effect.

Here we see a clear distinction between a pure part of our code and the impure part.

/yogsototh/deft/media/commit/48b47dd096f121cfbda2d8ff903131e99cfb238a/HWP/reflexion.jpg

☞ Pure vs Impure (function vs procedure)

That is one of the major difference between Haskell and other languages. Haskell provide a list of function that are considered to have border effects. Those functions are given a type of the form IO a.

And the type system will restrict the way you can manipulate function with type IO a.

So, first thing that might be counter intuitive. If an expression has a type of IO a it means that we potentially perform a side effect and we "return" something of type a.

And we don't want to ever perform a side effect while doing any pure evaluation. This is why you can't write something like:

-- DOESN'T COMPILE
main = do
   name = getLine
   putText ("Hello " <> name <> "!")

Because you need to "traverse" the IO barrier to get back the value after the evaluation. This is why you NEED to use the <- notation. Now knowing if a code is potentially making any side effect is explicit.

Strings in Haskell digression

Generally working with string is something you do at the beginning of learning a programming language. It is straightforward. In Haskell you have many different choices when dealing with Strings depending on the context. But let just say that 95% of the time, you'll want to use Text.

Here are all the possible choices:

  • String: Just a list of Char very inefficient representation,
  • Text: UTF-16 strings can be Lazy or Strict,
  • Bytestring: Raw stream of Char and also Lazy.Bytestring.

That is already 5 different choices. In Foundation the strings are UTF-8.

Mmmm so much choices.

So to make it clear, in general, don't use String for anything serious. Use Text most of the time. Use Bytestring if you need efficient bytes arrays.

Write a guess my age program

So far so good. But the logic part of the code should be in a library in src/ directory. Because this part is easier to test.

The src-exe/Main.hs should be very minimalist, so now let's change its content by:

import Protolude

import Guess (guess)

main :: IO ()
main = do
  guess
  putText "Thanks for playing!"

Now we need to create the file src/Guess.hs which should declare the function guess. Let's start with this content:

module Guess
  ( guess
  ) where

import Protolude

guess :: IO ()
guess = undefined

We declare a Guess module which use Protolude. We know that the type of guess must be IO (). We don't know yet what the code will be so I just used undefined. This way the program will be able to typecheck.

So here is the program that will try to guess your age:

guess :: IO ()
guess = guessBetween 0 120

guessBetween :: Integer -> Integer -> IO ()
guessBetween minAge maxAge = do
  let age = (maxAge + minAge) `div` 2
  if minAge == maxAge
    then putText ("You are " <> show minAge)
    else do
      putText ("Are you younger than " <> show age <> "?")
      answer <- getLine
      case answer of
        "y" -> guessBetween minAge (age - 1)
        _ ->  guessBetween (if age == minAge then age + 1 else age) maxAge

So going from there we declared the guess function to call the guessBetween function with the two paramters 0 and 120 to guess an age between 0 and 120.

And the main function is a classic recursive function. We ask for each age if the user is younger than some age.

the let keyword permit to introduce pure values in between IO ones. so age = (maxAge + minAge) `div` 2 is mostly straightforward. Note that we manipulate Integer and so that mean `div` is the integer division. so 3 `div` 2 = 1=.

We see that working in IO you can put print statements in the middle of your code. First remark we used a recursive function. In most imperative programming languages explicit loops are preferred to recursive functions for efficiency reasons. That shouldn't be the case in Haskell.

In Haskell recursive functions are the natural way to program things.

Important Remarks to note:

  • to test equality we use the (=)= operator.
  • Haskell is lazy, so the age value is only computed if needed. So if you are in the case where minAge = maxAge=, age value is not evaluated.
  • In Haskell if .. then .. else .. form always have an else body. There is no Implicit "no result" value in Haskell. Each expression need to return something explicitely. Even if it is the empty tuple ().

So now here we go:

> stack build
> stack exec -- guess-exe
Are you younger than 60?
y
Are you younger than 29?
n
Are you younger than 44?
y
Are you younger than 36?
n
Are you younger than 39?
n
Are you younger than 41?
y
Are you younger than 39?
n
You are 40
Bye!

We see we can still make the program better. For example, the same question is asked twice in that example. Still, it works.

TODO Use External Library Project 1 : use random numbers

Let's write another slightly more complex example. Instead of guessing the age of somebody. This will be the role of the user to guess a random number choosen by the program.

First we'll need to generate random numbers. To that end we'll use a the random package as a new dependency.

In the file package.yml add random under the dependencies.

dependencies:
- base >=4.8 && <5
- protolude
- random

That will tell stack to download the random package. You can get more information either on hackage or on stackage:

Hackage is the official place where to put Haskell public libraries. Stackage works in conjunction with stack and mainly it takes care of having a list of packages version working together. So that means that all packages in an LTS (Long Term Support) release can work together withoung any conflict.

Now let's use that package. We'll add a new function in the Guess.hs file now it should looks like:

module Guess
  ( guess
  , guessNumber
  ) where

import Protolude

import System.Random (randomRIO)

...

-- | Choose a random number and ask the user to find it.
guessNumber :: IO ()
guessNumber = do
  n <- randomRIO (0::Int,100)
  putText "I've choosen a number bettween 0 and 100"
  putText "Can you guess which number it was?"
  guessNum 0 n

-- | Given a number of try the user already made and the number to find
-- ask the user to find it.
guessNum :: Int -> Int -> IO ()
guessNum nbTry nbToFound = undefined

So for now we just focus on how to get a random number:

  do
    n <- randomRIO (0::Int,100)
    -- do stuff with n

You NEED to use the <- notation inside a do bloc. If you try to use let n = randomRIO (0::Int,100) it will fail because the types won't match.

And that's it!

Now to write the guessNum function, we'll write a classical recursive function:

-- | Given a number of try the user already made and the number to find
-- ask the user to find it.
guessNum :: Int -> Int -> IO ()
guessNum nbTry nbToFound = do
  putText "What is your guess?"
  answer <- getLine
  let guessedNumber = readMaybe (toS answer)
  case guessedNumber of
    Nothing -> putText "Please enter a number"
    Just n ->
      if n == nbToFound
        then putText ("You found it in " <> show (nbTry + 1) <> " tries.")
        else do
          if n < nbToFound
          then putText "Your answer is too low, try a higher number"
          else putText "Your answer is too high, try a lower number"
          guessNum (nbTry + 1) nbToFound

If you want to test it, change the src-exe/Main.hs file to use guessNumber instead of guess.

What did we learn so far?

So up until now, if you followed. You should be able to "reproduce" and make minimal changes. But I am certain than it still be difficult to make some changes. It is time to learn some general principles. I know it might be a bit repetitive but its important to be certain to ingest those informations.

A generic function of type IO () typically main should look like:

f :: IO a
f = do
    α <- f1
    β <- f2
    γ <- f3
    δ <- f4
    f5

where each expression fi is of type IO a for some a. You can use any value α, β, etc‥ as a parameter. In order to be valid. The last expression must have the same type as f. so here f5 :: IO a.

Now if I give you the following functions:

~getLine
IO Text~ that read a line from stdin.
~putText
Text -> IO ()~ that read a line from stdin.

With that you have the ability to read stdin and print things.

if τ then f1 else f2 where =τ
Bool= and the type of f1 and f2 must be the same. Generally this is denoted by: :type f1 ~ :type f2 and that type will be the same as the entire if ‥ then ‥ else ‥ expression.
?
you can compare things that can be compared with <, <=, >, >=, ==, /= (different).
?
you can concatenate things that could be concatenated (like Text) with <>
?
you can transform things as Text with show in particular numbers.

So that is a few number of component but they are all composable. And so far we only needed that to write our first programs.

Haskell libs will provide you with a lot more base functions but also a lot more composition functions.

TODO Command Line Application

TODO File Access

TODO DB Access

TODO REST API

TODO Conclusion

Congratulation for going this far. Now you should be able to work in Haskell at least as well as in any other programming language.

Now there are different directions:

  • learning more libraries
  • learn to optimise code to make it as fast as C
  • learn to understand details of the compilation and Haskell
  • learn tips and tricks
  • learn more about abstractions and type classes
  • learn parallel and concurrent programming
  • learn to deploy like a pro using nix

The order in which to learn all thoses things can be very different for everty need.

TODO Most common next steps

TODO Enhance reproductibility with docker

TODO Enhance reproductibility with nix

TODO How to deploy?

There are plenty of ways de deploy

Trashy and easy

Compile in docker and copy the binary.

With nix and nixops

TODO Code organisation

No organisation, everything in IO

TODO Lenses

This will only be an introduction for being an user of the library.

TODO Generics and lens-generic

TODO Common Type Classes

Monoid

Functors

Applicative

Monads

Arrows

TODO Monads Transformers

TODO MTL

TODO Dhall

Appendices

TODO The syntax

Let's put that behind us ASAP. Syntax is really the thing most people focus about when learning a new programming language.

With more experience, I find that its most of the time totally irrelevant. And the real interrest of a new programming language isn't about the syntax. Otherwise all programming languages would look either like LISP or Ruby.

TODO Copy from my article Learn Haskell Fast & Hard

  • Basic: spaces are meaningful like in Python.
  • Variables are like math variables. They are immutables.
  • Function definition, lack of parenthesis is one of the thing that make it the most specific and hard to adapt. f x y = x This is why I'll try to use more parenthesis than in "real world code".
  • Functions are first class (can be parameters like any other variables).
  • Curring can also be surprising but you should understand that as the ability to reach a higher level of abstraction.

VERY IMPORTANT PART! Typing Notation

So that will be VERY VERY IMPORTANT to be able to work with Haskell efficiently.

One of the central Haskell property is to try to help you, the developer, to write checks and constraints on your code while you write it. That way of writing code take some time to really be used to.

So here we go:

Basic Types

A type is a way of "labelling" an expression by providing some constraint on it. The most basic types are the types you might certainly be used to.

  • Bool: this type has only two possible values; True and False.
  • Char: a 8 bits char
  • Numbers (There are many of them)

    • Int: classical integer with min and max depending on your machine properties
    • Word: unsigned integral type with the same size as Int
    • Integer: unbounded integer representation
    • Float: single precision floating point
    • Double: double precision floating point

There is also another interresting type: Unit that is denoted (). Bool is inhabited by True and False, () is inhabited only by the value ().

It is a bit difficult but () denote at the same time a type when it is written in a context where we deal with types and as a value when the context make it clear we wait a value.

When you read Haskell code some part are about types and others are about values.

foo :: Int -- after the :: these are types
foo = 42 -- this is about values
Type Composition

One interresting thing to think about is that for each value we associate a type. But types themselves are categorized. And we use kind for that.

A kind is to a type what a type is to a value.

So all basic types are of kind *.

> stack ghci
...
Prelude> :t 'a'
'a' :: Char
Prelude> :k Char
Char :: *

Now you should imagine where this is going. Like functions, types can take another types as variables. So types can compose.

Basic types that help composes:

list: =[]
  • -> *=

    =[Char]
    *=
    =[Int]
    *=
tuples: =(,)
  • -> * -> *=

    =(,) Char
    • -> *=
    =(Char,Int)
    *=

One very important thing to note is that that functions can only be from type of kind * to type of kind *.

function: =(->)
TYPE q -> TYPE r -> *=
Custom Data Type / Records

So now:

  type Foo = Bool -- type synonym
  data Bar = BarConstr Int Char
  -- Bar is the type
  -- BarConstr is the type construction, it's a function of type: Int -> Char -> Bar
  -- :kind Bar :: *
  -- :kind BarConstr <-- ERROR, this is not a type
  data Baz a = BazConstr Char a
  -- :kind Baz :: * -> *
  -- :kind BazConstr <-- ERROR, a constructor is not a type

Standard library / Prelude / API

One of Haskell strength is that it is about composability. So in general you can achieve your goal by playing lego.

It is a lot like a UNIX shell in its spirit. Instead of having a big stand alone application that does a lot of things. You'll have a lot of small atomic functions you can use to construct a bigger one.

While the absolute minimum amount of function needed to build every other one can be small. In reallity a lot of intermediate functions are already at your disposal.

Bool
Numbers
Strings
Containers
List
Generic, Foldable
Useful Abstraction
Monoid

We can merge values

Functor
Applicative
Monad
Foldable

We can "fold" a list of values fold :: Monoid m => t m -> m

Traversable

sequenceA :: f (t a) -> t (f a) Example: [Maybe a] => Maybe [a] traverse :: (a -> f b) -> t a -> f (t b) Example:

TODO IO

If you know another popular programming language you probably aren't aware that you code "in" IO. What I mean by that is that you can write a print statement anywhere in your code and it will be executed when the program evaluate that line. This is generally the first method used in debugging or during development to understand what's going on.

So Haskell is slightly different in this regard. In Haskell there are places where you'll be able to add the same kind of print statements. But in some other places, it will be forbidden.

Example:

pureadd x y = x + y
ioAdd x y = do
  print x
  print y
  print (x+y)
  return (x+y)

So this is not much different than in Python for example:

>>> def add (x,y):
...  print x
...  print y
...  print (x+y)
...  return x+y
>>> add(3,4)
3
4
7
7

But one huge difference is the type inferred will be different:

pureadd :: Num a => a -> a -> a
ioAdd :: Num a => a -> a -> {-hi-}IO{-/hi-} a

The consequence is that you will only be allowed to use ioAdd in function whose type is also IO * for some value of *.

circonference :: Int -> Int -> Int
circonference height width = pureadd (2 * height) (2 * width) -- OK

circonferenceIO :: Int -> Int -> Int
circonferenceIO height width = ioAdd (2 * height) (2 * width) -- WON'T COMPILE

To fix it you could simply change the type of the calling function:

circonferenceIO :: Int -> Int -> IO Int
circonferenceIO height width = ioAdd (2 * height) (2 * width) -- OK

Now, I think, that's it. With that understandment, you should now be able to do usefull thing with Haskell.

The why is it this way? Why adding that layer of complexity? Just follow me, the answers will come in time.

Choices

Working environment