Haskell Beginners Workshop

Haskell


Part one: Preliminaries


What is Haskell?

A general purpose programming language. It is used for:

The most popular and active compiler for Haskell is GHC, which is written in Haskell as well.


About Us


Should I learn it?

Do you:

If so, consider learning Haskell. It's fun, it's practical, it's educational.


Influenced


What is this workshop about?


How?


Wait... I want to ask something!


Let's begin!


Part Two: Expressions and Types


A Haskell (*.hs) file structure


You can clone this git repo:

git clone https://github.com/soupi/haskell-workshop.git
cd haskell-workshop
stack runghc Main.hs

Let's Learn Some Haskell!


Declarations

<name> = <expression>

Expressions

five = 5

negThree = (2 * 3) - (4 + 5)

Naming things

comp =
  let
    left  = 2 * 3
    right = 4 + 5
  in
    left - right

Naming things

comp =
  left - right
  where
    left  = 2 * 3
    right = 4 + 5

Variables are immutable

x = 5

y =
  -- shadowing x in the scope of `let ... in ...`
  let
    x = 6
  in
    x + x

z = x + y

Indentation

A simple rule of thumb: Code which is part of some expression should be indented further in than the beginning of that expression


Type Aliases

type Value = Integer

val :: Value
val = 777

Note:


Special syntax: Lists

gil'sFavoritefruits :: [String]
gil'sFavoriteFruits =
  ["Orange", "Banana", "Kiwi", "Avocado", "Mango"]

Special syntax - Tuple

gil'sNameAndAge :: (String, Integer)
gil'sNameAndAge = ("Gil", 28)

Note:


Special syntax - Tuple and List values

gil'sNameAndAge :: (String, Integer)
gil'sNameAndAge =
  ( "Gil"
  , 28
  )

gil'sFavoritefruits :: [String]
gil'sFavoriteFruits =
  [ "Orange"
  , "Banana"
  , "Kiwi"
  , "Avocado"
  , "Mango"
  ]

Exercise One

Define a type which represents a database table.


Exercise Two

Define a sample table:


Part Three: Functions


Function Declarations

<function-name> <arg1> <arg2> ... <argN> = <expression>

Function Application

increment n = n + 1

six = increment five

seven = increment (increment five)

incAndAdd x y = increment x + increment y

factorial n =
  if n < 1
    then n
    else n * factorial (n - 1)

Exercise Three

Check and pretty-print a table:


Part Four: More on Functions


Function Calls - Partial Application

-- takes 3 arguments, so in this case N = 3
sum3 x y z = x + y + z

-- only supplies 2 arguments (K = 2), 0 and 1.
-- so newIncrement is a function that takes (N - K = 1) arguments
newIncrement = sum3 0 1

-- three is the value 3
three = newIncrement 2

Functions are first class citizens

compose f g x = f (g x)

-- evaluates to 8
eight = compose double increment 3

-- evaluates to [2,3,4,5]
twoThreeFourFive = map increment [1,2,3,4]

Functions are first class citizens

apply x f = f x

-- evaluates to [4,6,10]
fourSixTen = map (apply 5) [decrement, increment, double]

Functions are first class citizens


Exercise Four

  1. Define a type for a database as an association list (a list of tuples of keys and values) of tables and table names
  2. Define a sample Database value
  3. Define a myMap function.

    • It takes a function and a list as arguments
    • It returns a new list with the function applied to each element

    Use:

    • null to check for the empty list
    • head and tail to get the first value and the rest of the values of a list
    • : to "add" a value to the start of a list
  4. Define a ppTableNames function, which return a string which is the names of the tables in a database
    • Use the supplied function getName
    • Use either intercalate ", ", unwords or unlines to concat a list of strings to a string

Part Five - Do Notation


Do notation

main = do
  putStrLn "Hello!"
  putStrLn "What is your name?"
  result <- getLine
  putStrLn ("Nice to meet you, " ++ result)
  putStrLn "Here is the result of 1+1: "
  let calculation = factorial 100 -- no need for in
  putStrLn (show calculation)
  putStrLn "Bye!"

Common Error #1

module Main where

main :: IO ()
main = do
  putStrLn "Hello! What is your name?"
  putStrLn ("Nice to meet you, " ++ getLine)
Hello.hs:6:37: error:
    • Couldn't match expected type ‘[Char]’
                  with actual type ‘IO String’
    • In the first argument of ‘(++)’, namely ‘getLine’
      In the second argument of ‘(++)’, namely ‘getLine’
      In the expression: "Nice to meet you, " ++ getLine
  |
6 |   putStrLn ("Nice to meet you, " ++ getLine)
  |                                     ^^^^^^^

Common Error #1 - Using IO String in place of String


Common Error #2

module Main where

main :: IO ()
main = do
  putStrLn "Hello! What is your name?"
  name <- getLine
  putStrLn "Nice to meet you, " ++ name

Common Error #2

Hello.hs:7:3: error:
    • Couldn't match expected type ‘[Char]’ with actual type ‘IO ()’
    • In the first argument of ‘(++)’, namely
        ‘putStrLn "Nice to meet you, "’
      In a stmt of a 'do' block:
        putStrLn "Nice to meet you, " ++ name
      In the expression:
        do putStrLn "Hello! What is your name?"
           name <- getLine
           putStrLn "Nice to meet you, " ++ name
  |
7 |   putStrLn "Nice to meet you, " ++ name
  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Common Error #2 - Function application precedes operator application

putStrLn ("Nice to meet you, " ++ name)

Exercise Five


Part Six - Data Types

Let's talk about types and how to represent a query in Haskell


Defining Types

type Username = String

Type Signatures

We can give values a type signature using ::

myUsername :: Username
myUsername = "soupi"

Defining Types - Sum Types

data KnownColor -- the new type's name
  = Red         -- One possible value
  | Blue
  | Green

redColor :: KnownColor
redColor = Red

Defining Types - Product Types

data RGB
  = MkRGB Int Int Int
{-
      ^    ^   ^   ^
      |    |   |   |
      |    |   |   +- This is the blue component
      |    |   |
      |    |   +----- This is the green component
      |    |
      |    +--------- This is the red component
      |
      +------------- This is called the value constructor, or "tag"
-}

magenta :: RGB
magenta = MkRGB 255 0 255

Defining types - Sum and Product Types

data Color
  = Red
  | Blue
  | Green
  | RGB Int Int Int

blue :: Color
blue = Blue

magenta :: Color
magenta = RGB 255 0 255

Defining types - Records

data RGB = MkRGB
  { rgbRed   :: Int
  , rgbGreen :: Int
  , rgbBlue  :: Int
  }

red :: RGB
red = MkRGB
  { rgbRed   = 255
  , rgbGreen = 0
  , rgbBlue  = 0
  }

Exercise Six

Define a Query data type with two value constructors:

This value constructors represents an SQL data source query

-- This is like our value constructor, we write a table (list of rows) in-line
INSERT INTO t VALUES (1,2,3),(4,5,6);

-- This is like our `Table` value constructor,
-- We write a table name to reference a table in the database
SELECT * FROM t;

The Type of Functions

increment :: Int -> Int
increment n = n + 1

sum3 :: Int -> Int -> Int -> Int
sum3 x y z = x + y + z

supplyGreenAndBlue :: Int -> Int -> Color
supplyGreenAndBlue = RGB 100

The Type of Functions

increment :: Int -> Int
increment n = n + 1

sum3 :: (Int -> (Int -> (Int -> Int)))
sum3 x y z = x + y + z

supplyGreenAndBlue :: (Int -> (Int -> Color))
supplyGreenAndBlue = RGB 100

Parametric Polymorphism in Type Signatures


Parametric Polymorphism in Type Signatures

-- I only take concrete `Int` values
identityInt :: Int -> Int
identityInt x = x

five :: Int
five = identityInt 5

-- `a` represents any one type
identity :: a -> a
identity x = x

seven :: Int
seven = identity 7

true :: Bool
true = identity True

const :: a -> b -> a
const x y = x

Parametric Polymorphism in Type Signatures

-- will fail because nothing in the type signature suggests that
-- `a` and `b` necessarily represent the same type
identity1 :: a -> b
identity1 x = x

-- will fail because we don't know if `a` is `Int`
identity2 :: a -> Int
identity2 x = x

-- will fail because we don't know if `a` is `Int`
identity3 :: Int -> a
identity3 x = x

One More Thing About Functions

compose :: (b -> c) -> (a -> b) -> a -> c
compose f g x = f (g x)

f . g = compose f g

One More Thing About Functions

compose :: ((b -> c) -> ((a -> b) -> (a -> c)))
compose f g x = f (g x)

Exercise Seven


Recursive Types and Data Structures

data IntList
  = EndOfIntList
  | ValAndNext Int IntList

-- the list [1,2,3]
list123 :: IntList
list123 = ValAndNext 1 (ValAndNext 2 (ValAndNext 3 EndOfList))

Recursive Types and Data Structures

data IntTree
  = Leaf
  | Node
      IntTree      -- Left subtree
      Int          -- Node value
      IntTree      -- Right subtree

--     2
--    / \
--   1   3
--  /
-- 1
tree1123 :: IntTree
tree1123 =
  Node
    (Node (Node Leaf 1 Leaf) 1 Leaf)
    2
    (Node Leaf 3 Leaf)

Defining Types - Type variables

-- a value of type a or nothing
data Maybe a
  = Just a
  | Nothing

-- a value of type a or a value of type b
data Either a b
  = Left a
  | Right b

-- A linked list of `a`s

-- Note: there's also a built in syntax in Haskell for linked lists

data List a          -- [a]    -- special syntax for a linked list of a generic type `a`
  = Nil              -- []     -- special syntax for the empty list
  | Cons a (List a)  -- x : xs -- special operator for constructing a list

Exercise Eight

To your Query data type, add the a Select option with the following arguments:

This represents an SQL SELECT statement

SELECT x as x1, x as x2, y as y -- the select list
FROM t; -- the inner query (a table named 't')

Case Expression (Pattern Matching)

case <expr> of
  <pattern1> -> <result1>
  <pattern2> -> <result2>
  ...
  <patternN> -> <resultN>

Case Expression (Pattern Matching)

myIf :: Bool -> a -> a -> a
myIf test trueBranch falseBranch =
  case test of
    True  -> trueBranch
    False -> falseBranch

Case Expression (Pattern Matching)

factorial :: Int -> Int
factorial num =
  case num of
    0 -> 1
    n -> n * factorial (n - 1)

Case Expression (Pattern Matching)

colorName :: Color -> String
colorName color =
  case color of
    Red -> "red"
    Green -> "green"
    Blue -> "blue"
    RGB 255 0 255 -> "magenta"
    RGB _ 255 _ ->
      "well it has a lot of green in it"
    RGB _ 255 n ->
      "It has a lot of green and " ++ show n ++ " in the blue component"
    _ -> "i don't know this color"

Exercise Nine

Before jumping into writing a query engine, let's first write a direct-style interpreter for the following data type:

data Expr
  = Value Integer
  | Add Expr Expr
  | Mul Expr Expr

This type represents a simple mathematical expression.

Write a function eval :: Expr -> Int that calculates the result.


Exercise Ten

Write an interpreter for our Query data type

Clues:


Exercise Eleven - a REPL

Clues:


Exercise Twelve

This constructor represents a UNION operator in SQL

SELECT x as x1, y as y -- the select list
FROM
  t1 UNION t2 -- the inner queries (two tables named 't1' and 't2')
;

Exercise Thirteen

This constructor represents a where clause in SQL

SELECT x as x1, x as x2, y as y -- the select list
FROM t -- the inner query (a table named 't')
where y = 1
;

I hope we gave you a good taste of Haskell, but:

We barely scratched the surface!


We didn't cover a lot of things


Want to learn more?