lecture4 ::

  forall (m :: *) . Monoid m

  ⇒ [m] → m

lecture4 =

  foldr mappend mempty

Types really can help!

What can go wrong?..

But types can't help you much if you don't use their true power...

newtype Hash = MkHash String
class Hashable a where
    hash :: a -> Hash
hashPair :: Int -> Hash
hashPair n = hash (n, n)
hashPair :: Int -> Hash
hashPair n = hash n  -- oops, this is valid definition!

Phantom types

newtype Hash a = MkHash String -- `a` is a phantom type, not present in constructor
class Hashable a where
    hash :: a -> Hash a
hashPair :: Int -> Hash (Int, Int)
hashPair n = hash (n, n)
hashPair :: Int -> Hash (Int, Int)
hashPair n = hash n  -- This is no longer a valid definition!
Hash.hs:7:14: error:
    • Couldn't match type ‘Int’ with ‘(Int, Int)’
      Expected type: Hash (Int, Int)
        Actual type: Hash Int
    • In the expression: hash n
      In an equation for ‘hashPair’: hashPair n = hash n

Limitations of such approach?

Some real life examples

Crypto

newtype Signature a = Signature ByteString

sign :: Binary t => SecretKey -> t -> Signature t

path package: well-types paths

data Abs  -- absolute path
data Rel  -- relative path

data Dir  -- directory
data File -- file

newtype Path b t = Path FilePath

-- appending paths
(</>) :: Path b Dir -> Path Rel t -> Path b t

o-clock package: time-safe units

data Second
data Microsecond  -- rough approximation

newtype Time unit = Time (Ratio Natural)

So you should understand types well enough to embrace their true power

or ¬∀?

Can anybody completely explain the forall keyword in clear, plain English?

No.

Who said OOP?

showAll :: Show a => [a] -> [String]
showAll = map show 
ghci> showAll [2, 3]
["2", "3"]
ghci> showAll [1, "ABA"]  -- ??
    No instance for (Num [Char]) arising from the literal ‘1’
    In the expression: 1
    In the first argument of ‘showAll’, namely ‘[1, "ABA"]’
    In the expression: showAll [1, "ABA"]

a type variable is fixed

ghci> [False, "ABA"]  -- fails
  Couldn't match expected type ‘Bool’ with actual type ‘[Char]’
  In the expression: "ABA"
  In the expression: [False, "ABA"]
  In an equation for ‘it’: it = [False, "ABA"]

More types

ghci> reverse [2, 1, 3]
[3,1,2]
ghci> reverse [True, False]
[False,True]
applyTwo :: ([Int], [Bool])
applyTwo = let call f = (f [2, 1, 3], f [True, False]) in call reverse  -- (*)
-- but we get:
(*):29:
    No instance for (Num Bool) arising from the literal ‘2’
    In the expression: 2
    In the first argument of ‘f’, namely ‘[2, 1, 3]’
    In the expression: f [2, 1, 3]

(*):15:
    Couldn't match type ‘Int’ with ‘Bool’
    Expected type: ([Int], [Bool])
      Actual type: ([Int], [Int])
    In the expression: call reverse
    In the expression: let call f = (f ..., f ...) in call reverse

(*):20:
    Couldn't match type ‘Bool’ with ‘Int’
    Expected type: [Bool] -> [Int]
      Actual type: [Int] -> [Int]
    In the first argument of ‘call’, namely ‘reverse’
    In the expression: call reverse
-- and we want
gchi> applyTwo
([3,1,2], [False,True])

forall

id :: a -> a
id x = x
-- real type with explicit forall
id :: forall a . a -> a
id x = x

Read as:

For every type a this function

can be considered to have type a → a

forall example

ghci> applyTwo id        -- works like magic
([2,1,3],[True,False])
ghci> applyTwo reverse
([3,1,2],[False,True])
applyTwo :: (forall a . [a] -> [a]) -> ([Int], [Bool])
applyTwo f = (f [2, 1, 3], f [True, False])
applyTwo :: ([a] -> [a]) -> ([Int], [Bool])             -- doesn't compile
applyTwo f = (f [2, 1, 3], f [True, False])
ghci> (\(a, b) -> (reverse a, reverse b)) ("hello", [2, 1, 3])  -- ??
applyTwo :: forall a . ([a] -> [a]) -> ([Int], [Bool])  -- equivalent form ^
applyTwo f = (f [2, 1, 3], f [True, False])

Instead we should....

Does this work and why?

More generic applyTwo

applyTwo :: ([a] -> [a]) -> ([Int], [Bool])
applyTwo f = (f [2, 1, 3], f [True, False])

Trying to understand why this doesn't work

Let's make it more generic!

applyTwo :: ([a] -> [a]) -> [b] -> [c] -> ([b], [c])
applyTwo f x y = (f x, f y)
    • Couldn't match type ‘b’ with ‘a’
      ‘b’ is a rigid type variable bound by
        the type signature for:
          applyTwo :: forall a b c. ([a] -> [a]) -> [b] -> [c] -> ([b], [c])
ghci> applyTwo id [2,1,3] [True,False]
([2,1,3],[True,False])
ghci> applyTwo reverse [True,False] "patak"
([False,True],"katap")
applyTwo :: (forall a . [a] -> [a]) -> [b] -> [c] -> ([b], [c])
applyTwo f x y = (f x, f y)
reverse :: forall a . [a] -> [a]
applyTwo :: forall b c . (forall a . [a] -> [a]) -> [b] -> [c] -> ([b], [c])
applyTwo f x y = (f x, f y)

Type of reverse matches better our last implementation

Slide 13: <undefined error>

undefined :: forall a . a  -- intersection of all types
undefined = ⊥

What is undefined in Haskell (true language)?

What is undefined in bad languages?

{-# LANGUAGE RankNTypes #-}

Rank 0: Int
Rank 1: forall a . a -> Int
Rank 2: (forall a . a -> Int) -> Int           -- could be enabled by Rank2Types
Rank 3: ((forall a . a -> Int) -> Int) -> Int

A function type has rank n + 1 when its argument has rank n.

Int -> Int

Exercises:

Int -> Int                                   -- rank 0
forall a . a -> a
forall a . a -> a                            -- rank 1

The rank of a type describes the depth at which universal quantifiers appear in a contravariant position, i.e. to the left of a function arrow.

(forall a . a -> a) -> Int
(forall a . a -> a) -> Int                   -- rank 2
Int -> (forall a . a -> a) 
forall a . Int -> a -> a                     -- rank 1
forall a b . a -> b -> a
forall a b . a -> b -> a                     -- rank 1 
forall a . a -> (forall b . b -> a)
(a -> a) -> (forall b . b -> b) -> (c -> c)
forall a b . a -> b -> a                     -- rank 1
(a -> a) -> (forall b . b -> b) -> (c -> c)  -- rank 2

So you probably have such question:

Where I need forall in real life?

Show everything existed

showAll :: Show a => [a] -> [String]
showAll = map show
showAll :: forall a . Show a => [a] -> [String]
showAll = map show
showAll :: Show a => [forall a . a] -> [String]  -- 'a' not in scope for Show
showAll = map show
showAll :: [forall a . Show a => a] -> [String]  -- only bottoms
showAll = map show
{-# LANGUAGE ExistentialQuantification #-}

data ShowBox = forall a . Show a => SB a  -- existental constructor
showAll :: [ShowBox] -> [String]
showAll = map (\(SB a) -> show a)
ghci> showAll [SB (), SB 1, SB True]  -- again, this magic works
["()","1","True"]
showAll :: forall a . [Show a => a] -> [String]  -- ImpredicativeTypes (broken)
showAll = map show

You can simulate OOP-style of programming now!

Okay, I promised real life example...

ghci> incShow "3"  -- why does this work at all?
"4"
ghci> incShow "3.0"
"*** Exception: Prelude.read: no parse

How about this?

We want to deserialiaze data, perform some transformations, and then serialize back. Oh, and we want single function that will work for every type automatically.

-- incShow :: ???
incShow = show . (+1) . read
ghci> :t read  -- our simple 'deserialize' function
read :: Read a => String -> a
ghci> :t show  -- our simple 'serialize' function
show :: Show a => a -> String
incShow :: String -> String
incShow = show . (+1) . read

Type variable 'a' is not visible in type! How can we specify type?...

How to solve this problem in OOP world, btw?

Prepare for long story...

What a wonderful type system

-- v1.0.0: also compiles
prepend2 :: Int -> [Int] -> [Int]
prepend2 x xs = pair ++ xs 
  where pair = [x, x]
-- v3.0.0: doesn't compile!
prepend2 :: a -> [a] -> [a]
prepend2 x xs = pair ++ xs 
  where pair :: [a]
        pair = [x, x] 
-- v0.0.0: this compiles
prepend2 :: Int -> [Int] -> [Int]
prepend2 x xs = pairFun x ++ xs 
  where pairFun y = [y, y]
-- v2.0.0: compiles or not?
prepend2 :: a -> [a] -> [a]
prepend2 x xs = pair ++ xs 
  where pair = [x, x]
-- v2.1.0: everything works!
prepend2 :: a -> [a] -> [a]
prepend2 x xs = pairFun x ++ xs 
  where pairFun :: a -> [a]
        pairFun y = [y, y] 

Closer look at compiler error

-- v3.0.0: doesn't compile!
prepend2 :: a -> [a] -> [a]
prepend2 x xs = pair ++ xs 
  where pair :: [a]
        pair = [x, x] 
Forall.hs:18:17: error:
    • Couldn't match expected type ‘a1’ with actual type ‘a’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          prepend2 :: forall a. a -> [a] -> [a]
        at Forall.hs:15:1-27
      ‘a1’ is a rigid type variable bound by
        the type signature for:
          pair :: forall a1. [a1]
        at Forall.hs:17:9-19
    • In the expression: x
      In the expression: [x, x]
      In an equation for ‘pair’: pair = [x, x]
    • Relevant bindings include
        pair :: [a1] (bound at Forall.hs:18:9)
        xs :: [a] (bound at Forall.hs:16:12)
        x :: a (bound at Forall.hs:16:10)
        prepend2 :: a -> [a] -> [a] (bound at Forall.hs:16:1)
   |
18 |         pair = [x, x] 
   |                 ^

We need some scoping...

prepend2 :: a -> [a] -> [a]
prepend2 x xs = pair ++ xs 
  where 
    pair :: [a]
    pair = [x, x] 
id :: a -> a

This definition just...

...contains implicit forall!

id :: forall . a -> a

Inner forall shadows top-level forall!

prepend2 :: forall a . a -> [a] -> [a]
prepend2 x xs = pair ++ xs 
  where 
    pair :: forall a . [a]
    pair = [x, x] 

Same for functions inside where block!

-- it's basically the same reason why this doesn't compile
prepend2 :: forall a b . b -> [a] -> [a]
prepend2 x xs = [x, x] ++ xs 

-XScopedTypeVariables

{-# LANGUAGE ScopedTypeVariables #-}

prepend2 :: forall a . a -> [a] -> [a]
prepend2 x xs = pair ++ xs 
  where 
    pair :: [a]  -- uses same type variable 'a'
    pair = [x, x] 

-XScopedTypeVariables language extension allows to use type variables from top-level function signature inside this function body (including where block). Works only with forall keyword!

-XTypeApplications

ghci> read "3"   :: Int
3
ghci> read "3.0" :: Double
3.0
-- in some artifical syntax...
read :: (a :: Type) -> (_ :: String) -> (x :: a)

How many arguments read has?

read :: forall a . Read a => String -> a
ghci> :t read
read :: Read a => String -> a
ghci> read @Int "3"
3
ghci> read @Double "3.0"
3.0
ghci> :t read @Int
read @Int :: String -> Int
ghci> read "3"   :: Int  -- so, here we basically just pass Int type
3
ghci> read "3.0" :: Double
3.0

-XTypeApplications: visible type application

Now we can pass types as arguments to functions!

-XAllowAmbiguousTypes

-- do you see problems with this code?
class Size a where
    size :: Int
    • Could not deduce (Size a)
      from the context: Size a
        bound by the type signature for:
                   size :: forall a. Size a => Int
{-# LANGUAGE AllowAmbiguousTypes #-}

class Size a where size :: Int
instance Size Int    where size = 8
instance Size Double where size = 16
ghci> size  -- this all looks great, but how can I call 'size' function??????

    • Ambiguous type variable ‘a0’ arising from a use of ‘size’
      prevents the constraint ‘(Size a0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘a0’ should be.
      These potential instances exist:
        instance [safe] Size Double -- Defined at Forall.hs:12:10
        instance [safe] Size Int -- Defined at Forall.hs:9:10
ghci> size @Double  -- use -XTypeApplications
16
-- simple plain haskell
class Size a where
    size :: a -> Int

Show must go on

-- v0.0.0: this doesn't work obviously
incShow :: String -> String
incShow = show . (+1) . read

Result

ghci> incShow @Int "3"
"4"
ghci> incShow @Double "3.2"
"4.2"
ghci> incShow @Rational "3 % 5"
"8 % 5"
-- v0.0.1: why this doesn't work?
incShow :: (Read a, Show a, Num a) => String -> String
incShow = show . (+1) . read
{-# LANGUAGE AllowAmbiguousTypes #-}

-- v0.0.2: why still doesn't work??
incShow :: (Read a, Show a, Num a) => String -> String
incShow = show . (+1) . read
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeApplications    #-}

-- v0.0.3: why still this doesn't work???
incShow :: (Read a, Show a, Num a) => String -> String
incShow = show . (+1) . read @a
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}

-- v1.0.0: this works!
incShow :: forall a . (Read a, Show a, Num a) => String -> String
incShow = show . (+1) . read @a

New best friends

{-# LANGUAGE Rank2Types #-}

{-# LANGUAGE RankNTypes #-}

{-# LANGUAGE ExplicitForAll #-}

{-# LANGUAGE ExistentialQuantification #-}

{-# LANGUAGE ScopedTypeVariables #-}

{-# LANGUAGE TypeApplications #-}

{-# LANGUAGE AllowAmbiguosTypes #-}

Kinds

Types of types

ghci> :kind Int
Int :: *
ghci> :kind Char
Char :: *
ghci> :kind Maybe
Maybe :: * -> *
ghci> :kind Maybe String
Maybe :: *
ghci> :kind (Maybe Maybe)  -- ??
data MapTree k v 
  = Leaf 
  | Node k v (MapTree k v) (MapTree k v)

MapTree          :: * -> * -> *
MapTree a        :: * -> *
MapTree String v :: *

In FP everything is a function. So you think of types as of functions. And types also can be partially applied.

List type

ghci> :info []
data [] a = [] | a : [a] 	-- Defined in ‘GHC.Types’
...
ghci> :kind []
[] :: * -> *                    -- kind of list
ghci> :kind [] Int
[] Int :: *                     -- `[] a` is the same as [a]
ghci> :kind [String]
[String] :: *

Arrow type

ghci> :info (->)
data (->) t1 t2 	-- Defined in ‘GHC.Prim’
infixr 0 `(->)`
...
ghci> :k (->)
(->) :: * -> * -> *  -- kind of function
ghci> :k (->) Int
(->) Int :: * -> *     

-- ((->) Int) is the same as (Int -> )

Why care about kinds?

ghci> data Computable a f = Computation (f a) a
ghci> :kind Computable
Computable :: * -> (* -> *) -> *

Your types can be parametrized by higher-kinded types!

ghci> :kind Computable String
Computable String :: (* -> *) -> *
ghci> :kind Computable Int Maybe
Computable Int Maybe :: *
ghci> :kind Computable (Maybe String) ((->) Int)
Computable (Maybe String) ((->) Int) :: *

⇒ new level of abstraction

newtype IntComputation f = MkIntComp (f Int)    -- option 1: new data type
type    IntComputation f = Computable Int f     -- option 2: specialization
ghci> :k IntComputation  -- in both cases
IntComputation :: (* -> *) -> *

Not convinced?

You must understand compiler errors

Kind polymorphism

ghci> :set -XKindSignatures
ghci> :set -XPolyKinds
ghci> data PolyComputable (f :: k -> *) (a :: k) = PolyComputation (f a)
ghci> :kind PolyComputable 
PolyComputable :: (k -> *) -> k -> *

Parametric kinds (remeber: types are functions)

ghci> :k PolyComputable Maybe
PolyComputable Maybe :: * -> *
ghci> :k PolyComputable IntComputation 
PolyComputable IntComputation :: (* -> *) -> *

Still don't see how many levels of abstractions you can have and how awesome and parametric code you can write? Well...

Higher-kinded classes

class Box b where  -- b :: * -> *
    box   :: a -> b a
    unbox :: b a -> a

Not possible without types of higher kind (but possible in Haskell)

instance Box Maybe where  -- "instance Box (Maybe a) where" is compilation error
    box = Just            -- η-reduce from: box a = Just a
    
    unbox (Just a) = a
    unbox Nothing  = error "Can't unbox from empty context"
instance Box [] where
    box :: a -> [a]
    box x     = [x]

    unbox :: [a] -> a
    unbox [x] = x
boxCall :: Box m => (a -> b) -> m a -> m b
boxCall f b = box $ f $ unbox b
ghci> boxCall (+1) (Just 4)
Just 5

What kind of Haskell is it???

class Num a where ...
ghci> :kind Num
??

Hmmm, what if...

ghci> :kind Num
Num :: * -> Constraint
{-# LANGUAGE ConstraintKinds #-}

type MyConstraints a = (Read a, Num a, Show a)

foo :: MyConstraints a => String -> a -> a

You can create aliases for constraints!

ghci> :set -XRankNTypes 
ghci> type ConstraintEndomorphism p a = p a => a -> a
ghci> :kind ConstraintEndomorphism 
ConstraintEndomorphism :: (* -> Constraint) -> * -> *

And much more

Constraint aliases in real life

Expands to something like this...

Big alias

Math through types

OOP vs. Math

-- not in Haskell standard libraries
class Magma m where
    meld :: m -> m -> m

In OOP you invent programming patterns.

In FP you discover programming patterns.

1. (++)
2. max/min
3. x ○ y = xy + x^2 - y
-- actually in 'base'
class Semigroup m where
    (<>) :: m -> m -> m
Associativity law for Semigroup: 
  1. (x <> y) <> z ≡ x <> (y <> z)
1. (++)
2. max/min

Semigroups

Monoids

-- also in 'base' but...
class Semigroup m => Monoid m where
    mempty :: m
Identity laws for Monoid: 
  2. x <> mempty ≡ x
  3. mempty <> x ≡ x
1. (++)

Semigroup list instance

class Semigroup a where
    (<>)    :: a -> a -> a
    sconcat :: NonEmpty a -> a
    stimes  :: Integral b => b -> a -> a
instance Semigroup [a] where
    (<>) = (++)
ghci> [1..5] <> [2,4..10]
[1,2,3,4,5,2,4,6,8,10]
>>> 3 * list(range(1,6))
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

Python3

ghci> concat (replicate 3 [1..5])
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

Haskell

No!

ghci> 3 `stimes` [1..5]
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

Semigroup numeric instances

instance Semigroup Int where
    -- Which one to choose?
    1. (<>) = (+)
    2. (<>) = (*)

We can combine numbers as well. But how?

newtype Sum     a = Sum     { getSum     :: a }
newtype Product a = Product { getProduct :: a }
instance Num a => Semigroup (Sum a) where
  Sum x <> Sum y = Sum (x + y)

instance Num a => Semigroup (Product a) where
  Product x <> Product y = Product (x * y)

Haskell way:

emulate multiple instances for one type with multiple newtypes

ghci> 3 <> 5 :: Sum Int
Sum { getSum = 8 }
ghci> 3 <> 5 :: Product Int
Product { getProduct = 15 }

More Semigroup instances

newtype Max   a = Max   { getMax   :: a    }  -- max
newtype Min   a = Min   { getMin   :: a    }  -- min
newtype Any     = Any   { getAny   :: Bool }  -- ||
newtype All     = All   { getAll   :: Bool }  -- &&
newtype First a = First { getFirst :: a    }  -- fst
newtype Last  a = Last  { getLast  :: a    }  -- snd

Use same approach for different situations

ghci> Max 3 <> Max 10 <> Max 2
Max { getMax = 10 }
ghci> Min 3 <> Min 10 <> Min 2
Min { getMin = 2 }

ghci> Any True <> Any False <> Any True
Any { getAny = True }
ghci> All True <> All False <> All True
All { getAll = False }

ghci> First 5 <> First 10 <> First 1
First { getFirst = 5 }
ghci> Last 5 <> Last 10 <> Last 1
Last { getLast = 1 }

One <> to rule them all

Welcome Monoid

-- not a descendant of Semigroup for now
class Monoid a where
    mempty  :: a
    mappend :: a -> a -> a
    mconcat :: [a] -> a
instance Monoid [a] where
    mempty          = []
    l1 `mappend` l2 = l1 ++ l2

And all the rest is almost same...

instance (Monoid a, Monoid b) => Monoid (a,b) where
    mempty                    = (         mempty,          mempty)
    (a1,b1) `mappend` (a2,b2) = (a1 `mappend` a2, b1 `mappend` b2)

Not exactly monoid...

newtype First a = First { getFirst :: a }
newtype Last  a = Last  { getLast  :: a }

What about First and Last? How to write mempty ?

instance Semigroup a => Monoid (Maybe a) where
    mempty                    = Nothing
    Nothing `mappend` m       = m
    m       `mappend` Nothing = m
    Just m1 `mappend` Just m2 = Just (m1 <> m2)

With Maybe any Semigroup can be a Monoid

Last and First from Data.Monoid module:

newtype First a = First { getFirst :: Maybe a }
newtype Last  a = Last  { getLast  :: Maybe a }

Function is a Monoid

instance Monoid b => Monoid (a -> b) where
    mempty _ = mempty
    mappend f g x = f x `mappend` g x

I told you, everything is Monoid

ghci> (replicate 3 <> replicate 2) 1
[1, 1, 1, 1, 1]

And what does it mean?

Interesting Ordering of slides...

data Ordering = LT | EQ | GT

instance Monoid Ordering where
    mempty         = EQ
    LT `mappend` _ = LT
    EQ `mappend` y = y
    GT `mappend` _ = GT
module Data.Ord (comparing, ...) where

comparing :: Ord a => (b -> a) -> b -> b -> Ordering
comparing p x y = compare (p x) (p y)

Why we need such instance?

data ErrorPosition = ErrorPosition 
    { path   :: String
    , line   :: Int
    , offset :: Int 
    } deriving (Eq)

-- this implementation uses Monoid instances of (->) and Ordering
instance Ord ErrorPosition where
    compare = comparing path <> comparing line <> comparing offset

Lexicographically sort data types

Monoids in real life

Composability is what FP for

Vertical scaling vs. Horizontal scaling

1. Combination of plugins is a plugin

2. Combination of configurations is configuration of the same type

3. Combination of event sources is an event source

4. Combination of clusters is a cluster

5. Combination of parsers is a parser

6. Combination of streams is a stream

7. Combination of functions is a function

and so on...

data Options = Options
  { oRetryCount    :: Int
  , oHost          :: String
  , oCharacterCode :: Maybe Char
  } deriving (Show, Eq)

Goal: we want to have options. We want to create multiple versions of records and combine them (defaults + CLI + file config)

Use monoid! (full details in blog post)

data PartialOptions = PartialOptions
   { poRetryCount    :: Last Int
   , poHost          :: Last String
   , poCharacterCode :: Last (Maybe Char)
   } deriving (Show, Eq)
instance Monoid PartialOptions where
    mempty = PartialOptions mempty mempty mempty
    mappend x y = PartialOptions 
        { poRetryCount    = poRetryCount    x <> poRetryCount    y
        , poHost          = poHost          x <> poHost          y
        , poCharacterCode = poCharacterCode x <> poCharacterCode y
        }

And so on...

Holey Monoid

newtype HoleyMonoid m r a = HoleyMonoid { runHM :: (m -> r) -> a }

Monoid instance for the following data type:

ghci> format ("Person's name is " % text %  ", age is " % hex) "Dave" 58
"Person's name is Dave, age is 3a"

Allows to write formatting like this ( formatting library)

Beyond Monoid to the stars

Fold

foldr and foldl

foldr :: (a -> b -> b) -> b -> [a] -> b
foldl :: (b -> a -> b) -> b -> [a] -> b 

Simple generalization of recursion

foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b 

Generalization of generalization

ghci> foldr (+) 0 [2, 1, 10]
13
ghci> foldr (*) 3 [2, 1, 10]
60
foldr (\s rest -> rest + length s) 0 ["aaa", "bbb", "s"]
foldr (\rest s -> rest + length s) 0 ["aaa", "bbb", "s"]

Which one correct?

Foldable type class

-- | Simplified version of Foldable
class Foldable t where
    {-# MINIMAL foldMap | foldr #-}

    fold    :: Monoid m => t m -> m
    foldMap :: Monoid m => (a -> m) -> t a -> m
    foldr   :: (a -> b -> b) -> b -> t a -> b

Some basic instances

instance Foldable [] where
    foldr :: (a -> b -> b) -> b -> [a] -> b
    foldr _ z []     =  z
    foldr f z (x:xs) =  x `f` foldr f z xs
instance Foldable Maybe where
    foldr :: (a -> b -> b) -> b -> Maybe a -> b
    foldr _ z Nothing  = z
    foldr f z (Just x) = f x z

Reasons to drop Haskell

ghci> length (4, [1,2,3])
???
ghci> foldr (+) 1 (Just 3)
4
ghci> foldr (+) 0 Nothing
0
ghci> length (4, [1,2,3])
1

Grocking foldr

foldr (+) 0 (1:2:3:[]) ≡ 1+2+3+0

Think of folds as replacing constructors, i.e. ':', '[]', with functions/values.

ghci> import Debug.SimpleReflect
ghci> sum [1..5] :: Expr 
0 + 1 + 2 + 3 + 4 + 5
ghci> foldr f x [a,b,c]
f a ( f b ( f c x ) )

Monoidal parsing

ghci> balanced "(())"
True
ghci> balanced "(())("
False
ghci> balanced ")(())("
False

We want verify that sequance round brackets is balanced

data B = B Int Int
instance Monoid B where
    mempty = B 0 0
    mappend (B a b) (B c d)
        | b <= c    = B (a + c - b) d
        | otherwise = B a (d + b - c)
parse :: Char -> B
parse '(' = B 0 1
parse ')' = B 1 0
parse _   = B 0 0
balanced :: String -> Bool
balanced xs = foldMap parse xs == mempty

Exercises & challenges

secondMax :: Ord a => [a] -> Maybe a

ghci> secondMax [2, 1, 3]
Just 2
mconcat :: Monoid m => [m] -> m
foldMap :: (Foldable f, Monoid m) => (a -> m) -> f a -> m
foldr :: Foldable f => (a -> b -> b) -> b -> f a -> b

1. Default implementation of mconcat

2. foldMap using foldr

4*   foldr using foldMap

5*  second maximum in list using foldr

fold :: (Foldable f, Monoid m) => f m -> m

3. Default implementation of fold

Literature for all lazy people

Haskell Lecture 04: Kinda monoidal types

By Dmitrii Kovanikov

Haskell Lecture 04: Kinda monoidal types

Lecture about forall keyword, RankNTypes, kinds several standard type classes: Semigroup, Monoid, Foldable.

  • 673