it’s all about morphisms - jax londoni don’t care about monads, why should i? p r e c i s e l y...
TRANSCRIPT
It’s All About MorphismsIt’s All About Morphisms
Uberto Barbini @ramtop https://medium.com/@ramtop
#VoxxedVienna#morphisms#morphisms @ramtop
About meprogrammer OOP
TDD
Kotlin
Agile
Functional PRogramming
Blog: https://medium.com/ ramtop@
Twitter: ramtop@
FinanceIndustry
#VoxxedVienna#morphisms#morphisms @ramtop
Map of this presentationCategory Monoid
Functor
Applicative
Monad
Natural Transformation
Morphisms all the way down...
Yoneda
#VoxxedVienna#morphisms#morphisms @ramtop
I don’t care about Monads, why should I?
Precisely
Neither do Iwhat I care about is
to define system behaviour
#VoxxedVienna#morphisms#morphisms @ramtop
This presentation will be a success if most of you will not fall asleep
This presentation will be a success if most of you will not fall asleep
#VoxxedVienna#morphisms#morphisms @ramtop
You will consider that Functional Programming is about transformations and
preserving properties.Not (only) lambdas and
flatmap
This presentation will be a success if
#VoxxedVienna#morphisms#morphisms @ramtop
What is this Category thingy?Invented in 1940s “with the goal of understanding the processes that preserve mathematical structure.”
“Category Theory is about relation between things”
“General abstract nonsense”
#VoxxedVienna#morphisms#morphisms @ramtop
Once upon a time there was a Category of Stuffed Toys and a
Category of Tigers...
#VoxxedVienna#morphisms#morphisms @ramtop
1) A collection of Objects
A Category is defined in 5 steps:
#VoxxedVienna#morphisms#morphisms @ramtop
2) A collection of Arrows
A Category is defined in 5 steps:
#VoxxedVienna#morphisms#morphisms @ramtop
3) Each Arrow works on 2 Objects
A Category is defined in 5 steps:
#VoxxedVienna#morphisms#morphisms @ramtop
4) Arrows can be combined
A Category is defined in 5 steps:
#VoxxedVienna#morphisms#morphisms @ramtop
5) Each Object has an Identity(an arrow pointing to itself)
A Category is defined in 5 steps:
#VoxxedVienna#morphisms#morphisms @ramtop
#VoxxedVienna#morphisms#morphisms @ramtop
A Category ExampleTube Map: Objects stations→ stations
Arrows travel routes→ stations
Each Arrows connect 2 stationsArrow composition is travelling along the lineIdentity Arrow is staying in the same station
#VoxxedVienna#morphisms#morphisms @ramtop
London Tube Category
#VoxxedVienna#morphisms#morphisms @ramtop
This presentation is a Category as well!
Category Monoid
Functor
Applicative
Monad
Natural TransformationYoneda
#VoxxedVienna#morphisms#morphisms @ramtop
Event Source Category
NewOrder Ready
CancelledReturned
Dispatched
Closed
https://skillsmatter.com/skillscasts/11486-functional-cqrs
AddItem
Cancel
Dispatch
ReturnClose
#VoxxedVienna#morphisms#morphisms @ramtop
Functional Programmingis also a Category
Each programming language has a Category:
Types are the objects and Functions are the morphisms.
Partial functions don’t have a defined return for all inputs.
In reality all programming functions are partial: they can raise Exceptions or never end.
They always have an hidden return of Bottom Type ( )⊥)
#VoxxedVienna#morphisms#morphisms @ramtop
Change of mindset!
Object Oriented FunctionalLiving Bacteria Gears and Pipes
Opaque TransparentHidden State Immutable StateInterfaces Type Classes
#VoxxedVienna#morphisms#morphisms @ramtop
Kotlin for functional programming● Nullable and not-nullable types● Type Aliases● Class extensions● Tail recursion● Pattern matching (when)● Arrow-kt bindinds with coroutines● Arrow-kt Typeclasses (?)
#VoxxedVienna#morphisms#morphisms @ramtop
arrow-kt.io
#VoxxedVienna#morphisms#morphisms @ramtop
KEEP-87 TypeClasses
#VoxxedVienna#morphisms#morphisms @ramtop
Purity and ImmutabilityFor the Category morphisms to work in programming we need Purity and Immutability.
But they are not a goal per se, only a necessity for the main goal: composition and transformation.
Ultimately everything is converted in assembly which is neither pure nor immutable. We need those quality only for exposed code
#VoxxedVienna#morphisms#morphisms @ramtop
MonoidWhat about the category of morphisms of a category?Are they composable?It’s a Category with a single Object and lots of Morphisms
A Category with only one Object is a Monoid
#VoxxedVienna#morphisms#morphisms @ramtop
#VoxxedVienna#morphisms#morphisms @ramtop
Programming with MonoidsA type class with two methods
combine monoid append→ stations
empty neutral element→ stations
(x <> y) <> z = x <> (y <> z) -- associativity
empty <> x = x -- left identity
x <> empty = x -- right identity
#VoxxedVienna#morphisms#morphisms @ramtop
Generics Type Contructors
List<A> is just an abstract type to build List<Int> List<String> List<User> etc.
#VoxxedVienna#morphisms#morphisms @ramtop
Type Class vs Interface● Interfaces “unify” different types:
Cat and Dogs can be treated as Animals● Type Classes “group” types with similar behaviour, without
hiding their types: Cats and Dogs can both form couples but cats can mate only with cats and dogs with dogs. You cannot represent that with interfaces.
#VoxxedVienna#morphisms#morphisms @ramtop
Typeclass Instances ● Typeclasses work with instances (like a singleton)● List is not a Monoid nor a Functor nor a Monad but it
has an (or more) instance of Monoid one of Functor and one of Monad
● Technically we implement instances as an interface with a singletons specific implementation.
● We can have different implementation for difference in evaluation, for example because of concurrency
#VoxxedVienna#morphisms#morphisms @ramtop
Enough talk, let’s see the code!
MonoidTypeClass Instances...
...Give us the combine extension function
#VoxxedVienna#morphisms#morphisms @ramtop
The future (?)extension interface Monoid<T> {
infix fun T.add(t: T): T
}
extension object IntMonoid: Monoid<Int> {
inline fun Int.add(t: Int) = this + t
}
inline fun <T> sum(t1: T, t2: T, t3: T, with Monoid<T>) = t1 add t2 add t3
fun main() {
sum(1, 2, 3) //no boxing because of inlining
}
#VoxxedVienna#morphisms#morphisms @ramtop
Transformers a.k.a. Functors
Very Important!!
#VoxxedVienna#morphisms#morphisms @ramtop
FunctorsFunctors can map both objects (types) and morphism (functions) between two categories
Functors map must preserve the structure and some properties
But can also work inside the same category (Endofunctors)
#VoxxedVienna#morphisms#morphisms @ramtop
Functor LawsFunctor is a TypeClass with a Map function that works like this. Id is the identity function.
map id x = x
map (g <> f) = map g <> map f)
Passing the ID function must return the original value
Map must honour associativity of two functions
#VoxxedVienna#morphisms#morphisms @ramtop
#VoxxedVienna#morphisms#morphisms @ramtop
Try Functor
Failure without Exception
Keep a context and Map operation on `it
#VoxxedVienna#morphisms#morphisms @ramtop
Functors are also Forklifts
val lifted = Try.functor().lift {x:String -> x.toInt()}
lifted(Try.Success("42"))
//Try.Success(42)
Lift a function from A --> B to F<A> --> F<B>
#VoxxedVienna#morphisms#morphisms @ramtop
Yoneda LemmaYoneda's lemma concerns functors from a fixed category C to the category of sets, Set. If C is a locally small category (i.e. the hom-sets are actual sets and not proper classes), then each object A of C gives rise to a natural functor to Set called a hom-functor.
#VoxxedVienna#morphisms#morphisms @ramtop
Yoneda LemmaF<?>.map(A→B): F<B> (? must be A)
The actual implementation is useful to compose of mappings without executing until we decide
combine all the maps in one and then apply it.No copies of List
#VoxxedVienna#morphisms#morphisms @ramtop
Natural TransformationsA natural transformation provides a way of transforming one functor into another while respecting the internal structure of the categories involved.
Transforming Data → Functions
Transforming Functions → Functors
Transforming Functors → Natural Transformations
#VoxxedVienna#morphisms#morphisms @ramtop
Natural Transformations
#VoxxedVienna#morphisms#morphisms @ramtop
Natural Transformations val list = Try {"3".toInt()}.toOption().toList()
//[3]
val fail = Try {"xyz".toInt()}.toOption().toList()
//[]
#VoxxedVienna#morphisms#morphisms @ramtop
Combining FunctorsWe can imagine 2 ways to combine 2 functors
F + F = F F<f> map F<a> = F<f(a)> or F * F = F flatmap (a → F<a → F<a>>) = F a
#VoxxedVienna#morphisms#morphisms @ramtop
Applicative Functors● We can combine a value inside a Functor with a function
inside another Functor● If the function want more than 1 param, it will return a
function with x-1 params. ● Applicative can apply: ● F<A> applied to F<A->B> to create F<B>
#VoxxedVienna#morphisms#morphisms @ramtop
Try Applicative Functor
Silly example of function that can raise an Exception
Exception raised!
#VoxxedVienna#morphisms#morphisms @ramtop
The M word...● What about a category of (endo)functors?● Some functors have a monoid instance, others not. ● How can we call the Category of Endofunctors with a
Monoid instance?
A Monad is just a Monoid in the category of Endofunctors, what's the problem?
#VoxxedVienna#morphisms#morphisms @ramtop
Monad Recipe
#VoxxedVienna#morphisms#morphisms @ramtop
Monads Allow Sequences of Instructions
#VoxxedVienna#morphisms#morphisms @ramtop
Monads BindingFrom here:val university: IO<University> = getStudent("John Smith").flatMap { student -> getUniversity(student.universityId).flatMap { university -> getDean(university.deanId) } }
To here:val university: IO<University> = IO.monad().binding { val student = getStudent("John Smith").bind() val university = getUniversity(student.universityId).bind() val dean = getDean(university.deanId).bind() dean }
#VoxxedVienna#morphisms#morphisms @ramtop
Functional Programming Dilemma:
Perfectly Pure Programs are Perfectly Useless
Enter Effect
s
#VoxxedVienna#morphisms#morphisms @ramtop
Dependency Injectioncourtesy of Reader Monad
What’s happen if user cannot be fetched?
Runs here
fun getUser(userId:String):Reader<Context, User> {fun getCommonFriends(u1: User, u2: User): Reader<Context, List<User>>
#VoxxedVienna#morphisms#morphisms @ramtop
Monads ZooAll Monads are also Functors and Applicative, but the opposite is not true.
Each Monad as it’s specific logic on top of the Monads Laws
These are already available in many libraries but you can extend and create your own.
● Option
● Try
● List
● Either
● Reader
● Writer
● State
● IO
● Free
#VoxxedVienna#morphisms#morphisms @ramtop
Why studying Categories?
Morphisms allows you to work at compile time with your Domain Knowledge
Learn how to compose functions and preserve properties
Monads and other typeclasses should emerge from your code, not the other way round
Learn a common terminology and the reasons behind that
#VoxxedVienna#morphisms#morphisms @ramtop
How to use morphisms in real world programs?
#VoxxedVienna#morphisms#morphisms @ramtop
Real World ExamplesWeb Server as a function github.com/http4k/http4k
Functional Domain Design github.com/uberto/anticapizzeria
#VoxxedVienna#morphisms#morphisms @ramtop
To learn more:Category Theory for Programmers bartoszmilewski.com
Cats in scala typelevel.org/catsArrow in Kotlin arrow-kt.io
If you enjoyed please follow me on twitter and medium ramtop@ramtop
#VoxxedVienna#morphisms#morphisms @ramtop
#VoxxedVienna#morphisms#morphisms @ramtop
#VoxxedVienna#morphisms#morphisms @ramtop
#VoxxedVienna#morphisms#morphisms @ramtop
#VoxxedVienna#morphisms#morphisms @ramtop
Endofunctors● In programming we only work with Endofunctors in the
Category of our types● Endofunctor map a => F a where F a is a type
constructor in the same category of a (the type system category)
● Endofunctors are very important because we can compose them!
#VoxxedVienna#morphisms#morphisms @ramtop
Higher Kinded TypesIn Java we cannot abstract at more than one level like List<A<B>> or ignoring the second level List<A<?>>
But in Functional programming makes sense generalise over the container or context like:X<A> + f(A)->B = X<B>
#VoxxedVienna#morphisms#morphisms @ramtop
Monads Laws
"The diagram commutes" means that the map produced by following any path through the diagram is the same.
#VoxxedVienna#morphisms#morphisms @ramtop
List Applicative
Can you guess how it work?
Transform a standard List in a Arrow List (ListK)
#VoxxedVienna#morphisms#morphisms @ramtop
List Map2
Exactly same result
#VoxxedVienna#morphisms#morphisms @ramtop
Reader: A Functor on functions Val userId = 123
val reader = Reader{dbUrl:String -> DbConn(dbUrl)}
val name = reader.run("myDbConn")
.map{it.getUser(userId) }
.map{it.name}
.value()
//"Joe"
#VoxxedVienna#morphisms#morphisms @ramtop
Purity Bubbles + Events = Actors
#VoxxedVienna#morphisms#morphisms @ramtop
"Rather than thinking about function purity as a goal, you have to think about which properties you want your program to preserve." @raulraja
Perfectly Pure Programs are Perfectly Useless
#VoxxedVienna#morphisms#morphisms @ramtop
London Tube Map is a Monoid! (if you squint hard enough)
#VoxxedVienna#morphisms#morphisms @ramtop
Ok… but why using a Object Oriented Language for Functional Programming at all?
#VoxxedVienna#morphisms#morphisms @ramtop
Conclusions:why functional programming?
More composition
Less boilerplate
Less repetitions
Less bugs
Less need for tests
Easier concurrency
Less sugar
Different thinking
Performance issues
Complex state
More precision
Different testing style
VS
#VoxxedVienna#morphisms#morphisms @ramtop
Monads Transformers
● Monads don’t compose
#VoxxedVienna#morphisms#morphisms @ramtop
Validated example
● Better than Try
#VoxxedVienna#morphisms#morphisms @ramtop
Kleisli ArrowsOriginal problem: Monads don’t allow for concurrency
Arrows can be useful in Reactive Functional Programming
Kleisli is a type of Arrow for a Monadic context
It’s old ReaderT actually
#VoxxedVienna#morphisms#morphisms @ramtop
Arrows
#VoxxedVienna#morphisms#morphisms @ramtop
CandyDispenser
● Code example● Let’s bring all together● We have a function Seed (Seed, Candy)→ stations
● Another one (State, Input) State→ stations
● How can we combine them?● We want
(Dispenser, [Input]) (Dispenser, [Candy])→ stations
#VoxxedVienna#morphisms#morphisms @ramtop
Adjoint FunctorsFunctor F from category D to C
Functor G from C to D
Every adjunction 〈 F, G, , ε, η η 〉 gives rise to an associated monad 〈 T, , η μ 〉 in the category D.