On Github mkawalec / haskell-for-newbies
Michal Kawalec @monad_cat
data AContainer = AContainer Integer data BiggerContainer = BiggerContainer Text Bool data Maybe a = Nothing | Just a data RecordContainer = RecordContainer { name :: Text, rank :: Int, isActive :: Bool }
data Maybe a = Nothing | Just a data RecordContainer = RecordContainer { name :: Text, rank :: Int, isActive :: Bool } > let wrappedVal = Just "hello" > :t wrappedVal wrappedVal :: Maybe Text > let container = RecordContainer "Michał" 0 True > let modifiedContainer = container { rank = 5 }
addOne x = x + 1 > :t addOne Num a => a -> a
addOne :: Num a => a -> a addOne x = x + 1 class Num a where (+), (-), (*) :: a -> a -> a negate :: a -> a abs :: a -> a signum :: a -> a fromInteger :: Integer -> a x - y = x + negate y negate x = 0 - x
How do we signal that a value may be missing? Use Maybe
data Maybe a = Nothing | Just a
or better Either for an error message
data Either a b = Left a | Right b multIfLucky :: Int -> Either Text Int multIfLucky 42 = Right (42 * 2) multIfLucky _ = Left "you're unlucky"
Lists are not any special data type, we can define our own lists like:
data List a = Nil | Cons a (List a) > :t Cons 1 (Cons 4 (Cons 20 Nil)) Cons 1 (Cons 4 (Cons 20 Nil)) :: Num a => List a
In real programs Nil is defined with a [] operator and Cons is :
> :t 1 : 2 : 3 : [] 1 : 2 : 3 : [] :: Num a => [a]
Let's define a list
let aList = [4,2,5,1] > aList !! 2 5 > sort aList [1,2,4,5] > [1, "foobar"] No instance for (Num [Char]) arising from the literal ‘1’ In the expression: 1 In the expression: [1, "foobar"] In an equation for ‘it’: it = [1, "foobar"]
We can have functions accept other functions
> :t map map :: (a -> b) -> [a] -> [b]
Multiple ways of using this pattern:
> let awesomeList = [1,2,3,0,5] > let mult x = x * 2 > map mult awesomeList [2, 4, 6, 0, 10] > map (\x -> x * 2) awesomeList > map (\_ -> "foo") awesomeList ["foo","foo","foo","foo","foo"]
Let's take a look on the type of map
> :t map map :: (a -> b) -> [a] -> [b]
We can partially apply it and get a function that accepts a list
> let parap = map (\x -> x * 2) > :t parap parap :: Num b => [b] -> [b]
And apply it completely to get a result
> parap [1,2] [2,4]
Factorial is a nice example
factorial :: Integer -> Integer factorial 0 = 1 factorial n = n * factorial (n - 1) > factorial 2 2 > factorial 4 24 > map factorial [1..10] [1,2,6,24,120,720,5040,40320,362880,3628800]
let's write a fibbonacci
> let fib = 1 : (scanl (+) 1 fib) > take 10 fib [1,1,2,3,5,8,13,21,34,55]
> :t scanl scanl :: (b -> a -> b) -> b -> [a] -> [b] > let fib = 1 : (scanl (+) 1 fib) > take 10 fib [1,1,2,3,5,8,13,21,34,55]
Let's disect that per iteration:
fib = 1 : 1 fib = 1 : 1 : (1 + fib[0]) = 1 : 1 : 2 fib = 1 : 1 : 2 : (2 + fib[1]) = 1 : 1 : 2 : 3 fib = 1 : 1 : 2 : 3 : (3 + fib[2]) = 1 : 1 : 2 : 3 : 5
So we can work with bottoms as long as we only see their finite parts!
impure :: IO () impure = print "hey user"
what if we tried to make a pure version?
<interactive>:25:12: Couldn't match expected type ‘()’ with actual type ‘IO ()’ In the expression: print "a" :: () In an equation for ‘pure’: pure = print "a" :: ()
We can implement way more complex effects simply with the type system. Consider that:
data Env = Env { counter :: TVar Int, chan :: TChan Text } modify :: Env -> STM () modify env = do modifyTVar (counter env) (\x -> x + 1) writeTChan (chan env) "I've added one" > ourChan <- newTChanIO > let ourEnv = Env 0 ourChan > atomically (modify ourEnv) > readTVar (counter ourEnv) 1
There's a plenthora of other learning resources:
main = scotty 3000 $ do get "/:word" $ do beam <- param "word" html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
emailAddressParser :: Parser EmailAddress emailAddressParser = nameAddrParser <|> addrSpecParser nameAddrParser :: Parser EmailAddress nameAddrParser = do label <- takeWhile (/= '<') _ <- char '<' address <- takeWhile (/= '>') _ <- char '>' return $ EmailAddress address (Just $ strip label) addrSpecParser :: Parser EmailAddress addrSpecParser = do address <- takeWhile (\c -> c /= '\r' && c /= ',') return $ EmailAddress address Nothing
propIdempotent xs = sort (sort xs) == sort xs > quickCheck propIdempotent +++ OK, passed 100 tests. [1,5,-1,3,6]