Haskell can get a bit of a reputation for being this lofty, academic, difficult to learn language. This talk aims to dispel this myth and offer an introduction to this beautiful and pragmatic language. From the point of view of someone who has been functional programming in Scala and Clojure for a while now, but who has, more recently been taking a dive into Haskell, this talk will give a basic introduction to Haskell. Hopefully it will encourage anyone who hasn't tried functional programming in Haskell to dive in too and give it a go.
The talk will be a whistle stop tour of some functional programming fundamentals in Haskell from basic data structures, logic constructs, functional transformations, recursion to some of the basics of Haskell's type system with data declarations and type classes.
3. Explanation?
• I can’t just print hello world!
• Its purity means things can often be explained in relation to
maths
• Use of whitespace and annoying compilation errors from it
• Strict type system and purity means that programmers forced
to stick to a purely functional style
4. Sounds a bit complicated !
MATHS
MONADS
FUNCTORS
APPLICATIVES
5. I just want to print Hello World !
public class HelloWorld {
public static void main (String [] args) {
System.out.println("Hello World");
}
}
module HelloWorld where
main :: IO()
main = putStrLn("Hello World")
6. REPL Playground!
➜ ~ ghci
GHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /Users/tom/.ghci
ghci> print "Hello World"
"Hello World"
ghci> 1 + 1
2
ghci> "Haskell " ++ "is cool"
"Haskell is cool"
ghci> sum [1, 2, 3]
6
ghci> :l HelloWorld.hs
[1 of 1] Compiling HelloWorld ( Helloworld.hs, interpreted )
Ok, modules loaded: HelloWorld.
ghci> main
Hello World
ghci>
7. Katas Up and Running in
No Time
With basic loops and logic
10. Imperative Java Solution
public static boolean isPalindrome(String str) {
int strEndIndex = str.length() - 1;
for(int i = strEndIndex; i > (strEndIndex/2); i--)
if(str.charAt(i) != str.charAt(strEndIndex - i))
return false;
return true;
}
11. Haskell Palindrome
Logic and Recursion
isPalindrome :: String -> Bool
isPalindrome str =
loop strEndIndex where
strEndIndex = length str - 1
loop i =
if i <= (div strEndIndex 2) then True
else if (str !! i) /= str !! (strEndIndex - i) then False
else loop (i - 1)
DISCLAIMER: Not the Nicest Haskell Code, but gets us started !
15. List Type and Pattern Match
ghci> :info []
data [] a = [] | a : [a] -- Defined in ‘GHC.Types’
ghci> 1 : 2 : 3 : []
[1,2,3]
ghci> [1,2,3]
[1,2,3]
1
2
3
[]
17. Why the (Eq a) => ?
Type Classes
ghci> :info Eq
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
18. ghci> :l HaskellPlayground.hs
[1 of 1] Compiling HaskellPlayground
( HaskellPlayground.hs, interpreted )
HaskellPlayground.hs:35:31: error:
• No instance for (Eq a) arising from a use of ‘==’
Possible fix:
add (Eq a) to the context of
the type signature for:
isPalindrome :: [a] -> Bool
isPalindrome :: [a] -> Bool
isPalindrome :: (Eq a) => [a] -> Bool
isPalindrome [] = True
isPalindrome (x : []) = True
isPalindrome (x1 : x2 : []) = x1 == x2
isPalindrome (x1 : xs) = x1 == last xs && isPalindrome (init xs)
19. I want to call isPalindrome
on my own Type
data Book =
Single String
| Trilogy String
| Series String
deriving (Show)
data Book =
Single { title :: String }
| Trilogy { title :: String }
| Series { title :: String }
deriving (Show)
ghci> let b = Trilogy {title = "Lord of the Rings"}
ghci> b
Trilogy {title = "Lord of the Rings”}
ghci> title b
"Lord of the Rings"
ghci> let b2 = b {title = "Lord of the Rings Version 2"}
ghci> b2
Trilogy {title = "Lord of the Rings Version 2"}
ghci> b
Trilogy {title = "Lord of the Rings"}
20. Is my book list a
palindrome?
ghci> isPalindrome [(Single "The Hobbit"), (Trilogy
"Lord of the Rings"), (Single "The Hobbit")]
<interactive>:22:1: error:
• No instance for (Eq Book) arising from a use
of ‘isPalindrome’
21. Comparing my books
instance Eq Book where
(==) (Single t1) (Single t2) = t1 == t2
(==) (Trilogy t1) (Trilogy t2) = t1 == t2
(==) (Series t1) (Series t2) = t1 == t2
(==) _ _ = False
ghci> :info Eq
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
22. ghci> isPalindrome [(Single "The Hobbit"), (Trilogy "Lord of the Rings"),
(Single "The Hobbit")]
True
ghci> isPalindrome [(Single "The Hobbit"), (Trilogy "Lord of the Rings"),
(Series "The Hobbit")]
False
Now are my books a
palindrome?
23. Derving Type Classes
data Book =
Single String
| Trilogy String
| Series String
deriving (Show, Eq)
ghci> :info Show
class Show a where
….
show :: a -> String
….
25. Are they all palindromes?
Almost imperative style loop
areAllPalindromes :: (Eq a) => [[a]] -> Bool
areAllPalindromes listOfLists =
loop listOfLists where
loop lOfl =
case lOfl of
[] -> True
(x : xs) -> isPalindrome x && loop xs
ghci> areAllPalindromes ["racecar", "wow", "huzzuh"]
True
ghci> areAllPalindromes ["racecar", "wow", "huzzah"]
False
26. Are they all palindromes?
Refining the recursion
areAllPalindromes :: (Eq a) => [[a]] -> Bool
areAllPalindromes [] = True
areAllPalindromes (x : xs) = isPalindrome x && areAllPalindromes xs
27. Are they all palindromes?
FoldareAllPalindromes :: (Eq a) => [[a]] -> Bool
areAllPalindromes lOfLs =
foldr (x result -> isPalindrome x && result) True lOfLs
["racecar", "wow", "huzzah"]
reduces as follows:
(isPalindrome "racecar") True
&&
((isPalindrome "wow") True
&&
((isPalindrome “huzzah") False
&& True)))
28. Point Free Niceness !
palindromeWithAnding :: (Eq a) => [a] -> Bool -> Bool
palindromeWithAnding x b = (&&) (isPalindrome x) b
ghci> palindromeWithAnding "huzzuh" False
False
ghci> palindromeWithAnding "huzzuh" True
True
Function by Name Binding..
areAllPalindromes :: (Eq a) => [[a]] -> Bool
areAllPalindromes lOfLs = foldr palindromeWithAnding True
lOfLs
29. Map and then Fold
ghci> map isPalindrome ["racecar", "wow", “huzzah"]
[True,True,False]
areAllPalindromes :: (Eq a) => [[a]] -> Bool
areAllPalindromes lOfLs =
foldr (x result -> x && result) True (map isPalindrome lOfLs)
WITH POINT FREE &&
areAllPalindromes :: (Eq a) => [[a]] -> Bool
areAllPalindromes lOfLs =
foldr (&&) True (map isPalindrome lOfLs)
30. Automatic Currying
Niceness !map isPalindrome lOfLs
ghci> :t map
map :: (a -> b) -> [a] -> [b]
So..
map func list
pseudocode
map = func -> list -> ‘map this list with func function’
map isPalindrome
list -> ‘map this list with isPalindrome function’
32. Composition and Point Free
Pipeline Declaration
Focusing on Transformations
Map with isPalindrome
Fold with &&
["racecar", "wow", "huzzuh"]
[True, True, True]
True
34. Haskell Laziness
ghci> length ["racecar", "huzzah", undefined]
3
ghci> length (map head ["racecar", "huzzah", undefined])
3
ghci> take 2 (map head ["racecar", "huzzah", undefined])
“rh"
ghci> take 3 (map head ["racecar", "huzzah", undefined])
"rh*** Exception: Prelude.undefined
ghci> take 1 (filter isPalindrome ["racecar", "huzzah", undefined])
[“racecar"]
ghci> take 2 (filter isPalindrome ["racecar", "huzzah", undefined])
["racecar"*** Exception: Prelude.undefined
35. How do I get started
https://www.haskell.org/platform/
Start a REPL with
➜ ~ ghci
GHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /Users/tom/.ghci
ghci>
Play with the examples from this talk
GitHub priort