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.
Do you:
If so, consider learning Haskell. It's fun, it's practical, it's educational.

*.hs) file structurestack setup to setup a Haskell environmentMain.hs and work there for the rest of the exercisesgit clone https://github.com/soupi/haskell-workshop.git
cd haskell-workshop
stack runghc Main.hs<name> = <expression>5(2 * 3) - (4 + 5)five = 5
negThree = (2 * 3) - (4 + 5)We can give a name to a computation in a computation using let
comp =
let
left = 2 * 3
right = 4 + 5
in
left - rightWe can give a name to a computation in a computation using where
comp =
left - right
where
left = 2 * 3
right = 4 + 5= means that both sides are interchangeablex = 5
y =
-- shadowing x in the scope of `let ... in ...`
let
x = 6
in
x + x
z = x + yWhat's the value of z? Why?
A simple rule of thumb: Code which is part of some expression should be indented further in than the beginning of that expression
type <name> = <type>Integer, Bool, String(String, Bool)[Integer]type Value = Integer
val :: Value
val = 777
Note:
:: to declare the type of a name= to declare the value of a namegil'sFavoritefruits :: [String]
gil'sFavoriteFruits =
["Orange", "Banana", "Kiwi", "Avocado", "Mango"]gil'sNameAndAge :: (String, Integer)
gil'sNameAndAge = ("Gil", 28)Note:
' is a valid character in a binding name
gil'sNameAndAge :: (String, Integer)
gil'sNameAndAge =
( "Gil"
, 28
)
gil'sFavoritefruits :: [String]
gil'sFavoriteFruits =
[ "Orange"
, "Banana"
, "Kiwi"
, "Avocado"
, "Mango"
]Define a type which represents a database table.
IntegersDefine a sample table:
"x" and "y"Like value declarations, just add argument names before the =
<function-name> <arg1> <arg2> ... <argN> = <expression>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)Check and pretty-print a table:
-- 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 2compose 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]apply x f = f x
-- evaluates to [4,6,10]
fourSixTen = map (apply 5) [decrement, increment, double]map is left as an exercise to the reader
type for a database as an association list (a list of tuples of keys and values) of tables and table namesDatabase valueDefine a myMap function.
Use:
null to check for the empty listhead 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 listppTableNames function, which return a string which is the names of the tables in a databasegetNameintercalate ", ", unwords or unlines to concat a list of strings to a string<- is used to bind the result of an IO action to a variable when using do notationlet is used to bind an expression to a name (note: no need to write the accompanied in)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!"
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)
| ^^^^^^^IO String in place of StringString is defined as type String = [Char]String which was expected, with IO String which is the type of getLineIO a and a are different typesmodule Main where
main :: IO ()
main = do
putStrLn "Hello! What is your name?"
name <- getLine
putStrLn "Nice to meet you, " ++ nameHello.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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Parenthesis are needed around the expression string
putStrLn ("Nice to meet you, " ++ name)prompt IO action that:lookup)lookup returns a value of the type Maybe TableisJust to check if it's a table or notfromJust to cast it to a table if it's foundLet's talk about types and how to represent a query in Haskell
type to give a new alias to an existing type. They can be used interchangingly.type Username = StringWe can give values a type signature using ::
myUsername :: Username
myUsername = "soupi"data| to say "alternatively"data KnownColor -- the new type's name
= Red -- One possible value
| Blue
| Green
redColor :: KnownColor
redColor = Reddata to define compound data of existing typesdata 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 255Red, Blue, Green or RGB) create a value of the typeRGB), value constructors can be used as regular functions to build values of the typedata Color
= Red
| Blue
| Green
| RGB Int Int Int
blue :: Color
blue = Blue
magenta :: Color
magenta = RGB 255 0 255data RGB = MkRGB
{ rgbRed :: Int
, rgbGreen :: Int
, rgbBlue :: Int
}
red :: RGB
red = MkRGB
{ rgbRed = 255
, rgbGreen = 0
, rgbBlue = 0
}Define a Query data type with two value constructors:
Table - which is a name of a table in the databaseValues - which is an in-place table valueThis 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;Add the line deriving (Show, Read) at the end, this will enable us to use show (and print) and read on our Query data structure.
We use -> to denote the type of a function from one type to another type
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-> is right associative, The function definitions from the previous slide will be parsed like this:
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 100This is why partial function application works.
-- 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-- 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 = xcompose :: (b -> c) -> (a -> b) -> a -> c
compose f g x = f (g x)
f . g = compose f g-> in type signatures is right associativecompose :: ((b -> c) -> ((a -> b) -> (a -> c)))
compose f g x = f (g x)Go back to the functions you defined and create a type signature for them
data IntList
= EndOfIntList
| ValAndNext Int IntList
-- the list [1,2,3]
list123 :: IntList
list123 = ValAndNext 1 (ValAndNext 2 (ValAndNext 3 EndOfList))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)
Int or Bool like in the previous slide-- 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 listTo 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 <expr> of
<pattern1> -> <result1>
<pattern2> -> <result2>
...
<patternN> -> <resultN>myIf :: Bool -> a -> a -> a
myIf test trueBranch falseBranch =
case test of
True -> trueBranch
False -> falseBranchfactorial :: Int -> Int
factorial num =
case num of
0 -> 1
n -> n * factorial (n - 1)_ means match anythingcolorName :: 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"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 ExprThis type represents a simple mathematical expression.
Write a function eval :: Expr -> Int that calculates the result.
Write an interpreter for our Query data type
Database to lookup tablesClues:
Select, You'll need to use map (or myMap). In two different ways.lookupCol to search a value of a column in a row by the column's nameerror "Lookup failed" to terminate earlyCreate an IO action that reads a query of a user, interprets it and displays it back
Clues:
readQuery to parse the query from the userrepl with your IO action as a parameters to create a REPL.Query in the prompt to interpret it. For example: Select [("x", "x")] (Values [[("x",1)]]).Union constructor for our Query data type and handle it in the interpreterThis 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')
;Restrict constructor for our Query data type and handle it in the interpreterfilter which takes a predicate and a list and returns a list that contains only values that satisfy the predicateThis 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
;