SlideShare a Scribd company logo
FUNCTIONS, TYPES,
PROGRAMS AND EFFECTS
QUIZHANDS UP IF YOU RECOGNIZE
A => B
A => Option[B]
Either[A,B]
A / B
A Xor B
A => M[A]
scala.concurrent.Future[+A]
scalaz.concurrent.Task[+A]
What does this do?
(maybeInt1 |@| maybeInt2) { _ > _ } | false
scalaz.SemiGroup
scalaz.Monoid
scalaz.PlusEmpty
scalaz.Functor
scalaz.Applicative
scalaz.Monad
scalaz.ApplicativePlus
scalaz.MonadPlus
Does SI-2712 ring a bell?
scalaz.Unapply[TC[_[_]], MA]
traverseU
EitherT[F[_], A, B]
OptionT[F[_], A]
What's this all about?
type E = Task |: (String / ?) |: Option |: Base
type Stack = ReaderInt |: WriterString |: Eval |: NoEffect
type PRG[A] = (Log.DSL :@: DB.DSL :@: FXNil)#Cop[A]
Free[PRG, Xor[DBError, Entity]]
Do you use scalaz or cats for your day job?
FUNCTIONS, TYPES, PROGRAMS AND EFFECTS
Strict separation of safe and unsafe
Safe: Functions, Types, creating Programs
Unsafe: Effects, running Programs
WRITING FUNCTIONAL SCALA
1. Write functions, which return values
2. Choose effects (or write your own)
3. Construct a program that composes values into effects
4. Run the program (in an 'App' in the main method)
(Not necessarily in that order)
WRITING FUNCTIONAL SCALA
▸ You can possibly have programs of programs
▸ A program is very often defined in a for comprehension
A hypothetical example.
val program = for {
client <- server.connect(details)
Exchange(src, snk) = client.exchange
_ <- snk.sendRequest(request)
in = src.pipe(text.utf8Decode)
.to(io.stdOutLines)
} yield ()
program.run
Define first, run later.
BUT FIRST: SOMETHING
ABOUT INFORMATION LOSS
QUIZKEEP IN MIND: FUNCTIONS, TYPES,
PROGRAMS, EFFECTS
Anything 'wrong' with these methods / functions?
def getUser(username: Username): Future[User]
def createUser(details: UserDetails): Future[User]
def getPrimaryAccount(user: User): Future[Account]
Anything 'wrong' with this?
def getUser(username: Username): Future[Option[User]]
def getAccounts(user: User): Future[List[Account]]
def approved(user: User, accounts: List[Account]): Future[Boolean]
DATA LOSS
Boolean blindness
Functions return values, discarding them constrains the Program 1
1
Think of the information you need later on in a for comprehension
What is 'wrong' with this?
sealed trait Error
case object UserNotFound extends Error
case object UserNameNotFound extends Error
case object AccountNotFound extends Error
def getUser(username: Username): Future[Either[Error, User]]
def getAccounts(user: User): Future[Either[Error, Account]]
scalaz Disjunction (left or right)
val res: String / Int = /-(5)
val res1: String / Int = 5.right
val moreVerbose: String / Int = 5.right[String]
val res2: String / Int = "Some error".left
val friendlier = res2.leftMap(error => s"$error has occured, we apologize.")
val res3 = res1.map(five => five * 2) // /-(10)
Map over the right
From Throwable to /
/.fromTryCatchThrowable[String, Exception] {
getDangerousString()
} // returns a Exception / String
/.fromTryCatchThrowable[String, Exception] {
getDangerousString()
}.leftMap(e=> MyError(e)) // returns a MyError / String
From A / B to X
val res: String / Int = "Error!".left
val res1 = res.fold(left=> 0, r => r)
val res2 = res.fold(left=> 0, identity) // this is the same
Why should you not use this?
Combining /
case class Error(in: Int, reason: String)
def even(in: Int): Error / Int =
if(in % 2 == 0) in.right
else Error(in, "not even").left
def divByThree(in: Int): Error / Int =
if(in % 3 == 0) in.right
else Error(in, "not div 3").left
def evenAndByThree(in: Int) =
for {
evenNr <- even(in)
byThree <- divByThree(evenNr)
} yield byThree
println(evenAndByThree(12)) // /-(12)
println(evenAndByThree(3)) // -/-(3, "not even")
println(evenAndByThree(4)) // -/-(4, "not div 3")
def evenAndByThree(in: Int) =
for {
evenNr <- even(in)
byThree <- divByThree(evenNr)
} yield byThree
No loss of information (why the first error occurred).
Given
def getUser(username: Username): Future[Error / User]]
def getAccounts(user: User): Future[Error / List[Account]]
Does this compile?
val result = for {
user <- getUser(name)
accounts <- getAccounts(user)
} yield accounts
COMBINING
EFFECTS
Combining values
Monoid - collecting / combining values into one value
//simplified / pseudo
val append: (F,F) => F
val zero: F
//actual
def append(f1: F, f2: => F): F
def zero: F
COMBINING EFFECTS
Monad - collecting / combining effects 2
def point[A](a: => A): F[A] // Creates an 'effects collector'
def bind[A, B](fa: F[A])(f: A => F[B]): F[B] // adds an effect
def map[A,B](fa: F[A])(f: A => B):F[B] // transforms input for next effect
Can only combine for same type of F.2
2
The Monad is a monoid in the category of endofunctors joke
In scalaz, flatMap is defined as:
def flatMap[B](f: A => F[B]) = F.bind(self)(f)
flatMap ~= bind
Monads are great, but in general NOT composable.
Monad Transformers
Effect systems (i.e. Eff from eff-cats)
In common, a Type that combines N Monad types
Monad Transformer EitherT
val res = for {
user <- EitherT(getUser("bla"))
accounts <- EitherT(getAccounts(user))
} yield accounts // returns EitherT[Future, Error, List[Account]]
res.run // Future[Error / List[Account]]
Scalaz provides Future Monad instance in scalaz.std.scalaFuture
Construct the Program
val res = for {
user <- EitherT(getUser("bla"))
accounts <- EitherT(getAccounts(user))
} yield accounts
Run the Program
res.run
Sometimes... a Monad Wrapper is a good enough Program
WRITE YOUR OWN MONAD
WRAPPER 3
3
specific monad transformer?
WRITE YOUR OWN MONAD WRAPPER
Topics (Kafka library)
Combine Future and /
Covariant +A/+B
Know why a Kafka operation failed in a Program
Why not EitherT?
final case class EitherT[F[_], A, B](run: F[A / B])
Invariant in A,B
Covariant Error type
sealed trait TopicsError
sealed trait FindTopicError extends TopicsError
sealed trait SelectTopicError extends TopicsError
sealed trait DeleteTopicError extends TopicsError
Example program
val program: Topics[TopicsError, Chunk[String, String]] = for {
partition ← Topics.partitioned[String, String](topicDef, partitionId)
info ← partition.write(record)
chunk ← partition.read(info.offset, info.offset)
} yield chunk
// ... somewhere later
program.run(broker, executionContext)
Minor detour (type lambda)
Monad[{ type λ[α] = Topics[E, α] })#λ]
~=
type MyMonad[T] = Monad[Topics[E, T]]
only inline.
Roll your own Monad instance (Scalaz)
object Topics {
implicit def topicsMonad[E] = new Monad[({ type λ[α] = Topics[E, α] })#λ] {
def point[T](value: T): Topics[E, T] = Topics.point(value)
def bind[T, U](topics: Topics[E, T])(f: T Topics[E, U]): Topics[E, U] = topics.flatMap(f)
}
}
delegate to point and flatMap
Roll your own Monad
case class Topics[+E, +T](run: (TopicBroker, ExecutionContext) Future[E / T]) {
// more to follow
Roll your own Monad
case class Topics[+E, +T](run: (TopicBroker, ExecutionContext) Future[E / T]) {
def map[C](f: T C): Topics[E, C] =
Topics((broker, ec) run(broker, ec).map(_.map(f))(ec))
def flatMap[E1 >: E, C](f: T Topics[E1, C]): Topics[E1, C] =
Topics { (broker, ec)
run(broker, ec).flatMap(
_.fold(err Future.successful(-/(err)), res f(res).run(broker, ec))
)(ec)
}
}
map
case class Topics[+E, +T](run: (TopicBroker, ExecutionContext) Future[E / T]) {
def map[C](f: T C): Topics[E, C] =
Topics((broker, ec) run(broker, ec).map(_.map(f))(ec))
zoom in
Topics((broker, ec) run(broker, ec).map(_.map(f))(ec))
zoom in
run(broker, ec).map(_.map(f))
flatMap
Topics { (broker, ec)
run(broker, ec).flatMap(
_.fold(err Future.successful(-/(err)), res f(res).run(broker, ec))
)(ec)
}
zoom in
run(broker, ec).flatMap(
_.fold(err Future.successful(-/(err)), res f(res).run(broker, ec))
)(ec)
zoom in
fold(err Future.successful(-/(err)), res f(res).run(broker, ec))
Convenience methods
object Topics {
def run[E, T](broker: TopicBroker, action: Topics[E, T])(implicit ec: ExecutionContext): Future[E / T] = action.run(broker, ec)
def point[T](value: T): Topics[Nothing, T] = Topics[Nothing, T]((broker, ec) Future.successful(value.right))
def future[T](action: Future[T]): Topics[Nothing, T] = Topics((broker, ec) action.map(_.right)(ec))
def futureF[T](action: ExecutionContext Future[T]): Topics[Nothing, T] = Topics((broker, ec) action(ec).map(_.right)(ec))
def either[E, T](either: E / T): Topics[E, T] = Topics((_, _) Future.successful(either))
def futureEither[E, T](action: Future[E / T]): Topics[E, T] = Topics((_, _) action)
def futureEitherF[E, T](action: ExecutionContext Future[E / T]): Topics[E, T] = Topics((_, ec) action(ec))
// ... more code
}
What about Monad Laws?
import scalaz.scalacheck.ScalazProperties.monad
class MonadLawsSpec extends Spec {
def is = s2"""obey the monad laws $laws"""
def laws = {
implicit val b = broker
monad.laws[({ type l[a] = Topics[Int, a] })#l]
}
}
Another Program example
opt-parse-applicative
"net.bmjames" %% "scala-optparse-applicative" % "0.3"
case class Config(inputFile: Option[File], outputFile: Option[File])
def main(args: Array[String]): Unit = {
val config = parseArgs(args)
// ...
}
val inputFile = optional(opt[File](
ensure[File](readStr.map(str new File(str)), "INPUT_FILE must be an existing file", _.isFile),
long("input-file"),
metavar("INPUT_FILE"),
short('f'),
help("input file to read from")
))
val outputFile = optional(opt[File](
readStr.map(str new File(str)),
long("output-file"),
metavar("OUTPUT_FILE"),
short('o'),
help("output file to write to")
))
def parseArgs(args: Array[String]): Config = {
val inputFile = optional(opt[File]( // ... omitted
val outputFile = optional(opt[File]( // ... omitted
val parser = (input |@| inputFile)(Config.apply(_, _))
execParser(args, "copy", info(parser <*> helper,
header("The awesome copy utility.")
))
}
Define the program
val parser = (input |@| inputFile)(Config.apply(_, _))
Execute the program
execParser(args, "copy", info(parser <*> helper,
header("The awesome copy utility.")
))
Some more thoughts
Scalaz has virtually no docs )-:
Scalaz has really cool stuff (-:
On a lazy Sunday..
trait MonadPlus[F[_]] extends Monad[F] with ApplicativePlus[F] { self =>
//...
/** Generalized version of Haskell's `partitionEithers` */
def separate[G[_, _], A, B](value: F[G[A, B]])(implicit G: Bifoldable[G]): (F[A], F[B]) = {
val lefts = bind(value)((aa) => G.leftFoldable.foldMap(aa)(a => point(a))(monoid[A]))
val rights = bind(value)((bb) => G.rightFoldable.foldMap(bb)(b => point(b))(monoid[B]))
(lefts, rights)
}
//...
final class MonadPlusOps[F[_],A] private[syntax](val self: F[A])(implicit val F: MonadPlus[F]) extends Ops[F[A]] {
final def separate[G[_, _], B, C](implicit ev: A === G[B, C], G: Bifoldable[G]): (F[B], F[C]) =
F.separate(ev.subst(self))
//...
You read some code..
WTF
But, separate is very useful.
def separate[G[_, _], A, B](value: F[G[A, B]])(implicit G: Bifoldable[G]): (F[A], F[B])
Remove everything, keep args and return value
(value: F[G[A, B]]): (F[A], F[B])
(value: F[G[A, B]]): (F[A], F[B])
Remove remaining 'syntax'
F[G[A, B]] => (F[A], F[B])
Lets substitute F, G, A and B to something we know
F[G[A, B]] => (F[A], F[B])
F = List
G[A,B] = Error / Result
4
4
/ uses infix type notation, same as /[Error,Result]
List[Error / Result] => (List[Error], List[Result])
It's a function to separate the errors from the results!
There are many of these.
How do you find a concrete function if they are defined in the abstract?
DIG
Remove all syntax until you are left with a function
Then find which implicits / type classes are needed for the function.
Scalaz requires you to know how the typeclasses are organized
cats project has more docs
An introduction to cats
http://typelevel.org/cats/
Advanced Scala with Cats book 5
http://underscore.io/blog
5
http://underscore.io/books/advanced-scala/
Functional Programming in Scala (the red book) 6
6
https://www.manning.com/books/functional-programming-in-scala
http://typelevel.org/blog (Some articles are really advanced)
RECAP
WRITING FUNCTIONAL SCALA
▸ Write functions
▸ Choose effects (or write your own)
▸ Construct a program that composes the functions and effects
▸ Run the program (in an 'App' in the main method)
RETURN VALUES
Use data types like A / B that do not lose information about what
happened
Boolean blindness
'Option flatMap' blindness?
PROGRAMS
Choose a reasonable architecture to construct your Programs
Monad Wrappers
Monad Transformers
Effect Systems
EOF
Functions, Types, Programs and Effects

More Related Content

PDF
PPTX
Akka in-action
PDF
Swift internals
PPTX
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
PDF
Redux Sagas - React Alicante
PDF
From object oriented to functional domain modeling
PDF
Zio from Home
PDF
Fiber supervision in ZIO
Akka in-action
Swift internals
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Redux Sagas - React Alicante
From object oriented to functional domain modeling
Zio from Home
Fiber supervision in ZIO

What's hot (18)

ODP
PDF
FP in Java - Project Lambda and beyond
PDF
Swift 2
PDF
An introduction to property based testing
PDF
Functional Programming Patterns (NDC London 2014)
PDF
Thirteen ways of looking at a turtle
ODP
Decorators in Python
PDF
Google Guava for cleaner code
PDF
Google guava
PDF
Google guava - almost everything you need to know
PPT
Swiss army knife Spring
PDF
JavaScript Functions
PDF
The core libraries you always wanted - Google Guava
PDF
OOP and FP - Become a Better Programmer
PPTX
Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt.
PDF
Advanced Debugging Using Java Bytecodes
PDF
LetSwift RxSwift 시작하기
PDF
Python decorators
FP in Java - Project Lambda and beyond
Swift 2
An introduction to property based testing
Functional Programming Patterns (NDC London 2014)
Thirteen ways of looking at a turtle
Decorators in Python
Google Guava for cleaner code
Google guava
Google guava - almost everything you need to know
Swiss army knife Spring
JavaScript Functions
The core libraries you always wanted - Google Guava
OOP and FP - Become a Better Programmer
Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt.
Advanced Debugging Using Java Bytecodes
LetSwift RxSwift 시작하기
Python decorators
Ad

Similar to Functions, Types, Programs and Effects (20)

PDF
Fp in scala part 2
PDF
Scala Functional Patterns
PDF
DataWeave 2.0 - MuleSoft CONNECT 2019
PDF
Fp in scala with adts part 2
PDF
Principled Error Handling with FP
PPTX
Monads and friends demystified
PDF
Practical cats
PDF
The Essence of the Iterator Pattern (pdf)
PPTX
The Essence of the Iterator Pattern
PPTX
Functional programming
PDF
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
PPTX
Introduction to Monads in Scala (1)
ODP
Functions In Scala
PDF
Generic Functional Programming with Type Classes
PDF
Effects, Algebraically Yours using Scala
PPT
LECTURE 5-Function in Matlab how to use mathlab
PPTX
1. Ch_1 SL_1_Intro to Matlab.pptx
ODP
Very basic functional design patterns
PDF
Basic R Data Manipulation
PPT
Introduction to matlab
Fp in scala part 2
Scala Functional Patterns
DataWeave 2.0 - MuleSoft CONNECT 2019
Fp in scala with adts part 2
Principled Error Handling with FP
Monads and friends demystified
Practical cats
The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern
Functional programming
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
Introduction to Monads in Scala (1)
Functions In Scala
Generic Functional Programming with Type Classes
Effects, Algebraically Yours using Scala
LECTURE 5-Function in Matlab how to use mathlab
1. Ch_1 SL_1_Intro to Matlab.pptx
Very basic functional design patterns
Basic R Data Manipulation
Introduction to matlab
Ad

Recently uploaded (20)

PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Digital Strategies for Manufacturing Companies
PPTX
Odoo POS Development Services by CandidRoot Solutions
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PPTX
ISO 45001 Occupational Health and Safety Management System
PDF
AI in Product Development-omnex systems
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PPTX
ManageIQ - Sprint 268 Review - Slide Deck
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PDF
top salesforce developer skills in 2025.pdf
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
CHAPTER 2 - PM Management and IT Context
Digital Strategies for Manufacturing Companies
Odoo POS Development Services by CandidRoot Solutions
Softaken Excel to vCard Converter Software.pdf
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
ISO 45001 Occupational Health and Safety Management System
AI in Product Development-omnex systems
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Operating system designcfffgfgggggggvggggggggg
Odoo Companies in India – Driving Business Transformation.pdf
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Wondershare Filmora 15 Crack With Activation Key [2025
ManageIQ - Sprint 268 Review - Slide Deck
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
top salesforce developer skills in 2025.pdf

Functions, Types, Programs and Effects

  • 2. QUIZHANDS UP IF YOU RECOGNIZE
  • 3. A => B A => Option[B]
  • 5. A / B A Xor B
  • 9. What does this do? (maybeInt1 |@| maybeInt2) { _ > _ } | false
  • 13. Does SI-2712 ring a bell? scalaz.Unapply[TC[_[_]], MA] traverseU
  • 15. What's this all about? type E = Task |: (String / ?) |: Option |: Base type Stack = ReaderInt |: WriterString |: Eval |: NoEffect type PRG[A] = (Log.DSL :@: DB.DSL :@: FXNil)#Cop[A] Free[PRG, Xor[DBError, Entity]]
  • 16. Do you use scalaz or cats for your day job?
  • 17. FUNCTIONS, TYPES, PROGRAMS AND EFFECTS Strict separation of safe and unsafe Safe: Functions, Types, creating Programs Unsafe: Effects, running Programs
  • 18. WRITING FUNCTIONAL SCALA 1. Write functions, which return values 2. Choose effects (or write your own) 3. Construct a program that composes values into effects 4. Run the program (in an 'App' in the main method) (Not necessarily in that order)
  • 19. WRITING FUNCTIONAL SCALA ▸ You can possibly have programs of programs ▸ A program is very often defined in a for comprehension
  • 20. A hypothetical example. val program = for { client <- server.connect(details) Exchange(src, snk) = client.exchange _ <- snk.sendRequest(request) in = src.pipe(text.utf8Decode) .to(io.stdOutLines) } yield () program.run Define first, run later.
  • 21. BUT FIRST: SOMETHING ABOUT INFORMATION LOSS
  • 22. QUIZKEEP IN MIND: FUNCTIONS, TYPES, PROGRAMS, EFFECTS
  • 23. Anything 'wrong' with these methods / functions? def getUser(username: Username): Future[User] def createUser(details: UserDetails): Future[User] def getPrimaryAccount(user: User): Future[Account]
  • 24. Anything 'wrong' with this? def getUser(username: Username): Future[Option[User]] def getAccounts(user: User): Future[List[Account]] def approved(user: User, accounts: List[Account]): Future[Boolean]
  • 25. DATA LOSS Boolean blindness Functions return values, discarding them constrains the Program 1 1 Think of the information you need later on in a for comprehension
  • 26. What is 'wrong' with this? sealed trait Error case object UserNotFound extends Error case object UserNameNotFound extends Error case object AccountNotFound extends Error def getUser(username: Username): Future[Either[Error, User]] def getAccounts(user: User): Future[Either[Error, Account]]
  • 27. scalaz Disjunction (left or right) val res: String / Int = /-(5) val res1: String / Int = 5.right val moreVerbose: String / Int = 5.right[String] val res2: String / Int = "Some error".left val friendlier = res2.leftMap(error => s"$error has occured, we apologize.") val res3 = res1.map(five => five * 2) // /-(10) Map over the right
  • 28. From Throwable to / /.fromTryCatchThrowable[String, Exception] { getDangerousString() } // returns a Exception / String /.fromTryCatchThrowable[String, Exception] { getDangerousString() }.leftMap(e=> MyError(e)) // returns a MyError / String
  • 29. From A / B to X val res: String / Int = "Error!".left val res1 = res.fold(left=> 0, r => r) val res2 = res.fold(left=> 0, identity) // this is the same Why should you not use this?
  • 30. Combining / case class Error(in: Int, reason: String) def even(in: Int): Error / Int = if(in % 2 == 0) in.right else Error(in, "not even").left def divByThree(in: Int): Error / Int = if(in % 3 == 0) in.right else Error(in, "not div 3").left def evenAndByThree(in: Int) = for { evenNr <- even(in) byThree <- divByThree(evenNr) } yield byThree println(evenAndByThree(12)) // /-(12) println(evenAndByThree(3)) // -/-(3, "not even") println(evenAndByThree(4)) // -/-(4, "not div 3")
  • 31. def evenAndByThree(in: Int) = for { evenNr <- even(in) byThree <- divByThree(evenNr) } yield byThree No loss of information (why the first error occurred).
  • 32. Given def getUser(username: Username): Future[Error / User]] def getAccounts(user: User): Future[Error / List[Account]] Does this compile? val result = for { user <- getUser(name) accounts <- getAccounts(user) } yield accounts
  • 34. Combining values Monoid - collecting / combining values into one value //simplified / pseudo val append: (F,F) => F val zero: F //actual def append(f1: F, f2: => F): F def zero: F
  • 35. COMBINING EFFECTS Monad - collecting / combining effects 2 def point[A](a: => A): F[A] // Creates an 'effects collector' def bind[A, B](fa: F[A])(f: A => F[B]): F[B] // adds an effect def map[A,B](fa: F[A])(f: A => B):F[B] // transforms input for next effect Can only combine for same type of F.2 2 The Monad is a monoid in the category of endofunctors joke
  • 36. In scalaz, flatMap is defined as: def flatMap[B](f: A => F[B]) = F.bind(self)(f) flatMap ~= bind
  • 37. Monads are great, but in general NOT composable. Monad Transformers Effect systems (i.e. Eff from eff-cats) In common, a Type that combines N Monad types
  • 38. Monad Transformer EitherT val res = for { user <- EitherT(getUser("bla")) accounts <- EitherT(getAccounts(user)) } yield accounts // returns EitherT[Future, Error, List[Account]] res.run // Future[Error / List[Account]] Scalaz provides Future Monad instance in scalaz.std.scalaFuture
  • 39. Construct the Program val res = for { user <- EitherT(getUser("bla")) accounts <- EitherT(getAccounts(user)) } yield accounts Run the Program res.run
  • 40. Sometimes... a Monad Wrapper is a good enough Program
  • 41. WRITE YOUR OWN MONAD WRAPPER 3 3 specific monad transformer?
  • 42. WRITE YOUR OWN MONAD WRAPPER Topics (Kafka library) Combine Future and / Covariant +A/+B Know why a Kafka operation failed in a Program
  • 43. Why not EitherT? final case class EitherT[F[_], A, B](run: F[A / B]) Invariant in A,B
  • 44. Covariant Error type sealed trait TopicsError sealed trait FindTopicError extends TopicsError sealed trait SelectTopicError extends TopicsError sealed trait DeleteTopicError extends TopicsError
  • 45. Example program val program: Topics[TopicsError, Chunk[String, String]] = for { partition ← Topics.partitioned[String, String](topicDef, partitionId) info ← partition.write(record) chunk ← partition.read(info.offset, info.offset) } yield chunk // ... somewhere later program.run(broker, executionContext)
  • 46. Minor detour (type lambda) Monad[{ type λ[α] = Topics[E, α] })#λ] ~= type MyMonad[T] = Monad[Topics[E, T]] only inline.
  • 47. Roll your own Monad instance (Scalaz) object Topics { implicit def topicsMonad[E] = new Monad[({ type λ[α] = Topics[E, α] })#λ] { def point[T](value: T): Topics[E, T] = Topics.point(value) def bind[T, U](topics: Topics[E, T])(f: T Topics[E, U]): Topics[E, U] = topics.flatMap(f) } } delegate to point and flatMap
  • 48. Roll your own Monad case class Topics[+E, +T](run: (TopicBroker, ExecutionContext) Future[E / T]) { // more to follow
  • 49. Roll your own Monad case class Topics[+E, +T](run: (TopicBroker, ExecutionContext) Future[E / T]) { def map[C](f: T C): Topics[E, C] = Topics((broker, ec) run(broker, ec).map(_.map(f))(ec)) def flatMap[E1 >: E, C](f: T Topics[E1, C]): Topics[E1, C] = Topics { (broker, ec) run(broker, ec).flatMap( _.fold(err Future.successful(-/(err)), res f(res).run(broker, ec)) )(ec) } }
  • 50. map
  • 51. case class Topics[+E, +T](run: (TopicBroker, ExecutionContext) Future[E / T]) { def map[C](f: T C): Topics[E, C] = Topics((broker, ec) run(broker, ec).map(_.map(f))(ec)) zoom in Topics((broker, ec) run(broker, ec).map(_.map(f))(ec)) zoom in run(broker, ec).map(_.map(f))
  • 53. Topics { (broker, ec) run(broker, ec).flatMap( _.fold(err Future.successful(-/(err)), res f(res).run(broker, ec)) )(ec) } zoom in run(broker, ec).flatMap( _.fold(err Future.successful(-/(err)), res f(res).run(broker, ec)) )(ec) zoom in fold(err Future.successful(-/(err)), res f(res).run(broker, ec))
  • 54. Convenience methods object Topics { def run[E, T](broker: TopicBroker, action: Topics[E, T])(implicit ec: ExecutionContext): Future[E / T] = action.run(broker, ec) def point[T](value: T): Topics[Nothing, T] = Topics[Nothing, T]((broker, ec) Future.successful(value.right)) def future[T](action: Future[T]): Topics[Nothing, T] = Topics((broker, ec) action.map(_.right)(ec)) def futureF[T](action: ExecutionContext Future[T]): Topics[Nothing, T] = Topics((broker, ec) action(ec).map(_.right)(ec)) def either[E, T](either: E / T): Topics[E, T] = Topics((_, _) Future.successful(either)) def futureEither[E, T](action: Future[E / T]): Topics[E, T] = Topics((_, _) action) def futureEitherF[E, T](action: ExecutionContext Future[E / T]): Topics[E, T] = Topics((_, ec) action(ec)) // ... more code }
  • 56. import scalaz.scalacheck.ScalazProperties.monad class MonadLawsSpec extends Spec { def is = s2"""obey the monad laws $laws""" def laws = { implicit val b = broker monad.laws[({ type l[a] = Topics[Int, a] })#l] } }
  • 57. Another Program example opt-parse-applicative "net.bmjames" %% "scala-optparse-applicative" % "0.3"
  • 58. case class Config(inputFile: Option[File], outputFile: Option[File]) def main(args: Array[String]): Unit = { val config = parseArgs(args) // ... }
  • 59. val inputFile = optional(opt[File]( ensure[File](readStr.map(str new File(str)), "INPUT_FILE must be an existing file", _.isFile), long("input-file"), metavar("INPUT_FILE"), short('f'), help("input file to read from") )) val outputFile = optional(opt[File]( readStr.map(str new File(str)), long("output-file"), metavar("OUTPUT_FILE"), short('o'), help("output file to write to") ))
  • 60. def parseArgs(args: Array[String]): Config = { val inputFile = optional(opt[File]( // ... omitted val outputFile = optional(opt[File]( // ... omitted val parser = (input |@| inputFile)(Config.apply(_, _)) execParser(args, "copy", info(parser <*> helper, header("The awesome copy utility.") )) }
  • 61. Define the program val parser = (input |@| inputFile)(Config.apply(_, _)) Execute the program execParser(args, "copy", info(parser <*> helper, header("The awesome copy utility.") ))
  • 63. Scalaz has virtually no docs )-:
  • 64. Scalaz has really cool stuff (-:
  • 65. On a lazy Sunday.. trait MonadPlus[F[_]] extends Monad[F] with ApplicativePlus[F] { self => //... /** Generalized version of Haskell's `partitionEithers` */ def separate[G[_, _], A, B](value: F[G[A, B]])(implicit G: Bifoldable[G]): (F[A], F[B]) = { val lefts = bind(value)((aa) => G.leftFoldable.foldMap(aa)(a => point(a))(monoid[A])) val rights = bind(value)((bb) => G.rightFoldable.foldMap(bb)(b => point(b))(monoid[B])) (lefts, rights) } //... final class MonadPlusOps[F[_],A] private[syntax](val self: F[A])(implicit val F: MonadPlus[F]) extends Ops[F[A]] { final def separate[G[_, _], B, C](implicit ev: A === G[B, C], G: Bifoldable[G]): (F[B], F[C]) = F.separate(ev.subst(self)) //... You read some code..
  • 66. WTF
  • 67. But, separate is very useful. def separate[G[_, _], A, B](value: F[G[A, B]])(implicit G: Bifoldable[G]): (F[A], F[B]) Remove everything, keep args and return value (value: F[G[A, B]]): (F[A], F[B])
  • 68. (value: F[G[A, B]]): (F[A], F[B]) Remove remaining 'syntax' F[G[A, B]] => (F[A], F[B])
  • 69. Lets substitute F, G, A and B to something we know F[G[A, B]] => (F[A], F[B]) F = List G[A,B] = Error / Result 4 4 / uses infix type notation, same as /[Error,Result]
  • 70. List[Error / Result] => (List[Error], List[Result]) It's a function to separate the errors from the results!
  • 71. There are many of these. How do you find a concrete function if they are defined in the abstract?
  • 72. DIG Remove all syntax until you are left with a function Then find which implicits / type classes are needed for the function. Scalaz requires you to know how the typeclasses are organized
  • 73. cats project has more docs An introduction to cats http://typelevel.org/cats/ Advanced Scala with Cats book 5 http://underscore.io/blog 5 http://underscore.io/books/advanced-scala/
  • 74. Functional Programming in Scala (the red book) 6 6 https://www.manning.com/books/functional-programming-in-scala
  • 76. RECAP
  • 77. WRITING FUNCTIONAL SCALA ▸ Write functions ▸ Choose effects (or write your own) ▸ Construct a program that composes the functions and effects ▸ Run the program (in an 'App' in the main method)
  • 78. RETURN VALUES Use data types like A / B that do not lose information about what happened Boolean blindness 'Option flatMap' blindness?
  • 79. PROGRAMS Choose a reasonable architecture to construct your Programs Monad Wrappers Monad Transformers Effect Systems
  • 80. EOF