Monads and IO
maybePlus :: Maybe Int -> Maybe Int -> Maybe Int
maybePlus ma mb = case ma of
    Nothing -> Nothing
    Just a  -> case mb of
        Nothing -> Nothing
        Just b  -> Just (a + b)π₯Ίππ Awkward pattern matching
andThen :: Maybe Int -> (Int -> Maybe Int) -> Maybe Int
andThen ma f = case ma of
    Nothing -> Nothing
    Just x  -> f xmaybePlus :: Maybe Int -> Maybe Int -> Maybe Int
maybePlus ma mb = andThen ma (\a -> andThen mb (\b -> Just (a + b)))π Ordinary helper function
π Refactoring
andThen :: Maybe a -> (a -> Maybe b) -> Maybe b
andThen ma f = case ma of
    Nothing -> Nothing
    Just x  -> f xeitherPlus :: Either String Int -> Either String Int -> Either String Int
eitherPlus ea eb = case ea of
    Left err -> Left err
    Right a  -> case eb of
        Left err -> Left err
        Right b  -> Right (a + b)π₯Ίππ Awkward pattern matching strikes again
andThen :: Either String Int 
        -> (Int -> Either String Int) 
        -> Either String Int
andThen ea f = case ea of
    Left err -> Left err
    Right x  -> f xeitherPlus :: Either String Int -> Either String Int -> Either String Int
eitherPlus ea eb = andThen ea (\a -> andThen eb (\b -> Right (a + b)))π Ordinary helper function
π Refactoring
andThen :: Either e a 
        -> (a -> Either e b) 
        -> Either e b
andThen ea f = case ea of
    Left err -> Left err
    Right x  -> f xlistPlus :: [Int] -> [Int] -> [Int]
listPlus la lb = case la of
    []     -> []
    a : as -> case lb of
        [] -> []
        bs -> map (+ a) bs ++ listPlus as bsπ₯Ίππ Even more awkward pattern matching
andThen :: [Int] -> (Int -> [Int]) -> [Int]
andThen l f = case l of
    []     -> []
    x : xs -> f x ++ andThen xs flistPlus :: [Int] -> [Int] -> [Int]
listPlus la lb = andThen la (\a -> andThen lb (\b -> [a + b]))π Ordinary helper function
π Refactoring
andThen :: [a] -> (a -> [b]) -> [b]
andThen l f = case l of
    []     -> []
    x : xs -> f x ++ andThen xs fπ The function became slower after refactoring!
andThen :: Maybe    a  -> (a -> Maybe    b)  -> Maybe    b
andThen :: Either e a  -> (a -> Either e b)  -> Either e b
andThen ::         [a] -> (a ->         [b]) ->         [b]π Very similar type signatures
maybePlus  ma mb = andThen ma (\a -> andThen mb (\b -> Just  (a + b)))
eitherPlus ea eb = andThen ea (\a -> andThen eb (\b -> Right (a + b)))
listPlus   la lb = andThen la (\a -> andThen lb (\b ->       [a + b]))π Almost the same implementations
π Monad is a generalization of the "andThen" pattern
We need:
maybePlus  ma mb = andThen ma (\a -> andThen mb (\b -> Just  (a + b)))
eitherPlus ea eb = andThen ea (\a -> andThen eb (\b -> Right (a + b)))
listPlus   la lb = andThen la (\a -> andThen lb (\b ->       [a + b]))class Monad m where
    return :: a -> m a
    (>>=)  :: m a -> (a -> m b) -> m bπ§βπ What we can already learn from the definition
π« What we can't see from the above
instance Monad Maybe where
    return :: a -> Maybe a
    return x = Just x
    (>>=) :: Maybe a 
          -> (a -> Maybe b) 
          -> Maybe b
    Nothing >>= _ = Nothing
    Just x  >>= f = f xβΉοΈ Maybe
class Monad m where
    return :: a -> m a
    (>>=)  :: m a -> (a -> m b) -> m binstance Monad (Either e) where
    return :: a -> Either e a
    return x = Right x
    (>>=) :: Either e a 
          -> (a -> Either e b) 
          -> Either e b
    Left e  >>= _ = Left e
    Right x >>= f = f xinstance Monad [] where
    return :: a -> [a]
    return x = [x]
    (>>=) :: [a] -> (a -> [b]) -> [b]
    l >>= f = concatMap f lβΉοΈ Either
βΉοΈ List
instance Monad Maybe where
    return :: a -> Maybe a
    return x = Just x
    ...π Correct Monad instance for Maybe
Left identity
Right identity
Associativity
instance Monad Maybe where
    return :: a -> Maybe a
    return x = Nothing
    ...π Incorrect Monad instance for Maybe
andThen ::            Maybe    a  -> (a -> Maybe    b)  -> Maybe    b
andThen ::            Either e a  -> (a -> Either e b)  -> Either e b
andThen ::                    [a] -> (a ->         [b]) ->         [b]π Similar chaining functions
(>>=)   :: Monad m => m        a  -> (a -> m        b)  -> m        bπ Similar constructors
Just   ::            a -> Maybe    a
Right  ::            a -> Either e a
(:[])  ::            a ->         [a]    -- robot monkey operatorreturn :: Monad m => a -> m        amaybePlus :: Maybe Int -> Maybe Int -> Maybe Int
maybePlus ma mb = andThen ma (\a -> andThen mb (\b -> Just (a + b)))0οΈβ£ Starting with the basics
maybePlus :: Maybe Int -> Maybe Int -> Maybe Int
maybePlus ma mb = ma `andThen` (\a -> mb `andThen` (\b -> Just (a + b)))maybePlus :: Maybe Int -> Maybe Int -> Maybe Int
maybePlus ma mb = ma `andThen` \a -> mb `andThen` \b -> Just (a + b)1οΈβ£ Using infix form of andThen
2οΈβ£ Removing redundant ()
maybePlus :: Maybe Int -> Maybe Int -> Maybe Int
maybePlus ma mb = ma >>= \a -> mb >>= \b -> return (a + b)monadPlus :: Monad m => m Int -> m Int -> m Int
monadPlus ma mb = ma >>= \a -> mb >>= \b -> return (a + b)3οΈβ£ Replacing `andThen` with >>=Β and Just with return
4οΈβ£Generalizing the type and changing the name
class Functor f where
    fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
    pure  :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
    return :: a -> m a
    return = pure
    
    (>>=) :: m a -> (a -> m b) -> m bβΉοΈ Monad is a part of FAMily
π©βπ¬ Everyone uses pure instead of return nowadays
map :: (a -> b) -> [a] -> [b]π Pure functions β functions without side-effects that depend only on their explicit input arguments.
getLine :: Stringπ€ How would a function that reads a line would look like?
ghci> getTwoLines !! 1
???????π€π€ How about reading two lines??
getTwoLines :: [String]
getTwoLInes = [getLine, getLine]π€π€π€ How this should work???
π‘ We can't have side effects in Haskell without changing at least something. Otherwise, we'll lose the remainings of our sanity while working with the language.
data IO a = ...βΉοΈ Opaque data type IO
βΉοΈ Functions with side-effects
getLine :: IO Stringπ©βπ¬ getLine is a function that returns a value of type String and also performs some side-effects
βΉοΈ IO has the Monad instance
instance Monad IO where
    return :: a -> IO a
    (>>=)  :: IO a -> (a -> IO b) -> IO b
    ...π©βπ¬ The Monad instance for IO allows to chain effectful actions
getTwoLines :: IO [String]
getTwoLines =
    getLine >>= \line1 -> getLine >>= \line2 -> pure [line1, line2]String
IO String
βΉοΈ A helper function to print to the terminal
putStrLn :: String -> IO ()π©βπ¬ putStrLn is a function that takes a value of type String, prints it the terminal and doesn't return any meaningful value.
ghci> ()  -- a unit type
()
ghci> :t ()
() :: ()ghci> putStrLn "Hi! π"
Hi! πghci> :t getLine 
getLine :: IO String
ghci> getLine
Alice
"Alice"ghci> fmap length getLine
Bob
3module Main where
main :: IO ()
main = ... your program goes here ...π©βπ¬ Haskell program starts with the main function in the module Main
module Main where
main :: IO ()
main = putStrLn "Hello, world!"βWe want to print two different lines
putStrLn :: String -> IO ()π©βπ¬ We use the bind operator (>>=) and ignore the putStrLn resutl
module Main where
main :: IO ()
main = putStrLn "Hello, Alice!" >>= \_ -> putStrLn "Hello, Bob!"(>>) :: Monad m => m a -> m b -> m b
action1 >> action2 = action1 >>= \_ -> action2βΉοΈ A helper function
main :: IO ()
main = putStrLn "Hello, Alice!" >> putStrLn "Hello, Bob!"βΉοΈ Slightly shorter version
π do-notation β syntax sugar for >>=,Β >> and let-in
example1 =
    fun1 >>= \result -> fun2 resultexample1 = do
    result <- fun1 
    fun2 resultβΉοΈ >>= rule
example1 = fun1 >>= fun2Eta-reduced example1 with >>=
βΉοΈ >>Β rule
example2 = fun1 >> fun2example2 = do
    fun1 
    fun2βΉοΈ let-in rule
example3 = let x = f y in fun xexample3 = do
    let x = f y 
    fun xβοΈ Write a program that reads a line and prints its reverse
main :: IO ()
main =
    getLine >>= \line ->
    let rev = reverse line in
    putStrLn revmain :: IO ()
main = do
    line <- getLine
    let rev = reverse line
    putStrLn revπ Classic Monad Style
let line = getLinerev <- reverse lineπ do-Style
π Cabal β format for describing structure of Haskell packages.
my-project/
βββ app/
β   βββ Main.hs
βββ src/
β   βββ MyModule.hs
β   βββ AnotherModule.hs
βββ my-project.cabalcabal-version:      2.4
name:               my-project
version:            1.0.0.0
library
  hs-source-dirs:      src
  exposed-modules:     MyModule
                       AnotherModule
  build-depends:       base ^>= 4.14
executable my-project
    hs-source-dirs:   app
    main-is:          Main.hs
    build-depends:    my-project
                    , baseπ A Haskell package
π Files with the .cabalΒ file extension describe the package.Β
π cabal-install β a Haskell build tool that works with Cabal packages.
module Main (main) where
import qualified MyModule
main :: IO ()
main = MyModule.mainlibrary
  hs-source-dirs:      src
  exposed-modules:     MyModule
  build-depends:       base       ^>= 4.14
                     , containers ^>= 0.6
                     , text       ^>= 1.2
                     , time       ^>= 1.9π The build-depends field in the .cabal file specifies external Haskell packages and their versions you want to use.
π¦ base β standard Haskell library
π¦ containers β Map and Set data structures
π¦ text β efficient UTF-16 strings (UTF-8 since version 2.0)
π¦ bytestring β byte arrays
π¦ time β time, clocks and calendar data types and functions
π¦ aeson β JSON parsing
βΉοΈ Hackage has 16K+ packages
readFile   :: FilePath -> IO Text
countWords :: Text -> IO (Map Text Int)
printWord  :: Map Text Int -> IO ()π©βπ¬ Haskell allows controlling side effects. Separate pure logic from effectful computations and require IO only when needed.
βοΈ Write a program that finds the most frequent word in a file
π’ Imperative style
π€Έ Functional style
readFile      :: FilePath -> IO Text
countWords    :: Text -> Map Text Int
findFrequent  :: Map Text Int -> Maybe (Text, Int)
displayResult :: Maybe (Text, Int) -> Text
putStrLn      :: Text -> IO ()