SlideShare a Scribd company logo
An excuse to Try, Either,
folding, and Future.
sequence
or parsing command line options with
crappy-optional
@gerferra
crappy-optional?
“Don’t worry be crappy” mantra
optional library from DRMacIver
and paulp
optional
optional is a command line option
parser and library.
optional
YOU WRITE:
object MyAwesomeCommandLineTool extends optional.Application {
// for instance...
def main(count: Option[Int], file: Option[java.io.File],
arg1: String) {
[...]
}
}
optional
THEN YOU DO:
scala MyAwesomeCommandLineTool --count 5 quux
optional
AND YOUR MAIN METHOD WILL BE INVOKED SUCH THAT:
count = Some(5)
file = None
arg1 = quux
optional
AND YOUR MAIN METHOD WILL BE INVOKED SUCH THAT:
count = Some(5)
file = None
arg1 = quux
object MyAwesomeCommandLineTool extends optional.Application {
// for instance...
def main(count: Option[Int], file: Option[java.io.File],
arg1: String) {
[...]
}
}
scala MyAwesomeCommandLineTool --count 5 quux
Ref by name
optional
AND YOUR MAIN METHOD WILL BE INVOKED SUCH THAT:
count = Some(5)
file = None
arg1 = quux
object MyAwesomeCommandLineTool extends optional.Application {
// for instance...
def main(count: Option[Int], file: Option[java.io.File],
arg1: String) {
[...]
}
}
scala MyAwesomeCommandLineTool --count 5 quux
Ref by name
Optional parameter
optional
AND YOUR MAIN METHOD WILL BE INVOKED SUCH THAT:
count = Some(5)
file = None
arg1 = quux
object MyAwesomeCommandLineTool extends optional.Application {
// for instance...
def main(count: Option[Int], file: Option[java.io.File],
arg1: String) {
[...]
}
}
scala MyAwesomeCommandLineTool --count 5 quux
Ref by name
Optional parameter
Ref by position
optional
HOW IT WORKS:
optional
HOW IT WORKS:
Reflection, man.
optional
HOW IT WORKS:
(Java) Reflection, man.
optional
HOW IT WORKS:
(Java) Reflection, man.
doesn’t work OK with Scala since 2.9 (I guess) ...
used to work
So...
… copy the idea, but use Scala reflection
instead …
… and don’t worry and be crappy :) ...
Show me the code!
trait Application {
def main(args: Array[String]) {
runMain(this, args.toList)
}
private def runMain[T: ClassTag](
instance: T, args: List[String]) {
// ... something happens here ...
}
}
Show me the code!
private def runMain[T: ClassTag](instance: T, args: List[String]) {
val instanceMirror = ru.runtimeMirror(getClass.getClassLoader).reflect(instance)
val theType = instanceMirror.symbol.typeSignature
val methods = theType.members.collect {
case m if m.isMethod => m.asMethod
}
val mainMethods = methods.filter(m => m.name == ru.TermName("main") &&
m.fullName != classOf[Application].getName + ".main")
val methodOrError = mainMethods.headOption.toRight("No main method defined")
// Scala support multiple parameter lists, I care only about the first (if exists ...)
val = methodOrError.right.map(m => m.paramLists.headOption.getOrElse(Nil))
paramsOrError
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right // mysterious instantiation of things ...
} yield {
val refMethod = instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*) // here is the execution of the main method happening
}
invocationOrError.fold(println, identity /* nothing to do because it already executed as a side effect */)
}
Step by step: method definition
Scala method:
private def runMain[T: ClassTag](
instance: T, args: List[String]) {
private, so it’s not inherited
with a type parameter T
with a context bound ClassTag
Step by step: method definition
… with a context bound ClassTag?
private def runMain[T: ClassTag](
instance: T, args: List[String]) {
≡
private def runMain[T](
instance: T, args: List[String])(
implicit ev: ClassTag[T]) {
both expressions are analogous
Step by step: method definition
we ask for an evidence that a ClassTag can
be constructed for the type T ...
… so we can use runtime reflection on it ...
Step by step: reflecting on instance
val instanceMirror =
ru
.runtimeMirror(getClass.getClassLoader)
.reflect(instance)
Step by step: reflecting on instance
val instanceMirror =
ru
.runtimeMirror(getClass.getClassLoader)
.reflect(instance)
(mystic voice)
…You need a mirror to access the reflection…
Step by step: reflecting on instance
val instanceMirror =
ru
.runtimeMirror(getClass.getClassLoader)
.reflect(instance)
`ru` here is just an alias to package
scala.reflect.runtime.universe
import scala.reflect.runtime.{ universe => ru }
Step by step: reflecting on instance
val instanceMirror =
ru
.runtimeMirror(getClass.getClassLoader)
.reflect(instance)
A ClassTag is needed here to overcome the
erasure of the type at runtime T
Step by step: obtaining the method
I want the methods ...
val theType =
instanceMirror.symbol.typeSignature
val methods = theType.members.collect {
case m if m.isMethod => m.asMethod
}
Step by step: obtaining the method
… to get the right main methods ...
val mainMethods =
methods.filter(m =>
m.name == ru.TermName("main") &&
m.fullName !=
classOf[Application].getName + ".main")
Step by step: obtaining the method
… to get the right main methods ...
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
Give me either the first of the methods, if there
is one, or an error message
Step by step: obtaining the method
… to get the right main methods ...
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
Give me either the first of the methods, if there
is one, or an error message
Step by step: obtaining the method
… to get the right main methods …
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
Give me either the first of the methods, if there
is one, or an error message
Step by step: obtaining the method
… to get the right main methods …
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
headOption ~ List[A] => Option[A]
Step by step: obtaining the method
… to get the right main methods …
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
headOption ~ List[A] => Option[A]
Option[A] =
Some(a: A)
| None
Step by step: obtaining the method
… to get the right main methods …
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
headOption ~ List[A] => Option[A]
not scala (!)
Option[A] =
Some(a: A)
| None
Step by step: obtaining the method
… to get the right main methods …
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
headOption ~ List[A] => Option[A]
def headOption: Option[A] =
if (isEmpty) None else Some(head)
Option[A] =
Some(a: A)
| None
Step by step: obtaining the method
… to get the right main methods …
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
toRight[X] ~ Option[A] => (=> X) => Either[X,A]
Step by step: obtaining the method
… to get the right main methods …
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
toRight[X] ~ Option[A] => (=> X) => Either[X,A]
Either[A, B] =
Left(a: A)
| Right(b: B)
Step by step: obtaining the method
… to get the right main methods …
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
toRight[X] ~ Option[A] => (=> X) => Either[X,A]
def toRight[X](left: => X): Either[X,A] =
if (isEmpty) Left(left) else Right(this.get)
Either[A, B] =
Left(a: A)
| Right(b: B)
Step by step: obtaining the method
… to get the right main methods …
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
toRight[X] ~ Option[A] => (=> X) => Either[X,A]
def toRight[X](left: => X): Either[X,A] =
if (isEmpty) Left(left) else Right(this.get)
Either[A, B] =
Left(a: A)
| Right(b: B)
Either[String, ru.MethodSymbol]
Step by step: obtaining the method
… to get the right main methods …
val methodOrError =
mainMethods
.headOption
.toRight("No main method defined")
toRight[X] ~ Option[A] => (=> X) => Either[X,A]
def toRight[X](left: => X): Either[X,A] =
if (isEmpty) Left(left) else Right(this.get)
Either[A, B] =
Left(a: A)
| Right(b: B)
Either[String, ru.MethodSymbol]
Step by step: obtaining the params
Ok. Now I need the parameters:
val paramsOrError =
methodOrError.right.map(m =>
m.paramLists
.headOption.getOrElse(Nil))
Scala support multiple parameter lists.
I care only about the first (if exists …)
Step by step: obtaining the params
Ok. Now I need the parameters:
val paramsOrError =
methodOrError.right.map(m =>
m.paramLists
.headOption.getOrElse(Nil))
Either[String, ru.MethodSymbol]
Step by step: obtaining the params
Ok. Now I need the parameters:
val paramsOrError =
methodOrError.right.map(m =>
m.paramLists
.headOption.getOrElse(Nil))
Either[String, ru.MethodSymbol]
only the right part please
Step by step: obtaining the params
Ok. Now I need the parameters:
val paramsOrError =
methodOrError.right.map(m =>
m.paramLists
.headOption.getOrElse(Nil))
map[X] ~ Either[A,B] => (B => X) => Either[A,X]
Either[String, ru.MethodSymbol]
only the right part please
Step by step: obtaining the params
Ok. Now I need the parameters:
val paramsOrError =
methodOrError.right.map(m =>
m.paramLists
.headOption.getOrElse(Nil))
map[X] ~ Either[A,B] => (B => X) => Either[A,X]
def map[X](f: B => X) = e match {
case Left(a) => Left(a)
case Right(b) => Right(f(b))
}
Either[String, ru.MethodSymbol]
only the right part please
Step by step: obtaining the params
Ok. Now I need the parameters:
val paramsOrError =
methodOrError.right.map(m =>
m.paramLists
.headOption.getOrElse(Nil))
List[List[ru.Symbol]]
Step by step: obtaining the params
Ok. Now I need the parameters:
val paramsOrError =
methodOrError.right.map(m =>
m.paramLists
.headOption.getOrElse(Nil))
Option[List[ru.Symbol]]
List[List[ru.Symbol]]
Step by step: obtaining the params
Ok. Now I need the parameters:
val paramsOrError =
methodOrError.right.map(m =>
m.paramLists
.headOption.getOrElse(Nil))
f: ru.MethodSymbol => List[ru.Symbol]
Step by step: obtaining the params
Ok. Now I need the parameters:
val paramsOrError =
methodOrError.right.map(m =>
m.paramLists
.headOption.getOrElse(Nil))
f: ru.MethodSymbol => List[ru.Symbol]
paramsOrError: Either[String, List[ru.Symbol]]
Step by step: obtaining the params
Ok. Now I need the parameters:
val paramsOrError =
methodOrError.right.map(m =>
m.paramLists
.headOption.getOrElse(Nil))
f: ru.MethodSymbol => List[ru.Symbol]
paramsOrError: Either[String, List[ru.Symbol]]
paramsOrError have the parameters OR the
original error "No main method defined"
Step by step: invocation
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right
} yield {
val refMethod =
instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*)
}
Step by step: invocation
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right
} yield {
val refMethod =
instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*)
}
Same as before
Step by step: invocation
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right
} yield {
val refMethod =
instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*)
}
Same as before
The main method if all is right
Step by step: invocation
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right
} yield {
val refMethod =
instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*)
}
Same as before
The parameters if all is right
Step by step: invocation
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right
} yield {
val refMethod =
instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*)
}
Same as before
The values to use to invoke
the method
Step by step: invocation
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right
} yield {
val refMethod =
instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*)
}
Same as before
The values to use to invoke
the method if all is right :-)
Step by step: invocation
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right
} yield {
val refMethod =
instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*)
}
Will compute only if all OK
Step by step: invocation
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right
} yield {
val refMethod =
instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*)
}
Either[String, Any]
Step by step: invocation
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right
} yield {
val refMethod =
instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*)
}
Either[String, Any]
The first error that occurs (if any)
Step by step: invocation
val invocationOrError =
for {
method <- methodOrError.right
params <- paramsOrError.right
values <- instantiate(params, args).right
} yield {
val refMethod =
instanceMirror.reflectMethod(method)
refMethod(values.unzip._2: _*)
}
Either[String, Any]
The first error that occurs (if any)
The value
returned by the
method (if all OK)
Step by step: handling ending cases
invocationOrError.fold(println, identity)
Step by step: handling ending cases
invocationOrError.fold(println, identity)
fold[X] ~
Either[A, B] => (A => X) => (B => X) => X
Step by step: handling ending cases
invocationOrError.fold(println, identity)
fold[X] ~
Either[A, B] => (A => X) => (B => X) => X
If you can convert the value at the left to X, and
the value at the right to X, then you can convert
an Either[A,B] to X
Step by step: handling ending cases
invocationOrError.fold(println, identity)
fold[X] ~
Either[A, B] => (A => X) => (B => X) => X
If you can convert the value at the left to X, and
the value at the right to X, then you can convert
an Either[A,B] to X
Step by step: handling ending cases
invocationOrError.fold(println, identity)
fold[X] ~
Either[A, B] => (A => X) => (B => X) => X
If you can convert the value at the left to X, and
the value at the right to X, then you can convert
an Either[A,B] to X
Step by step: handling ending cases
invocationOrError.fold(println, identity)
fold[X] ~
Either[A, B] => (A => X) => (B => X) => X
If you can convert the value at the left to X, and
the value at the right to X, then you can convert
an Either[A,B] to X
Step by step: handling ending cases
invocationOrError.fold(println, identity)
fold[X] ~
Either[A, B] => (A => X) => (B => X) => X
def fold[X](fa: A=>X, fb: B=>X) = this match {
case Left(a) => fa(a)
case Right(b) => fb(b)
}
Step by step: handling ending cases
invocationOrError.fold(println, identity)
fold[X] ~
Either[A, B] => (A => X) => (B => X) => X
In this case, if there is an error (a Left value) it
prints it to the console. If all went OK (a Right
value) it does nothing, because the method
was already executed as a side-effect
instantiate?
def instantiate(paramList: List[ru.Symbol], argList: List[String]): Either[Error, List[(Name, Any)]] = {
val params = paramList.map(p => p.name.encodedName.toString -> p.typeSignature)
val paramNames = params.unzip._1
val argsOptName = assignNames(argList)
val argsPartition = argsOptName.partition(_._1.isEmpty)
val unnamedArgs = argsPartition._1.unzip._2
val namedArgsTmp = argsPartition._2.collect { case (Some(n), value) => n -> value }
def sortFunc(s: String): Int = { val i = paramNames.indexOf(s); if (i == -1) Int.MaxValue else i }
val namedArgs = namedArgsTmp.sortBy(p => sortFunc(p._1))
val argsNames = namedArgs.unzip._1
val (unreferencedParams, referencedParams) = params.partition(p => !argsNames.contains(p._1))
val unreferenced = instantiateUnreferencedParams(unreferencedParams, unnamedArgs)
val referenced = instantiateParams(referencedParams, namedArgs)
for {
unref <- unreferenced.right
ref <- referenced.right
} yield {
(unref ++ ref).sortBy(p => sortFunc(p._1))
}
}
More ...
assignNames
instantiateUnreferencedParams
instantiateParams
coerce
Example:
Suppose we want to query user information,
but it is scattered in multiple servers (ldap and
database).
Most of the time we will be searching by name,
but sometimes we will want to search by uid or
by the user login.
And we want it now! :)
Example: usr
So we create a command `usr` with the
following arguments:
name: Option[String]
limit: Option[Int]
uid: Option[String]
login: Option[String]
debug: Option[Boolean]
Example: usr
So we create a command `usr` with the
following arguments:
name: Option[String]
limit: Option[Int]
uid: Option[String]
login: Option[String]
debug: Option[Boolean]
limit the number of
results
Example: usr
So we create a command `usr` with the
following arguments:
name: Option[String]
limit: Option[Int]
uid: Option[String]
login: Option[String]
debug: Option[Boolean]
extra debug info?
Example: usr
So we create a command `usr` with the
following arguments:
name: Option[String]
limit: Option[Int]
uid: Option[String]
login: Option[String]
debug: Option[Boolean]
First so they can be
used without name
Example: usr
So we create a command `usr` with the
following arguments:
name: Option[String]
limit: Option[Int]
uid: Option[String]
login: Option[String]
debug: Option[Boolean]
Any individual
argument is optional
Example: usr
So we create a command `usr` with the
following arguments:
name: Option[String]
limit: Option[Int]
uid: Option[String]
login: Option[String]
debug: Option[Boolean]
String, Int, Boolean ...
Example: usr
Some usage examples:
usr "juan perez"
usr "juan perez" 10
usr --uid jperez
usr --login jperez --limit 10 --debug
Example: usr
object usr extends optional.Application {
def main(name: Option[String],
limit: Option[Int],
uid: Option[String],
login: Option[String],
debug: Option[Boolean]) {
// THE LOGIC ...
}
}
Example: usr
object usr extends optional.Application {
def main(name: Option[String], limit: Option[Int], uid: Option[String],
login: Option[String], debug: Option[Boolean]) {
if (Seq(name, uid, login).flatten.isEmpty) {
println("You must specify at least one search criteria")
System.exit(1)
}
val effDebug = debug.getOrElse(false)
val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) }
val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) }
val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) }
futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug))
futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug))
futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug))
val res = Await.ready(
Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf)
}
}
Example: usr
object usr extends optional.Application {
def main(name: Option[String], limit: Option[Int], uid: Option[String],
login: Option[String], debug: Option[Boolean]) {
if (Seq(name, uid, login).flatten.isEmpty) {
println("You must specify at least one search criteria")
System.exit(1)
}
val effDebug = debug.getOrElse(false)
val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) }
val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) }
val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) }
futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug))
futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug))
futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug))
val res = Await.ready(
Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf)
}
}
One of them must
be specified
Example: usr
object usr extends optional.Application {
def main(name: Option[String], limit: Option[Int], uid: Option[String],
login: Option[String], debug: Option[Boolean]) {
if (Seq(name, uid, login).flatten.isEmpty) {
println("You must specify at least one search criteria")
System.exit(1)
}
val effDebug = debug.getOrElse(false)
val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) }
val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) }
val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) }
futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug))
futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug))
futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug))
val res = Await.ready(
Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf)
}
}
No debug by
default
Example: usr
object usr extends optional.Application {
def main(name: Option[String], limit: Option[Int], uid: Option[String],
login: Option[String], debug: Option[Boolean]) {
if (Seq(name, uid, login).flatten.isEmpty) {
println("You must specify at least one search criteria")
System.exit(1)
}
val effDebug = debug.getOrElse(false)
val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) }
val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) }
val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) }
futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug))
futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug))
futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug))
val res = Await.ready(
Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf)
}
}
Query the three
sources in parallel
Example: usr
object usr extends optional.Application {
def main(name: Option[String], limit: Option[Int], uid: Option[String],
login: Option[String], debug: Option[Boolean]) {
if (Seq(name, uid, login).flatten.isEmpty) {
println("You must specify at least one search criteria")
System.exit(1)
}
val effDebug = debug.getOrElse(false)
val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) }
val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) }
val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) }
futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug))
futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug))
futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug))
val res = Await.ready(
Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf)
}
}
After completion, print
the result as a side effect
Example: usr
object usr extends optional.Application {
def main(name: Option[String], limit: Option[Int], uid: Option[String],
login: Option[String], debug: Option[Boolean]) {
if (Seq(name, uid, login).flatten.isEmpty) {
println("You must specify at least one search criteria")
System.exit(1)
}
val effDebug = debug.getOrElse(false)
val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) }
val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) }
val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) }
futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug))
futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug))
futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug))
val res = Await.ready(
Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf)
}
}
Wait until the three
queries finish
Example: usr
object usr extends optional.Application {
def main(name: Option[String], limit: Option[Int], uid: Option[String],
login: Option[String], debug: Option[Boolean]) {
if (Seq(name, uid, login).flatten.isEmpty) {
println("You must specify at least one search criteria")
System.exit(1)
}
val effDebug = debug.getOrElse(false)
val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) }
val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) }
val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) }
futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug))
futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug))
futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug))
val res = Await.ready(
Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf)
}
}
Has a bug!
Example: completing futures
futRes.onComplete(printResult)
def onComplete[U](f: Try[T] => U): Unit
Try[T] =
Success(t: T)
| Failure(e: Throwable)
futRes.onComplete(printResult)
def onComplete[U](f: Try[T] => U): Unit
Example: completing futures
Either[Throwable, T]~
~
Try[T] =
Success(t: T)
| Failure(e: Throwable)
futRes.onComplete(printResult)
def onComplete[U](f: Try[T] => U): Unit
When this Future finishes, either correctly (Success) or
with an exception (Failure), apply the function f which
knows how to handle both cases to compute some side
effect
Example: completing futures
Either[Throwable, T]
Try[T] =
Success(t: T)
| Failure(e: Throwable)
~
~
futRes.onComplete(printResult)
def onComplete[U](f: Try[T] => U): Unit
In this case the side effect is just printing the result to the
console.
Example: completing futures
Either[Throwable, T]~
~
Try[T] =
Success(t: T)
| Failure(e: Throwable)
Future.sequence(
Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB))
def sequence[A, M[_]](
in: M[Future[A]]): Future[M[A]]
Example: sequencing futures
Future.sequence(
Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB))
def sequence[A, M[_]](
in: M[Future[A]]): Future[M[A]]
Convert some container M of Future[A] in a Future M[A].
Example: sequencing futures
Future.sequence(
Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB))
def sequence[A, M[_]](
in: M[Future[A]]): Future[M[A]]
Convert some container M of Future[A] in a Future M[A].
Example: sequencing futures
M[X] <: TraversableOnce[X]
Future.sequence(
Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB))
def sequence[A, M[_]](
in: M[Future[A]]): Future[M[A]]
Convert some container M of Future[A] in a Future M[A].
Useful to reduce many Futures into a single Future.
Example: sequencing futures
Future.sequence(
Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB))
def sequence[A, M[_]](
in: M[Future[A]]): Future[M[A]]
Convert some container M of Future[A] in a Future M[A].
Useful to reduce many Futures into a single Future.
In this case we reduce three independent future results to a
single future result with a Seq of the individual results.
Example: sequencing futures
implicit class TryHarder[T](val t: Try[T])
extends AnyVal {
def toEither: Either[Throwable, T] = t match {
case Success(s) => Right(s)
case Failure(f) => Left(f)
}
def fold[U](success: T => U,
failure: Throwable => U): U =
toEither.fold(failure, success)
}
Example: folding on Try
implicit class TryHarder[T](val t: Try[T])
extends AnyVal {
def toEither: Either[Throwable, T] = t match {
case Success(s) => Right(s)
case Failure(f) => Left(f)
}
def fold[U](success: T => U,
failure: Throwable => U): U =
toEither.fold(failure, success)
}
Example: folding on Try
Try is pretty much a
specialized Either
implicit class TryHarder[T](val t: Try[T])
extends AnyVal {
def toEither: Either[Throwable, T] = t match {
case Success(s) => Right(s)
case Failure(f) => Left(f)
}
def fold[U](success: T => U,
failure: Throwable => U): U =
toEither.fold(failure, success)
}
Example: folding on Try
Try is pretty much a
specialized Either
Example: fixing the bug
object usr extends optional.Application {
def main(name: Option[String], limit: Option[Int], uid: Option[String],
login: Option[String], debug: Option[Boolean]) {
if (Seq(name, uid, login).flatten.isEmpty) {
println("You must specify at least one search criteria")
System.exit(1)
}
val effDebug = debug.getOrElse(false)
val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) }
val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) }
val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) }
futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug))
futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug))
futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug))
val res = Await.ready(
Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf)
}
}
The problem is that onComplete
executes after the Future is
completed.
Await only awaits to the completion,
not the execution of onComplete and
the program can terminate “too soon”.
implicit class FutureOps[T](val f: Future[T])
extends AnyVal {
def continue[S](
cont: Try[T] => S): Future[S] = {
val p = Promise[S]()
f.onComplete { tt =>
p.complete(Try(cont(tt)))
}
p.future
}
}
Example: fixing the bug
implicit class FutureOps[T](val f: Future[T])
extends AnyVal {
def continue[S](
cont: Try[T] => S): Future[S] = {
val p = Promise[S]()
f.onComplete { tt =>
p.complete(Try(cont(tt)))
}
p.future
}
}
Example: fixing the bug
Like onComplete but
returning another Future
Example: now works :)
object usr extends optional.Application {
def main(name: Option[String], limit: Option[Int], uid: Option[String],
login: Option[String], debug: Option[Boolean]) {
if (Seq(name, uid, login).flatten.isEmpty) {
println("You must specify at least one search criteria")
System.exit(1)
}
val effDebug = debug.getOrElse(false)
val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) }
val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) }
val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) }
val endLDAPA = futUsrLDAPA.continue(printResult("--- LDAP A ---"))
val endLDAPB = futUsrLDAPB.continue(printResult("--- LDAP B ---"))
val endDB = futUsrDB.continue(printResult("--- Data Base ---"))
val res = Await.ready(
Future.sequence(Seq(endLDAPA, endLDAPB, endDB)), Duration.Inf)
}
}
github.com/gerferra/crappy-optional
FIN
sub
mini-demo?
FIN

More Related Content

What's hot

Functional programming with_scala
Functional programming with_scalaFunctional programming with_scala
Functional programming with_scala
Raymond Tay
 
Scalaz
ScalazScalaz
Scalaz
mpilquist
 
Lambdaconf2019 talk
Lambdaconf2019 talkLambdaconf2019 talk
Lambdaconf2019 talk
Dominic Egger
 
On Growth and Software
On Growth and SoftwareOn Growth and Software
On Growth and Software
Carlo Pescio
 
Javascript arrays
Javascript arraysJavascript arrays
Javascript arrays
Hassan Dar
 
Scala by Luc Duponcheel
Scala by Luc DuponcheelScala by Luc Duponcheel
Scala by Luc Duponcheel
Stephan Janssen
 
Introduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaIntroduction to Functional Programming with Scala
Introduction to Functional Programming with Scala
Daniel Cukier
 
Scala-对Java的修正和超越
Scala-对Java的修正和超越Scala-对Java的修正和超越
Scala-对Java的修正和超越Caoyuan Deng
 
1.5.recommending music with apache spark ml
1.5.recommending music with apache spark ml1.5.recommending music with apache spark ml
1.5.recommending music with apache spark ml
leorick lin
 
Basic swift
Basic swiftBasic swift
Basic swift
富生 王
 
Scala jargon cheatsheet
Scala jargon cheatsheetScala jargon cheatsheet
Scala jargon cheatsheet
Ruslan Shevchenko
 
Scalaz 8: A Whole New Game
Scalaz 8: A Whole New GameScalaz 8: A Whole New Game
Scalaz 8: A Whole New Game
John De Goes
 
学生向けScalaハンズオンテキスト
学生向けScalaハンズオンテキスト学生向けScalaハンズオンテキスト
学生向けScalaハンズオンテキスト
Opt Technologies
 
学生向けScalaハンズオンテキスト part2
学生向けScalaハンズオンテキスト part2学生向けScalaハンズオンテキスト part2
学生向けScalaハンズオンテキスト part2
Opt Technologies
 

What's hot (16)

Functional programming with_scala
Functional programming with_scalaFunctional programming with_scala
Functional programming with_scala
 
Scalaz
ScalazScalaz
Scalaz
 
Lambdaconf2019 talk
Lambdaconf2019 talkLambdaconf2019 talk
Lambdaconf2019 talk
 
On Growth and Software
On Growth and SoftwareOn Growth and Software
On Growth and Software
 
Javascript arrays
Javascript arraysJavascript arrays
Javascript arrays
 
Scala by Luc Duponcheel
Scala by Luc DuponcheelScala by Luc Duponcheel
Scala by Luc Duponcheel
 
Introduction to Functional Programming with Scala
Introduction to Functional Programming with ScalaIntroduction to Functional Programming with Scala
Introduction to Functional Programming with Scala
 
Scala-对Java的修正和超越
Scala-对Java的修正和超越Scala-对Java的修正和超越
Scala-对Java的修正和超越
 
1.5.recommending music with apache spark ml
1.5.recommending music with apache spark ml1.5.recommending music with apache spark ml
1.5.recommending music with apache spark ml
 
SacalaZa #1
SacalaZa #1SacalaZa #1
SacalaZa #1
 
ppopoff
ppopoffppopoff
ppopoff
 
Basic swift
Basic swiftBasic swift
Basic swift
 
Scala jargon cheatsheet
Scala jargon cheatsheetScala jargon cheatsheet
Scala jargon cheatsheet
 
Scalaz 8: A Whole New Game
Scalaz 8: A Whole New GameScalaz 8: A Whole New Game
Scalaz 8: A Whole New Game
 
学生向けScalaハンズオンテキスト
学生向けScalaハンズオンテキスト学生向けScalaハンズオンテキスト
学生向けScalaハンズオンテキスト
 
学生向けScalaハンズオンテキスト part2
学生向けScalaハンズオンテキスト part2学生向けScalaハンズオンテキスト part2
学生向けScalaハンズオンテキスト part2
 

Viewers also liked

Scala en proyectos de vinculación Ancap-UR - 2013-03
Scala en proyectos de vinculación Ancap-UR - 2013-03Scala en proyectos de vinculación Ancap-UR - 2013-03
Scala en proyectos de vinculación Ancap-UR - 2013-03
Germán Ferrari
 
Transform your State \/ Err
Transform your State \/ ErrTransform your State \/ Err
Transform your State \/ Err
Germán Ferrari
 
Scala eXchange opening
Scala eXchange openingScala eXchange opening
Scala eXchange opening
Martin Odersky
 
Devoxx
DevoxxDevoxx
Oscon keynote: Working hard to keep it simple
Oscon keynote: Working hard to keep it simpleOscon keynote: Working hard to keep it simple
Oscon keynote: Working hard to keep it simple
Martin Odersky
 
Scala - The Simple Parts, SFScala presentation
Scala - The Simple Parts, SFScala presentationScala - The Simple Parts, SFScala presentation
Scala - The Simple Parts, SFScala presentation
Martin Odersky
 
The Evolution of Scala
The Evolution of ScalaThe Evolution of Scala
The Evolution of Scala
Martin Odersky
 
Scala Days San Francisco
Scala Days San FranciscoScala Days San Francisco
Scala Days San Francisco
Martin Odersky
 

Viewers also liked (8)

Scala en proyectos de vinculación Ancap-UR - 2013-03
Scala en proyectos de vinculación Ancap-UR - 2013-03Scala en proyectos de vinculación Ancap-UR - 2013-03
Scala en proyectos de vinculación Ancap-UR - 2013-03
 
Transform your State \/ Err
Transform your State \/ ErrTransform your State \/ Err
Transform your State \/ Err
 
Scala eXchange opening
Scala eXchange openingScala eXchange opening
Scala eXchange opening
 
Devoxx
DevoxxDevoxx
Devoxx
 
Oscon keynote: Working hard to keep it simple
Oscon keynote: Working hard to keep it simpleOscon keynote: Working hard to keep it simple
Oscon keynote: Working hard to keep it simple
 
Scala - The Simple Parts, SFScala presentation
Scala - The Simple Parts, SFScala presentationScala - The Simple Parts, SFScala presentation
Scala - The Simple Parts, SFScala presentation
 
The Evolution of Scala
The Evolution of ScalaThe Evolution of Scala
The Evolution of Scala
 
Scala Days San Francisco
Scala Days San FranciscoScala Days San Francisco
Scala Days San Francisco
 

Similar to An excuse to Try, Either, folding, and Future. sequence

Practical cats
Practical catsPractical cats
Practical cats
Raymond Tay
 
Leveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better ValidationLeveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better Validation
Tomer Gabel
 
First-Class Patterns
First-Class PatternsFirst-Class Patterns
First-Class Patterns
John De Goes
 
ScalaBlitz
ScalaBlitzScalaBlitz
Sequence and Traverse - Part 2
Sequence and Traverse - Part 2Sequence and Traverse - Part 2
Sequence and Traverse - Part 2
Philip Schwarz
 
Lazy Java
Lazy JavaLazy Java
Lazy Java
J On The Beach
 
Lazy Java
Lazy JavaLazy Java
Lazy Java
Nicola Pedot
 
Mario Fusco - Lazy Java - Codemotion Milan 2018
Mario Fusco - Lazy Java - Codemotion Milan 2018Mario Fusco - Lazy Java - Codemotion Milan 2018
Mario Fusco - Lazy Java - Codemotion Milan 2018
Codemotion
 
Lazy java
Lazy javaLazy java
Lazy java
Mario Fusco
 
Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)stasimus
 
Scala Back to Basics: Type Classes
Scala Back to Basics: Type ClassesScala Back to Basics: Type Classes
Scala Back to Basics: Type Classes
Tomer Gabel
 
Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kirill Rozov
 
Extractors & Implicit conversions
Extractors & Implicit conversionsExtractors & Implicit conversions
Extractors & Implicit conversions
Knoldus Inc.
 
Functional Programming in Java 8 - Exploiting Lambdas
Functional Programming in Java 8 - Exploiting LambdasFunctional Programming in Java 8 - Exploiting Lambdas
Functional Programming in Java 8 - Exploiting Lambdas
Ganesh Samarthyam
 
Scala: Functioneel programmeren in een object georiënteerde wereld
Scala: Functioneel programmeren in een object georiënteerde wereldScala: Functioneel programmeren in een object georiënteerde wereld
Scala: Functioneel programmeren in een object georiënteerde wereld
Werner Hofstra
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?Tomasz Wrobel
 
Ponies and Unicorns With Scala
Ponies and Unicorns With ScalaPonies and Unicorns With Scala
Ponies and Unicorns With Scala
Tomer Gabel
 
Taxonomy of Scala
Taxonomy of ScalaTaxonomy of Scala
Taxonomy of Scalashinolajla
 

Similar to An excuse to Try, Either, folding, and Future. sequence (20)

Practical cats
Practical catsPractical cats
Practical cats
 
Leveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better ValidationLeveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better Validation
 
First-Class Patterns
First-Class PatternsFirst-Class Patterns
First-Class Patterns
 
ScalaBlitz
ScalaBlitzScalaBlitz
ScalaBlitz
 
Scala for curious
Scala for curiousScala for curious
Scala for curious
 
Sequence and Traverse - Part 2
Sequence and Traverse - Part 2Sequence and Traverse - Part 2
Sequence and Traverse - Part 2
 
Lazy Java
Lazy JavaLazy Java
Lazy Java
 
Lazy Java
Lazy JavaLazy Java
Lazy Java
 
Mario Fusco - Lazy Java - Codemotion Milan 2018
Mario Fusco - Lazy Java - Codemotion Milan 2018Mario Fusco - Lazy Java - Codemotion Milan 2018
Mario Fusco - Lazy Java - Codemotion Milan 2018
 
Lazy java
Lazy javaLazy java
Lazy java
 
Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)
 
Scala Back to Basics: Type Classes
Scala Back to Basics: Type ClassesScala Back to Basics: Type Classes
Scala Back to Basics: Type Classes
 
Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3Kotlin Advanced - Apalon Kotlin Sprint Part 3
Kotlin Advanced - Apalon Kotlin Sprint Part 3
 
Extractors & Implicit conversions
Extractors & Implicit conversionsExtractors & Implicit conversions
Extractors & Implicit conversions
 
Functional Programming in Java 8 - Exploiting Lambdas
Functional Programming in Java 8 - Exploiting LambdasFunctional Programming in Java 8 - Exploiting Lambdas
Functional Programming in Java 8 - Exploiting Lambdas
 
Scala: Functioneel programmeren in een object georiënteerde wereld
Scala: Functioneel programmeren in een object georiënteerde wereldScala: Functioneel programmeren in een object georiënteerde wereld
Scala: Functioneel programmeren in een object georiënteerde wereld
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?
 
Scala ntnu
Scala ntnuScala ntnu
Scala ntnu
 
Ponies and Unicorns With Scala
Ponies and Unicorns With ScalaPonies and Unicorns With Scala
Ponies and Unicorns With Scala
 
Taxonomy of Scala
Taxonomy of ScalaTaxonomy of Scala
Taxonomy of Scala
 

Recently uploaded

openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
Shane Coughlan
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
Łukasz Chruściel
 
Game Development with Unity3D (Game Development lecture 3)
Game Development  with Unity3D (Game Development lecture 3)Game Development  with Unity3D (Game Development lecture 3)
Game Development with Unity3D (Game Development lecture 3)
abdulrafaychaudhry
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
NYGGS Automation Suite
 
Pro Unity Game Development with C-sharp Book
Pro Unity Game Development with C-sharp BookPro Unity Game Development with C-sharp Book
Pro Unity Game Development with C-sharp Book
abdulrafaychaudhry
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Globus
 
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
Alina Yurenko
 
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket ManagementUtilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
Neo4j
 
Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
abdulrafaychaudhry
 
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI AppAI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
Google
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Crescat
 
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptxText-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
ShamsuddeenMuhammadA
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
timtebeek1
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Globus
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Globus
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Mind IT Systems
 
Introduction to Pygame (Lecture 7 Python Game Development)
Introduction to Pygame (Lecture 7 Python Game Development)Introduction to Pygame (Lecture 7 Python Game Development)
Introduction to Pygame (Lecture 7 Python Game Development)
abdulrafaychaudhry
 
Empowering Growth with Best Software Development Company in Noida - Deuglo
Empowering Growth with Best Software  Development Company in Noida - DeugloEmpowering Growth with Best Software  Development Company in Noida - Deuglo
Empowering Growth with Best Software Development Company in Noida - Deuglo
Deuglo Infosystem Pvt Ltd
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
XfilesPro
 

Recently uploaded (20)

openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
 
Game Development with Unity3D (Game Development lecture 3)
Game Development  with Unity3D (Game Development lecture 3)Game Development  with Unity3D (Game Development lecture 3)
Game Development with Unity3D (Game Development lecture 3)
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
 
Pro Unity Game Development with C-sharp Book
Pro Unity Game Development with C-sharp BookPro Unity Game Development with C-sharp Book
Pro Unity Game Development with C-sharp Book
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
 
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
 
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket ManagementUtilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
 
Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
 
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI AppAI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
 
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptxText-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
 
Introduction to Pygame (Lecture 7 Python Game Development)
Introduction to Pygame (Lecture 7 Python Game Development)Introduction to Pygame (Lecture 7 Python Game Development)
Introduction to Pygame (Lecture 7 Python Game Development)
 
Empowering Growth with Best Software Development Company in Noida - Deuglo
Empowering Growth with Best Software  Development Company in Noida - DeugloEmpowering Growth with Best Software  Development Company in Noida - Deuglo
Empowering Growth with Best Software Development Company in Noida - Deuglo
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
 

An excuse to Try, Either, folding, and Future. sequence

  • 1. An excuse to Try, Either, folding, and Future. sequence or parsing command line options with crappy-optional @gerferra
  • 2. crappy-optional? “Don’t worry be crappy” mantra optional library from DRMacIver and paulp
  • 3. optional optional is a command line option parser and library.
  • 4. optional YOU WRITE: object MyAwesomeCommandLineTool extends optional.Application { // for instance... def main(count: Option[Int], file: Option[java.io.File], arg1: String) { [...] } }
  • 5. optional THEN YOU DO: scala MyAwesomeCommandLineTool --count 5 quux
  • 6. optional AND YOUR MAIN METHOD WILL BE INVOKED SUCH THAT: count = Some(5) file = None arg1 = quux
  • 7. optional AND YOUR MAIN METHOD WILL BE INVOKED SUCH THAT: count = Some(5) file = None arg1 = quux object MyAwesomeCommandLineTool extends optional.Application { // for instance... def main(count: Option[Int], file: Option[java.io.File], arg1: String) { [...] } } scala MyAwesomeCommandLineTool --count 5 quux Ref by name
  • 8. optional AND YOUR MAIN METHOD WILL BE INVOKED SUCH THAT: count = Some(5) file = None arg1 = quux object MyAwesomeCommandLineTool extends optional.Application { // for instance... def main(count: Option[Int], file: Option[java.io.File], arg1: String) { [...] } } scala MyAwesomeCommandLineTool --count 5 quux Ref by name Optional parameter
  • 9. optional AND YOUR MAIN METHOD WILL BE INVOKED SUCH THAT: count = Some(5) file = None arg1 = quux object MyAwesomeCommandLineTool extends optional.Application { // for instance... def main(count: Option[Int], file: Option[java.io.File], arg1: String) { [...] } } scala MyAwesomeCommandLineTool --count 5 quux Ref by name Optional parameter Ref by position
  • 12. optional HOW IT WORKS: (Java) Reflection, man.
  • 13. optional HOW IT WORKS: (Java) Reflection, man. doesn’t work OK with Scala since 2.9 (I guess) ... used to work
  • 14. So... … copy the idea, but use Scala reflection instead … … and don’t worry and be crappy :) ...
  • 15. Show me the code! trait Application { def main(args: Array[String]) { runMain(this, args.toList) } private def runMain[T: ClassTag]( instance: T, args: List[String]) { // ... something happens here ... } }
  • 16. Show me the code! private def runMain[T: ClassTag](instance: T, args: List[String]) { val instanceMirror = ru.runtimeMirror(getClass.getClassLoader).reflect(instance) val theType = instanceMirror.symbol.typeSignature val methods = theType.members.collect { case m if m.isMethod => m.asMethod } val mainMethods = methods.filter(m => m.name == ru.TermName("main") && m.fullName != classOf[Application].getName + ".main") val methodOrError = mainMethods.headOption.toRight("No main method defined") // Scala support multiple parameter lists, I care only about the first (if exists ...) val = methodOrError.right.map(m => m.paramLists.headOption.getOrElse(Nil)) paramsOrError val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right // mysterious instantiation of things ... } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) // here is the execution of the main method happening } invocationOrError.fold(println, identity /* nothing to do because it already executed as a side effect */) }
  • 17. Step by step: method definition Scala method: private def runMain[T: ClassTag]( instance: T, args: List[String]) { private, so it’s not inherited with a type parameter T with a context bound ClassTag
  • 18. Step by step: method definition … with a context bound ClassTag? private def runMain[T: ClassTag]( instance: T, args: List[String]) { ≡ private def runMain[T]( instance: T, args: List[String])( implicit ev: ClassTag[T]) { both expressions are analogous
  • 19. Step by step: method definition we ask for an evidence that a ClassTag can be constructed for the type T ... … so we can use runtime reflection on it ...
  • 20. Step by step: reflecting on instance val instanceMirror = ru .runtimeMirror(getClass.getClassLoader) .reflect(instance)
  • 21. Step by step: reflecting on instance val instanceMirror = ru .runtimeMirror(getClass.getClassLoader) .reflect(instance) (mystic voice) …You need a mirror to access the reflection…
  • 22. Step by step: reflecting on instance val instanceMirror = ru .runtimeMirror(getClass.getClassLoader) .reflect(instance) `ru` here is just an alias to package scala.reflect.runtime.universe import scala.reflect.runtime.{ universe => ru }
  • 23. Step by step: reflecting on instance val instanceMirror = ru .runtimeMirror(getClass.getClassLoader) .reflect(instance) A ClassTag is needed here to overcome the erasure of the type at runtime T
  • 24. Step by step: obtaining the method I want the methods ... val theType = instanceMirror.symbol.typeSignature val methods = theType.members.collect { case m if m.isMethod => m.asMethod }
  • 25. Step by step: obtaining the method … to get the right main methods ... val mainMethods = methods.filter(m => m.name == ru.TermName("main") && m.fullName != classOf[Application].getName + ".main")
  • 26. Step by step: obtaining the method … to get the right main methods ... val methodOrError = mainMethods .headOption .toRight("No main method defined") Give me either the first of the methods, if there is one, or an error message
  • 27. Step by step: obtaining the method … to get the right main methods ... val methodOrError = mainMethods .headOption .toRight("No main method defined") Give me either the first of the methods, if there is one, or an error message
  • 28. Step by step: obtaining the method … to get the right main methods … val methodOrError = mainMethods .headOption .toRight("No main method defined") Give me either the first of the methods, if there is one, or an error message
  • 29. Step by step: obtaining the method … to get the right main methods … val methodOrError = mainMethods .headOption .toRight("No main method defined") headOption ~ List[A] => Option[A]
  • 30. Step by step: obtaining the method … to get the right main methods … val methodOrError = mainMethods .headOption .toRight("No main method defined") headOption ~ List[A] => Option[A] Option[A] = Some(a: A) | None
  • 31. Step by step: obtaining the method … to get the right main methods … val methodOrError = mainMethods .headOption .toRight("No main method defined") headOption ~ List[A] => Option[A] not scala (!) Option[A] = Some(a: A) | None
  • 32. Step by step: obtaining the method … to get the right main methods … val methodOrError = mainMethods .headOption .toRight("No main method defined") headOption ~ List[A] => Option[A] def headOption: Option[A] = if (isEmpty) None else Some(head) Option[A] = Some(a: A) | None
  • 33. Step by step: obtaining the method … to get the right main methods … val methodOrError = mainMethods .headOption .toRight("No main method defined") toRight[X] ~ Option[A] => (=> X) => Either[X,A]
  • 34. Step by step: obtaining the method … to get the right main methods … val methodOrError = mainMethods .headOption .toRight("No main method defined") toRight[X] ~ Option[A] => (=> X) => Either[X,A] Either[A, B] = Left(a: A) | Right(b: B)
  • 35. Step by step: obtaining the method … to get the right main methods … val methodOrError = mainMethods .headOption .toRight("No main method defined") toRight[X] ~ Option[A] => (=> X) => Either[X,A] def toRight[X](left: => X): Either[X,A] = if (isEmpty) Left(left) else Right(this.get) Either[A, B] = Left(a: A) | Right(b: B)
  • 36. Step by step: obtaining the method … to get the right main methods … val methodOrError = mainMethods .headOption .toRight("No main method defined") toRight[X] ~ Option[A] => (=> X) => Either[X,A] def toRight[X](left: => X): Either[X,A] = if (isEmpty) Left(left) else Right(this.get) Either[A, B] = Left(a: A) | Right(b: B) Either[String, ru.MethodSymbol]
  • 37. Step by step: obtaining the method … to get the right main methods … val methodOrError = mainMethods .headOption .toRight("No main method defined") toRight[X] ~ Option[A] => (=> X) => Either[X,A] def toRight[X](left: => X): Either[X,A] = if (isEmpty) Left(left) else Right(this.get) Either[A, B] = Left(a: A) | Right(b: B) Either[String, ru.MethodSymbol]
  • 38. Step by step: obtaining the params Ok. Now I need the parameters: val paramsOrError = methodOrError.right.map(m => m.paramLists .headOption.getOrElse(Nil)) Scala support multiple parameter lists. I care only about the first (if exists …)
  • 39. Step by step: obtaining the params Ok. Now I need the parameters: val paramsOrError = methodOrError.right.map(m => m.paramLists .headOption.getOrElse(Nil)) Either[String, ru.MethodSymbol]
  • 40. Step by step: obtaining the params Ok. Now I need the parameters: val paramsOrError = methodOrError.right.map(m => m.paramLists .headOption.getOrElse(Nil)) Either[String, ru.MethodSymbol] only the right part please
  • 41. Step by step: obtaining the params Ok. Now I need the parameters: val paramsOrError = methodOrError.right.map(m => m.paramLists .headOption.getOrElse(Nil)) map[X] ~ Either[A,B] => (B => X) => Either[A,X] Either[String, ru.MethodSymbol] only the right part please
  • 42. Step by step: obtaining the params Ok. Now I need the parameters: val paramsOrError = methodOrError.right.map(m => m.paramLists .headOption.getOrElse(Nil)) map[X] ~ Either[A,B] => (B => X) => Either[A,X] def map[X](f: B => X) = e match { case Left(a) => Left(a) case Right(b) => Right(f(b)) } Either[String, ru.MethodSymbol] only the right part please
  • 43. Step by step: obtaining the params Ok. Now I need the parameters: val paramsOrError = methodOrError.right.map(m => m.paramLists .headOption.getOrElse(Nil)) List[List[ru.Symbol]]
  • 44. Step by step: obtaining the params Ok. Now I need the parameters: val paramsOrError = methodOrError.right.map(m => m.paramLists .headOption.getOrElse(Nil)) Option[List[ru.Symbol]] List[List[ru.Symbol]]
  • 45. Step by step: obtaining the params Ok. Now I need the parameters: val paramsOrError = methodOrError.right.map(m => m.paramLists .headOption.getOrElse(Nil)) f: ru.MethodSymbol => List[ru.Symbol]
  • 46. Step by step: obtaining the params Ok. Now I need the parameters: val paramsOrError = methodOrError.right.map(m => m.paramLists .headOption.getOrElse(Nil)) f: ru.MethodSymbol => List[ru.Symbol] paramsOrError: Either[String, List[ru.Symbol]]
  • 47. Step by step: obtaining the params Ok. Now I need the parameters: val paramsOrError = methodOrError.right.map(m => m.paramLists .headOption.getOrElse(Nil)) f: ru.MethodSymbol => List[ru.Symbol] paramsOrError: Either[String, List[ru.Symbol]] paramsOrError have the parameters OR the original error "No main method defined"
  • 48. Step by step: invocation val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) }
  • 49. Step by step: invocation val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) } Same as before
  • 50. Step by step: invocation val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) } Same as before The main method if all is right
  • 51. Step by step: invocation val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) } Same as before The parameters if all is right
  • 52. Step by step: invocation val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) } Same as before The values to use to invoke the method
  • 53. Step by step: invocation val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) } Same as before The values to use to invoke the method if all is right :-)
  • 54. Step by step: invocation val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) } Will compute only if all OK
  • 55. Step by step: invocation val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) } Either[String, Any]
  • 56. Step by step: invocation val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) } Either[String, Any] The first error that occurs (if any)
  • 57. Step by step: invocation val invocationOrError = for { method <- methodOrError.right params <- paramsOrError.right values <- instantiate(params, args).right } yield { val refMethod = instanceMirror.reflectMethod(method) refMethod(values.unzip._2: _*) } Either[String, Any] The first error that occurs (if any) The value returned by the method (if all OK)
  • 58. Step by step: handling ending cases invocationOrError.fold(println, identity)
  • 59. Step by step: handling ending cases invocationOrError.fold(println, identity) fold[X] ~ Either[A, B] => (A => X) => (B => X) => X
  • 60. Step by step: handling ending cases invocationOrError.fold(println, identity) fold[X] ~ Either[A, B] => (A => X) => (B => X) => X If you can convert the value at the left to X, and the value at the right to X, then you can convert an Either[A,B] to X
  • 61. Step by step: handling ending cases invocationOrError.fold(println, identity) fold[X] ~ Either[A, B] => (A => X) => (B => X) => X If you can convert the value at the left to X, and the value at the right to X, then you can convert an Either[A,B] to X
  • 62. Step by step: handling ending cases invocationOrError.fold(println, identity) fold[X] ~ Either[A, B] => (A => X) => (B => X) => X If you can convert the value at the left to X, and the value at the right to X, then you can convert an Either[A,B] to X
  • 63. Step by step: handling ending cases invocationOrError.fold(println, identity) fold[X] ~ Either[A, B] => (A => X) => (B => X) => X If you can convert the value at the left to X, and the value at the right to X, then you can convert an Either[A,B] to X
  • 64. Step by step: handling ending cases invocationOrError.fold(println, identity) fold[X] ~ Either[A, B] => (A => X) => (B => X) => X def fold[X](fa: A=>X, fb: B=>X) = this match { case Left(a) => fa(a) case Right(b) => fb(b) }
  • 65. Step by step: handling ending cases invocationOrError.fold(println, identity) fold[X] ~ Either[A, B] => (A => X) => (B => X) => X In this case, if there is an error (a Left value) it prints it to the console. If all went OK (a Right value) it does nothing, because the method was already executed as a side-effect
  • 66. instantiate? def instantiate(paramList: List[ru.Symbol], argList: List[String]): Either[Error, List[(Name, Any)]] = { val params = paramList.map(p => p.name.encodedName.toString -> p.typeSignature) val paramNames = params.unzip._1 val argsOptName = assignNames(argList) val argsPartition = argsOptName.partition(_._1.isEmpty) val unnamedArgs = argsPartition._1.unzip._2 val namedArgsTmp = argsPartition._2.collect { case (Some(n), value) => n -> value } def sortFunc(s: String): Int = { val i = paramNames.indexOf(s); if (i == -1) Int.MaxValue else i } val namedArgs = namedArgsTmp.sortBy(p => sortFunc(p._1)) val argsNames = namedArgs.unzip._1 val (unreferencedParams, referencedParams) = params.partition(p => !argsNames.contains(p._1)) val unreferenced = instantiateUnreferencedParams(unreferencedParams, unnamedArgs) val referenced = instantiateParams(referencedParams, namedArgs) for { unref <- unreferenced.right ref <- referenced.right } yield { (unref ++ ref).sortBy(p => sortFunc(p._1)) } }
  • 68. Example: Suppose we want to query user information, but it is scattered in multiple servers (ldap and database). Most of the time we will be searching by name, but sometimes we will want to search by uid or by the user login. And we want it now! :)
  • 69. Example: usr So we create a command `usr` with the following arguments: name: Option[String] limit: Option[Int] uid: Option[String] login: Option[String] debug: Option[Boolean]
  • 70. Example: usr So we create a command `usr` with the following arguments: name: Option[String] limit: Option[Int] uid: Option[String] login: Option[String] debug: Option[Boolean] limit the number of results
  • 71. Example: usr So we create a command `usr` with the following arguments: name: Option[String] limit: Option[Int] uid: Option[String] login: Option[String] debug: Option[Boolean] extra debug info?
  • 72. Example: usr So we create a command `usr` with the following arguments: name: Option[String] limit: Option[Int] uid: Option[String] login: Option[String] debug: Option[Boolean] First so they can be used without name
  • 73. Example: usr So we create a command `usr` with the following arguments: name: Option[String] limit: Option[Int] uid: Option[String] login: Option[String] debug: Option[Boolean] Any individual argument is optional
  • 74. Example: usr So we create a command `usr` with the following arguments: name: Option[String] limit: Option[Int] uid: Option[String] login: Option[String] debug: Option[Boolean] String, Int, Boolean ...
  • 75. Example: usr Some usage examples: usr "juan perez" usr "juan perez" 10 usr --uid jperez usr --login jperez --limit 10 --debug
  • 76. Example: usr object usr extends optional.Application { def main(name: Option[String], limit: Option[Int], uid: Option[String], login: Option[String], debug: Option[Boolean]) { // THE LOGIC ... } }
  • 77. Example: usr object usr extends optional.Application { def main(name: Option[String], limit: Option[Int], uid: Option[String], login: Option[String], debug: Option[Boolean]) { if (Seq(name, uid, login).flatten.isEmpty) { println("You must specify at least one search criteria") System.exit(1) } val effDebug = debug.getOrElse(false) val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) } val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) } val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) } futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug)) futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug)) futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug)) val res = Await.ready( Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf) } }
  • 78. Example: usr object usr extends optional.Application { def main(name: Option[String], limit: Option[Int], uid: Option[String], login: Option[String], debug: Option[Boolean]) { if (Seq(name, uid, login).flatten.isEmpty) { println("You must specify at least one search criteria") System.exit(1) } val effDebug = debug.getOrElse(false) val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) } val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) } val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) } futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug)) futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug)) futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug)) val res = Await.ready( Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf) } } One of them must be specified
  • 79. Example: usr object usr extends optional.Application { def main(name: Option[String], limit: Option[Int], uid: Option[String], login: Option[String], debug: Option[Boolean]) { if (Seq(name, uid, login).flatten.isEmpty) { println("You must specify at least one search criteria") System.exit(1) } val effDebug = debug.getOrElse(false) val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) } val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) } val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) } futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug)) futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug)) futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug)) val res = Await.ready( Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf) } } No debug by default
  • 80. Example: usr object usr extends optional.Application { def main(name: Option[String], limit: Option[Int], uid: Option[String], login: Option[String], debug: Option[Boolean]) { if (Seq(name, uid, login).flatten.isEmpty) { println("You must specify at least one search criteria") System.exit(1) } val effDebug = debug.getOrElse(false) val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) } val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) } val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) } futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug)) futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug)) futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug)) val res = Await.ready( Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf) } } Query the three sources in parallel
  • 81. Example: usr object usr extends optional.Application { def main(name: Option[String], limit: Option[Int], uid: Option[String], login: Option[String], debug: Option[Boolean]) { if (Seq(name, uid, login).flatten.isEmpty) { println("You must specify at least one search criteria") System.exit(1) } val effDebug = debug.getOrElse(false) val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) } val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) } val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) } futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug)) futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug)) futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug)) val res = Await.ready( Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf) } } After completion, print the result as a side effect
  • 82. Example: usr object usr extends optional.Application { def main(name: Option[String], limit: Option[Int], uid: Option[String], login: Option[String], debug: Option[Boolean]) { if (Seq(name, uid, login).flatten.isEmpty) { println("You must specify at least one search criteria") System.exit(1) } val effDebug = debug.getOrElse(false) val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) } val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) } val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) } futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug)) futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug)) futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug)) val res = Await.ready( Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf) } } Wait until the three queries finish
  • 83. Example: usr object usr extends optional.Application { def main(name: Option[String], limit: Option[Int], uid: Option[String], login: Option[String], debug: Option[Boolean]) { if (Seq(name, uid, login).flatten.isEmpty) { println("You must specify at least one search criteria") System.exit(1) } val effDebug = debug.getOrElse(false) val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) } val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) } val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) } futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug)) futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug)) futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug)) val res = Await.ready( Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf) } } Has a bug!
  • 84. Example: completing futures futRes.onComplete(printResult) def onComplete[U](f: Try[T] => U): Unit Try[T] = Success(t: T) | Failure(e: Throwable)
  • 85. futRes.onComplete(printResult) def onComplete[U](f: Try[T] => U): Unit Example: completing futures Either[Throwable, T]~ ~ Try[T] = Success(t: T) | Failure(e: Throwable)
  • 86. futRes.onComplete(printResult) def onComplete[U](f: Try[T] => U): Unit When this Future finishes, either correctly (Success) or with an exception (Failure), apply the function f which knows how to handle both cases to compute some side effect Example: completing futures Either[Throwable, T] Try[T] = Success(t: T) | Failure(e: Throwable) ~ ~
  • 87. futRes.onComplete(printResult) def onComplete[U](f: Try[T] => U): Unit In this case the side effect is just printing the result to the console. Example: completing futures Either[Throwable, T]~ ~ Try[T] = Success(t: T) | Failure(e: Throwable)
  • 88. Future.sequence( Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)) def sequence[A, M[_]]( in: M[Future[A]]): Future[M[A]] Example: sequencing futures
  • 89. Future.sequence( Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)) def sequence[A, M[_]]( in: M[Future[A]]): Future[M[A]] Convert some container M of Future[A] in a Future M[A]. Example: sequencing futures
  • 90. Future.sequence( Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)) def sequence[A, M[_]]( in: M[Future[A]]): Future[M[A]] Convert some container M of Future[A] in a Future M[A]. Example: sequencing futures M[X] <: TraversableOnce[X]
  • 91. Future.sequence( Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)) def sequence[A, M[_]]( in: M[Future[A]]): Future[M[A]] Convert some container M of Future[A] in a Future M[A]. Useful to reduce many Futures into a single Future. Example: sequencing futures
  • 92. Future.sequence( Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)) def sequence[A, M[_]]( in: M[Future[A]]): Future[M[A]] Convert some container M of Future[A] in a Future M[A]. Useful to reduce many Futures into a single Future. In this case we reduce three independent future results to a single future result with a Seq of the individual results. Example: sequencing futures
  • 93. implicit class TryHarder[T](val t: Try[T]) extends AnyVal { def toEither: Either[Throwable, T] = t match { case Success(s) => Right(s) case Failure(f) => Left(f) } def fold[U](success: T => U, failure: Throwable => U): U = toEither.fold(failure, success) } Example: folding on Try
  • 94. implicit class TryHarder[T](val t: Try[T]) extends AnyVal { def toEither: Either[Throwable, T] = t match { case Success(s) => Right(s) case Failure(f) => Left(f) } def fold[U](success: T => U, failure: Throwable => U): U = toEither.fold(failure, success) } Example: folding on Try Try is pretty much a specialized Either
  • 95. implicit class TryHarder[T](val t: Try[T]) extends AnyVal { def toEither: Either[Throwable, T] = t match { case Success(s) => Right(s) case Failure(f) => Left(f) } def fold[U](success: T => U, failure: Throwable => U): U = toEither.fold(failure, success) } Example: folding on Try Try is pretty much a specialized Either
  • 96. Example: fixing the bug object usr extends optional.Application { def main(name: Option[String], limit: Option[Int], uid: Option[String], login: Option[String], debug: Option[Boolean]) { if (Seq(name, uid, login).flatten.isEmpty) { println("You must specify at least one search criteria") System.exit(1) } val effDebug = debug.getOrElse(false) val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) } val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) } val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) } futUsrLDAPA.onComplete(printResult("--- LDAP A ---", debug = effDebug)) futUsrLDAPB.onComplete(printResult("--- LDAP B ---", debug = effDebug)) futUsrDB.onComplete(printResult("--- Data Base ---", debug = effDebug)) val res = Await.ready( Future.sequence(Seq(futUsrLDAPA, futUsrLDAPB, futUsrDB)), Duration.Inf) } } The problem is that onComplete executes after the Future is completed. Await only awaits to the completion, not the execution of onComplete and the program can terminate “too soon”.
  • 97. implicit class FutureOps[T](val f: Future[T]) extends AnyVal { def continue[S]( cont: Try[T] => S): Future[S] = { val p = Promise[S]() f.onComplete { tt => p.complete(Try(cont(tt))) } p.future } } Example: fixing the bug
  • 98. implicit class FutureOps[T](val f: Future[T]) extends AnyVal { def continue[S]( cont: Try[T] => S): Future[S] = { val p = Promise[S]() f.onComplete { tt => p.complete(Try(cont(tt))) } p.future } } Example: fixing the bug Like onComplete but returning another Future
  • 99. Example: now works :) object usr extends optional.Application { def main(name: Option[String], limit: Option[Int], uid: Option[String], login: Option[String], debug: Option[Boolean]) { if (Seq(name, uid, login).flatten.isEmpty) { println("You must specify at least one search criteria") System.exit(1) } val effDebug = debug.getOrElse(false) val futUsrLDAPA = Future { ldapA.findUsr(name, limit, uid, login) } val futUsrLDAPB = Future { ldapB.findUsr(name, limit, uid, login) } val futUsrDB = Future { dbase.findUsr(name, limit, uid, login) } val endLDAPA = futUsrLDAPA.continue(printResult("--- LDAP A ---")) val endLDAPB = futUsrLDAPB.continue(printResult("--- LDAP B ---")) val endDB = futUsrDB.continue(printResult("--- Data Base ---")) val res = Await.ready( Future.sequence(Seq(endLDAPA, endLDAPB, endDB)), Duration.Inf) } }