hwp-book/6__1_Beginner.org
Yann Esposito (Yogsototh) 44e1b7805c
initial commit
2018-06-20 22:11:04 +02:00

6.6 KiB

Haskell for the working programmer

Beginner Level

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.

TO-CLEAN 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:

TO-CLEAN 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
TO-CLEAN 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 -> *=
TO-CLEAN 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

TO-CLEAN 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.

TODO Bool
TODO Numbers
TODO Strings
TODO Containers
TODO List
TODO Generic, Foldable
TODO 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:

TO-CLEAN 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.