Advanced Functional Programming in F#
Deep dive into map
let rec map f list =
match list with
| [] -> []
| x::xs -> (f x) :: (map f xs)Can we map into other things?
Category Theory

Container Type
Type Container<'a> = Container of 'a
let map f xCont =
match xCont with
| Container x -> Container (f x)
> map (fun x -> x + 1) (Container 1)
// val it : int Container = Container 2
Functors
- Generic data type
- Map function
- functor laws
Map
- Names: map, fmap, lift, select
- Operators: <$>, <!>, $
- Signature: (a -> b) -> E<a> -> E<b>
Identity
-
Names: identity, id
-
Operators : None
-
Signature: a -> a
Option
type Option <'a> =
| Some of 'a
| None
let map f xOpt =
match xOpt with
| Some x -> Some (f x)
| None -> None
let (<!>) = map
let id x = x
//Example
let add1 x = x + 1
let someone = Some 1
>add1 <!> someone
//val it : int option = Some 2
>id someone
//val it : int option = Some 1List
let rec map f list =
match list with
| [] -> []
| x::xs -> (f x) :: (map f xs)
let (<!>) = map
let id x = x
//Example
let add1 = x + 1
let myList = [1; 2; 3]
>add1 <!> myList
//val it : int list = [2; 3; 4]
>id myList
//val it : int list = [1; 2; 3]Functor Laws (in Haskell)
Identity: fmap id = id
Composition: fmap (g . f) = fmap g . fmap fApplicative Functors
- Generic Data Type
- Apply and Pure functions
- Applicative Functor Laws
Pure
- Names: return, pure, unit, yield, point
- Operators: none
- Signature: a -> E<a>
Apply
- Names: apply, ap
- Operators: <*>
- Signature: E<(a->b)> -> E<a> -> E<b>
Option
let pure x = Some x
let apply fOpt xOpt =
match fOpt, xOpt with
| Some f, Some x -> Some (f x)
| _ -> None
let (<*>) = apply
//Example
let add x y = x + y
> (Some (add 1)) <*> (Some 2)
//val it : int option = Some 3
> add <!> (Some 1) <*> (Some 2)
//val it : int option = Some 3
> pure add <*> (Some 1) <*> (Some 2)
//val it : int option = Some 3List
let pure x = [x]
let apply fList xList =
[ for f in fList do
for x in xList do
yield f x ]
let (<*>) = apply
//Example
let add x y = x + y
>[(add 1); (add 2)] <*> [3; 4]
//val it : int list = [4; 5; 5; 6]
>add <!> [1; 2] <*> [3; 4]
//val it : int list = [4; 5; 5; 6]
>pure add <*> [1; 2] <*> [3; 4]
//val it : int list = [4; 5; 5; 6]
Applicative Functor Laws
Identity: pure id <*> v = v
Homomorphism: pure f <*> pure x = pure (f x)
Interchange: u <*> pure y = pure ($ y) <*> u
Composition: pure (.) <*> u <*> v <*> w = u <*> (v <*> w)Lift2
- Names: lift2, lift3, .. liftN
- Operators: None
- Signature for Lift2: (a -> b -> c) -> E<a> -> E<b> -> E<c>
LiftN
let lift2 f x y =
f <!> x <*> y
let lift3 f x y z =
f <!> x <*> y <*> z
let lift4 f x y z w =
f <!> x <*> y <*> z <*> w
let lift5 f x y z w v =
f <!> x <*> y <*> z <*> w <*> v
let lift6 f x y z w v u =
f <!> x <*> y <*> z <*> w <*> v <*> u
let lift7 f x y z w v u t =
f <!> x <*> y <*> z <*> w <*> v <*> u <*> t
>lift2 add (Some 1) (Some 2)
//val it : int option = Some 3Redefining map
let map f x =
(pure f) <*> x
let map' f x =
x |> apply (pure f)Redefining Apply
let lift2' f x y =
match x, y with
| Some z, Some w -> Some (f z w)
| _ -> None
let apply' fOpt xOpt =
lift2 (fun g x -> g x) fOpt xOptCombiners
let (<*) x y =
lift2 (fun left right -> left) x y
let (*>) x y =
lift2 (fun left right -> right) x y
> [1;2] <* [3;4;5]
//val it : int list = [1; 1; 1; 2; 2; 2]
> [1;2] *> [3;4;5]
//val it : int list = [3; 4; 5; 3; 4; 5]ZipList
let rec zipList fList xList =
match fList,xList with
| [],_ | _,[] -> []
| (f::fs),(x::xs) ->
(f x) :: (zipList fs xs)
let add10 x = x + 10
let add20 x = x + 20
let add30 x = x + 30
let (<*>) = zipList
>[add10; add20; add30] <*> [1; 2; 3]
//val it : int list = [11; 22; 33]Monads
- Generic Data Types
- Bind and Return functions
- Monad Laws
Return
- Names: return, pure, unit, yield, point
- Operators: none
- Signature: a -> E<a>
Bind
- Names: bind, flatMap, andThen, collect, SelectMany
- Operators: >>=, =<<
- Signature: (a -> E<b>) -> E<a> -> E<b>
Option
let bind f xOpt =
match xOpt with
| Some x -> f x
| None -> None
let (>>=) x f = bind f x
let (=<<) = bind
let retn x = Some x
//Example
let isApple phone =
if phone = "apple" then Some phone
else None
> (Some "apple") >>= isApple
//val it : string option = Some "apple"
> (Some "android") >>= isApple
//val it : string option = None
> None >>= isApple
//val it : string option = NoneList
let bind f list =
[ for x in list do
for y in f x do
yield y ]
let (>>=) x f = bind f x
let (=<<) = bind
let retn x = [x]
//Example
let add1 x = [x + 1]
>[1; 2; 3] >>= add1
//val it : int list = [2; 3; 4]
let evens x = if x % 2 = 0 then [x] else []
[1; 2; 3] >>= evens
//val it : int list = [2]
[] >>= evens
//val it : int list = []Monad Laws
right unit: m >>= return = m
left unit: return x >>= f = f x
associativity: (m >>= f) >>= g = m >>= (\x -> f x >>= g)Redefining Map
let map f =
bind (f >> retn)
Redefining Apply
let apply f x =
f |> bind (fun g -> map g x)Kliesli Composition
let (>=>) f g x =
match f x with
| Some s -> g s
| None -> None
//Example
let validate =
checkName
>> bind checkAge
>> bind checkHeight
let validate' =
checkName
>=> checkAge
>=> checkHeightAlternate Bind
//val map : f:('a -> 'b) -> x:'a option -> 'b option
let map f x =
match x with
| Some x -> Some (f x)
| None -> None
//val join : x:'a option option -> 'a option
let join x =
match x with
| Some x -> x
| None -> None
// val bind : f:('a -> 'b option) -> x:'a -> 'b option
let bind f x =
join (map f x)Example
//Example
let answer = Some "42"
let (|Int|_|) str =
match System.Int32.TryParse(str) with
| (true,int) -> Some(int)
| _ -> None
let whatIsTheMeaningOfLife x =
match x with
| Int i -> Some i
| None -> None
>map whatIsTheMeaningOfLife answer
//val it : int option option = Some (Some 42)
>join (map whatIsTheMeaningOfLife answer)
//val it : int option = Some 42
>bind whatIsTheMeaningOfLife answer
//val it : int option = Some 42
Map, Apply & Bind
Signatures
id: a -> a
pure: a -> E<a>
map: (a -> b) -> E<a> -> E<b>
apply: E<(a -> b)> -> E<a> -> E<b>
bind: (a -> E<b>) -> E<a> -> E<b>Scale of Power
let replicate n x =
[1..n] *> [x]
> ((*)2) <!> [2;5;6]
val it : int list = [4; 10; 12] //Length or context does not change
> [((*)2);((*)3)] <*> [2;5;6]
val it : int list = [4,10,12,6,15,18]
//Context does not change, length is the product of the two lists
> [1; 2; 5] >>= (fun x -> replicate x x);;
val it : int list = [1; 2; 2; 5; 5; 5; 5; 5]
> [0; 0; 0] >>= (fun x -> replicate x x);;
val it : int list = []
//Context does not change in F# (in Haskell it can), length is dynamic
Applicative vs Monadic Style
Independent Data: Applicative
Dependent Data: Monadic
> lift3 diplayKingdoms getBacteria getArchaea getEukaryota
\\ val it : string list =
[ "Bacteria";"Archaea";"Excavata";"Amoebozoa";
"Opisthokonta";"Rhizaria";"Chromalveolata";"Archaeplastida" ]> let prog data = data |> validateData >> bind saveToDb
> prog goodData
\\val it : Result<'a> = Success goodData
> prog badData
\\val it : Result<'a> = Failure ["Failed to validate."]
Traversables
- Generic data type
- Traverse and Sequence functions
- Traversable Laws
Traverse
- Names: mapM, traverse, for
- Operators: none
- Signature: (a -> E<b>) -> a list -> E<b list>
Sequence
- Names: sequence
- Operators: none
- Signature: E<a> list -> E <a list>
Traverse Option
let cons x xs = x::xs
let rec traverseOptionA f list =
match list with
| [] -> pure []
| x::xs -> pure cons <*> (f x) <*> (traverseOptionA f xs)
let rec traverseOptionM f list =
match list with
| [] -> retn []
| x::xs ->
f x >>= (fun y ->
traverseOptionM f xs >>= (fun z ->
retn (cons y z) ))
//Example
let add1 x = if x < 3 then Some (x + 1) else None
>traverseOptionA add1 [1; 2; 3]
//val it : int list option = Some [2; 3; 4]
>traverseOptionA add1 [1; 2; 3; 4]
//val it int list option = None
>traverseOptionM add1 [1; 2; 3]
//val it : int list option = Some [2; 3; 4]
>traverseOptionM add1 [1; 2; 3; 4]
//val it int list option = NoneSequence Option
let sequenceOptionA x = traverseOptionA id x
let sequenceOptionM x = traverseOptionM id x
//Example
>sequenceOptionA [(Some 1); (Some 2); (Some 3)]
//val it : int list option = Some [1; 2; 3]
>sequenceOptionA [(Some 1); (Some 2); None]
//val it : int list option = None
>sequenceOptionM [(Some 1); (Some 2); (Some 3)]
//val it : int list option = Some [1; 2; 3]
>sequenceOptionM [(Some 1); (Some 2); None]
//val it : int list option = None
Redefining Traverse
let traverseOptionA f list =
let initState = pure []
let folder x xs =
pure cons <*> (f x) <*> xs
List.foldBack folder list initState
let traverseOptionM f list =
let initState = retn []
let folder x xs =
f x >>= (fun y ->
xs >>= (fun z ->
retn (cons x xs) ))
List.foldBack folder list initStateDemo
Resources

Want more?
- Functional programming
- Catamorphisms and Foldables
- Monoids
- Lenses
- F# Features
- Computational expressions
- Type providers
- Functional programming in Javascript
Advanced Functional Programming in F#
By cgoboncan_ebsi
Advanced Functional Programming in F#
- 1,333