Beginning Haskell, Dive In, Its Not That Scary!
Transcript of Beginning Haskell, Dive In, Its Not That Scary!
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
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")
REPL Playground!➜ ~ ghciGHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for helpLoaded GHCi configuration from /Users/tom/.ghcighci> print "Hello World""Hello World"ghci> 1 + 12ghci> "Haskell " ++ "is cool""Haskell is cool"ghci> sum [1, 2, 3]6ghci> :l HelloWorld.hs[1 of 1] Compiling HelloWorld ( Helloworld.hs, interpreted )Ok, modules loaded: HelloWorld.ghci> mainHello Worldghci>
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; }
Haskell Palindrome Logic and Recursion
isPalindrome :: String -> BoolisPalindrome 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 !
GuardsisPalindrome :: String -> BoolisPalindrome str = loop strEndIndex where strEndIndex = length str - 1 loop i | i <= (div strEndIndex 2) = True | (str !! i) /= str !! (strEndIndex - i) = False | otherwise = loop (i - 1)
Refining the Palindrome Pattern Matching and Recursion
isPalindrome :: String -> BoolisPalindrome str = case str of [] -> True x : [] -> True x1 : x2 : [] -> x1 == x2 x1 : xs -> x1 == last xs && isPalindrome (init xs)
Refining the Palindrome Pattern matching without caseisPalindrome :: String -> BoolisPalindrome [] = TrueisPalindrome (x : []) = TrueisPalindrome (x1 : x2 : []) = x1 == x2isPalindrome (x1 : xs) = x1 == last xs && isPalindrome (init xs)
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]
12
3
[]
Refining the Palindrome Is whatever a palindrome?
isPalindrome :: (Eq a) => [a] -> BoolisPalindrome [] = TrueisPalindrome (x : []) = TrueisPalindrome (x1 : x2 : []) = x1 == x2isPalindrome (x1 : xs) = x1 == last xs && isPalindrome (init xs)
ghci> isPalindrome [1,2,3]Falseghci> isPalindrome [1,2,1]True
Why the (Eq a) => ?Type Classes
ghci> :info Eqclass Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool
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] -> BoolisPalindrome [] = TrueisPalindrome (x : []) = TrueisPalindrome (x1 : x2 : []) = x1 == x2isPalindrome (x1 : xs) = x1 == last xs && isPalindrome (init xs)
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> bTrilogy {title = "Lord of the Rings”}ghci> title b"Lord of the Rings"ghci> let b2 = b {title = "Lord of the Rings Version 2"}ghci> b2Trilogy {title = "Lord of the Rings Version 2"}ghci> bTrilogy {title = "Lord of the Rings"}
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’
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 Eqclass Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool
ghci> isPalindrome [(Single "The Hobbit"), (Trilogy "Lord of the Rings"), (Single "The Hobbit")]Trueghci> isPalindrome [(Single "The Hobbit"), (Trilogy "Lord of the Rings"), (Series "The Hobbit")]False
Now are my books a palindrome?
Derving Type Classes
data Book = Single String | Trilogy String | Series String deriving (Show, Eq)
ghci> :info Showclass Show a where …. show :: a -> String ….
Are they all palindromes?["racecar","wow","huzzah"]
allPalindromes = areAllPalindromes [ [(Single "S"), (Trilogy "T"), (Single "S")], [(Series "S"), (Trilogy "T"), (Series "S")] ]
allPalindromes = areAllPalindromes [ [(Single "S"), (Trilogy "T"), (Single "S")], [(Series "S"), (Trilogy "T"), (Series "S2")] ]
Are they all palindromes? Almost imperative style loop
areAllPalindromes :: (Eq a) => [[a]] -> BoolareAllPalindromes listOfLists = loop listOfLists where loop lOfl = case lOfl of [] -> True (x : xs) -> isPalindrome x && loop xs
ghci> areAllPalindromes ["racecar", "wow", "huzzuh"]Trueghci> areAllPalindromes ["racecar", "wow", "huzzah"]False
Are they all palindromes? Refining the recursion
areAllPalindromes :: (Eq a) => [[a]] -> BoolareAllPalindromes [] = TrueareAllPalindromes (x : xs) = isPalindrome x && areAllPalindromes xs
Are they all palindromes? Fold
areAllPalindromes :: (Eq a) => [[a]] -> BoolareAllPalindromes 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)))
Point Free Niceness !palindromeWithAnding :: (Eq a) => [a] -> Bool -> BoolpalindromeWithAnding x b = (&&) (isPalindrome x) b
ghci> palindromeWithAnding "huzzuh" FalseFalseghci> palindromeWithAnding "huzzuh" TrueTrue
Function by Name Binding..
areAllPalindromes :: (Eq a) => [[a]] -> Bool areAllPalindromes lOfLs = foldr palindromeWithAnding True lOfLs
Map and then Foldghci> map isPalindrome ["racecar", "wow", “huzzah"][True,True,False]
areAllPalindromes :: (Eq a) => [[a]] -> BoolareAllPalindromes lOfLs = foldr (\x result -> x && result) True (map isPalindrome lOfLs)
WITH POINT FREE &&
areAllPalindromes :: (Eq a) => [[a]] -> BoolareAllPalindromes lOfLs = foldr (&&) True (map isPalindrome lOfLs)
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’
areAllPalindromes lOfLs = (foldr (&&) True) (map isPalindrome lOfLs)
map and then fold
fold . map
areAllPalindromes lOfLs = (foldr (&&) True) . (map isPalindrome) $ lOfLs
areAllPalindromes = (foldr (&&) True) . (map isPalindrome)
areAllPalindromes = foldr (&&) True . map isPalindrome
Function Composition Niceness !
Composition and Point Free Pipeline Declaration
Focusing on Transformations
Map with isPalindrome
Fold with &&
["racecar", "wow", "huzzuh"]
[True, True, True]
True
onlyPalindromes :: (Eq a) => [[a]] -> [[a]]onlyPalindromes xs = filter isPalindrome xs
ghci> onlyPalindromes ["racecar", "huzzah", "wow"][“racecar","wow"]
USING AUTOMATIC CURRYING
onlyPalindromes :: (Eq a) => [[a]] -> [[a]]onlyPalindromes = filter isPalindrome
Filter Palindromes
Haskell Lazinessghci> 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
How do I get startedhttps://www.haskell.org/platform/
Start a REPL with
➜ ~ ghciGHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for helpLoaded GHCi configuration from /Users/tom/.ghcighci>
Play with the examples from this talk GitHub priort