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
Objects don't
parametric types
M A
Examples:
ListSetFutureMaybeParser// 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 B
newVal = val >>= g
newVal: 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
- 686