Open Source toolkit for building Concurrent, Distributed, Resilient Message-Driven applications on the JVM
Agenda:
What does this evaluate to in Java?
if (x > 0) {
true;
} else {
false;
}
boolean positive;
if (x > 0) {
positive = true;
} else {
positive = false;
}
boolean shouldAct;
if (x > 0) {
shouldAct = true;
}
What does this evaluate to in Scala?
if (x > 0) {
true
} else {
false
}
What does this evaluate to in Scala?
val positive = if (x > 0) {
true
} else {
false
}
val positive = if (x > 0) true else false
Statements have side effects
Expressions evaluate to values
val x: Int = ???
val y = x * x
val x = 4
val x = { println("haha"); 4}
type WebRequest = HttpRequest => HttpResponse
case class Person(name: String)
case class Customer(customerNumber: Int, p: Person)
trait Serialiser {
def serialise(c: Customer): HttpResponse
def deserialie(input: HttpRequest): Person
}
class WebController(
service: ApplicationService,
serialiser: Serialiser) {
def register(p: HttpRequest): HttpResponse = {
val person = serialiser.deserialie(p)
val c = service.registerCustomer(person)
serialiser.serialise(c)
}
}
class ApplicationService(dataStore: CustomerRepo) {
def registerCustomer(p: Person): Customer = {
// validate them?
// Generate a unique customer number?
val c = Customer(1, p)
dataStore.saveCustomer(c)
c
}
}
trait CustomerRepo {
def saveCustomer(p: Customer): Unit
}
public static int add(int x, int y) {
return x + y;
}
public static int add10(int x) {
return add(x, 10);
}
public static int multiply(int x, int y) {
return x * y;
}
public static int multiplyBy10(int x) {
return multiply(x, 10);
}
public static int addTenThenMultiplyByTen(int x) {
return multiplyBy10(add10(x));
}
def add(x: Int, y: Int) = x + y
def add10(x: Int) = add(x, 10)
def addCurried(x: Int)(y: Int): Int = x + y
val whatTypeAmI = addCurried(10) _
val whatTypeAmI: Int => Int = addCurried(10)
val add10Curried = (add _).curried(10)
def add(x: Int)(y: Int): Int = x + y
def multiply(x: Int)(y: Int): Int = x * y
val add10multiply10Compose: Int => Int =
multiply(10) _ compose add(10)
val add10multiply10 = add(10) _ andThen multiply(10)
f: String -> Int
g: Int -> Banana
g
compose f
f
andThen g
String -> Banana
type WebRequest = HttpRequest => HttpResponse
case class Person(name: String)
case class Customer(customerNumber: Int, p: Person)
trait Serialiser {
def serialise(c: Customer): HttpResponse
def deserialie(input: HttpRequest): Person
}
class WebController(
service: ApplicationService,
serialiser: Serialiser) {
def register(p: HttpRequest): HttpResponse = {
val person = serialiser.deserialie(p)
val c = service.registerCustomer(person)
serialiser.serialise(c)
}
}
class ApplicationService(dataStore: CustomerRepo) {
def registerCustomer(p: Person): Customer = {
// validate them?
// Generate a unique customer number?
val c = Customer(1, p)
dataStore.saveCustomer(c)
c
}
}
trait CustomerRepo {
def saveCustomer(p: Customer): Unit
}
type WebRequest = HttpRequest => HttpResponse
val deSerialisePerson: HttpRequest => Person = ???
val createCustomer: Person => Customer = ???
val saveCustomer: Customer => Customer = ???
val serialiseCustomer: Customer => HttpResponse = ???
val registerCustomer: WebRequest =
deSerialisePerson andThen
createCustomer andThen
saveCustomer andThen
serialiseCustomer
type WebRequest = HttpRequest => HttpResponse
val databaseConnection = new DatabaseConnection
object DataAccess {
def saveCustomer(databaseConnection: DatabaseConnection)
(c: Customer): Customer = ???
}
val saveCustomer: Customer => Customer =
DataAccess.saveCustomer(dbConnection)
val registerCustomer: WebRequest =
deSerialisePerson andThen
createCustomer andThen
saveCustomer andThen
serialiseCustomer
type WebRequest = HttpRequest => HttpResponse
class DataAccessClass(databaseConnection: DatabaseConnection) {
def saveCustomer(c: Customer): Customer = ???
}
val dac = new DataAccessClass(dbConnection)
val saveCustomer: Customer => Customer = dac.saveCustomer
object DatabaseAccessImplicit {
def saveCustomer(c: Customer)
(implicit db: DatabaseConnection): Customer = ???
}
val saveCustomer: Customer => Customer =
DatabaseAccessImplicit.saveCustomer
implicit val dbConnection = new DatabaseConnection
String
int
Person
Customer
vs
List<T>
Iterable<T>
List<T> :: T -> List<T>
String -> List<String>
List is a type level function takes a type and produces a concrete type
Map<K, V>
Map takes two types and produces a concrete type
Abstract over a type:
List<A>
Abstract over a type constructor:
T<Int>
Why not both?
T<A>
type WebRequest = HttpRequest => Future[HttpResponse]
type WebRequest2 = HttpRequest => Either[Error, HttpResponse]
type WebRequest = HttpRequest => Future[HttpResponse]
def serialiseCustomer: Customer => HttpResponse = ???
def deSerialisePerson: HttpRequest => Person = ???
def createCustomer: Person => Future[Customer] = ???
def saveCustomer: Customer => Future[Customer] = ???
val fstStep: HttpRequest => Future[Customer] =
deSerialisePerson andThen
createCustomer
def flatMap(f: A => Future[B]): Future[B]
def map(f: A => B): Future[B]
val customerSaved: HttpRequest => Future[Customer] = hr =>
fstStep(hr).flatMap(saveCustomer)
val fullRequest: HttpRequest => Future[HttpResponse] = hr =>
customerSaved(hr).map(serialiseCustomer)
f: A => Future[B]
g: B => Future[C]
g superCompose f : A => Future[C]
http://eed3si9n.com/herding-cats/composing-monadic-functions.html
abstract class Abstracted[F[_] : Mappable] {
def serialiseCustomer: Customer => HttpResponse = ???
def deSerialisePerson: HttpRequest => Person = ???
def createCustomer: Person => F[Customer] = ???
def saveCustomer: Customer => F[Customer] = ???
}
trait Mappable[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
}
val first: HttpRequest => F[Customer] =
deSerialisePerson andThen
createCustomer
val fullRequest: HttpRequest => F[HttpResponse] = hr =>
first(hr)
.flatMap(saveCustomer)
.map(serialiseCustomer)
val withAFuture = new Abstracted[Future] {}
val whatTypeIsThis: HttpRequest => Future[HttpResponse] =
withAFuture.fullRequest
Many sensible uses of implicits replace reflection Reflection is more implicit than implicits!!
case class Cat(name: String)
case class Dog(name: String, status: String)
def cleanDog(dog: Dog): Dog =
dog.copy(status = "clean with hose pipe")
val ruby = Cat("Ruby")
case class Cat(name: String)
case class Dog(name: String, status: String)
def cleanDog(dog: Dog): Dog =
dog.copy(status = "clean with hose pipe")
val ruby = Cat("Ruby")
cleanDog(ruby)
implicit def catToDog(cat: Cat): Dog = Dog(cat.name, "clean")
trait Mappable[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
}
implicit def futureMappable = new Mappable[Future] {
def map[A, B](fa: Future[A])(f: A => B): Future[B] =
fa.map(f)
def flatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] =
fa.flatMap(f)
}
implicit def completableFutureMappable= new Mappable[CompletableFuture] {
def map[A, B](fa: CompletableFuture[A])(f: A => B): CompletableFuture[B] =
fa.thenApply(a => f(a))
def flatMap[A, B](fa: CompletableFuture[A])(f: A => CompletableFuture[B]): CompletableFuture[B] =
fa.thenCompose(a => f(a))
}
Guidelines for a successful Scala project
trait Request[Req, Resp] {
def invoke(in: Req): Future[Resp]
}
val liftedCall: Request[String, Int] =
RequestBuilder[String, Int]()
.withTimeout(10.seconds)
.withCircuitBreaker()
.build()
liftedCall.invoke("go go gadget go")
val noParamRequest = new Request[Unit, String] {
def invoke(in: Unit): Future[String] = ???
}
trait NoParamRequest[Resp] {
def invoke(): Future[Resp]
}
trait Request[Req, Resp] {
def invoke(in: Req): Future[Resp]
def invoke()(implicit ev: Req =:= Unit): Future[Resp]
}
liftedCall.invoke()
Cannot prove that String =:= Unit. liftedCall.invoke()
Scala