Haskell for the noobs

functions!

Fresh examples

hello = putStrLn "Hello DevFest Nantes!"
helloFred = do
  putStrLn "Hello Nantes,"
  putStrLn "my name is Fred!"
square x = x ^ 2

map square [1, 2, 3]

map square [1..10]

Function application

Signature

fun :: Type1 -> Type2 -> Type3 -> Type4
fun arg1 arg2 arg3 = expression

Every expression is a function

constant :: Int
constant = 2
increment :: Int -> Int
increment a = a + 1

Partially applied functions

increment a = add 1 a

simplified into

increment = add 1

Similarly

add a b = a + b

add a b = (+) a b

add = (+)

Let's compose

The dot operator

(.) :: (b -> c) -> (a -> b) -> a -> c
incrementTwice n = (increment . increment) n
incrementTwice = increment . increment

Pure functions

Signature define all the action

Functions in Haskell are like functions in maths.

Very handy for parallel computing but... how do you do anything?

Polymorphism

Parametric polymorphism

map :: (a -> b) -> [a] -> [b]

map is parametrically polymorphism: implementation doesn't depends on a and b.

Polymorphism

Ad-hoc polymorphism & type classes

(+) :: Num a => a -> a -> a

+ is ad-hoc polymorphism: implementation is type dependent

Num is a type class : a trait that you can implement outside the datatype definition.

Polymorphism on return type

fromInteger :: Num a => Integer -> a

mempty :: Monoid a => a

data!

Data structures made easy

data MyType = MyConstructor Attribute1 Attribute2

Multiple constructors possible

data Color = Red | Blue | Green

Data type can be parametric

data Vector a = Vector a

Functor

Generalize mapping over a parametric datatype

map :: (a -> b) -> [a] -> [b]
fmap :: Functor f => (a -> b) -> f a -> f b
fmap (+1) (Just 1)
fmap (+1) [1, 2, 3]

Various functors

Maybe

(*2) <$> Just 1
(*2) <$> Nothing

IO

getLine :: IO String

getInt :: IO Int
getInt = read <$> getLine

Applicatives

Generalize functors to multiple arguments functions

(+) <$> Just 1 <*> Just 2
(++) <$> getLine <*> getLine

Parsec

Parser Combinator

Build parser by combining other parsers, with basic parsers

char :: Char -> Parser Char
string :: String -> Parser String
space :: Parser Char
space = char ' ' 

Parser Combinator

Build by combining and fmapping

oneOf :: [Char] -> Parser Char
many :: Parser a -> Parser [a]
many1 :: Parser a -> Parser [a]
parsePiece :: Parser Piece
parsePiece = decodePiece <$> oneOf "PNBRQK"
decodePiece 'P' = Pawn
decodePiece 'N' = Knight
decodePiece 'B' = Bishop
decodePiece 'R' = Rook
decodePiece 'Q' = Queen
decodePiece 'K' = King

Knights & Bishops

World Championship 2016

Carlsen - Karjakin, 11-30 November, New-York

Credits Nina Berglund

Credits Nina Berglund

On révise

PGN

Portable Game Notation, context of the game and notation of moves.

[Event "F/S Return Match"]
[Site "Belgrade, Serbia JUG"]
[Date "1992.11.04"]
[Round "29"]
[White "Fischer, Robert J."]
[Black "Spassky, Boris V."]
[Result "1/2-1/2"]

1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3
O-O 9. h3 Nb8 10. d4 Nbd7 11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15.
Nb1 h6 16. Bh4 c5 17. dxe5 Nxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21.
Nc4 Nxc4 22. Bxc4 Nb6 23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7
27. Qe3 Qg5 28. Qxg5 hxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33.
f3 Bc8 34. Kf2 Bf5 35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5
40. Rd6 Kc5 41. Ra6 Nf2 42. g4 Bd3 43. Re6 1/2-1/2

PGN

PGN files can also include annotations and variations (here look at moves 19. and 22.)

[Event "Interclubs FRA"]
[Site "?"]
[Date "????.??.??"]
[Round "?"]
[White "Calistri, Tristan"]
[Black "Bauduin, Etienne"]
[Result "1-0"]

1.e4 c5 2.Nf3 e6 3.d4 cxd4 4.Nxd4 Nc6 5.Nc3 a6 6.Be2 Qc7 7.O-O Nf6 8.Be3 Bb4 
9.Na4 O-O 10.c4 Bd6 11.g3 Nxe4 12.Bf3 f5 13.Bxe4 fxe4 14.c5 Be7 {Les Noirs ont 
un pion d'avance mais de gros problèmes pour mettre leur Fc8 et leur Ta8 en jeu} 
15.Qg4 Ne5 16.Qxe4 d5 17.cxd6 Bxd6 18.Rac1 Qa5 19.Nb3 +/- {D} { Les blancs ont 
récupéré leur pion et toutes leurs pièces sont mobilisées  }
19...Qb4 
    (19...Qd5 20.Qxd5 exd5 21.Nb6 Bh3 22.Nxa8 Nf3+ 23.Kh1 Bxf1 24.Rxf1 Rxa8 25.Rd1±)
    (19...Nf3+ 20.Kg2 Qh5)
20.Qxb4 Bxb4 21.Nb6 $18 {Les noirs n'arriveront jamais à sortir leur Fc8}
21...Rb8 22.Bc5 Bxc5 
    (22...Nd3 23.Bxf8 Nxc1 24.Rxc1 Bxf8 25.Rxc8 Rxc8 26.Nxc8 +/-)
23.Nxc5 Rd8 24.Rfd1 Re8 25.Ne4 Nf7 26.Rc7 Kf8 27.Rdc1 1-0

FEN

Forsyth–Edwards Notation, describes a position and let you start over from it.

rnbqkbnr/pp2pppp/2p5/3P4/3P4/8/PP2PPPP/RNBQKBNR b KQkq - 0 3
rnbqkbnr/pp2pppp/2p5/3P4/3P4/8/PP2PPPP/RNBQKBNR   <--   board
                                              b   <--   player to move (w or b)
                                           KQkq   <--   available castling sides
                                              -   <--   en passant?
                                              0   <--   half-moves since last capture
                                              3   <--   moves

Pieces

Pieces are encoded with english names

English French
P Pawn Pion
N Knight Cavalier
B Bishop Fou
R Rook Tour
Q Queen Dame
K King Roi

Our test cases

After 3. cxd5 rnbqkbnr/pp2pppp/2p5/3P4/3P4/8/PP2PPPP/RNBQKBNR b KQkq - 0 3

After 9. O-O r2qkb1r/pp3ppp/2n1pn2/3p4/3P4/2N1PB2/PP3PPP/R1BQ1RK1 b kq - 1 9

After 49. Bf5+ 8/7k/p4K2/P4B1P/1P6/8/8/8 b - - 2 49

Models

type File = Char
type Rank = Char
type Square = (File, Rank)
data Color = White | Black
data Piece = Pawn | Knight | Bishop | Rook | Queen | King

data SquareContent = ColoredPiece Piece Color | Void

Models

type Position = [[SquareContent]]
type EnPassant = Maybe Square
data CastlingCapacity = CanLongCastle Color | CanShortCastle Color

data Board = Board Position Color [CastlingCapacity] EnPassant Int Int

Simple parsers

parseFile :: Parser File
parseFile = oneOf "abcdefgh"
parseRank :: Parser File
parseRank = oneOf "12345678"
parseSquare :: Parser Square
parseSquare = do
  file <- parseFile
  rank <- parseRank
  return (file, rank)

Applicative style

parseSquare' :: Parser Square
parseSquare' = mkSquare <$> parseFile <*> parseRank
  where mkSquare f r = (f, r)
parseSquare'' :: Parser Square
parseSquare'' = (,) <$> parseFile <*> parseRank

Half-moves and moves

parseInt :: Parser Int
parseInt = read <$> many1 digit
(,,) <$> parseInt <*> space <*> parseInt
(,) <$> parseInt <* space <*> parseInt
(,) <$> parseInt <* space
    <*> parseInt

Alternatives

instance Alternative Parser
parseColor :: Parser Color
parseColor = tries [ White <$ char 'w'
                   , Black <$ char 'b'
                   ]

More complex parsers

parseEnPassantSquare :: Parser Square
parseEnPassantSquare = (,) <$> parseFile <*> oneOf "36"
parseEnPassant :: Parser EnPassant
parseEnPassant =
  tries [ Nothing <$  char '-'
        , Just    <$> parseEnPassantSquare
        ]

More complex parsers

parseCastlingCapacity :: Parser CastlingCapacity
parseCastlingCapacity =
  tries [ CanLongCastle  White <$ char 'Q'
        , CanShortCastle White <$ char 'K'
        , CanLongCastle  Black <$ char 'q'
        , CanShortCastle Black <$ char 'k'
        ]
parseCastlingCapacities :: Parser [CastlingCapacity]
parseCastlingCapacities =
  tries [ [] <$ char '-'
        , many parseCastlingCapacity
        ]

Final boss

parseRow :: Parser [SquareContent]
parseRow = assuming (ofLength 8) $ mconcat <$> many parseSquareContent
parsePosition :: Parser Position
parsePosition = assuming (ofLength 8) $ parseRow `sepBy` char '/'

All together

boardParser :: Parser Board
boardParser = Board <$> parsePosition           <* space
                    <*> parseColor              <* space
                    <*> parseCastlingCapacities <* space
                    <*> parseEnPassant          <* space
                    <*> parseInt                <* space
                    <*> parseInt

Use our model (a bit)

colorToMove :: Board -> Color
colorToMove (Board _ color _ _ _ _) = color
canWhiteLongCastle :: Board -> Bool
canWhiteLongCastle (Board _ _ castles _ _ _) = elem whiteLongCastle castles
  where whiteLongCastle = CanLongCastle White