Error in user YAML: (<unknown>): did not find expected ',' or ']' while parsing a flow sequence at line 3 column 1
---
# Reading Simple
[![Haskell](https://www.haskell.org/static/img/haskell-logo.svg)](https://haskell.org)
---
- Markdown format available here.
Haskell is a general purpose programming language, and can be used to build:
- Compilers (PureScript, Elm, Agda, Corrode and many more)
- Build systems (Shake)
- Scripts (Turtle)
- Web servers and frontend applications (Yesod, Servant, Miso, Reflex and many more)
- etc
Haskell's main compiler is GHC.
- Compiler extensions - we won't talk about those in this talk
- Comments -
--
for single line comment,{- -}
for block comments - Module name
- Exports
- Imports
- Definitions
- Left-hand side is the name of the value
=
is used to declare the expression that is bound to the name on the left side (value definition)
five = 5
- Add argument names after a name
- Call functions without parentheses
- Function call is left associative
- Function call takes precendence over operators
increment n = n + 1
six = increment five
seven = increment (increment five)
incAndAdd x y = increment x + increment y
- You can also define operators
x +- y = (x + x) - (y + y)
- We can supply only some of the arguments to a function
- If we have a function that takes N arguments and we supply K arguments, we'll get a function that takes the remaining (N - K) arguments
-- 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
- We can name part of the computation using
let
orwhere
let [<definition>] in <expression>
is an expression and can be used anywherewhere
is special syntax
sumOf3 x y z =
let temp = x + y
in temp + z
-- or:
sumOf3 x y z = temp + z
where temp = x + y
- Concrete types starts with an uppercase letter
- Use
type
to give a new alias to an existing type. They can be used interchangingly.
type Nickname = String
We can give values a type signature using ::
myNickname :: Nickname
myNickname = "suppi"
- We can define our own types using the keyword
data
- Sum types are alternative possible values of a given type
- Similar to enums in other languages
- We use
|
to say "alternatively" - To calculate how many possible values the new type has, we count and sum all the possible values, therefore "sum type"
- Each option must start with an uppercase letter
data KnownColor -- the new type's name
= Red -- One possible value
| Blue
| Green
redColor :: KnownColor
redColor = Red
- We can also use
data
to define compound data of existing types - Similar to structs in other languages
- To calculate how many possible values the new type has, we count and multiply the amount of possible values for each type. Therefore "product type"
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
- We can mix sum and product types in one type
- This is often called an algebraic data type, or ADT
- Value constructors (like
Red
,Blue
,Green
orRGB
) create a value of the type - If they represent a product (like
RGB
), value constructors can be used as regular functions to build values of the type - This also means they can be partially applied
data Color
= Red
| Blue
| Green
| RGB Int Int Int
blue :: Color
blue = Blue
magenta :: Color
magenta = RGB 255 0 255
- Records allow us to name the fields in a product type
- There is more to records, but we won't talk too much about it here
data RGB = MkRGB
{ rgbRed :: Int
, rgbGreen :: Int
, rgbBlue :: Int
}
red :: RGB
red = MkRGB
{ rgbRed = 255
, rgbGreen = 0
, rgbBlue = 0
}
- 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 100
- This is why partial function application works.
- Also known as "generics" in other languages
- Names that starts with an upper case letter in types are concrete types
- Names that starts with a lower case letter in types are type variables
- Just as a variable represent some value of a given type, a type variable represents some type
- A type variable represents one type across the type signature (and function definition) in the same way a variable represent a value throughout the scope it's defined in
-- 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 = x
- In Haskell functions are first class values
- They can be put in variables, passed and returned from functions, etc
- This is a function that takes two functions and a value, applies the second function to the value and then applies the first function to the result
- AKA function composition
compose :: (b -> c) -> (a -> b) -> a -> c
compose f g x = f (g x)
f . g = compose f g
- Remember,
->
in type signatures is right associative - Doesn't it look like we take two functions and return a third from the type signature?
compose :: ((b -> c) -> ((a -> b) -> (a -> c)))
compose f g x = f (g x)
As we saw earlier, Haskell is globally type inferred. We can remove almost all type signatures and Haskell will choose the most general type signature for us.
- A recursive data type is a data definition that refers to itself
- This lets us define even more interesting data structures such as linked lists and trees
data IntList
= EndOfIntList
| ValAndNext Int IntList
-- the list [1,2,3]
list123 :: IntList
list123 = ValAndNext 1 (ValAndNext 2 (ValAndNext 3 EndOfList))
- A recursive data type is a data definition that refers to itself
- This lets us define even more interesting data structures such as linked lists and trees
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)
- We can use type variables when defining types
- We can define generic structures
- This way we don't have to restrict our structure to a specific type such as
Int
orBool
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 list
- Allows us to write control flows on data types
- Matches from top to bottom
case <expr> of
<pattern1> -> <result1>
<pattern2> -> <result2>
...
<patternN> -> <resultN>
- Allows us to write control flows on data types
- Matches from top to bottom
myIf :: Bool -> a -> a -> a
myIf test trueBranch falseBranch =
case test of
True -> trueBranch
False -> falseBranch
- Allows us to write control flows on data types
- Matches from top to bottom
factorial :: Int -> Int
factorial num =
case num of
0 -> 1
n -> n * factorial (n - 1)
- Allows us to write control flows on data types
- Matches from top to bottom
- The pattern
_
means match anything
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"
_ -> "i don't know this color"
- Do notation is special syntax for writing IO actions in a way that looks imperative
<-
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
main :: IO ()
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 -- note that when using do notation we don't need to use `in`
putStrLn (show calculation)
putStrLn "Bye!"
- A simple JSON EDSL
- Try it in repl.it to see the result
- Writing Simple Haskell
- Install a Haskell compiler and environment
- Minimal Haskell IDE based on Emacs
- Haskell Study Plan