Functional composition with monads

Yuriy Taras

about me

Technical lead @ SoftServe
Java @ Past
Ruby @ Work
Haskell & Scala @ Home

ytaras.github.io @ WWW
yura.taras @ gmail.com

brian berkman

data

String name
int age
double weight

code

String upcase(String)
int fac(int)

Pure function

fac(0) = 1
fac(1) = 1
fac(2) = 2
fac(3) = 6
...

Data

name: String
age: ing
weight: double
fac: int -> int
upcase: String -> String

Monoid

A // Set
A ⊹ A : A // Addition
zero: A

Clock monoid

a ⊹ b = (a + b) % 12
zero = 12

Monoid laws

(a ⊹ b) ⊹ c = a ⊹ (b ⊹ c)
zero ⊹ a = a ⊹ zero = a 

Monoid examples

  • Integer - +, 0
  • Integer - *, 1
  • String - +, ""
  • List - append, []
  • Other

Monoid ~ composition


Function chain

f: A -> A
g: A -> A
c : A -> A
c x = f (g x)
// c x = g(f x) 

Function is data

∘: (A -> A) -> (A -> A) -> (A -> A) 
c = g ∘ f
c x = g (f x)
// Think Unix pipes 

example

> :t upcase
upcase :: String -> String
> :t stripSpace
stripSpace :: String -> String
> let upcaseStripped = upcase . stripSpace
> upcaseStripped "hello world"
"HELLOWORLD"
> :t upcaseStripped
upcaseStripped :: String -> String 

Function monoid

(A -> A) ∘ (A -> A) == (A -> A)
 zero = id

id: A -> A
id x = x 

monoidal category

relax the monoid constant

f: A -> B
g: B -> C
c = g ∘ f
(B -> C) ∘ (A -> B) -> (A -> C)

example

> :t digitToInt
digitToInt :: Char -> Int
> :t head
head :: String -> Char -- Not it's real type
> let firstDigitToInt = digitToInt . head
> :t firstDigitToInt
firstDigitToInt :: String -> Int
> firstDigitToInt "1sadfsdaf"
1 

so what

Functions have monoids
Objects don't

Functions compose
Objects don't

parametric types

M A

Examples:
List
Set
Future
Maybe
Parser
// Tons of other

loss of composability

f: A -> M A
g: A -> M A

// They don't compose
// c = g ∘ f
> :t splitByComma
splitByComma :: String -> [String]
> :t words
words :: String -> [String]
> let coolSplit = splitByComma . words
...
    Expected type: String -> String
      Actual type: String -> [String]
...

Kleisi arrow

f: A -> M A
g: A -> M A

c = f >=>  g c : A -> M A // f gets called first, unlike (f . g)
> let splitBySpaceAndComma =
   splitByComma >=> words
> :t splitBySpaceAndComma
splitBySpaceAndComma :: String -> [String]
> splitBySpaceAndComma "h,el,lo w,orl,d,"
["h","el","lo","w","orl","d"]

zero

zero = return // Awful name!!!
zero = wrap   // is it any better?

wrap: A -> M A 

not yet a monad

M -- container type
(>=>): (A -> M A) -> (A -> M A)        -> (A -> M A)
wrap: A -> M A 

monad laws

(f >=> g) >=> h === f >=> (g >=> h)
f >=> wrap === wrap >=> f === f 

monoidal category

more generic kleisi arrow

f: A -> M B
g: B -> M C

h = f >=> g
h: A -> M C
val: M BnewVal = val >>= gnewVal: M C

but if we have instance

(A -> M B) >=> (B -> M C) === A -> M C
      M B  >>= (B -> M C) ===      M C
x >>= f = (\_ -> x) >=> f $ ()
g >=> f = \x -> (g x) >>= g

example

> ["123","456"] >>= stringToInts
[1,2,3,4,5,6]
> :t stringToInts
stringToInts :: String -> [Int]

monads

containers

  • List
  • Maybe
  • Set

Computations

  • Future
  • Parser

IdEOLOGICAL

  • IO
  • Reader
  • Writer
  • State

monad syntax

haskell

main = do
    print "What is your name?"
    name <- getLine
    print ("Hello " ++ name ++ "!")

scala 

val purchase = for {
  usd <- usdQuote
  chf <- chfQuote
  if isProfitable(usd, chf)
} yield connection.buy(amount, chf) 


C#

var res = from n in TryReadInt()
          from m in TryReadInt()
          select n + m;
ytaras.github.io @ WWW
yura.taras @ gmail.com
sampik @ twitter

Functional composition with monads

By Yura Taras

Functional composition with monads

  • 676