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

What's hot (18)

ODP
Akka
Tim Dalton
 
PDF
FP in Java - Project Lambda and beyond
Mario Fusco
 
PDF
Swift 2
Jens Ravens
 
PDF
An introduction to property based testing
Scott Wlaschin
 
PDF
Functional Programming Patterns (NDC London 2014)
Scott Wlaschin
 
PDF
Thirteen ways of looking at a turtle
Scott Wlaschin
 
ODP
Decorators in Python
Ben James
 
PDF
Google Guava for cleaner code
Mite Mitreski
 
PDF
Google guava
t fnico
 
PDF
Google guava - almost everything you need to know
Tomasz Dziurko
 
PPT
Swiss army knife Spring
Mario Fusco
 
PDF
JavaScript Functions
Colin DeCarlo
 
PDF
The core libraries you always wanted - Google Guava
Mite Mitreski
 
PDF
OOP and FP - Become a Better Programmer
Mario Fusco
 
PPTX
Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt.
Samuel Fortier-Galarneau
 
PDF
Advanced Debugging Using Java Bytecodes
Ganesh Samarthyam
 
PDF
LetSwift RxSwift 시작하기
Wanbok Choi
 
PDF
Python decorators
Guillermo Blasco Jiménez
 
FP in Java - Project Lambda and beyond
Mario Fusco
 
Swift 2
Jens Ravens
 
An introduction to property based testing
Scott Wlaschin
 
Functional Programming Patterns (NDC London 2014)
Scott Wlaschin
 
Thirteen ways of looking at a turtle
Scott Wlaschin
 
Decorators in Python
Ben James
 
Google Guava for cleaner code
Mite Mitreski
 
Google guava
t fnico
 
Google guava - almost everything you need to know
Tomasz Dziurko
 
Swiss army knife Spring
Mario Fusco
 
JavaScript Functions
Colin DeCarlo
 
The core libraries you always wanted - Google Guava
Mite Mitreski
 
OOP and FP - Become a Better Programmer
Mario Fusco
 
Decorators Explained: A Powerful Tool That Should Be in Your Python Toolbelt.
Samuel Fortier-Galarneau
 
Advanced Debugging Using Java Bytecodes
Ganesh Samarthyam
 
LetSwift RxSwift 시작하기
Wanbok Choi
 
Python decorators
Guillermo Blasco Jiménez
 

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

PDF
Fp in scala part 2
Hang Zhao
 
PDF
Introducing Monads and State Monad at PSUG
David Galichet
 
PDF
Monads and Monoids by Oleksiy Dyagilev
JavaDayUA
 
PDF
Monads - Dublin Scala meetup
Mikhail Girkin
 
PDF
Monad Transformers In The Wild
StackMob Inc
 
PDF
Practical cats
Raymond Tay
 
PDF
Monad Transformers - Part 1
Philip Schwarz
 
PDF
pure-functional-programming.pdf
PuneetChaturvedi23
 
PDF
Principled Error Handling with FP
Luka Jacobowitz
 
PDF
Algebraic Thinking for Evolution of Pure Functional Domain Models
Debasish Ghosh
 
PDF
One Monad to Rule Them All
John De Goes
 
PPTX
Monads and friends demystified
Alessandro Lacava
 
PDF
Scala. Introduction to FP. Monads
Kirill Kozlov
 
PDF
Scala or functional programming from a python developer's perspective
gabalese
 
PPT
Thesis PPT
Drew Ferkin
 
PPT
Thesis
Drew Ferkin
 
PDF
Demystifying functional programming with Scala
Denis
 
PDF
Functional Effects - Part 2
Philip Schwarz
 
PDF
Real world cats
Ikhoon Eom
 
PDF
Sequence and Traverse - Part 2
Philip Schwarz
 
Fp in scala part 2
Hang Zhao
 
Introducing Monads and State Monad at PSUG
David Galichet
 
Monads and Monoids by Oleksiy Dyagilev
JavaDayUA
 
Monads - Dublin Scala meetup
Mikhail Girkin
 
Monad Transformers In The Wild
StackMob Inc
 
Practical cats
Raymond Tay
 
Monad Transformers - Part 1
Philip Schwarz
 
pure-functional-programming.pdf
PuneetChaturvedi23
 
Principled Error Handling with FP
Luka Jacobowitz
 
Algebraic Thinking for Evolution of Pure Functional Domain Models
Debasish Ghosh
 
One Monad to Rule Them All
John De Goes
 
Monads and friends demystified
Alessandro Lacava
 
Scala. Introduction to FP. Monads
Kirill Kozlov
 
Scala or functional programming from a python developer's perspective
gabalese
 
Thesis PPT
Drew Ferkin
 
Thesis
Drew Ferkin
 
Demystifying functional programming with Scala
Denis
 
Functional Effects - Part 2
Philip Schwarz
 
Real world cats
Ikhoon Eom
 
Sequence and Traverse - Part 2
Philip Schwarz
 
Ad

Recently uploaded (20)

PPTX
Transforming Mining & Engineering Operations with Odoo ERP | Streamline Proje...
SatishKumar2651
 
PDF
HiHelloHR – Simplify HR Operations for Modern Workplaces
HiHelloHR
 
PDF
Online Queue Management System for Public Service Offices in Nepal [Focused i...
Rishab Acharya
 
PDF
Automate Cybersecurity Tasks with Python
VICTOR MAESTRE RAMIREZ
 
PDF
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pdf
Varsha Nayak
 
PDF
MiniTool Partition Wizard 12.8 Crack License Key LATEST
hashhshs786
 
PDF
Generic or Specific? Making sensible software design decisions
Bert Jan Schrijver
 
PDF
Open Chain Q2 Steering Committee Meeting - 2025-06-25
Shane Coughlan
 
PDF
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
PPTX
Home Care Tools: Benefits, features and more
Third Rock Techkno
 
PDF
vMix Pro 28.0.0.42 Download vMix Registration key Bundle
kulindacore
 
PDF
iTop VPN With Crack Lifetime Activation Key-CODE
utfefguu
 
PPTX
Help for Correlations in IBM SPSS Statistics.pptx
Version 1 Analytics
 
PDF
Wondershare PDFelement Pro Crack for MacOS New Version Latest 2025
bashirkhan333g
 
PDF
SAP Firmaya İade ABAB Kodları - ABAB ile yazılmıl hazır kod örneği
Salih Küçük
 
PPTX
In From the Cold: Open Source as Part of Mainstream Software Asset Management
Shane Coughlan
 
PDF
MiniTool Partition Wizard Free Crack + Full Free Download 2025
bashirkhan333g
 
PDF
Alarm in Android-Scheduling Timed Tasks Using AlarmManager in Android.pdf
Nabin Dhakal
 
PPTX
Human Resources Information System (HRIS)
Amity University, Patna
 
PDF
Digger Solo: Semantic search and maps for your local files
seanpedersen96
 
Transforming Mining & Engineering Operations with Odoo ERP | Streamline Proje...
SatishKumar2651
 
HiHelloHR – Simplify HR Operations for Modern Workplaces
HiHelloHR
 
Online Queue Management System for Public Service Offices in Nepal [Focused i...
Rishab Acharya
 
Automate Cybersecurity Tasks with Python
VICTOR MAESTRE RAMIREZ
 
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pdf
Varsha Nayak
 
MiniTool Partition Wizard 12.8 Crack License Key LATEST
hashhshs786
 
Generic or Specific? Making sensible software design decisions
Bert Jan Schrijver
 
Open Chain Q2 Steering Committee Meeting - 2025-06-25
Shane Coughlan
 
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
Home Care Tools: Benefits, features and more
Third Rock Techkno
 
vMix Pro 28.0.0.42 Download vMix Registration key Bundle
kulindacore
 
iTop VPN With Crack Lifetime Activation Key-CODE
utfefguu
 
Help for Correlations in IBM SPSS Statistics.pptx
Version 1 Analytics
 
Wondershare PDFelement Pro Crack for MacOS New Version Latest 2025
bashirkhan333g
 
SAP Firmaya İade ABAB Kodları - ABAB ile yazılmıl hazır kod örneği
Salih Küçük
 
In From the Cold: Open Source as Part of Mainstream Software Asset Management
Shane Coughlan
 
MiniTool Partition Wizard Free Crack + Full Free Download 2025
bashirkhan333g
 
Alarm in Android-Scheduling Timed Tasks Using AlarmManager in Android.pdf
Nabin Dhakal
 
Human Resources Information System (HRIS)
Amity University, Patna
 
Digger Solo: Semantic search and maps for your local files
seanpedersen96
 
Ad

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