SlideShare a Scribd company logo
PRACTICAL CATS
sharing what i’ve learnt
ABOUT MYSELF
Practical cats
CATS….
To use cats effectively, understand what each
construct does:
Functor
Monads
Applicatives
Monoids
Semigroups
SO MANY ACRONYMS !!!
Validated
Practical cats
Building blocks …
Understand that they are building blocks

so that you can write code that is pure and
code that has side-effects — separation of
concerns.
Typeclasses …
Each of the type class (e.g. functors,
monoids, monads etc) are governed by laws.
Typeclasses! 

they are behaviours that can be “inherited”

by your code.
Semigroups - what are they?
trait Semigroup[A] {

def combine(x: A, y: A) : A

}
 general structure to define things 

that can be combined.

*Cats provides “default” implementations; developers 

(like you & me) need to provide implementations that conform to the traits. *
Monoids - what are they?
trait Monoid[A] extends Semigroup[A] {

def empty: A

def combine(x: A, y: A) : A

}
 general structure to define things 

that can be combined and has a “default”

element.

*Cats provides “default” implementations; developers 

(like you & me) need to provide implementations that conform to the traits. *
Monoids - what are they?
> import cats._, data._, implicits._

> Monoid[String].combine(“hi”, “there”)

// res0: String = “hithere”

> “hi” |+| “there”

// res1: String = “hithere”
Use case for Monoids/Semigroups
They’re good for combining 2 or more things of a similar
nature

data-type-a data-type-b
data-stream end-
point
parser
collector
of either data-type-a or
data-type-b
Use case #1 - Monoids for “smashing” values
* all names used here do not reflect the actuals *
// Monoid[DataTypeAB] defined somewhere else
def buildDataFromStream(datatypeA : DataTypeA,
datatypeB : DataTypeB,
accumulator: DatatypeAB) =
validateData(datatypeA, datatypeB).fold(
onError => {
// `orError` is lifted into the datatype
val errors = Monoid[DatatypeAB].empty.copy(lift(onError))
Monoid[DatatypeAB].combine(accumulator, errors)
},
parsedValue => {
// `parsedValue` is lifted into the datatype
val newValue = Monoid[DatatypeAB].empty.copy(lift(parsedValue))
Monoid[DatatypeAB].combine(accumulator, newValue)
}
)
Functors - what are they?
trait Functor[F[_]] {

def map[A,B](fa: F[A])(f: A => B) : F[B]

}

general structure to represent something

that can be mapped over. If you’ve been using Lists

, Options, Eithers, Futures in Scala, you’ve been using
functors.

!!! They are very common structures indeed ☺ !!!
* functors are used in clever things like recursion-schemes *
Functors - what are they?
> import cats._, data._, implicits._

> Functor[List].lift((x:Int) => x + 1)

// res0: List[Int] => List[Int]

> res0(List(1))

// res1: List[Int] = List(2)
* Nugget of info: Functors preserve “structure” *
Monads
Monads are meant for 

sequencing computations
Monads
someList.flatmap(element => 

someOtherList.flatmap(element2 =>

(element, element2)))
*No tuples generated if either “someList”

Or “someOtherList” is empty*
Monads
someList.flatmap(element => 

someOtherList.flatmap(element2 =>

(element, element2)))
Monads allows for short-circuiting of

computations
Monads - a quick summary?
Writers - information can be carried along with
the computation

Readers - compose operations that depend

on some input.

State - allows state to be “propagated”

Eval - abstracts over eager vs lazy evaluation.
Monads - examples
> List(1,2,3) >>= (value => List(value+1))

> res0: List[Int] = List(2,3,4)
def >>=[A,B](fa: F[A])(f:A => F[B]): F[B] =
flatMap(fa)(f)
“>>=“ is also known as “bind” (in Cats, its really “flatMap”)
Monads - examples
> Monad[List].lift((x:Int) => x + 1)(List(1,2,3))

> res1: List[Int] = List(2,3,4)
Typeclasses allows you to define re-usable code by
lifting functions.
Monads - examples
> Monad[List].pure(4)

> res2: List[Int] = List(4)
This is to lift values into a context, in this case
Monadic context.
Monads - flow
scala> def first = Reader( (x:Int) => Monad[List].ifM(List(true,true))(x::Nil, Nil) )

extractGroups: cats.data.Reader[Int,List[Int]]

scala> def second = Reader( (x:Int) => Monad[List].ifM(List(true,true))(x::Nil, Nil) )

loadGroup: cats.data.Reader[Int,List[Int]]

scala> for { g <- first(4); gg <- second(g) } yield gg

res21: List[Int] = List(4, 4, 4, 4)
Writer Monad
“Writers” are typically used to carry not only the value
of a computation but also some other information (typically, its
used to carry logging info).
source: http://eed3si9n.com/herding-cats/Writer.html
Writer Monad
scala> def logNumber(x: Int): Writer[List[String], Int] =
         Writer(List("Got number: " + x.show), 3)
logNumber: (x: Int)cats.data.Writer[List[String],Int]
scala> def multWithLog: Writer[List[String], Int] =
         for {
           a <- logNumber(3)
           b <- logNumber(5)
         } yield a * b
multWithLog: cats.data.Writer[List[String],Int]
scala> multWithLog.run
res2: cats.Id[(List[String], Int)] = (List(Got number: 3, Got number: 5),9)
scala> multWithLog.reset
res6: cats.data.WriterT[cats.Id,List[String],Int] = WriterT((List(),9))
scala> multWithLog.swap
res8: cats.data.WriterT[cats.Id,Int,List[String]] = WriterT((9,List(Got number: 4, Got
number: 3)))
scala> multWithLog.value
res9: cats.Id[Int] = 9
scala> multWithLog.written
res10: cats.Id[List[String]] = List(Got number: 4, Got number: 3)
source: http://eed3si9n.com/herding-cats/Writer.html
Compose Writers
Reader Monad
“Readers” allows us to compose operations which depend
on some input.
source: http://eed3si9n.com/herding-cats/Reader.html
Reader Monad
case class Config(setting: String, value: String)
def getSetting = Reader {
(config: Config) => config.setting
}
def getValue = Reader {
(config: Config) => config.value
}
for {
s <- getSetting
v <- getValue
} yield Config(s, v)
Compose Readers
FP-style to abstract and
encapsulate.
State Monad
Allows us to pass state-information around in a computation.
http://eed3si9n.com/herding-cats/State.html
Use case #3 - Reader + State Monad
def process: Reader[Elem, Seq[Mapping]] = Reader {
(xml: Elem) =>
for {
groups <- extractGroups(dataXml).toOption
group <- groups
grpCfg <- loadGroupConfig(group).toOption
stateObj <- ConfigState(grpCfg).pure[Option]
records <- loadRecords(group).toOption
record <- records
row <- processRecord(i)(stateObj)(record).pure[Option]
} yield {
// processing …
}
}
case class ConfigState(init: Config) {
private[this] var currentState: Config = init
def storeCfg : State[Config, Config] =
State{ (cfg: Config) =>
val prevState = currentState
currentState = cfg
(currentState, prevState) }
def loadCfg : Config =
( for {
s <- State.get[Config]
} yield s ).runA(currentState).value
}
Use case #3 - Reader + State Monad
def process: Reader[Elem, Seq[Mapping]] = Reader {
(xml: Elem) =>
for {
groups <- extractGroups(dataXml).toOption
group <- groups
grpCfg <- loadGroupConfig(group).toOption
stateObj <- ConfigState(grpCfg).pure[Option]
records <- loadRecords(group).toOption
record <- records
row <- processRecord(i)(stateObj)(record).pure[Option]
} yield {
// processing …
}
}
case class ConfigState(init: Config) {
private[this] var currentState: Config = init
def storeCfg : State[Config, Config] =
State{ (cfg: Config) =>
val prevState = currentState
currentState = cfg
(currentState, prevState) }
def loadCfg : Config =
( for {
s <- State.get[Config]
} yield s ).runA(currentState).value
}
Separation of concerns
State management
Applicative
Applicatives allows for functions to be
lifted over a structure (Functor).

Because the function and the value it’s being applied 

to both have structures, hence its needs to be
combined.
Applicative - examples
scala> Applicative[List].lift((x:Int) => x + 1)
res1: List[Int] => List[Int] = <function1>
scala> Applicative[List].lift(
| (x:List[Int=>Int]) =>
| x.map(f => f(2)))
| (List( List((x:Int) => x + 1 )))
res7: List[List[Int]] = List(List(3))
scala> val fs = List(List((x:Int) => x + 1))
fs: List[List[Int => Int]] = List(List(<function1>))
scala> fs.map(_(2))
res15: cats.data.Nested[List,List,Int] =
Nested(List(List(3)))
Applicative is like a Functor
Applicative - examples
scala> Applicative[List].lift((x:Int) => x + 1)
res1: List[Int] => List[Int] = <function1>
scala> Applicative[List].lift(
| (x:List[Int=>Int]) =>
| x.map(f => f(2)))
| (List( List((x:Int) => x + 1 )))
res7: List[List[Int]] = List(List(3))
scala> val fs = List(List((x:Int) => x + 1))
fs: List[List[Int => Int]] = List(List(<function1>))
scala> fs.map(_(2))
res15: cats.data.Nested[List,List,Int] =
Nested(List(List(3)))
Applicative is like a Functor
Applying a function which is nested.
Applicative - examples
scala> Applicative[List].lift((x:Int) => x + 1)
res1: List[Int] => List[Int] = <function1>
scala> Applicative[List].lift(
| (x:List[Int=>Int]) =>
| x.map(f => f(2)))
| (List( List((x:Int) => x + 1 )))
res7: List[List[Int]] = List(List(3))
scala> val fs = List(List((x:Int) => x + 1))
fs: List[List[Int => Int]] = List(List(<function1>))
scala> fs.map(_(2))
res15: cats.data.Nested[List,List,Int] =
Nested(List(List(3)))
Applicative is like a Functor
Applying a function which is nested.
Cat has a “Nested” to achieve the same.
Applicative - examples
A typical application is to leverage Applicatives in writing
Logic to validate configurations, forms etc
import cats.Cartesian
import cats.data.Validated
import cats.instances.list._ // Semigroup for List
type AllErrorsOr[A] = Validated[List[String], A]
Cartesian[AllErrorsOr].product(
Validated.invalid(List("Error 1")),
Validated.invalid(List("Error 2")) )
// res1: AllErrorsOr[(Nothing, Nothing)] = Invalid(List(Error 1,Error 2))
Applicative - examples
package xxx.config
import scala.concurrent.duration.{Duration,FiniteDuration}
import cats._
import cats.data._
import cats.implicits._
import cats.data.Validated
import cats.data.Validated.{Invalid, Valid}
// code that needs to remain hidden
sealed abstract class ConfigError
final case class MissingConfig(field : String) extends ConfigError
final case class ParseError(field: String) extends ConfigError
case class Config(map : Map[String,String])
case class HuffConfig(
clusterName: String,
clusterPort : Int,
clusterAddress : String,
hostname: String,
listeningPort: Int)
object Validator {
def getHuffConfig(config: Config) : ValidatedNel[ConfigError, HuffConfig] =
Apply[ValidatedNel[ConfigError, ?]].map5(
config.parse[String] ("DL_CLUSTER_NAME").toValidatedNel,
config.parse[Int] ("DL_CLUSTER_PORT").toValidatedNel,
config.parse[String] ("DL_CLUSTER_ADDRESS").toValidatedNel,
config.parse[String] ("DL_HTTP_ADDRESS").toValidatedNel,
config.parse[Int] ("DL_HTTP_PORT").toValidatedNel) {
case (clusterName, clusterPort, clusterAddress, httpAddr, httpPort) =>
HuffConfig(clusterName, clusterPort, clusterAddress, httpAddr, httpPort)
}
}
package xxx.config
import scala.concurrent.duration.{Duration,FiniteDuration}
import cats._
import cats.data._
import cats.implicits._
import cats.data.Validated
import cats.data.Validated.{Invalid, Valid}
// code that needs to remain hidden
sealed abstract class ConfigError
final case class MissingConfig(field : String) extends ConfigError
final case class ParseError(field: String) extends ConfigError
case class Config(map : Map[String,String])
case class HuffConfig(
clusterName: String,
clusterPort : Int,
clusterAddress : String,
hostname: String,
listeningPort: Int)
object Validator {
def getHuffConfig(config: Config) : ValidatedNel[ConfigError, HuffConfig] =
Apply[ValidatedNel[ConfigError, ?]].map5(
config.parse[String] ("DL_CLUSTER_NAME").toValidatedNel,
config.parse[Int] ("DL_CLUSTER_PORT").toValidatedNel,
config.parse[String] ("DL_CLUSTER_ADDRESS").toValidatedNel,
config.parse[String] ("DL_HTTP_ADDRESS").toValidatedNel,
config.parse[Int] ("DL_HTTP_PORT").toValidatedNel) {
case (clusterName, clusterPort, clusterAddress, httpAddr, httpPort) =>
HuffConfig(clusterName, clusterPort, clusterAddress, httpAddr, httpPort)
}
}
Define types to represent “errors"
package xxx.config
import scala.concurrent.duration.{Duration,FiniteDuration}
import cats._
import cats.data._
import cats.implicits._
import cats.data.Validated
import cats.data.Validated.{Invalid, Valid}
// code that needs to remain hidden
sealed abstract class ConfigError
final case class MissingConfig(field : String) extends ConfigError
final case class ParseError(field: String) extends ConfigError
case class Config(map : Map[String,String])
case class HuffConfig(
clusterName: String,
clusterPort : Int,
clusterAddress : String,
hostname: String,
listeningPort: Int)
object Validator {
def getHuffConfig(config: Config) : ValidatedNel[ConfigError, HuffConfig] =
Apply[ValidatedNel[ConfigError, ?]].map5(
config.parse[String] ("DL_CLUSTER_NAME").toValidatedNel,
config.parse[Int] ("DL_CLUSTER_PORT").toValidatedNel,
config.parse[String] ("DL_CLUSTER_ADDRESS").toValidatedNel,
config.parse[String] ("DL_HTTP_ADDRESS").toValidatedNel,
config.parse[Int] ("DL_HTTP_PORT").toValidatedNel) {
case (clusterName, clusterPort, clusterAddress, httpAddr, httpPort) =>
HuffConfig(clusterName, clusterPort, clusterAddress, httpAddr, httpPort)
}
}
Define types to represent “errors"
Validate and read into configuration object.
package xxx.config
import scala.concurrent.duration.{Duration,FiniteDuration}
import cats._
import cats.data._
import cats.implicits._
import cats.data.Validated
import cats.data.Validated.{Invalid, Valid}
// code that needs to remain hidden
sealed abstract class ConfigError
final case class MissingConfig(field : String) extends ConfigError
final case class ParseError(field: String) extends ConfigError
case class Config(map : Map[String,String])
case class HuffConfig(
clusterName: String,
clusterPort : Int,
clusterAddress : String,
hostname: String,
listeningPort: Int)
object Validator {
def getHuffConfig(config: Config) : ValidatedNel[ConfigError, HuffConfig] =
Apply[ValidatedNel[ConfigError, ?]].map5(
config.parse[String] ("DL_CLUSTER_NAME").toValidatedNel,
config.parse[Int] ("DL_CLUSTER_PORT").toValidatedNel,
config.parse[String] ("DL_CLUSTER_ADDRESS").toValidatedNel,
config.parse[String] ("DL_HTTP_ADDRESS").toValidatedNel,
config.parse[Int] ("DL_HTTP_PORT").toValidatedNel) {
case (clusterName, clusterPort, clusterAddress, httpAddr, httpPort) =>
HuffConfig(clusterName, clusterPort, clusterAddress, httpAddr, httpPort)
}
}
Define types to represent “errors"
Validate and read into configuration object.
Validation logic
How does anyone create a stack of Monads ?
Monad Transformers
How does anyone create a stack of Monads ?
Monad Transformers
Let’s take a closer look
scala> case class Cat(name: String, alive: Boolean)
defined class Cat
scala> def isAlive = Reader{ (u:User) => if (u.alive) u.asRight[Throwable].toOption:: Nil
| else scala.util.Try(throw new Exception("Dead!")).asLeft[User].toOption::Nil }
isAlive2: cats.data.Reader[User,List[Option[User]]]
scala> def lookup = Cat("cat", true).some::Nil
lookup: List[Option[Cat]]
scala> for {
| someCat <- lookup
| } yield {
| for {
| cat <- someCat
| } yield isAlive(cat)
|}
res47: List[Option[cats.Id[List[Option[Cat]]]]] = List(Some(List(User(cat,true))))
Let’s say we like to look up a cat and find out whether its alive.
We would use Option[Cat] to say whether we can find one, and perhaps
Either[Throwable,Cat] to represent when cat is dead, we throw an exception
else we return the Cat
First Attempt
Let’s take a closer look
scala> case class Cat(name: String, alive: Boolean)
defined class Cat
scala> def isAlive =
| Reader{ (u: Cat) => if (u.alive) OptionT( u.asRight[Throwable].toOption:: Nil)
| else OptionT( scala.util.Try(throw new Exception("Dead!")).asLeft[Cat].toOption::Nil) }
isAlive: cats.data.Reader[Cat,cats.data.OptionT[List, Cat]]
scala> def lookup = OptionT(Cat("cat", true).some::Nil)
lookup: cats.data.OptionT[List, Cat]
scala> for {
| cat <- lookup
| checked <- isAlive(cat)
| } yield checked
res32: cats.data.OptionT[List, Cat] = OptionT(List(Some(Cat(cat,true))))
The nested-yield loops can quickly get very confusing ….
that’s where Monad Transformers help!
Second Attempt
Effectful Monads aka Eff-Monads
Effectful Monads

An alternative to Monad Transformers
http://atnos-org.github.io/eff/
Use-case #4
Putting in the type-definitions: making use of the

Reader, Writer, Either Effects from Eff !
import xxx.workflow.models.{WorkflowDescriptor, Service}
import scala.language.{postfixOps, higherKinds}
import org.atnos.eff._, all._, syntax.all._
import com.typesafe.config._
import com.typesafe.scalalogging._
class LoadWorkflowDescriptorEff {
import cats._, data._, implicits._
import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._
lazy val config = ConfigFactory.load()
lazy val logger = Logger(getClass)
type WorkflowIdReader[A] = Reader[String, A]
type WriterString[A] = Writer[String,A]
type DecodeFailure[A] = io.circe.DecodingFailure Either A
type ParseFailure[A] = io.circe.ParsingFailure Either A
// ...
}
import java.time._
type LoadDescStack =
Fx.fx6[WorkflowIdReader, WriterString, DecodeFailure, ParseFailure, Throwable Either ?, Eval]
def loadDescriptor : Eff[LoadDescStack, WorkflowDescriptor] =
for {
workflowId <- ask[LoadDescStack,String]
_ <- tell[LoadDescStack,String](s"[${Instant.now()}] About to load data about workflow: $workflowId")
contents <- fromEither[LoadDescStack,java.lang.Throwable,String](loadContents(workflowId))
_ <- tell[LoadDescStack,String](s"[${Instant.now()}] Data is loaded from storage: $contents")
json <- fromEither[LoadDescStack,io.circe.ParsingFailure,io.circe.Json](parse(contents))
_ <- tell[LoadDescStack, String](s"[${Instant.now()}] Workflow descriptor parsed successfully")
desc <- fromEither[LoadDescStack, io.circe.DecodingFailure, WorkflowDescriptor](json.as[WorkflowDescriptor])
_ <- tell[LoadDescStack, String](s"[${Instant.now()}] Workflow descriptor hydrated into object.")
} yield desc
// Below is a test and you can choose either runEval or attemptEval
// attemptEval is a better option as it captures any errors met during the
// computation.
//println(loadDescriptor.runReader("1").runWriter.runEither.runEither.runEither.runPure)
lazy val result = {
val a = loadDescriptor.runReader("1").runWriter.runEither.runEither.runEither.runPure
val t = a.get
t.joinRight
}
// the logging version
lazy val result2 = {
val a = loadDescriptor.runReader("1").runWriterLog.runEither.runEither.runEither.runPure
val t = a.get
t.joinRight
}
}
Use-case #4
import java.time._
type LoadDescStack =
Fx.fx6[WorkflowIdReader, WriterString, DecodeFailure, ParseFailure, Throwable Either ?, Eval]
def loadDescriptor : Eff[LoadDescStack, WorkflowDescriptor] =
for {
workflowId <- ask[LoadDescStack,String]
_ <- tell[LoadDescStack,String](s"[${Instant.now()}] About to load data about workflow: $workflowId")
contents <- fromEither[LoadDescStack,java.lang.Throwable,String](loadContents(workflowId))
_ <- tell[LoadDescStack,String](s"[${Instant.now()}] Data is loaded from storage: $contents")
json <- fromEither[LoadDescStack,io.circe.ParsingFailure,io.circe.Json](parse(contents))
_ <- tell[LoadDescStack, String](s"[${Instant.now()}] Workflow descriptor parsed successfully")
desc <- fromEither[LoadDescStack, io.circe.DecodingFailure, WorkflowDescriptor](json.as[WorkflowDescriptor])
_ <- tell[LoadDescStack, String](s"[${Instant.now()}] Workflow descriptor hydrated into object.")
} yield desc
// Below is a test and you can choose either runEval or attemptEval
// attemptEval is a better option as it captures any errors met during the
// computation.
//println(loadDescriptor.runReader("1").runWriter.runEither.runEither.runEither.runPure)
lazy val result = {
val a = loadDescriptor.runReader("1").runWriter.runEither.runEither.runEither.runPure
val t = a.get
t.joinRight
}
// the logging version
lazy val result2 = {
val a = loadDescriptor.runReader("1").runWriterLog.runEither.runEither.runEither.runPure
val t = a.get
t.joinRight
}
}
Use-case #4
Eff-Monads allows us to stack computations
Learning resources
https://www.haskell.org/tutorial/monads.html
http://eed3si9n.com/herding-cats/
http://typelevel.org/cats/
http://blog.higher-order.com/
https://gitter.im/typelevel/cats
That’s it from me :)
Questions ?
Ad

More Related Content

What's hot (20)

Python programming : Arrays
Python programming : ArraysPython programming : Arrays
Python programming : Arrays
Emertxe Information Technologies Pvt Ltd
 
Introducing scala
Introducing scalaIntroducing scala
Introducing scala
Meetu Maltiar
 
Getting Started With Scala
Getting Started With ScalaGetting Started With Scala
Getting Started With Scala
Meetu Maltiar
 
An Introduction to Programming in Java: Arrays
An Introduction to Programming in Java: ArraysAn Introduction to Programming in Java: Arrays
An Introduction to Programming in Java: Arrays
Martin Chapman
 
Java arrays
Java arraysJava arrays
Java arrays
BHUVIJAYAVELU
 
Java Arrays
Java ArraysJava Arrays
Java Arrays
OXUS 20
 
Arrays in python
Arrays in pythonArrays in python
Arrays in python
Lifna C.S
 
An Introduction to Part of C++ STL
An Introduction to Part of C++ STLAn Introduction to Part of C++ STL
An Introduction to Part of C++ STL
乐群 陈
 
Arrays In Python | Python Array Operations | Edureka
Arrays In Python | Python Array Operations | EdurekaArrays In Python | Python Array Operations | Edureka
Arrays In Python | Python Array Operations | Edureka
Edureka!
 
Lec 25 - arrays-strings
Lec 25 - arrays-stringsLec 25 - arrays-strings
Lec 25 - arrays-strings
Princess Sam
 
LectureNotes-03-DSA
LectureNotes-03-DSALectureNotes-03-DSA
LectureNotes-03-DSA
Haitham El-Ghareeb
 
Ti1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: PolymorphismTi1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: Polymorphism
Eelco Visser
 
Array in Java
Array in JavaArray in Java
Array in Java
Shehrevar Davierwala
 
Array lecture
Array lectureArray lecture
Array lecture
Joan Saño
 
Java collections concept
Java collections conceptJava collections concept
Java collections concept
kumar gaurav
 
Java chapter 6 - Arrays -syntax and use
Java chapter 6 - Arrays -syntax and useJava chapter 6 - Arrays -syntax and use
Java chapter 6 - Arrays -syntax and use
Mukesh Tekwani
 
Arrays in Java
Arrays in JavaArrays in Java
Arrays in Java
Abhilash Nair
 
Arrays
ArraysArrays
Arrays
Trupti Agrawal
 
Python array
Python arrayPython array
Python array
Arnab Chakraborty
 
Arrays in python
Arrays in pythonArrays in python
Arrays in python
moazamali28
 
Getting Started With Scala
Getting Started With ScalaGetting Started With Scala
Getting Started With Scala
Meetu Maltiar
 
An Introduction to Programming in Java: Arrays
An Introduction to Programming in Java: ArraysAn Introduction to Programming in Java: Arrays
An Introduction to Programming in Java: Arrays
Martin Chapman
 
Java Arrays
Java ArraysJava Arrays
Java Arrays
OXUS 20
 
Arrays in python
Arrays in pythonArrays in python
Arrays in python
Lifna C.S
 
An Introduction to Part of C++ STL
An Introduction to Part of C++ STLAn Introduction to Part of C++ STL
An Introduction to Part of C++ STL
乐群 陈
 
Arrays In Python | Python Array Operations | Edureka
Arrays In Python | Python Array Operations | EdurekaArrays In Python | Python Array Operations | Edureka
Arrays In Python | Python Array Operations | Edureka
Edureka!
 
Lec 25 - arrays-strings
Lec 25 - arrays-stringsLec 25 - arrays-strings
Lec 25 - arrays-strings
Princess Sam
 
Ti1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: PolymorphismTi1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: Polymorphism
Eelco Visser
 
Java collections concept
Java collections conceptJava collections concept
Java collections concept
kumar gaurav
 
Java chapter 6 - Arrays -syntax and use
Java chapter 6 - Arrays -syntax and useJava chapter 6 - Arrays -syntax and use
Java chapter 6 - Arrays -syntax and use
Mukesh Tekwani
 
Arrays in python
Arrays in pythonArrays in python
Arrays in python
moazamali28
 

Similar to Practical cats (20)

Spark workshop
Spark workshopSpark workshop
Spark workshop
Wojciech Pituła
 
Scala Bootcamp 1
Scala Bootcamp 1Scala Bootcamp 1
Scala Bootcamp 1
Knoldus Inc.
 
Humble introduction to category theory in haskell
Humble introduction to category theory in haskellHumble introduction to category theory in haskell
Humble introduction to category theory in haskell
Jongsoo Lee
 
Scala Collections
Scala CollectionsScala Collections
Scala Collections
Meetu Maltiar
 
Scala Introduction
Scala IntroductionScala Introduction
Scala Introduction
Constantine Nosovsky
 
Frp2016 3
Frp2016 3Frp2016 3
Frp2016 3
Kirill Kozlov
 
Scala. Introduction to FP. Monads
Scala. Introduction to FP. MonadsScala. Introduction to FP. Monads
Scala. Introduction to FP. Monads
Kirill Kozlov
 
Why Haskell Matters
Why Haskell MattersWhy Haskell Matters
Why Haskell Matters
romanandreg
 
R교육1
R교육1R교육1
R교육1
Kangwook Lee
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
Hang Zhao
 
Stata Programming Cheat Sheet
Stata Programming Cheat SheetStata Programming Cheat Sheet
Stata Programming Cheat Sheet
Laura Hughes
 
Functions in advanced programming
Functions in advanced programmingFunctions in advanced programming
Functions in advanced programming
VisnuDharsini
 
Pune Clojure Course Outline
Pune Clojure Course OutlinePune Clojure Course Outline
Pune Clojure Course Outline
Baishampayan Ghose
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risks
SeniorDevOnly
 
An introduction to scala
An introduction to scalaAn introduction to scala
An introduction to scala
Mohsen Zainalpour
 
Stata cheatsheet programming
Stata cheatsheet programmingStata cheatsheet programming
Stata cheatsheet programming
Tim Essam
 
Functions In Scala
Functions In Scala Functions In Scala
Functions In Scala
Knoldus Inc.
 
Scala in Places API
Scala in Places APIScala in Places API
Scala in Places API
Łukasz Bałamut
 
C# programming
C# programming C# programming
C# programming
umesh patil
 
Monads and Monoids by Oleksiy Dyagilev
Monads and Monoids by Oleksiy DyagilevMonads and Monoids by Oleksiy Dyagilev
Monads and Monoids by Oleksiy Dyagilev
JavaDayUA
 
Humble introduction to category theory in haskell
Humble introduction to category theory in haskellHumble introduction to category theory in haskell
Humble introduction to category theory in haskell
Jongsoo Lee
 
Scala. Introduction to FP. Monads
Scala. Introduction to FP. MonadsScala. Introduction to FP. Monads
Scala. Introduction to FP. Monads
Kirill Kozlov
 
Why Haskell Matters
Why Haskell MattersWhy Haskell Matters
Why Haskell Matters
romanandreg
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
Hang Zhao
 
Stata Programming Cheat Sheet
Stata Programming Cheat SheetStata Programming Cheat Sheet
Stata Programming Cheat Sheet
Laura Hughes
 
Functions in advanced programming
Functions in advanced programmingFunctions in advanced programming
Functions in advanced programming
VisnuDharsini
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risks
SeniorDevOnly
 
Stata cheatsheet programming
Stata cheatsheet programmingStata cheatsheet programming
Stata cheatsheet programming
Tim Essam
 
Functions In Scala
Functions In Scala Functions In Scala
Functions In Scala
Knoldus Inc.
 
Monads and Monoids by Oleksiy Dyagilev
Monads and Monoids by Oleksiy DyagilevMonads and Monoids by Oleksiy Dyagilev
Monads and Monoids by Oleksiy Dyagilev
JavaDayUA
 
Ad

More from Raymond Tay (8)

Principled io in_scala_2019_distribution
Principled io in_scala_2019_distributionPrincipled io in_scala_2019_distribution
Principled io in_scala_2019_distribution
Raymond Tay
 
Building a modern data platform with scala, akka, apache beam
Building a modern data platform with scala, akka, apache beamBuilding a modern data platform with scala, akka, apache beam
Building a modern data platform with scala, akka, apache beam
Raymond Tay
 
Toying with spark
Toying with sparkToying with spark
Toying with spark
Raymond Tay
 
Distributed computing for new bloods
Distributed computing for new bloodsDistributed computing for new bloods
Distributed computing for new bloods
Raymond Tay
 
Functional programming with_scala
Functional programming with_scalaFunctional programming with_scala
Functional programming with_scala
Raymond Tay
 
Introduction to cuda geek camp singapore 2011
Introduction to cuda   geek camp singapore 2011Introduction to cuda   geek camp singapore 2011
Introduction to cuda geek camp singapore 2011
Raymond Tay
 
Introduction to Erlang
Introduction to ErlangIntroduction to Erlang
Introduction to Erlang
Raymond Tay
 
Introduction to CUDA
Introduction to CUDAIntroduction to CUDA
Introduction to CUDA
Raymond Tay
 
Principled io in_scala_2019_distribution
Principled io in_scala_2019_distributionPrincipled io in_scala_2019_distribution
Principled io in_scala_2019_distribution
Raymond Tay
 
Building a modern data platform with scala, akka, apache beam
Building a modern data platform with scala, akka, apache beamBuilding a modern data platform with scala, akka, apache beam
Building a modern data platform with scala, akka, apache beam
Raymond Tay
 
Toying with spark
Toying with sparkToying with spark
Toying with spark
Raymond Tay
 
Distributed computing for new bloods
Distributed computing for new bloodsDistributed computing for new bloods
Distributed computing for new bloods
Raymond Tay
 
Functional programming with_scala
Functional programming with_scalaFunctional programming with_scala
Functional programming with_scala
Raymond Tay
 
Introduction to cuda geek camp singapore 2011
Introduction to cuda   geek camp singapore 2011Introduction to cuda   geek camp singapore 2011
Introduction to cuda geek camp singapore 2011
Raymond Tay
 
Introduction to Erlang
Introduction to ErlangIntroduction to Erlang
Introduction to Erlang
Raymond Tay
 
Introduction to CUDA
Introduction to CUDAIntroduction to CUDA
Introduction to CUDA
Raymond Tay
 
Ad

Recently uploaded (20)

SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdfSAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
Precisely
 
Build Your Own Copilot & Agents For Devs
Build Your Own Copilot & Agents For DevsBuild Your Own Copilot & Agents For Devs
Build Your Own Copilot & Agents For Devs
Brian McKeiver
 
Big Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur MorganBig Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur Morgan
Arthur Morgan
 
Role of Data Annotation Services in AI-Powered Manufacturing
Role of Data Annotation Services in AI-Powered ManufacturingRole of Data Annotation Services in AI-Powered Manufacturing
Role of Data Annotation Services in AI-Powered Manufacturing
Andrew Leo
 
Generative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in BusinessGenerative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in Business
Dr. Tathagat Varma
 
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In FranceManifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
chb3
 
Cyber Awareness overview for 2025 month of security
Cyber Awareness overview for 2025 month of securityCyber Awareness overview for 2025 month of security
Cyber Awareness overview for 2025 month of security
riccardosl1
 
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc
 
Cybersecurity Identity and Access Solutions using Azure AD
Cybersecurity Identity and Access Solutions using Azure ADCybersecurity Identity and Access Solutions using Azure AD
Cybersecurity Identity and Access Solutions using Azure AD
VICTOR MAESTRE RAMIREZ
 
Dev Dives: Automate and orchestrate your processes with UiPath Maestro
Dev Dives: Automate and orchestrate your processes with UiPath MaestroDev Dives: Automate and orchestrate your processes with UiPath Maestro
Dev Dives: Automate and orchestrate your processes with UiPath Maestro
UiPathCommunity
 
How analogue intelligence complements AI
How analogue intelligence complements AIHow analogue intelligence complements AI
How analogue intelligence complements AI
Paul Rowe
 
TrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business ConsultingTrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business Consulting
Trs Labs
 
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded DevelopersLinux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Toradex
 
Drupalcamp Finland – Measuring Front-end Energy Consumption
Drupalcamp Finland – Measuring Front-end Energy ConsumptionDrupalcamp Finland – Measuring Front-end Energy Consumption
Drupalcamp Finland – Measuring Front-end Energy Consumption
Exove
 
Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025
Splunk
 
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
BookNet Canada
 
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
BookNet Canada
 
Linux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdfLinux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdf
RHCSA Guru
 
AI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global TrendsAI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global Trends
InData Labs
 
ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes Partner Innovation Updates for May 2025ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes
 
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdfSAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
Precisely
 
Build Your Own Copilot & Agents For Devs
Build Your Own Copilot & Agents For DevsBuild Your Own Copilot & Agents For Devs
Build Your Own Copilot & Agents For Devs
Brian McKeiver
 
Big Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur MorganBig Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur Morgan
Arthur Morgan
 
Role of Data Annotation Services in AI-Powered Manufacturing
Role of Data Annotation Services in AI-Powered ManufacturingRole of Data Annotation Services in AI-Powered Manufacturing
Role of Data Annotation Services in AI-Powered Manufacturing
Andrew Leo
 
Generative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in BusinessGenerative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in Business
Dr. Tathagat Varma
 
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In FranceManifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
chb3
 
Cyber Awareness overview for 2025 month of security
Cyber Awareness overview for 2025 month of securityCyber Awareness overview for 2025 month of security
Cyber Awareness overview for 2025 month of security
riccardosl1
 
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc Webinar: Consumer Expectations vs Corporate Realities on Data Broker...
TrustArc
 
Cybersecurity Identity and Access Solutions using Azure AD
Cybersecurity Identity and Access Solutions using Azure ADCybersecurity Identity and Access Solutions using Azure AD
Cybersecurity Identity and Access Solutions using Azure AD
VICTOR MAESTRE RAMIREZ
 
Dev Dives: Automate and orchestrate your processes with UiPath Maestro
Dev Dives: Automate and orchestrate your processes with UiPath MaestroDev Dives: Automate and orchestrate your processes with UiPath Maestro
Dev Dives: Automate and orchestrate your processes with UiPath Maestro
UiPathCommunity
 
How analogue intelligence complements AI
How analogue intelligence complements AIHow analogue intelligence complements AI
How analogue intelligence complements AI
Paul Rowe
 
TrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business ConsultingTrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business Consulting
Trs Labs
 
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded DevelopersLinux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Toradex
 
Drupalcamp Finland – Measuring Front-end Energy Consumption
Drupalcamp Finland – Measuring Front-end Energy ConsumptionDrupalcamp Finland – Measuring Front-end Energy Consumption
Drupalcamp Finland – Measuring Front-end Energy Consumption
Exove
 
Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025
Splunk
 
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
BookNet Canada
 
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
BookNet Canada
 
Linux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdfLinux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdf
RHCSA Guru
 
AI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global TrendsAI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global Trends
InData Labs
 
ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes Partner Innovation Updates for May 2025ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes
 

Practical cats

  • 4. CATS…. To use cats effectively, understand what each construct does: Functor Monads Applicatives Monoids Semigroups SO MANY ACRONYMS !!! Validated
  • 6. Building blocks … Understand that they are building blocks so that you can write code that is pure and code that has side-effects — separation of concerns.
  • 7. Typeclasses … Each of the type class (e.g. functors, monoids, monads etc) are governed by laws. Typeclasses! they are behaviours that can be “inherited” by your code.
  • 8. Semigroups - what are they? trait Semigroup[A] { def combine(x: A, y: A) : A } general structure to define things that can be combined. *Cats provides “default” implementations; developers (like you & me) need to provide implementations that conform to the traits. *
  • 9. Monoids - what are they? trait Monoid[A] extends Semigroup[A] { def empty: A def combine(x: A, y: A) : A } general structure to define things that can be combined and has a “default” element. *Cats provides “default” implementations; developers (like you & me) need to provide implementations that conform to the traits. *
  • 10. Monoids - what are they? > import cats._, data._, implicits._ > Monoid[String].combine(“hi”, “there”) // res0: String = “hithere” > “hi” |+| “there” // res1: String = “hithere”
  • 11. Use case for Monoids/Semigroups They’re good for combining 2 or more things of a similar nature data-type-a data-type-b data-stream end- point parser collector of either data-type-a or data-type-b
  • 12. Use case #1 - Monoids for “smashing” values * all names used here do not reflect the actuals * // Monoid[DataTypeAB] defined somewhere else def buildDataFromStream(datatypeA : DataTypeA, datatypeB : DataTypeB, accumulator: DatatypeAB) = validateData(datatypeA, datatypeB).fold( onError => { // `orError` is lifted into the datatype val errors = Monoid[DatatypeAB].empty.copy(lift(onError)) Monoid[DatatypeAB].combine(accumulator, errors) }, parsedValue => { // `parsedValue` is lifted into the datatype val newValue = Monoid[DatatypeAB].empty.copy(lift(parsedValue)) Monoid[DatatypeAB].combine(accumulator, newValue) } )
  • 13. Functors - what are they? trait Functor[F[_]] { def map[A,B](fa: F[A])(f: A => B) : F[B] } general structure to represent something that can be mapped over. If you’ve been using Lists , Options, Eithers, Futures in Scala, you’ve been using functors. !!! They are very common structures indeed ☺ !!! * functors are used in clever things like recursion-schemes *
  • 14. Functors - what are they? > import cats._, data._, implicits._ > Functor[List].lift((x:Int) => x + 1) // res0: List[Int] => List[Int] > res0(List(1)) // res1: List[Int] = List(2) * Nugget of info: Functors preserve “structure” *
  • 15. Monads Monads are meant for sequencing computations
  • 16. Monads someList.flatmap(element => someOtherList.flatmap(element2 => (element, element2))) *No tuples generated if either “someList” Or “someOtherList” is empty*
  • 17. Monads someList.flatmap(element => someOtherList.flatmap(element2 => (element, element2))) Monads allows for short-circuiting of computations
  • 18. Monads - a quick summary? Writers - information can be carried along with the computation Readers - compose operations that depend on some input. State - allows state to be “propagated” Eval - abstracts over eager vs lazy evaluation.
  • 19. Monads - examples > List(1,2,3) >>= (value => List(value+1)) > res0: List[Int] = List(2,3,4) def >>=[A,B](fa: F[A])(f:A => F[B]): F[B] = flatMap(fa)(f) “>>=“ is also known as “bind” (in Cats, its really “flatMap”)
  • 20. Monads - examples > Monad[List].lift((x:Int) => x + 1)(List(1,2,3)) > res1: List[Int] = List(2,3,4) Typeclasses allows you to define re-usable code by lifting functions.
  • 21. Monads - examples > Monad[List].pure(4) > res2: List[Int] = List(4) This is to lift values into a context, in this case Monadic context.
  • 22. Monads - flow scala> def first = Reader( (x:Int) => Monad[List].ifM(List(true,true))(x::Nil, Nil) ) extractGroups: cats.data.Reader[Int,List[Int]] scala> def second = Reader( (x:Int) => Monad[List].ifM(List(true,true))(x::Nil, Nil) ) loadGroup: cats.data.Reader[Int,List[Int]] scala> for { g <- first(4); gg <- second(g) } yield gg res21: List[Int] = List(4, 4, 4, 4)
  • 23. Writer Monad “Writers” are typically used to carry not only the value of a computation but also some other information (typically, its used to carry logging info). source: http://eed3si9n.com/herding-cats/Writer.html
  • 24. Writer Monad scala> def logNumber(x: Int): Writer[List[String], Int] =          Writer(List("Got number: " + x.show), 3) logNumber: (x: Int)cats.data.Writer[List[String],Int] scala> def multWithLog: Writer[List[String], Int] =          for {            a <- logNumber(3)            b <- logNumber(5)          } yield a * b multWithLog: cats.data.Writer[List[String],Int] scala> multWithLog.run res2: cats.Id[(List[String], Int)] = (List(Got number: 3, Got number: 5),9) scala> multWithLog.reset res6: cats.data.WriterT[cats.Id,List[String],Int] = WriterT((List(),9)) scala> multWithLog.swap res8: cats.data.WriterT[cats.Id,Int,List[String]] = WriterT((9,List(Got number: 4, Got number: 3))) scala> multWithLog.value res9: cats.Id[Int] = 9 scala> multWithLog.written res10: cats.Id[List[String]] = List(Got number: 4, Got number: 3) source: http://eed3si9n.com/herding-cats/Writer.html Compose Writers
  • 25. Reader Monad “Readers” allows us to compose operations which depend on some input. source: http://eed3si9n.com/herding-cats/Reader.html
  • 26. Reader Monad case class Config(setting: String, value: String) def getSetting = Reader { (config: Config) => config.setting } def getValue = Reader { (config: Config) => config.value } for { s <- getSetting v <- getValue } yield Config(s, v) Compose Readers FP-style to abstract and encapsulate.
  • 27. State Monad Allows us to pass state-information around in a computation. http://eed3si9n.com/herding-cats/State.html
  • 28. Use case #3 - Reader + State Monad def process: Reader[Elem, Seq[Mapping]] = Reader { (xml: Elem) => for { groups <- extractGroups(dataXml).toOption group <- groups grpCfg <- loadGroupConfig(group).toOption stateObj <- ConfigState(grpCfg).pure[Option] records <- loadRecords(group).toOption record <- records row <- processRecord(i)(stateObj)(record).pure[Option] } yield { // processing … } } case class ConfigState(init: Config) { private[this] var currentState: Config = init def storeCfg : State[Config, Config] = State{ (cfg: Config) => val prevState = currentState currentState = cfg (currentState, prevState) } def loadCfg : Config = ( for { s <- State.get[Config] } yield s ).runA(currentState).value }
  • 29. Use case #3 - Reader + State Monad def process: Reader[Elem, Seq[Mapping]] = Reader { (xml: Elem) => for { groups <- extractGroups(dataXml).toOption group <- groups grpCfg <- loadGroupConfig(group).toOption stateObj <- ConfigState(grpCfg).pure[Option] records <- loadRecords(group).toOption record <- records row <- processRecord(i)(stateObj)(record).pure[Option] } yield { // processing … } } case class ConfigState(init: Config) { private[this] var currentState: Config = init def storeCfg : State[Config, Config] = State{ (cfg: Config) => val prevState = currentState currentState = cfg (currentState, prevState) } def loadCfg : Config = ( for { s <- State.get[Config] } yield s ).runA(currentState).value } Separation of concerns State management
  • 30. Applicative Applicatives allows for functions to be lifted over a structure (Functor). Because the function and the value it’s being applied to both have structures, hence its needs to be combined.
  • 31. Applicative - examples scala> Applicative[List].lift((x:Int) => x + 1) res1: List[Int] => List[Int] = <function1> scala> Applicative[List].lift( | (x:List[Int=>Int]) => | x.map(f => f(2))) | (List( List((x:Int) => x + 1 ))) res7: List[List[Int]] = List(List(3)) scala> val fs = List(List((x:Int) => x + 1)) fs: List[List[Int => Int]] = List(List(<function1>)) scala> fs.map(_(2)) res15: cats.data.Nested[List,List,Int] = Nested(List(List(3))) Applicative is like a Functor
  • 32. Applicative - examples scala> Applicative[List].lift((x:Int) => x + 1) res1: List[Int] => List[Int] = <function1> scala> Applicative[List].lift( | (x:List[Int=>Int]) => | x.map(f => f(2))) | (List( List((x:Int) => x + 1 ))) res7: List[List[Int]] = List(List(3)) scala> val fs = List(List((x:Int) => x + 1)) fs: List[List[Int => Int]] = List(List(<function1>)) scala> fs.map(_(2)) res15: cats.data.Nested[List,List,Int] = Nested(List(List(3))) Applicative is like a Functor Applying a function which is nested.
  • 33. Applicative - examples scala> Applicative[List].lift((x:Int) => x + 1) res1: List[Int] => List[Int] = <function1> scala> Applicative[List].lift( | (x:List[Int=>Int]) => | x.map(f => f(2))) | (List( List((x:Int) => x + 1 ))) res7: List[List[Int]] = List(List(3)) scala> val fs = List(List((x:Int) => x + 1)) fs: List[List[Int => Int]] = List(List(<function1>)) scala> fs.map(_(2)) res15: cats.data.Nested[List,List,Int] = Nested(List(List(3))) Applicative is like a Functor Applying a function which is nested. Cat has a “Nested” to achieve the same.
  • 34. Applicative - examples A typical application is to leverage Applicatives in writing Logic to validate configurations, forms etc
  • 35. import cats.Cartesian import cats.data.Validated import cats.instances.list._ // Semigroup for List type AllErrorsOr[A] = Validated[List[String], A] Cartesian[AllErrorsOr].product( Validated.invalid(List("Error 1")), Validated.invalid(List("Error 2")) ) // res1: AllErrorsOr[(Nothing, Nothing)] = Invalid(List(Error 1,Error 2)) Applicative - examples
  • 36. package xxx.config import scala.concurrent.duration.{Duration,FiniteDuration} import cats._ import cats.data._ import cats.implicits._ import cats.data.Validated import cats.data.Validated.{Invalid, Valid} // code that needs to remain hidden sealed abstract class ConfigError final case class MissingConfig(field : String) extends ConfigError final case class ParseError(field: String) extends ConfigError case class Config(map : Map[String,String]) case class HuffConfig( clusterName: String, clusterPort : Int, clusterAddress : String, hostname: String, listeningPort: Int) object Validator { def getHuffConfig(config: Config) : ValidatedNel[ConfigError, HuffConfig] = Apply[ValidatedNel[ConfigError, ?]].map5( config.parse[String] ("DL_CLUSTER_NAME").toValidatedNel, config.parse[Int] ("DL_CLUSTER_PORT").toValidatedNel, config.parse[String] ("DL_CLUSTER_ADDRESS").toValidatedNel, config.parse[String] ("DL_HTTP_ADDRESS").toValidatedNel, config.parse[Int] ("DL_HTTP_PORT").toValidatedNel) { case (clusterName, clusterPort, clusterAddress, httpAddr, httpPort) => HuffConfig(clusterName, clusterPort, clusterAddress, httpAddr, httpPort) } }
  • 37. package xxx.config import scala.concurrent.duration.{Duration,FiniteDuration} import cats._ import cats.data._ import cats.implicits._ import cats.data.Validated import cats.data.Validated.{Invalid, Valid} // code that needs to remain hidden sealed abstract class ConfigError final case class MissingConfig(field : String) extends ConfigError final case class ParseError(field: String) extends ConfigError case class Config(map : Map[String,String]) case class HuffConfig( clusterName: String, clusterPort : Int, clusterAddress : String, hostname: String, listeningPort: Int) object Validator { def getHuffConfig(config: Config) : ValidatedNel[ConfigError, HuffConfig] = Apply[ValidatedNel[ConfigError, ?]].map5( config.parse[String] ("DL_CLUSTER_NAME").toValidatedNel, config.parse[Int] ("DL_CLUSTER_PORT").toValidatedNel, config.parse[String] ("DL_CLUSTER_ADDRESS").toValidatedNel, config.parse[String] ("DL_HTTP_ADDRESS").toValidatedNel, config.parse[Int] ("DL_HTTP_PORT").toValidatedNel) { case (clusterName, clusterPort, clusterAddress, httpAddr, httpPort) => HuffConfig(clusterName, clusterPort, clusterAddress, httpAddr, httpPort) } } Define types to represent “errors"
  • 38. package xxx.config import scala.concurrent.duration.{Duration,FiniteDuration} import cats._ import cats.data._ import cats.implicits._ import cats.data.Validated import cats.data.Validated.{Invalid, Valid} // code that needs to remain hidden sealed abstract class ConfigError final case class MissingConfig(field : String) extends ConfigError final case class ParseError(field: String) extends ConfigError case class Config(map : Map[String,String]) case class HuffConfig( clusterName: String, clusterPort : Int, clusterAddress : String, hostname: String, listeningPort: Int) object Validator { def getHuffConfig(config: Config) : ValidatedNel[ConfigError, HuffConfig] = Apply[ValidatedNel[ConfigError, ?]].map5( config.parse[String] ("DL_CLUSTER_NAME").toValidatedNel, config.parse[Int] ("DL_CLUSTER_PORT").toValidatedNel, config.parse[String] ("DL_CLUSTER_ADDRESS").toValidatedNel, config.parse[String] ("DL_HTTP_ADDRESS").toValidatedNel, config.parse[Int] ("DL_HTTP_PORT").toValidatedNel) { case (clusterName, clusterPort, clusterAddress, httpAddr, httpPort) => HuffConfig(clusterName, clusterPort, clusterAddress, httpAddr, httpPort) } } Define types to represent “errors" Validate and read into configuration object.
  • 39. package xxx.config import scala.concurrent.duration.{Duration,FiniteDuration} import cats._ import cats.data._ import cats.implicits._ import cats.data.Validated import cats.data.Validated.{Invalid, Valid} // code that needs to remain hidden sealed abstract class ConfigError final case class MissingConfig(field : String) extends ConfigError final case class ParseError(field: String) extends ConfigError case class Config(map : Map[String,String]) case class HuffConfig( clusterName: String, clusterPort : Int, clusterAddress : String, hostname: String, listeningPort: Int) object Validator { def getHuffConfig(config: Config) : ValidatedNel[ConfigError, HuffConfig] = Apply[ValidatedNel[ConfigError, ?]].map5( config.parse[String] ("DL_CLUSTER_NAME").toValidatedNel, config.parse[Int] ("DL_CLUSTER_PORT").toValidatedNel, config.parse[String] ("DL_CLUSTER_ADDRESS").toValidatedNel, config.parse[String] ("DL_HTTP_ADDRESS").toValidatedNel, config.parse[Int] ("DL_HTTP_PORT").toValidatedNel) { case (clusterName, clusterPort, clusterAddress, httpAddr, httpPort) => HuffConfig(clusterName, clusterPort, clusterAddress, httpAddr, httpPort) } } Define types to represent “errors" Validate and read into configuration object. Validation logic
  • 40. How does anyone create a stack of Monads ? Monad Transformers
  • 41. How does anyone create a stack of Monads ? Monad Transformers
  • 42. Let’s take a closer look scala> case class Cat(name: String, alive: Boolean) defined class Cat scala> def isAlive = Reader{ (u:User) => if (u.alive) u.asRight[Throwable].toOption:: Nil | else scala.util.Try(throw new Exception("Dead!")).asLeft[User].toOption::Nil } isAlive2: cats.data.Reader[User,List[Option[User]]] scala> def lookup = Cat("cat", true).some::Nil lookup: List[Option[Cat]] scala> for { | someCat <- lookup | } yield { | for { | cat <- someCat | } yield isAlive(cat) |} res47: List[Option[cats.Id[List[Option[Cat]]]]] = List(Some(List(User(cat,true)))) Let’s say we like to look up a cat and find out whether its alive. We would use Option[Cat] to say whether we can find one, and perhaps Either[Throwable,Cat] to represent when cat is dead, we throw an exception else we return the Cat First Attempt
  • 43. Let’s take a closer look scala> case class Cat(name: String, alive: Boolean) defined class Cat scala> def isAlive = | Reader{ (u: Cat) => if (u.alive) OptionT( u.asRight[Throwable].toOption:: Nil) | else OptionT( scala.util.Try(throw new Exception("Dead!")).asLeft[Cat].toOption::Nil) } isAlive: cats.data.Reader[Cat,cats.data.OptionT[List, Cat]] scala> def lookup = OptionT(Cat("cat", true).some::Nil) lookup: cats.data.OptionT[List, Cat] scala> for { | cat <- lookup | checked <- isAlive(cat) | } yield checked res32: cats.data.OptionT[List, Cat] = OptionT(List(Some(Cat(cat,true)))) The nested-yield loops can quickly get very confusing …. that’s where Monad Transformers help! Second Attempt
  • 44. Effectful Monads aka Eff-Monads Effectful Monads An alternative to Monad Transformers http://atnos-org.github.io/eff/
  • 45. Use-case #4 Putting in the type-definitions: making use of the Reader, Writer, Either Effects from Eff ! import xxx.workflow.models.{WorkflowDescriptor, Service} import scala.language.{postfixOps, higherKinds} import org.atnos.eff._, all._, syntax.all._ import com.typesafe.config._ import com.typesafe.scalalogging._ class LoadWorkflowDescriptorEff { import cats._, data._, implicits._ import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._ lazy val config = ConfigFactory.load() lazy val logger = Logger(getClass) type WorkflowIdReader[A] = Reader[String, A] type WriterString[A] = Writer[String,A] type DecodeFailure[A] = io.circe.DecodingFailure Either A type ParseFailure[A] = io.circe.ParsingFailure Either A // ... }
  • 46. import java.time._ type LoadDescStack = Fx.fx6[WorkflowIdReader, WriterString, DecodeFailure, ParseFailure, Throwable Either ?, Eval] def loadDescriptor : Eff[LoadDescStack, WorkflowDescriptor] = for { workflowId <- ask[LoadDescStack,String] _ <- tell[LoadDescStack,String](s"[${Instant.now()}] About to load data about workflow: $workflowId") contents <- fromEither[LoadDescStack,java.lang.Throwable,String](loadContents(workflowId)) _ <- tell[LoadDescStack,String](s"[${Instant.now()}] Data is loaded from storage: $contents") json <- fromEither[LoadDescStack,io.circe.ParsingFailure,io.circe.Json](parse(contents)) _ <- tell[LoadDescStack, String](s"[${Instant.now()}] Workflow descriptor parsed successfully") desc <- fromEither[LoadDescStack, io.circe.DecodingFailure, WorkflowDescriptor](json.as[WorkflowDescriptor]) _ <- tell[LoadDescStack, String](s"[${Instant.now()}] Workflow descriptor hydrated into object.") } yield desc // Below is a test and you can choose either runEval or attemptEval // attemptEval is a better option as it captures any errors met during the // computation. //println(loadDescriptor.runReader("1").runWriter.runEither.runEither.runEither.runPure) lazy val result = { val a = loadDescriptor.runReader("1").runWriter.runEither.runEither.runEither.runPure val t = a.get t.joinRight } // the logging version lazy val result2 = { val a = loadDescriptor.runReader("1").runWriterLog.runEither.runEither.runEither.runPure val t = a.get t.joinRight } } Use-case #4
  • 47. import java.time._ type LoadDescStack = Fx.fx6[WorkflowIdReader, WriterString, DecodeFailure, ParseFailure, Throwable Either ?, Eval] def loadDescriptor : Eff[LoadDescStack, WorkflowDescriptor] = for { workflowId <- ask[LoadDescStack,String] _ <- tell[LoadDescStack,String](s"[${Instant.now()}] About to load data about workflow: $workflowId") contents <- fromEither[LoadDescStack,java.lang.Throwable,String](loadContents(workflowId)) _ <- tell[LoadDescStack,String](s"[${Instant.now()}] Data is loaded from storage: $contents") json <- fromEither[LoadDescStack,io.circe.ParsingFailure,io.circe.Json](parse(contents)) _ <- tell[LoadDescStack, String](s"[${Instant.now()}] Workflow descriptor parsed successfully") desc <- fromEither[LoadDescStack, io.circe.DecodingFailure, WorkflowDescriptor](json.as[WorkflowDescriptor]) _ <- tell[LoadDescStack, String](s"[${Instant.now()}] Workflow descriptor hydrated into object.") } yield desc // Below is a test and you can choose either runEval or attemptEval // attemptEval is a better option as it captures any errors met during the // computation. //println(loadDescriptor.runReader("1").runWriter.runEither.runEither.runEither.runPure) lazy val result = { val a = loadDescriptor.runReader("1").runWriter.runEither.runEither.runEither.runPure val t = a.get t.joinRight } // the logging version lazy val result2 = { val a = loadDescriptor.runReader("1").runWriterLog.runEither.runEither.runEither.runPure val t = a.get t.joinRight } } Use-case #4 Eff-Monads allows us to stack computations
  • 49. That’s it from me :) Questions ?