Jean-Rémi Desjardins
Scala By The Bay 2015
an alternative to for-comprehensions
def getPhone: Future[Phone]
def getAddress: Future[Address]
def getSpamScore: Future[SpamScore]
for {p <- getPhone
a <- getAddress
s <- getSpamScore} yield render(p, a, s)
phone: Future[Phone]
address: Future[Address]
spamScore: Future[SpamScore]
for {p <- phone
a <- address
s <- spamScore} yield render(p, a, s)
phone: Future[Phone] // 1 second
address: Future[Address] // fail after 10 milliseconds
spamScore: Future[SpamScore] // 2 seconds
for {p <- phone
a <- address
s <- spamScore} yield render(p, a, s)
phone: Future[Phone] // 1 second
address: Future[Address] // fail after 10 milliseconds
spamScore: Future[SpamScore] // 2 seconds
Applicative[Future].apply3(a,b,c)(combine)
See my talk at PNWScala
trait Monad[F[_]] {
def bind[A,B](fa: F[A])(f : a => F[B]): F[B]
}
Expressions are an alternative to for-comprehensions that support failing fast among a few other things
val a: Future[A]
val b: Future[B]
val c: Future[C]
for {aa <- a
bb <- b
cc <- c} yield combine(a, b, c)
val a: Future[A]
val b: Future[B]
val c: Future[C]
for {aa <- a
bb <- b
cc <- c} yield combine(a, b, c)
Expression { combine(a,b,c) }
val a: Future[A]
val b: Future[B]
val c: Future[C]
val a: Future[A]
val b: Future[B]
val c: Future[C]
for {aa <- a
bb <- b
cc <- c} yield if (aa == something) polish(bb) else polish(cc)
val a: Future[A]
val b: Future[B]
val c: Future[C]
for {aa <- a
bb <- b
cc <- c} yield if (aa == something) polish(bb) else polish(cc)
(for (aa <- a) yield
if (aa == something) for (bb <- b) yield polish(bb)
else for (cc <- c) yield polish(cc)).flatMap(identity)
val a: Future[A]
val b: Future[B]
val c: Future[C]
for {aa <- a
bb <- b
cc <- c} yield if (aa == something) polish(bb) else polish(cc)
(for (aa <- a) yield
if (aa == something) for (bb <- b) yield polish(bb)
else for (cc <- c) yield polish(cc)).flatMap(identity)
val a: Future[A]
val b: Future[B]
val c: Future[C]
Expression { if(extract(a) == something) polish(b) else polish(c) }
def time[A](task: Task[A]): Task[(Duration, A)] = for {
t1 <- Task.delay(System.currentTimeMillis)
a <- task
t2 <- Task.delay(System.currentTimeMillis)
} yield ((t2 - t1).milliseconds, a)
val response: Future[HTML] = Expression {
val phone = extract(lookupPhone(phoneString))
val address = extract(lookupAddress(phone))
val rep = extract(lookupReputation(phone))
renderPage(phone, address, rep)
}
That supports:
Monad
Applicative Functor
Functor
conceivably others
Wouldn't that be nice Professor? One single elegant equation to explain everything?
trait Monad[F[_]] {
def bind[A,B](m: F[T], f: A => F[B]): F[B]
}
val phoneString: String = ???
val lookupPhone: String => Future[Phone] = ???
val lookupAddress: Phone => Future[Address] = ???
val lookupReputation: Phone => Future[Score] = ???
val renderPage: (Phone, Address, Int) => HTML = ???
val response: Future[HTML] = Expr {
val phone = lookupPhone(phoneString)
val address = lookupAddress(phone)
val rep = lookupReputation(phone)
renderPage(phone, address, rep)
}
import com.github.jedeash.Expr
import com.github.jedesah.Expr.extract
val phoneString: String = ???
val lookupPhone: String => Future[Phone] = ???
val lookupAddress: Phone => Future[Address] = ???
val lookupReputation: Phone => Future[Score] = ???
val renderPage: (Phone, Address, Int) => HTML = ???
val response: Future[HTML] = Expr {
val phone = extract(lookupPhone(phoneString))
val address = lookupAddress(phone)
val rep = lookupReputation(phone)
renderPage(phone, extract(address), extract(rep))
}
Explicit
Implicit
import com.github.jedeash.Expr
import com.github.jedesah.Expr.auto.extract
val phoneString: String = ???
val lookupPhone: String => Future[Phone] = ???
val lookupAddress: Phone => Future[Address] = ???
val lookupReputation: Phone => Future[Score] = ???
val renderPage: (Phone, Address, Int) => HTML = ???
val response: Future[HTML] = Expr {
val phone = lookupPhone(phoneString)
val address = lookupAddress(phone)
val rep = lookupReputation(phone)
renderPage(phone, address, rep)
}
Effectful*
Replacement for for-comprehension
Generalizes Async/Await
Only supports Monad
Scala Workflow*
Supports everything I do and more
Uses untyped macros
Reimplements parts of scalac including scoping which leads to minimal coverage of the language
*https://github.com/pelotom/effectful
*https://github.com/aztek/scala-workflow/