SlideShare ist ein Scribd-Unternehmen logo
1 von 83
Bestiary of FP
Tomasz Kogut - @almendar
2016.09.03
with Cats
About me:
• Software / Data Engineer at
• Develops AdTech Platform
• Research department
• Works on processing lots of data really fast
• Scala since ~2011
Warning!
• This presentation is intended to be practical guide how
to start with FP and cats
• May contain inaccurate information on purpose
• May contain simply wrong information because of my
mistake for which I sincerely apologize - I’m no expert
• Questions during presentation will be highly
appreciated
Why this talk? (1)
• FP is a different kind of beast
• Does not reassembles anything you know
• Reading blogs do not help, it makes it probably even
worse
• It’s frustrating
Why this talk? (2)
• There is much to learn
• Accepting this is the first step you have to make
• I’ve made some of the hops and I want to share so
you don’t have to go through some of the daunting
things
What FP is all about?
(1)
• Functions?
• Higher order functions?
• Immutability?
• Referential transparency?
What FP is all about?
(2)
• How stupid it may sound FP is about values
• Values are: bits, ints, data structures,
functions, types
• What FP does for us it allows us to
combine values and look at them as a new
value
• This is what we’ll be doing throughout the
presentation
Combining
values
Product
class Foo
class Bar
case class FooBarProduct(
foo: Foo,
bar: Bar
)
Tuples and cartesian product anyone?
Semigroup
Take two values and produce one
trait Semigroup[A] {
}
trait Semigroup[A] {
def combine(a1: A, a2: A): A
}
Group-like structures
Totalityα Associativity Identity Divisibility Commutativity
Semicategory Unneeded Required Unneeded Unneeded Unneeded
Category Unneeded Required Required Unneeded Unneeded
Groupoid Unneeded Required Required Required Unneeded
Magma Required Unneeded Unneeded Unneeded Unneeded
Quasigroup Required Unneeded Unneeded Required Unneeded
Loop Required Unneeded Required Required Unneeded
Semigroup Required Required Unneeded Unneeded Unneeded
Monoid Required Required Required Unneeded Unneeded
Group Required Required Required Required Unneeded
Abelian Group Required Required Required Required Required
https://en.wikipedia.org/wiki/Outline_of_algebraic_structures
Why “Semigroup” and not “ValuesCombiner”
object SuperAdder {
def add[A : Semigroup](a1: A, a2: A): A = Semigroup[A].combine(a1,a2)
}
case class ComplexNumber(x: Double,i: Double)
object ComplexNumber {
implicit val semiGroup = new Semigroup[ComplexNumber] {
def combine(a1: ComplexNumber, a2: ComplexNumber): ComplexNumber =
ComplexNumber(a1.x+a2.x, a1.i + a2.i)
}
}
SuperAdder.add(1,2) //res1: Int = 3
SuperAdder.add(ComplexNumber(1.2, 2.3), ComplexNumber(2.4, 3.4)) //res2: ComplexNum
SuperAdder.add("Hello ", "world") //res3: String = Hello world
What about Semigroup
for List, Option,
Future?
<console>:13: error: class Option takes type parameters
val optionSemigroup = new Semigroup[Option] {}
val optionSemigroup = new Semigroup[Option[Int]] {}
This would do the job but we can do better
Higher Kinded Types
• Option, List ect. are type constructors.
Like normal constructors but for types.
• They take a type as argument
• Some say that they have a “hole”
• Scala compiler knows about this
scala> :kind -v String
java.lang.String's kind is A
*
This is a proper type.
scala> :kind -v Either
scala.util.Either's kind is F[+A1,+A2]
* -(+)-> * -(+)-> *
This is a type constructor: a 1st-order-kinded type.
scala> :kind -v Option
scala.Option's kind is F[+A]
* -(+)-> *
This is a type constructor: a 1st-order-kinded type
Now the scary part
scala> :kind -v trait Foo[X[_]]
Foo's kind is X[F[A]]
(* -> *) -> *
This is a type constructor that takes type constructor(s): a higher-kin
at when you put inside a type that when put inside another type create
trait SemigroupK[F[_]] {
def combineK[A](fa1: F[A], fa2: F[A]): F[A]
}
implicit val optionSemigroupK = new SemigroupK[Option] {
def combineK[A](fa1: Option[A], fa2: Option[A]): Option[A] =
fa1 orElse fa2
}
implicit val listSemigroup = new SemigroupK[List] {
def combineK[A](fa1: List[A], fa2: List[A]): List[A] = fa1 ++ fa2
}
listSemigroup.combineK(List(1,2,3), List(4,5,6))
//res3: List[Int] = List(1, 2, 3, 4, 5, 6)
SemigroupK
• TypeK is a thing
• “K” stands for kind
• Our effect works on higher-kinder types
• SemigroupK, MonoidK, FunctionK
Semigroup(K) in Cats
• Both effects are provided by cats
• For std types there are ready instances
• The whole thing is to do the right imports
import cats._
import cats.implicits._
Semigroup[Int].combine(1,2)
//res0: Int = 3
SemigroupK[List].combineK(List(Some(1), Some(3)), List(Some(2)))
//res1: List[Some[Int]] = List(Some(1), Some(3), Some(2))
Semigroup[Int]
What is this?
object Semigroup extends SemigroupFunctions[Semigroup] {
@inline
final def apply[A](implicit ev: Semigroup[A]): Semigroup[A] = ev
}
Creating Effects
• This is very common in Cats
• TypeConstructor[X].someMethod
• Heavy use of implicits and packages
objects
• import cats._
make all types available in scope like
Semigroup, Monad, Applicative ect.
Like Predef for Scala.
• import cats.implicits._
puts ALL implicit into the scope using
package object trick
package object instances {
object all extends AllInstances
object either extends EitherInstances
object function extends FunctionInstances
object list extends ListInstances
object option extends OptionInstances
object set extends SetInstances
object stream extends StreamInstances
object vector extends VectorInstances
object map extends MapInstances
object future extends FutureInstances
object string extends StringInstances
object int extends IntInstances
object byte extends ByteInstances
object long extends LongInstances
object char extends CharInstances
object short extends ShortInstances
object float extends FloatInstances
object double extends DoubleInstances
object boolean extends BooleanInstances
object unit extends UnitInstances
object bigInt extends BigIntInstances
object bigDecimal extends BigDecimalInstances
object try_ extends TryInstances
object tuple extends TupleInstances
}
package object syntax {
object all extends AllSyntax
object applicative extends ApplicativeSyntax
object applicativeError extends ApplicativeErrorSyntax
object apply extends ApplySyntax
object bifunctor extends BifunctorSyntax
object bifoldable extends BifoldableSyntax
object bitraverse extends BitraverseSyntax
object cartesian extends CartesianSyntax
object coflatMap extends CoflatMapSyntax
object coproduct extends CoproductSyntax
object comonad extends ComonadSyntax
object compose extends ComposeSyntax
object contravariant extends ContravariantSyntax
object either extends EitherSyntax
object eq extends EqSyntax
object flatMap extends FlatMapSyntax
object foldable extends FoldableSyntax
object functor extends FunctorSyntax
object functorFilter extends FunctorFilterSyntax
object group extends GroupSyntax
object invariant extends InvariantSyntax
object list extends ListSyntax
object monadCombine extends MonadCombineSyntax
object monadFilter extends MonadFilterSyntax
object monoid extends MonoidSyntax
object option extends OptionSyntax
object order extends OrderSyntax
object partialOrder extends PartialOrderSyntax
object profunctor extends ProfunctorSyntax
object reducible extends ReducibleSyntax
object semigroup extends SemigroupSyntax
object semigroupk extends SemigroupKSyntax
object show extends Show.ToShowOps
object split extends SplitSyntax
object strong extends StrongSyntax
object transLift extends TransLiftSyntax
object traverse extends TraverseSyntax
object traverseFilter extends TraverseFilterSyntax
object tuple extends TupleSyntax
object validated extends ValidatedSyntax
object writer extends WriterSyntax
}
package cats
object implicits extends syntax.AllSyntax with instances.AllInstances
Instances
trait ListInstances extends cats.kernel.instances.ListInstances {
implicit val catsStdInstancesForList:
TraverseFilter[List]
with MonadCombine[List]
with Monad[List]
with CoflatMap[List]
with RecursiveTailRecM[List] =
new TraverseFilter[List]
with MonadCombine[List]
with Monad[List]
with CoflatMap[List]
with RecursiveTailRecM[List] {
def combineK[A](x: List[A], y: List[A]): List[A] = x ++ y
(...)
}
MonadCombine
Alternative
MonoidK
SemigroupK
Syntax
trait SemigroupSyntax {
implicit def catsSyntaxSemigroup[A: Semigroup](a: A):
SemigroupOps[A] = new SemigroupOps[A](a)
}
final class SemigroupOps[A: Semigroup](lhs: A) {
def |+|(rhs: A): A = macro Ops.binop[A, A]
def combine(rhs: A): A = macro Ops.binop[A, A]
}
List(1) |+| List(2, 2, 4)
We can do both:
List(1) combine List(2, 2, 4)
Monoid(K)
trait Monoid with Semigroup[A] {
def empty: A
}
@typeclass
trait MonoidK[F[_]] extends SemigroupK[F] { self =>
def empty[A]: F[A]
}
• @typeclass annotation comes from simulacrum
• It generates code
• “Jump to source” might not work in Cats src code
Monoid examples
• type: Int
combine: +
empty: 0
• type: Int
combine: *
empty: 1
• type: String
combine: +
empty: “”
Can we always create a monoid?
type NEL[A] = (A, List[A])
val nelMonoid = new MonoidK[NEL] {
def combineK[A](n1: NEL[A], n2: NEL[A]): NEL[A] =
(n1._1, (n1._2 :+ n2._1) ++ n2._2)
def empty[A]: NEL = ???
}
• One may end-up writing effect class for some data
structure that it is impossible
• http://stackoverflow.com/questions/32477292/fold-on-
nonemptylist/32479640#32479640
• It took me about 2 hours to realize this
trait Ord
case object GT extends Ord
case object LT extends Ord
case object EQ extends Ord
object Ord {
implicit object OrderingMonoid extends Monoid[Ord] {
def empty(): Ord = EQ
def combine(x:Ord, y: Ord): Ord = x match {
case EQ => y
case LT => LT
case GT => GT
}
}
}
Monoid example
def palindromeFirst(s1: String, s2: String): Ord
def shorterFirst(s1: String, s2: String): Ord
val res = List(palindromeFirst _, shorterFirst _).map{ f =>
f("ANNA", “BARBARA”)
}
Foldable[List].fold(res)(implicit OrderingMonoid)
// res0(: Ord = GT
Foldable
• Fold is surprisingly powerful
http://www.cs.nott.ac.uk/~pszgmh/fold.pdf
• Foldable has most of the collections api on it:
find, exists, forAll, filter, isEmpty,
• It allows to reduce collection to single element
• It can make use of Monoids
Foldable and monoid
example 2
def foldMap[A, B](fa: F[A])(f: A => B)(implicit B: Monoid[B]): B =
foldLeft(fa, B.empty)((b, a) => B.combine(b, f(a)))
val lineItems: List[LineItem] = ...
//explicit summoning Foldable
val totalInvoiceValue = Foldable[List].foldMap(lineItems){_.value}
//using syntax ops
val totalInvoiceValue = lineItems.foldMap { _.value }
Functors
trait Functor[F[_]] {
def map[A,B](fa:F[A])(f: A=>B): F[B]
}
Functor[List].map(List(1,2,3))(_ + 1)
Composing functors
val k = Functor[Try] compose Functor[List] compose Functor[Option]
k.map(Success(List(Some(22), Some(33), None)))(_+1)
//res19: scala.util.Try[List[Option[Int]]] = Success(List(Some(23), Some(34), None)))
Applicatives
The story of derived combinators
//we want map for this function
val addTwoInts = {(_:Int) + (_:Int)}
addTwoInts(2,3) //res0:Int = 5
trait Applicative[F[_]] {
def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C]
def unit[A](a: => A): F[A]
}
Let’s get some stuff for free!!!
Applicatives
trait Applicative[F[_]] {
def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C]
def unit[A](a: => A): F[A]
}
trait Applicative[F[_]] {
def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C]
def unit[A](a: => A): F[A]
def map[A,B](fa:F[A])(f: A=>B): F[B] =
map2(fa, (): Unit))((a,_) => f(a))
}
trait Applicative[F[_]] {
def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C]
def unit[A](a: => A): F[A]
def map[A,B](fa:F[A])(f: A=>B): F[B] =
map2(fa, (): Unit))((a,_) => f(a))
def product[A,B](fa: F[A], fb: F[B]): F[(A,B)] =
map2(fa,fb)((a,b) => (a,b))
}
trait Applicative[F[_]] {
def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C]
def unit[A](a: => A): F[A]
def map[A,B](fa:F[A])(f: A=>B): F[B] =
map2(fa, (): Unit))((a,_) => f(a))
def product[A,B](fa: F[A], fb: F[B]): F[(A,B)] =
map2(fa,fb)((a,b) => (a,b))
def lift[A,B,C](x: A => B): F[A] => F[B] = { fa:F[A] =>
map(fa)(x)
} //pimp my API
}
trait Applicative[F[_]] {
def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C]
def unit[A](a: => A): F[A]
def map[A,B](fa:F[A])(f: A=>B): F[B] =
apply(unit(f))(fa)
def product[A,B](fa: F[A], fb: F[B]): F[(A,B)] =
map2(fa,fb)((a,b) => (a,b))
def lift[A,B,C](x: A => B): F[A] => F[B] = { fa:F[A] =>
map(fa)(x) } //pimp my func
def traverse[A,B](as: List[A])(f: A => F[B]): F[List[B]] =
as.foldRight(unit(List[B]()))((a,fbs) => map2(f(a), fbs)
{ _ :: _ }
)
}
trait Applicative[F[_]] {
(…)
def unit[A](a: => A): F[A]
def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C]
def map3[A,B,C,D](fa: F[A], fb: F[B], fc: F[C])
(f:(A,B,C) => D): F[D] =
map2(map2(map2(unit(f.curried), fa)(_(_)), fb)(_(_)), fc)(_(_))
}
def curried[A,B,C](f: (A,B) => C): A => B => C = { a => { b =>
f(a,b)
}}
trait Applicative[F[_]] {
(…)
def map3[A,B,C,D](fa: F[A], fb: F[B], fc: F[C])
(f:(A,B,C) => D): F[D] =
map2(map2(map2(unit(f.curried), fa)(_(_)), fb)(_(_)), fc)(_(_))
}
trait Applicative[F[_]] {
(…)
def map3[A,B,C,D](fa: F[A], fb: F[B], fc: F[C])
(f:(A,B,C) => D): F[D] =
map2(map2(map2(unit(f.curried), fa)(_(_)), fb)(_(_)), fc)(_(_))
def apply[A,B](fab: F[A=>B])(fa:F[A]): F[B] =
map2(fab,fa)(_(_))
}
trait Applicative[F[_]] {
(…)
def map3[A,B,C,D](fa: F[A], fb: F[B], fc: F[C])
(f:(A,B,C) => D): F[D] =
apply(apply(apply(unit(f.curried))(fa))(fb))(fc)
def apply[A,B](fab: F[A=>B])(fa:F[A]): F[B] =
map2(fab,fa)(_(_))
}
trait Applicative[F[_]] {
(…)
def map3[A,B,C,D](fa: F[A], fb: F[B], fc: F[C])
(f:(A,B,C) => D): F[D] =
apply(apply(apply(unit(f.curried))(fa))(fb))(fc)
def map4[A,B,C,D,E]
def map5[A,B,C,D,E,F]
def apply[A,B](fab: F[A=>B])(fa:F[A]): F[B] =
map2(fab,fa)(_(_))
}
mapN
Writting combinators
• It turn out that applicative can be expressed with
different set of operations
• map2 + unit
• apply + unit
def apply[A,B](fab: F[A=>B])(fa:F[A]): F[B] =
map2(fab,fa)(_(_))
def map2[A,B,C](fa: F[A], fb: F[B])(f: (A,B) => C): F[C] =
apply(apply(unit(f.curried))(fa))(fb)
map2 and apply
Usage of Applicative
case class Charts(
books: Seq[Book],
music: Seq[Album],
games: Seq[Game]
)
def topBooks: Future[Seq[Book]] = ???
def topMusic: Future[Seq[Album]] = ???
def topGames: Future[Seq[Album]] = ???
Applicative[Future].map3(topBooks, topMusic, topGames)(Charts(_,_,_))
Another use case is Validation
Cats Applicative helpers
import cats.implicits._
(topBooks |@| topMusic |@| topGames) map { Charts(_,_,_) }
Applicative builders using code generation
/* Very, very, very high level view on how this works */
class CartesianBuilderN {
def |@|(a: Applicative): CartesianBuilderN+1
def map(f: FunctionN): Applicative
}
Applicatives compose
val futOptAppl =
Applicative[Future] compose
Applicative[Option]
futOptAppl.map2(Future(Some(22)),Future(Some(33))) { _ + _ }
//res47: scala.concurrent.Future[Option[Int]] = Success(Some(55))
Monads
• You use them every day map/flatMap
• Monads are powerful abstraction
• They have most of the combinators
• At the same time not all data structures
can be expressed as Monads
class DBRepo[F[_]] {
def getUserLoging(id: Long)(implicit F: Monad[F]): F[String] =
F.pure(id.toString)
def getUserEmail(id: Long)(implicit F: Monad[F]): F[String] =
F.pure(id.toString)
def getUser(id: Long)(implicit F: Monad[F]) : F[User] = {
F.flatMap(getUserLoging(id)) { login =>
F.map(getUserEmail(id)) { email =>
User(login, email)
}
}
}
}
val repo1 = new DBRepo[Future]
val repo2 = new DBRepo[Task]
class DBRepo[F[_]] {
def getUserLoging(id: Long)(implicit F: Monad[F]): F[String] =
F.pure(id.toString)
def getUserEmail(id: Long)(implicit F: Monad[F]): F[String] =
F.pure(id.toString)
def getUser(id: Long)(implicit F: Monad[F]) : F[User] =
for {
login <- getUserLoging(id)
email <- getUserEmail(id)
} yield User(login, email)
}
val repo1 = new DBRepo[Future]
val repo2 = new DBRepo[Task]
Monads
for {
i <- List(Option(1), Option(2))
j <- List(Option(3), Option(4))
} yield i + j
Monads don’t compose (usually), so the two below won’t wor
Monad[List] compose Monad[Option]
Monads
this will, but it’s ugly
val p = for {
i <- List(Option(1), Option(2))
j <- List(Option(3), Option(4))
} yield {
for {
k <- i
l <- j
} yield k+l
}
Monad transformers
val k = for {
i <- OptionT(List[Option[Int]](Option(1), Option(2)))
j <- OptionT(List[Option[Int]](Option(3), Option(4)))
} yield i + j
…this will also and it’s nice:
Monad transformers
• Cats have multiple instances of those
• EitherT, IdT, OptionT, StateT, WriterT
• TypeT[F[_], A] wraps F[Type[A]]
• E.g. OptionT[List, Int] wraps List[Option[Int]]
Effectful functions
• A => F[B]
• Returned value in some kind of
effect/context
• More common than one might think
// Id => Future[Long]
def getCustomerById(long: Id): Future[Customer]
// CharSequence => Option[String]
def findFirstIn(source: CharSequence): Option[String]
//Int => List[Int]
def listFromZeroToN(n: Int): List[Int]
we want to combine those
Kleisli
final case class Kleisli[F[_], A, B](run: A => F[B])
• It has all the good’ol combinators: flatMap, map, compose, apply ect.
• Used for composing effectful functions
• What kind of combinator can you use depends on what F is
• If you can have implicit effect for F you can call certain methods
def map[C](f: B => C)
(implicit F: Functor[F]): Kleisli[F, A, C] =
Kleisli(a => F.map(run(a))(f))
def flatMap[C](f: B => Kleisli[F, A, C])
(implicit F: FlatMap[F]): Kleisli[F, A, C] =
Kleisli((r: A) => F.flatMap[B, C](run(r))((b: B) => f(b).run(r)))
Kleisli
(A => B) andThen (B => C) => (A => C)
(A => F[B]) andThen (B => F[C]) => won't work
Kleisli(A => F[B]) andThen Kleisli(B => F[C]) => Kleisli(A => F[C])
There is more…
• Xor
• State
• Validated
• FreeMonads and FreeApplicatives
• Show
• Traverse
Simple RPC
• Let’s build a quick RPC API with
focus on HTTP
• We’ll take building blocks from what
we’ve seen
package object http {
type Service[A,B] = Kleisli[Future, A, B]
}
package object http {
type Service[A,B] = Kleisli[Future, A, B]
type HttpService = Service[Request, Response]
}
package object http {
type Service[A,B] = Kleisli[Future, A, B]
type HttpService = Service[Request, Response]
//Future[Either[A, B]]
type DecodeResult[T] =
EitherT[Future, DecodeFailure, T]
}
object Service {
def lift[A,B](f: A => Future[B]): Service[A,B] = Kleisli(f)
}
object HttpService {
def apply(f: PartialFunction[Request, Response]):
HttpService = Service.lift(liftToAsync(f))
def liftToAsync[A,B](f: A => B): A => Future[B] =
(a: A) => Future(f(a))
}
val httpService = HttpService {
case r1 @ Request(Get, "/") => Response(Ok)
case r2 @ Request(Post, "/") = Response(NotFound)
}
Http.runService(httpService)
Server
Client
We can reuse the HttpService type
val httpClient: HttpService = ???
val jsonResponseFromPipeline = httpService.map(_.body[Json])
val jsonFut: Future[DecodeResul[Json]] =
jsonResponseFromPipeline(Request(Get,"/"))
class AHClientWrapper(realClient: AHClient)
extends Request => Future[Response] {
def apply(req: Request): Future[Response] = {
//call realClient and return response
}
}
val httpClient: HttpService =
Kleisli(new AHClientWrapper(new AHClient))
Client
httpService.map(_.body[Json]) // Kleisli[Future, Request, Json]
//implementation
case class Response(code: HttpCode) extends Message {
def body[A](implicit decoder: EntityDecoder[A]):
DecodeResult[A] =
decoder.decode(this)
}
Decoding
trait EntityDecoder[T] { self =>
def decode(msg: Message): DecodeResult[T]
def map[T2](f: T => T2): EntityDecoder[T2] =
new EntityDecoder[T2] {
override def decode(msg: Message): DecodeResult[T2]
= self.decode(msg).map(f)
}
}
type DecodeResult[T] =
EitherT[Future, DecodeFailure, T]
This map is interesting
object EntitiyDecoder {
implicit def stringInstance = new EntityDecoder[String] {
def decode(msg: Message): DecodeResult[String] =
EitherT.pure[Future, DecodeFailure, String]("SomeString")
}
implicit def jsonInstance: EntityDecoder[Json] =
stringInstance.map(_.toJson)
}
trait Json
object Json {
implicit def fromString(s: String): JsonOps = JsonOps(s)
case class JsonOps(s: String) {
def toJson = new Json {}
}
}
Takeaways
• This stuff is hard
• You must want to learn it
• There is no other way as building your knowledge from the ground up
• Approach it without being biased - this is just a tool
• It will help you understand/read/write high-level scala code
• Not everyone will appreciate that style of coding and that’s fine.
Thank you
Bestiary of Functional Programming with Cats

Weitere ähnliche Inhalte

Was ist angesagt?

Inheritance And Traits
Inheritance And TraitsInheritance And Traits
Inheritance And Traits
Piyush Mishra
 
Ti1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: PolymorphismTi1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: Polymorphism
Eelco Visser
 
Introduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaIntroduction to Functional Programming with Scala
Introduction to Functional Programming with Scala
pramode_ce
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdf
Hiroshi Ono
 

Was ist angesagt? (19)

Python Programming - II. The Basics
Python Programming - II. The BasicsPython Programming - II. The Basics
Python Programming - II. The Basics
 
Linked to ArrayList: the full story
Linked to ArrayList: the full storyLinked to ArrayList: the full story
Linked to ArrayList: the full story
 
Scala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To KnowScala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To Know
 
Scala Talk at FOSDEM 2009
Scala Talk at FOSDEM 2009Scala Talk at FOSDEM 2009
Scala Talk at FOSDEM 2009
 
Inheritance And Traits
Inheritance And TraitsInheritance And Traits
Inheritance And Traits
 
Ti1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: PolymorphismTi1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: Polymorphism
 
Introduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaIntroduction to Functional Programming with Scala
Introduction to Functional Programming with Scala
 
Programming in Scala: Notes
Programming in Scala: NotesProgramming in Scala: Notes
Programming in Scala: Notes
 
Getting Started With Scala
Getting Started With ScalaGetting Started With Scala
Getting Started With Scala
 
Scala fundamentals
Scala fundamentalsScala fundamentals
Scala fundamentals
 
Functional Programming In Practice
Functional Programming In PracticeFunctional Programming In Practice
Functional Programming In Practice
 
Functions In Scala
Functions In Scala Functions In Scala
Functions In Scala
 
Scala: A brief tutorial
Scala: A brief tutorialScala: A brief tutorial
Scala: A brief tutorial
 
Functional programming in kotlin with Arrow [Sunnytech 2018]
Functional programming in kotlin with Arrow [Sunnytech 2018]Functional programming in kotlin with Arrow [Sunnytech 2018]
Functional programming in kotlin with Arrow [Sunnytech 2018]
 
Demystifying functional programming with Scala
Demystifying functional programming with ScalaDemystifying functional programming with Scala
Demystifying functional programming with Scala
 
Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?
 
Java 103
Java 103Java 103
Java 103
 
Metaprogramming in Scala 2.10, Eugene Burmako,
Metaprogramming  in Scala 2.10, Eugene Burmako, Metaprogramming  in Scala 2.10, Eugene Burmako,
Metaprogramming in Scala 2.10, Eugene Burmako,
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdf
 

Ähnlich wie Bestiary of Functional Programming with Cats

Under the hood of scala implicits (kl10tch 10.03.2015)
Under the hood of scala implicits (kl10tch 10.03.2015)Under the hood of scala implicits (kl10tch 10.03.2015)
Under the hood of scala implicits (kl10tch 10.03.2015)
Alexander Podkhalyuzin
 
Effective Scala (JavaDay Riga 2013)
Effective Scala (JavaDay Riga 2013)Effective Scala (JavaDay Riga 2013)
Effective Scala (JavaDay Riga 2013)
mircodotta
 
TI1220 Lecture 8: Traits & Type Parameterization
TI1220 Lecture 8: Traits & Type ParameterizationTI1220 Lecture 8: Traits & Type Parameterization
TI1220 Lecture 8: Traits & Type Parameterization
Eelco Visser
 
Taxonomy of Scala
Taxonomy of ScalaTaxonomy of Scala
Taxonomy of Scala
shinolajla
 

Ähnlich wie Bestiary of Functional Programming with Cats (20)

Exploring type level programming in Scala
Exploring type level programming in ScalaExploring type level programming in Scala
Exploring type level programming in Scala
 
Under the hood of scala implicits (kl10tch 10.03.2015)
Under the hood of scala implicits (kl10tch 10.03.2015)Under the hood of scala implicits (kl10tch 10.03.2015)
Under the hood of scala implicits (kl10tch 10.03.2015)
 
Under the hood of scala implicits (Scala eXchange 2014)
Under the hood of scala implicits (Scala eXchange 2014)Under the hood of scala implicits (Scala eXchange 2014)
Under the hood of scala implicits (Scala eXchange 2014)
 
Cats in Scala
Cats in ScalaCats in Scala
Cats in Scala
 
Scala for curious
Scala for curiousScala for curious
Scala for curious
 
Practical cats
Practical catsPractical cats
Practical cats
 
10 Things I Hate About Scala
10 Things I Hate About Scala10 Things I Hate About Scala
10 Things I Hate About Scala
 
Effective Scala (JavaDay Riga 2013)
Effective Scala (JavaDay Riga 2013)Effective Scala (JavaDay Riga 2013)
Effective Scala (JavaDay Riga 2013)
 
Functional programming with Scala
Functional programming with ScalaFunctional programming with Scala
Functional programming with Scala
 
Writing DSL with Applicative Functors
Writing DSL with Applicative FunctorsWriting DSL with Applicative Functors
Writing DSL with Applicative Functors
 
TI1220 Lecture 8: Traits & Type Parameterization
TI1220 Lecture 8: Traits & Type ParameterizationTI1220 Lecture 8: Traits & Type Parameterization
TI1220 Lecture 8: Traits & Type Parameterization
 
Python to scala
Python to scalaPython to scala
Python to scala
 
Go &lt;-> Ruby
Go &lt;-> RubyGo &lt;-> Ruby
Go &lt;-> Ruby
 
Ruby Programming Assignment Help
Ruby Programming Assignment HelpRuby Programming Assignment Help
Ruby Programming Assignment Help
 
Ruby Programming Assignment Help
Ruby Programming Assignment HelpRuby Programming Assignment Help
Ruby Programming Assignment Help
 
Taxonomy of Scala
Taxonomy of ScalaTaxonomy of Scala
Taxonomy of Scala
 
Fp in scala part 1
Fp in scala part 1Fp in scala part 1
Fp in scala part 1
 
Functional Programming With Scala
Functional Programming With ScalaFunctional Programming With Scala
Functional Programming With Scala
 
Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And Monad
 
The Swift Compiler and Standard Library
The Swift Compiler and Standard LibraryThe Swift Compiler and Standard Library
The Swift Compiler and Standard Library
 

Kürzlich hochgeladen

TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
mohitmore19
 

Kürzlich hochgeladen (20)

Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 

Bestiary of Functional Programming with Cats

  • 1. Bestiary of FP Tomasz Kogut - @almendar 2016.09.03 with Cats
  • 2. About me: • Software / Data Engineer at • Develops AdTech Platform • Research department • Works on processing lots of data really fast • Scala since ~2011
  • 3. Warning! • This presentation is intended to be practical guide how to start with FP and cats • May contain inaccurate information on purpose • May contain simply wrong information because of my mistake for which I sincerely apologize - I’m no expert • Questions during presentation will be highly appreciated
  • 4. Why this talk? (1) • FP is a different kind of beast • Does not reassembles anything you know • Reading blogs do not help, it makes it probably even worse • It’s frustrating
  • 5. Why this talk? (2) • There is much to learn • Accepting this is the first step you have to make • I’ve made some of the hops and I want to share so you don’t have to go through some of the daunting things
  • 6. What FP is all about? (1) • Functions? • Higher order functions? • Immutability? • Referential transparency?
  • 7. What FP is all about? (2) • How stupid it may sound FP is about values • Values are: bits, ints, data structures, functions, types • What FP does for us it allows us to combine values and look at them as a new value • This is what we’ll be doing throughout the presentation
  • 9. Product class Foo class Bar case class FooBarProduct( foo: Foo, bar: Bar ) Tuples and cartesian product anyone?
  • 10. Semigroup Take two values and produce one
  • 12. trait Semigroup[A] { def combine(a1: A, a2: A): A }
  • 13. Group-like structures Totalityα Associativity Identity Divisibility Commutativity Semicategory Unneeded Required Unneeded Unneeded Unneeded Category Unneeded Required Required Unneeded Unneeded Groupoid Unneeded Required Required Required Unneeded Magma Required Unneeded Unneeded Unneeded Unneeded Quasigroup Required Unneeded Unneeded Required Unneeded Loop Required Unneeded Required Required Unneeded Semigroup Required Required Unneeded Unneeded Unneeded Monoid Required Required Required Unneeded Unneeded Group Required Required Required Required Unneeded Abelian Group Required Required Required Required Required https://en.wikipedia.org/wiki/Outline_of_algebraic_structures Why “Semigroup” and not “ValuesCombiner”
  • 14. object SuperAdder { def add[A : Semigroup](a1: A, a2: A): A = Semigroup[A].combine(a1,a2) } case class ComplexNumber(x: Double,i: Double) object ComplexNumber { implicit val semiGroup = new Semigroup[ComplexNumber] { def combine(a1: ComplexNumber, a2: ComplexNumber): ComplexNumber = ComplexNumber(a1.x+a2.x, a1.i + a2.i) } } SuperAdder.add(1,2) //res1: Int = 3 SuperAdder.add(ComplexNumber(1.2, 2.3), ComplexNumber(2.4, 3.4)) //res2: ComplexNum SuperAdder.add("Hello ", "world") //res3: String = Hello world
  • 15. What about Semigroup for List, Option, Future?
  • 16. <console>:13: error: class Option takes type parameters val optionSemigroup = new Semigroup[Option] {} val optionSemigroup = new Semigroup[Option[Int]] {} This would do the job but we can do better
  • 17. Higher Kinded Types • Option, List ect. are type constructors. Like normal constructors but for types. • They take a type as argument • Some say that they have a “hole” • Scala compiler knows about this
  • 18. scala> :kind -v String java.lang.String's kind is A * This is a proper type. scala> :kind -v Either scala.util.Either's kind is F[+A1,+A2] * -(+)-> * -(+)-> * This is a type constructor: a 1st-order-kinded type. scala> :kind -v Option scala.Option's kind is F[+A] * -(+)-> * This is a type constructor: a 1st-order-kinded type
  • 19. Now the scary part scala> :kind -v trait Foo[X[_]] Foo's kind is X[F[A]] (* -> *) -> * This is a type constructor that takes type constructor(s): a higher-kin at when you put inside a type that when put inside another type create
  • 20. trait SemigroupK[F[_]] { def combineK[A](fa1: F[A], fa2: F[A]): F[A] } implicit val optionSemigroupK = new SemigroupK[Option] { def combineK[A](fa1: Option[A], fa2: Option[A]): Option[A] = fa1 orElse fa2 } implicit val listSemigroup = new SemigroupK[List] { def combineK[A](fa1: List[A], fa2: List[A]): List[A] = fa1 ++ fa2 } listSemigroup.combineK(List(1,2,3), List(4,5,6)) //res3: List[Int] = List(1, 2, 3, 4, 5, 6) SemigroupK
  • 21. • TypeK is a thing • “K” stands for kind • Our effect works on higher-kinder types • SemigroupK, MonoidK, FunctionK
  • 22. Semigroup(K) in Cats • Both effects are provided by cats • For std types there are ready instances • The whole thing is to do the right imports
  • 23. import cats._ import cats.implicits._ Semigroup[Int].combine(1,2) //res0: Int = 3 SemigroupK[List].combineK(List(Some(1), Some(3)), List(Some(2))) //res1: List[Some[Int]] = List(Some(1), Some(3), Some(2))
  • 24. Semigroup[Int] What is this? object Semigroup extends SemigroupFunctions[Semigroup] { @inline final def apply[A](implicit ev: Semigroup[A]): Semigroup[A] = ev }
  • 25. Creating Effects • This is very common in Cats • TypeConstructor[X].someMethod • Heavy use of implicits and packages objects
  • 26. • import cats._ make all types available in scope like Semigroup, Monad, Applicative ect. Like Predef for Scala. • import cats.implicits._ puts ALL implicit into the scope using package object trick
  • 27. package object instances { object all extends AllInstances object either extends EitherInstances object function extends FunctionInstances object list extends ListInstances object option extends OptionInstances object set extends SetInstances object stream extends StreamInstances object vector extends VectorInstances object map extends MapInstances object future extends FutureInstances object string extends StringInstances object int extends IntInstances object byte extends ByteInstances object long extends LongInstances object char extends CharInstances object short extends ShortInstances object float extends FloatInstances object double extends DoubleInstances object boolean extends BooleanInstances object unit extends UnitInstances object bigInt extends BigIntInstances object bigDecimal extends BigDecimalInstances object try_ extends TryInstances object tuple extends TupleInstances } package object syntax { object all extends AllSyntax object applicative extends ApplicativeSyntax object applicativeError extends ApplicativeErrorSyntax object apply extends ApplySyntax object bifunctor extends BifunctorSyntax object bifoldable extends BifoldableSyntax object bitraverse extends BitraverseSyntax object cartesian extends CartesianSyntax object coflatMap extends CoflatMapSyntax object coproduct extends CoproductSyntax object comonad extends ComonadSyntax object compose extends ComposeSyntax object contravariant extends ContravariantSyntax object either extends EitherSyntax object eq extends EqSyntax object flatMap extends FlatMapSyntax object foldable extends FoldableSyntax object functor extends FunctorSyntax object functorFilter extends FunctorFilterSyntax object group extends GroupSyntax object invariant extends InvariantSyntax object list extends ListSyntax object monadCombine extends MonadCombineSyntax object monadFilter extends MonadFilterSyntax object monoid extends MonoidSyntax object option extends OptionSyntax object order extends OrderSyntax object partialOrder extends PartialOrderSyntax object profunctor extends ProfunctorSyntax object reducible extends ReducibleSyntax object semigroup extends SemigroupSyntax object semigroupk extends SemigroupKSyntax object show extends Show.ToShowOps object split extends SplitSyntax object strong extends StrongSyntax object transLift extends TransLiftSyntax object traverse extends TraverseSyntax object traverseFilter extends TraverseFilterSyntax object tuple extends TupleSyntax object validated extends ValidatedSyntax object writer extends WriterSyntax } package cats object implicits extends syntax.AllSyntax with instances.AllInstances
  • 28. Instances trait ListInstances extends cats.kernel.instances.ListInstances { implicit val catsStdInstancesForList: TraverseFilter[List] with MonadCombine[List] with Monad[List] with CoflatMap[List] with RecursiveTailRecM[List] = new TraverseFilter[List] with MonadCombine[List] with Monad[List] with CoflatMap[List] with RecursiveTailRecM[List] { def combineK[A](x: List[A], y: List[A]): List[A] = x ++ y (...) }
  • 30. Syntax trait SemigroupSyntax { implicit def catsSyntaxSemigroup[A: Semigroup](a: A): SemigroupOps[A] = new SemigroupOps[A](a) } final class SemigroupOps[A: Semigroup](lhs: A) { def |+|(rhs: A): A = macro Ops.binop[A, A] def combine(rhs: A): A = macro Ops.binop[A, A] } List(1) |+| List(2, 2, 4) We can do both: List(1) combine List(2, 2, 4)
  • 31. Monoid(K) trait Monoid with Semigroup[A] { def empty: A } @typeclass trait MonoidK[F[_]] extends SemigroupK[F] { self => def empty[A]: F[A] } • @typeclass annotation comes from simulacrum • It generates code • “Jump to source” might not work in Cats src code
  • 32. Monoid examples • type: Int combine: + empty: 0 • type: Int combine: * empty: 1 • type: String combine: + empty: “”
  • 33. Can we always create a monoid? type NEL[A] = (A, List[A]) val nelMonoid = new MonoidK[NEL] { def combineK[A](n1: NEL[A], n2: NEL[A]): NEL[A] = (n1._1, (n1._2 :+ n2._1) ++ n2._2) def empty[A]: NEL = ??? }
  • 34. • One may end-up writing effect class for some data structure that it is impossible • http://stackoverflow.com/questions/32477292/fold-on- nonemptylist/32479640#32479640 • It took me about 2 hours to realize this
  • 35. trait Ord case object GT extends Ord case object LT extends Ord case object EQ extends Ord object Ord { implicit object OrderingMonoid extends Monoid[Ord] { def empty(): Ord = EQ def combine(x:Ord, y: Ord): Ord = x match { case EQ => y case LT => LT case GT => GT } } } Monoid example
  • 36. def palindromeFirst(s1: String, s2: String): Ord def shorterFirst(s1: String, s2: String): Ord val res = List(palindromeFirst _, shorterFirst _).map{ f => f("ANNA", “BARBARA”) } Foldable[List].fold(res)(implicit OrderingMonoid) // res0(: Ord = GT
  • 37. Foldable • Fold is surprisingly powerful http://www.cs.nott.ac.uk/~pszgmh/fold.pdf • Foldable has most of the collections api on it: find, exists, forAll, filter, isEmpty, • It allows to reduce collection to single element • It can make use of Monoids
  • 38. Foldable and monoid example 2 def foldMap[A, B](fa: F[A])(f: A => B)(implicit B: Monoid[B]): B = foldLeft(fa, B.empty)((b, a) => B.combine(b, f(a))) val lineItems: List[LineItem] = ... //explicit summoning Foldable val totalInvoiceValue = Foldable[List].foldMap(lineItems){_.value} //using syntax ops val totalInvoiceValue = lineItems.foldMap { _.value }
  • 39. Functors trait Functor[F[_]] { def map[A,B](fa:F[A])(f: A=>B): F[B] } Functor[List].map(List(1,2,3))(_ + 1)
  • 40. Composing functors val k = Functor[Try] compose Functor[List] compose Functor[Option] k.map(Success(List(Some(22), Some(33), None)))(_+1) //res19: scala.util.Try[List[Option[Int]]] = Success(List(Some(23), Some(34), None)))
  • 41. Applicatives The story of derived combinators //we want map for this function val addTwoInts = {(_:Int) + (_:Int)} addTwoInts(2,3) //res0:Int = 5
  • 42. trait Applicative[F[_]] { def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C] def unit[A](a: => A): F[A] } Let’s get some stuff for free!!! Applicatives
  • 43. trait Applicative[F[_]] { def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C] def unit[A](a: => A): F[A] }
  • 44. trait Applicative[F[_]] { def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C] def unit[A](a: => A): F[A] def map[A,B](fa:F[A])(f: A=>B): F[B] = map2(fa, (): Unit))((a,_) => f(a)) }
  • 45. trait Applicative[F[_]] { def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C] def unit[A](a: => A): F[A] def map[A,B](fa:F[A])(f: A=>B): F[B] = map2(fa, (): Unit))((a,_) => f(a)) def product[A,B](fa: F[A], fb: F[B]): F[(A,B)] = map2(fa,fb)((a,b) => (a,b)) }
  • 46. trait Applicative[F[_]] { def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C] def unit[A](a: => A): F[A] def map[A,B](fa:F[A])(f: A=>B): F[B] = map2(fa, (): Unit))((a,_) => f(a)) def product[A,B](fa: F[A], fb: F[B]): F[(A,B)] = map2(fa,fb)((a,b) => (a,b)) def lift[A,B,C](x: A => B): F[A] => F[B] = { fa:F[A] => map(fa)(x) } //pimp my API }
  • 47. trait Applicative[F[_]] { def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C] def unit[A](a: => A): F[A] def map[A,B](fa:F[A])(f: A=>B): F[B] = apply(unit(f))(fa) def product[A,B](fa: F[A], fb: F[B]): F[(A,B)] = map2(fa,fb)((a,b) => (a,b)) def lift[A,B,C](x: A => B): F[A] => F[B] = { fa:F[A] => map(fa)(x) } //pimp my func def traverse[A,B](as: List[A])(f: A => F[B]): F[List[B]] = as.foldRight(unit(List[B]()))((a,fbs) => map2(f(a), fbs) { _ :: _ } ) }
  • 48. trait Applicative[F[_]] { (…) def unit[A](a: => A): F[A] def map2[A,B,C](fa:F[A], fb: F[B])(f: (A,B) => C): F[C] def map3[A,B,C,D](fa: F[A], fb: F[B], fc: F[C]) (f:(A,B,C) => D): F[D] = map2(map2(map2(unit(f.curried), fa)(_(_)), fb)(_(_)), fc)(_(_)) } def curried[A,B,C](f: (A,B) => C): A => B => C = { a => { b => f(a,b) }}
  • 49. trait Applicative[F[_]] { (…) def map3[A,B,C,D](fa: F[A], fb: F[B], fc: F[C]) (f:(A,B,C) => D): F[D] = map2(map2(map2(unit(f.curried), fa)(_(_)), fb)(_(_)), fc)(_(_)) }
  • 50. trait Applicative[F[_]] { (…) def map3[A,B,C,D](fa: F[A], fb: F[B], fc: F[C]) (f:(A,B,C) => D): F[D] = map2(map2(map2(unit(f.curried), fa)(_(_)), fb)(_(_)), fc)(_(_)) def apply[A,B](fab: F[A=>B])(fa:F[A]): F[B] = map2(fab,fa)(_(_)) }
  • 51. trait Applicative[F[_]] { (…) def map3[A,B,C,D](fa: F[A], fb: F[B], fc: F[C]) (f:(A,B,C) => D): F[D] = apply(apply(apply(unit(f.curried))(fa))(fb))(fc) def apply[A,B](fab: F[A=>B])(fa:F[A]): F[B] = map2(fab,fa)(_(_)) }
  • 52. trait Applicative[F[_]] { (…) def map3[A,B,C,D](fa: F[A], fb: F[B], fc: F[C]) (f:(A,B,C) => D): F[D] = apply(apply(apply(unit(f.curried))(fa))(fb))(fc) def map4[A,B,C,D,E] def map5[A,B,C,D,E,F] def apply[A,B](fab: F[A=>B])(fa:F[A]): F[B] = map2(fab,fa)(_(_)) } mapN
  • 53. Writting combinators • It turn out that applicative can be expressed with different set of operations • map2 + unit • apply + unit
  • 54. def apply[A,B](fab: F[A=>B])(fa:F[A]): F[B] = map2(fab,fa)(_(_)) def map2[A,B,C](fa: F[A], fb: F[B])(f: (A,B) => C): F[C] = apply(apply(unit(f.curried))(fa))(fb) map2 and apply
  • 55. Usage of Applicative case class Charts( books: Seq[Book], music: Seq[Album], games: Seq[Game] ) def topBooks: Future[Seq[Book]] = ??? def topMusic: Future[Seq[Album]] = ??? def topGames: Future[Seq[Album]] = ??? Applicative[Future].map3(topBooks, topMusic, topGames)(Charts(_,_,_)) Another use case is Validation
  • 56. Cats Applicative helpers import cats.implicits._ (topBooks |@| topMusic |@| topGames) map { Charts(_,_,_) } Applicative builders using code generation /* Very, very, very high level view on how this works */ class CartesianBuilderN { def |@|(a: Applicative): CartesianBuilderN+1 def map(f: FunctionN): Applicative }
  • 57. Applicatives compose val futOptAppl = Applicative[Future] compose Applicative[Option] futOptAppl.map2(Future(Some(22)),Future(Some(33))) { _ + _ } //res47: scala.concurrent.Future[Option[Int]] = Success(Some(55))
  • 58. Monads • You use them every day map/flatMap • Monads are powerful abstraction • They have most of the combinators • At the same time not all data structures can be expressed as Monads
  • 59. class DBRepo[F[_]] { def getUserLoging(id: Long)(implicit F: Monad[F]): F[String] = F.pure(id.toString) def getUserEmail(id: Long)(implicit F: Monad[F]): F[String] = F.pure(id.toString) def getUser(id: Long)(implicit F: Monad[F]) : F[User] = { F.flatMap(getUserLoging(id)) { login => F.map(getUserEmail(id)) { email => User(login, email) } } } } val repo1 = new DBRepo[Future] val repo2 = new DBRepo[Task]
  • 60. class DBRepo[F[_]] { def getUserLoging(id: Long)(implicit F: Monad[F]): F[String] = F.pure(id.toString) def getUserEmail(id: Long)(implicit F: Monad[F]): F[String] = F.pure(id.toString) def getUser(id: Long)(implicit F: Monad[F]) : F[User] = for { login <- getUserLoging(id) email <- getUserEmail(id) } yield User(login, email) } val repo1 = new DBRepo[Future] val repo2 = new DBRepo[Task]
  • 61. Monads for { i <- List(Option(1), Option(2)) j <- List(Option(3), Option(4)) } yield i + j Monads don’t compose (usually), so the two below won’t wor Monad[List] compose Monad[Option]
  • 62. Monads this will, but it’s ugly val p = for { i <- List(Option(1), Option(2)) j <- List(Option(3), Option(4)) } yield { for { k <- i l <- j } yield k+l }
  • 63. Monad transformers val k = for { i <- OptionT(List[Option[Int]](Option(1), Option(2))) j <- OptionT(List[Option[Int]](Option(3), Option(4))) } yield i + j …this will also and it’s nice:
  • 64. Monad transformers • Cats have multiple instances of those • EitherT, IdT, OptionT, StateT, WriterT • TypeT[F[_], A] wraps F[Type[A]] • E.g. OptionT[List, Int] wraps List[Option[Int]]
  • 65. Effectful functions • A => F[B] • Returned value in some kind of effect/context • More common than one might think
  • 66. // Id => Future[Long] def getCustomerById(long: Id): Future[Customer] // CharSequence => Option[String] def findFirstIn(source: CharSequence): Option[String] //Int => List[Int] def listFromZeroToN(n: Int): List[Int] we want to combine those
  • 67. Kleisli final case class Kleisli[F[_], A, B](run: A => F[B]) • It has all the good’ol combinators: flatMap, map, compose, apply ect. • Used for composing effectful functions • What kind of combinator can you use depends on what F is • If you can have implicit effect for F you can call certain methods def map[C](f: B => C) (implicit F: Functor[F]): Kleisli[F, A, C] = Kleisli(a => F.map(run(a))(f)) def flatMap[C](f: B => Kleisli[F, A, C]) (implicit F: FlatMap[F]): Kleisli[F, A, C] = Kleisli((r: A) => F.flatMap[B, C](run(r))((b: B) => f(b).run(r)))
  • 68. Kleisli (A => B) andThen (B => C) => (A => C) (A => F[B]) andThen (B => F[C]) => won't work Kleisli(A => F[B]) andThen Kleisli(B => F[C]) => Kleisli(A => F[C])
  • 69. There is more… • Xor • State • Validated • FreeMonads and FreeApplicatives • Show • Traverse
  • 70. Simple RPC • Let’s build a quick RPC API with focus on HTTP • We’ll take building blocks from what we’ve seen
  • 71. package object http { type Service[A,B] = Kleisli[Future, A, B] }
  • 72. package object http { type Service[A,B] = Kleisli[Future, A, B] type HttpService = Service[Request, Response] }
  • 73. package object http { type Service[A,B] = Kleisli[Future, A, B] type HttpService = Service[Request, Response] //Future[Either[A, B]] type DecodeResult[T] = EitherT[Future, DecodeFailure, T] }
  • 74. object Service { def lift[A,B](f: A => Future[B]): Service[A,B] = Kleisli(f) } object HttpService { def apply(f: PartialFunction[Request, Response]): HttpService = Service.lift(liftToAsync(f)) def liftToAsync[A,B](f: A => B): A => Future[B] = (a: A) => Future(f(a)) }
  • 75. val httpService = HttpService { case r1 @ Request(Get, "/") => Response(Ok) case r2 @ Request(Post, "/") = Response(NotFound) } Http.runService(httpService) Server
  • 76. Client We can reuse the HttpService type val httpClient: HttpService = ??? val jsonResponseFromPipeline = httpService.map(_.body[Json]) val jsonFut: Future[DecodeResul[Json]] = jsonResponseFromPipeline(Request(Get,"/"))
  • 77. class AHClientWrapper(realClient: AHClient) extends Request => Future[Response] { def apply(req: Request): Future[Response] = { //call realClient and return response } } val httpClient: HttpService = Kleisli(new AHClientWrapper(new AHClient)) Client
  • 78. httpService.map(_.body[Json]) // Kleisli[Future, Request, Json] //implementation case class Response(code: HttpCode) extends Message { def body[A](implicit decoder: EntityDecoder[A]): DecodeResult[A] = decoder.decode(this) } Decoding
  • 79. trait EntityDecoder[T] { self => def decode(msg: Message): DecodeResult[T] def map[T2](f: T => T2): EntityDecoder[T2] = new EntityDecoder[T2] { override def decode(msg: Message): DecodeResult[T2] = self.decode(msg).map(f) } } type DecodeResult[T] = EitherT[Future, DecodeFailure, T] This map is interesting
  • 80. object EntitiyDecoder { implicit def stringInstance = new EntityDecoder[String] { def decode(msg: Message): DecodeResult[String] = EitherT.pure[Future, DecodeFailure, String]("SomeString") } implicit def jsonInstance: EntityDecoder[Json] = stringInstance.map(_.toJson) } trait Json object Json { implicit def fromString(s: String): JsonOps = JsonOps(s) case class JsonOps(s: String) { def toJson = new Json {} } }
  • 81. Takeaways • This stuff is hard • You must want to learn it • There is no other way as building your knowledge from the ground up • Approach it without being biased - this is just a tool • It will help you understand/read/write high-level scala code • Not everyone will appreciate that style of coding and that’s fine.

Hinweis der Redaktion

  1. słowek wprowadzenia - Bestiariusz, średniowieczny gatunek literacki, opis zwierząt – realnych i baśniowych cats co to?
  2. podział 90 / 10 przy dobrych wiatrach nie zawsze uczymy się praktycznych rzeczy
  3. - Co oznacza “A : Semigroup"
  4. Abstrakcja po typie F