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

Functions, Types, Programs and Effects

  • 1.
  • 2.
    QUIZHANDS UP IFYOU RECOGNIZE
  • 3.
    A => B A=> Option[B]
  • 4.
  • 5.
    A / B AXor B
  • 6.
  • 7.
  • 8.
  • 9.
    What does thisdo? (maybeInt1 |@| maybeInt2) { _ > _ } | false
  • 10.
  • 11.
  • 12.
  • 13.
    Does SI-2712 ringa bell? scalaz.Unapply[TC[_[_]], MA] traverseU
  • 14.
  • 15.
    What's this allabout? 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 usescalaz or cats for your day job?
  • 17.
    FUNCTIONS, TYPES, PROGRAMSAND 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. valprogram = 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.
  • 22.
    QUIZKEEP IN MIND:FUNCTIONS, TYPES, PROGRAMS, EFFECTS
  • 23.
    Anything 'wrong' withthese methods / functions? def getUser(username: Username): Future[User] def createUser(details: UserDetails): Future[User] def getPrimaryAccount(user: User): Future[Account]
  • 24.
    Anything 'wrong' withthis? 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 Functionsreturn 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 (leftor 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 classError(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
  • 33.
  • 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, flatMapis 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 valres = 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 valres = for { user <- EitherT(getUser("bla")) accounts <- EitherT(getAccounts(user)) } yield accounts Run the Program res.run
  • 40.
    Sometimes... a MonadWrapper is a good enough Program
  • 41.
    WRITE YOUR OWNMONAD WRAPPER 3 3 specific monad transformer?
  • 42.
    WRITE YOUR OWNMONAD WRAPPER Topics (Kafka library) Combine Future and / Covariant +A/+B Know why a Kafka operation failed in a Program
  • 43.
    Why not EitherT? finalcase class EitherT[F[_], A, B](run: F[A / B]) Invariant in A,B
  • 44.
    Covariant Error type sealedtrait 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 (typelambda) Monad[{ type λ[α] = Topics[E, α] })#λ] ~= type MyMonad[T] = Monad[Topics[E, T]] only inline.
  • 47.
    Roll your ownMonad 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 ownMonad case class Topics[+E, +T](run: (TopicBroker, ExecutionContext) Future[E / T]) { // more to follow
  • 49.
    Roll your ownMonad 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.
  • 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))
  • 52.
  • 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 }
  • 55.
  • 56.
    import scalaz.scalacheck.ScalazProperties.monad class MonadLawsSpecextends 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 valparser = (input |@| inputFile)(Config.apply(_, _)) Execute the program execParser(args, "copy", info(parser <*> helper, header("The awesome copy utility.") ))
  • 62.
  • 63.
  • 64.
    Scalaz has reallycool stuff (-:
  • 65.
    On a lazySunday.. 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.
  • 67.
    But, separate isvery 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 manyof these. How do you find a concrete function if they are defined in the abstract?
  • 72.
    DIG Remove all syntaxuntil 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 hasmore 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 inScala (the red book) 6 6 https://www.manning.com/books/functional-programming-in-scala
  • 75.
  • 76.
  • 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 datatypes like A / B that do not lose information about what happened Boolean blindness 'Option flatMap' blindness?
  • 79.
    PROGRAMS Choose a reasonablearchitecture to construct your Programs Monad Wrappers Monad Transformers Effect Systems
  • 80.