SlideShare a Scribd company logo
1 of 35
Download to read offline
SCALA3 & ASYNC:


[BEHIND FUTURES]
RUSLAN SHEVCHENKO <RUSLAN@SHEVCHENKO.KIEV.UA>


@RSSH1
https://github.com/rssh/dotty-cps-async
[Q]. Need 101 ?
Can be functional concurrent programming be liberated


from the monadic style ?


// ScalaUA-2020, ScalaR-2020. (Year ago)


- what’s after a year.

- what’s interesting behind async/await
Remind-01. : Monadic Effect
E
ff
ect: [Something, which change the behaviour of program in non-functional way]

Exceptions

Context switching.

IO

 Monadic E
ff
ect

Behaviour injected in monad.

M(f), when we throw exception, we call method in M which suspect f and return exception

You can look at e
ff
ect monads as an interpreter layer.
Reminder: 02: reactivity
def doBoringStaff(request: Request): Response =


val query = buildQuery(request)


val result = db.select(query)


resut.toResponse
App Server
Web DB
Thread blocked
Reminder: 02: reactivity
def doBoringStaff(request: Request): Response =


val query = buildQuery(request)


val result = db.select(query)


resut.toResponse
App Server
Web DB
Thread blocked
Web
DB
Oops, all thread blocked
Request will wait.
Reminder: 02: reactivity
def doBoringStaff(request: Request): Response =


val query = buildQuery(request)


val result = db.select(query)


resut.toResponse
Web
DB
Oops, all thread blocked
Request will wait.
WTF
- operation system should care about this ?
- context switching between kernel/user mode is slow
- operation system research is died slow
- language runtime should care about this ?
Reminder: 02: reactivity
def doBoringStaff(request: Request): Response =


val query = buildQuery(request)


val result = db.select(query)


resut.toResponse
WTF
- language runtime should care about this ?
- JVM interpreter is stack based, each switch to other thread


means changing/ copying stack.
- Project Loom: https://openjdk.java.net/projects/loom/
- Second attempt to do this on JVM


- Virtual threads [Java Entity] with same API as native Thread


- Work in progress ….
Reminder: 02: reactivity
def doBoringStaff(request: Request): Response =


val query = buildQuery(request)


val result = db.select(query)


resut.toResponse
def doBoringStaff(request: Request): F[Response] =


query = buildQuery(request)


for{


result <- db.select(query)


} yield resut.toResponse
Effect Monad
Reminder: 03: Effect monads: for comprehension
def doBoringStaff(request: Request): F[Response] =


query = buildQuery(request)


for{


result <- db.select(query)


} yield resut.toResponse
Effect Monad
All monads are equal but some are more equal than others
Future
Cats-Effect IO
ZIO
Abstract F[_]
Monix Task Roll you own …
Haskell IO
ScalaZ IO
def doBoringStaff(request: Request): F[Response] =


for{


optUser <- usersStorage.findUser(request.userId)


user <- F.fromOption(optUser)


_ <- monitoredAccess(user).ifM(logAccess(user), IO.void)


query = buildQuery(user, request)


result1 <- db1.select(query)


result2 <- db2.select(query)


result = merge(result1,result2)


} yield resut.toResponse


Effect monads: for comprehension: real world
def doBoringStaff(request: Request): F[Response] =


for{


optUser <- usersStorage.findUser(request.userId)


user <- F.fromOption(optUser)


_ <- monitoredAccess(user).ifM(logAccess(user), IO.void)


query = buildQuery(user, request)


result1 <- db1.select(query)


result2 <- db2.select(query)


result = merge(result1,result2)


} yield resut.toResponse


Effect monads: for comprehension: real world
Sequential execution
Effect monads: for comprehension: real world
def doBoringStaff(request: Request): F[Response] =


for{


optUser <- usersStorage.findUser(request.userId)


user <- F.fromOption(optUser)


_ <- monitoredAccess(user).ifM(logAccess(user), IO.void)


query = buildQuery(user, request)


(result1, result2) <- db1.select(query)|+|db2.select(query)


result = merge(result1,result2)


} yield result.toResponse


DSL instead control flow.
What to do if we need ‘real for’ ?
Extra layer of complexity.
Effect monads: async/await: real world
def doBoringStaff(request: Request):F[Response] = async[F] {


user = await(userStorage.findUser(request.userId).getOrElse(


throw new IllegalArgumentException("user not found")))


if (await(monitoredAccess(user)))


await(logAccess(user))


val query = buildQuery(user, request)


val result1 = db1.select(query)


val result2 = db2.select(query)


merge( await(result1), await(result2) ).toResponse


}
Better, but:
Manual colouring:
Too many awaits:
Effect monads: async/await: colouring
def doBoringStaff(request: Request): F[Response] = async[F] {


user = await(userStorage.findUser(request.userId)).getOrElse(


throw new IllegalArgumentException("user not found"))


if (await(monitoredAccess(user)))


await(logAccess(user))


val query = buildQuery(user, request)


val result1 = db1.select(query)


val result2 = db2.select(query)


merge( await(result1), await(result2) ).toResponse


}
Better, but:
Manual colouring:
Too many awaits:
Effect monads: async/await: automatic colouring
import cps.imlicitAwait


def doBoringStaff(request: Request): F[Response] = async[F] {


user = userStorage.findUser(request.userId).getOrElse(


throw new IllegalArgumentException("user not found"))


if (monitoredAccess(user))


await(logAccess(user))


val query = buildQuery(user, equest)


val result1 = db1.select(query)


val result2 = db2.select(query)


merge( result1, result2 ).toResponse


}


[currently work only for Future, support for IO is in progress]
Reminder: 02: reactivity
def doBoringStaff(request: Request): Response =


val query = buildQuery(request)


val result = db.select(query)


resut.toResponse
def doBoringStaff(request: Request): F[Response] = async[F] {


val query = buildQuery(request)


val result = db.select(query)


resut.toResponse


}
How to do this [?]
https://github.com/rssh/dotty-cps-async

Optimised monadic CPS [Continuation Passing Style] transform.

Challenges:

Size of the language.

Monads interoperability.

High-order functions.

Automatic colouring.
Optimised monadic CPS transform
def doBoringStaff(request: Request): F[Response] = async[F] {


val query = buildQuery(request)


val result = db.select(query)


resut.toResponse


}
def doBoringStaff(request: Request): F[Response] = {


val m = summon[CpsMonad[F]]


val query = buildQuery(request)


m.map(db.select(query)){ x =>


val result = x


resut.toResponse


}


}
// implicitly[CpsMonad[F]]. In scala2
Effect monads: async/await: automatic colouring
import cps.imlicitAwait


def doBoringStaff(request: Request): F[Response] = async[F] {


user = userStorage.findUser(request.userId).getOrElse(


throw new IllegalArgumentException("user not found"))


if (monitoredAccess(user))


await(logAccess(user))


val query = buildQuery(user, equest)


val result1 = db1.select(query)


val result2 = db2.select(query)


merge( result1, result2 ).toResponse


}
Effect monads: async/await: macro output
def doBoringStaff(request: Request): IO[Response] = {


m = summon[CpsMonad[F]]


m.flatMap(userStorage.findUser(request.userId)){ x =>


m.flatMap(summon[AsyncShift[Option[User]]].getOrElse(m,u)(


() => m.error(new IllegalArgumentException("user not found")))


){ x =>


val user = x


m.flatMap(monitoredAccess(user)){ x =>


m.flatMap(logAccess(user)){ x =>


val query = buildQuery(user, equest)


val result1 = db1.select(query)


val result2 = db2.select(query)


m.flatMap(result1){ a1 =>


m.flatMap(result2){ a2 =>


merge( a1, a2 )


} } } } } } }


// In principle, near the same code as written by hand with for-comprehension
What we need from monad:
Control flow: trait CpsMonad[F[_]]:


def pure[A](x:A):F[A]


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]


Try/catch: trait CpsTryMonad[F[_]] extends CpsMonad[F]:


def error[A](e: Throwable): F[A]


def flatMapTry[A,B](fa: [A])(f: Try[A]=>B)
async[M] {


await[Future](…)


}
trait CpsAsyncMonad[F[_]] extends CpsTryMonad[F]:


def adoptCallbackStyle[A](source: (Try[A]=>Unit) => Unit): F[A]


async[Future] {


await[M](…)


}
trait CpsSchedulingMonad[F[_]] extends CpsAsyncMonad[F]:


def spawn[A](op: F[A]): F[A]
Automatic colouring: trait CpsMemoizingMonad[F[_]] extends CpsAsyncMonad[F]:


transient inline def memoize[A](v: F[A])
Monads interoperability
def doPOST(uri: String, value: ujson.Value): Future[ujson.Value] = async[Future] {


val request = HttpRequest.newBuilder()


.uri(new URI(uri))


.header("Content-Type","application/json")


.POST(HttpRequest.BodyPublishers.ofString(write(value)))


.build()


val response = await(client.sendAsync(request,HttpResponse.BodyHandlers.ofString()))


parseResponse(response)


}
java.util.concurrent.CompletableFuture[HttpResponse[String]]
Monads interoperability
def doPOST(uri: String, value: ujson.Value): Future[ujson.Value] = async[Future] {


val request = HttpRequest.newBuilder()


.uri(new URI(uri))


.header("Content-Type","application/json")


.POST(HttpRequest.BodyPublishers.ofString(write(value)))


.build()


val response = await(client.sendAsync(request,HttpResponse.BodyHandlers.ofString()))


parseResponse(response)


}
java.util.concurrent.CompletableFuture[HttpResponse[String]]
trait CpsMonadConversion[F[_],G[_]]:


def apply[T](mf:CpsMonad[F],mg:CpsMonad[G],ft:F[T]):G[T]
given toFutureConversion[F[_]](using ExecutionContext, CpsSchedulingMonad[F]): CpsMonadConversion[F,Future] =


new CpsMonadConversion[F, Future] {


override def apply[T](mf: CpsMonad[F], mg: CpsMonad[Future], ft:F[T]): Future[T] =


val p = Promise[T]()


val u = summon[CpsSchedulingMonad[F]].mapTry(ft){


case Success(v) => p.success(v)


case Failure(ex) => p.failure(ex)


}


summon[CpsSchedulingMonad[F]].spawn(u)


p.future


}
Monads interoperability
def doPOST(uri: String, value: ujson.Value): Future[ujson.Value] = async[Future] {


val request = HttpRequest.newBuilder()


.uri(new URI(uri))


.header("Content-Type","application/json")


.POST(HttpRequest.BodyPublishers.ofString(write(value)))


.build()


val response = await(client.sendAsync(request,HttpResponse.BodyHandlers.ofString()))


parseResponse(response)


}
java.util.concurrent.CompletableFuture[HttpResponse[String]]
given toFutureConversion[F[_]](using ExecutionContext, CpsSchedulingMonad[F]): CpsMonadConversion[F,Future] =


new CpsMonadConversion[F, Future] {


override def apply[T](mf: CpsMonad[F], mg: CpsMonad[Future], ft:F[T]): Future[T] =


val p = Promise[T]()


val u = summon[CpsSchedulingMonad[F]].mapTry(ft){


case Success(v) => p.success(v)


case Failure(ex) => p.failure(ex)


}


summon[CpsSchedulingMonad[F]].spawn(u)


p.future


}
High-order functions [1]
1-st approach: 







By-Name parameters as functions from 0 arguments: 





Local substitutions

Private data
Iterable[A] . map[B](f : A ⇒ B) : Iterable[B]
AsyncShift[Iterable[A]] . map[F[_], B](m : CpsMonad[F], c : Iterable[A])
(f : A = > F[B]) : F[Iterable[B]]
Option[T] . getOrElse(x := > T)
AsyncShift[Option[T]] . getOrElse[F[_])(m : CpsMonad[F], c : Option[T])(x : () = > F[T])
High-order functions [Local Substitutions]
async[F]{


for{ log <- logs if (await(isMalicious(log.url))


} yield (await(report(log)))


}
async[F] {


logs.withFilter( log => await(isMalicious(log.url)) )


.map(log => await(report(log)))


}
=
We want navigate via list of logs once.


With previous approach we will get filtered list of urls, and then iterate on it.
High-order functions [Local Substitutions]
Solution: create local WithFilter, which will run both filter and map in map.
class DelayedWithFilter[F[_], A, C[_], CA <: C[A]](c: CA,


m: CpsMonad[F],


p:A=>F[Boolean],


) extends CallChainAsyncShiftSubst[F, WithFilter[A,C], F[WithFilter[A,C]] ]:


// return eager copy


def _origin: F[WithFilter[A,C]] = ...


def map[B](f: A => B): F[C[B]] = ...


def map_async[B](f: A => F[B]): F[C[B]] = ...


def flatMap[B](f: A => IterableOnce[B]): F[C[B]] = ...


def flatMap_async[B](f: A => F[IterableOnce[B]]): F[C[B]] = ...


def withFilter(q: A=>Boolean) =


DelayedWithFilter(c, m, x => m.map(p(x))(r => r && q(x)) )


def withFilter_async(q: A=> F[Boolean]) = ...


...
High-order functions [Local Substitutions]: Categorical interpreation.
Arrows:
Functors:
Left Kan Extension …
High-order functions: private data
class MyIntController:


def modify(f: Int => Int): Int = …


def modify_async[F[_]](m: CpsMonad[M])(f: Int => F[Int]): F[Int] = …
- add a <x>_async (or <x>Async) method to you class….
class Select[F[_]]:


def fold[S](s0:S)(step: S => S | SelectFold.Done[S]): S = …


def foldAsync[S](s0:S)(step: S => F[S | SelectFold.Done[S]]): F[S] = …


- F[_] can be in class. (Useful for DSL-s).
Effect monads: async/await: automatic colouring
import cps.imlicitAwait


def doBoringStaff(request: Request): F[Response] = async[F] {


user = userStorage.findUser(request.userId).getOrElse(


throw new IllegalArgumentException("user not found"))


if (monitoredAccess(user))


await(logAccess(user))


val query = buildQuery(user, equest)


val result1 = db1.select(query)


val result2 = db2.select(query)


merge( result1, result2 ).toResponse


}


- when F = Future[_]. - all ok.


- F = IO[_] — ambiguity
All monads are equal but some are more equal than others
Future:


represent already started computation.


cached by default.
Cats-effect IO, ZIO, scalaz IO:


represent computation, which are ‘not started’


not cached


referential transparency
val future = Future{ something }


val a = await(future)


val b = await(future)
- something will be evaluated once.


- the a and b will be the same object.
val io = IO.delay{ something }


val a = await(io)


val b = await(io)


- something will be evaluated twice.


- the a and b will not be the same object.
Automatic colouring: IO problems
def ctr(name:String) = async[IO] {


val counter = counters(name).getOrThrow()


val value = counter(name).increment()


if (value % LOG_MOD == 0) then


log(s"counter $name is $value")


if (value == THRESHOLD) then


log(s"counter $name exceeded treshold")


}


def ctr(name:String) = async[IO] {


val counter = counters(name).getOrThrow()


val value = counter(name).increment()


if (await(value) % LOG_MOD == 0) then


log(s"counter $name is ${await(value)}”)


if (await(value) == THRESHOLD) then


log(s"counter $name exceeded treshold")


}
def ctr(name:String) = async[IO] {


val counter = counters(name).getOrThrow()


val value = await(counter(name).increment())


if (value % LOG_MOD == 0) then


log(s"counter $name is $value")


if (value == THRESHOLD) then


log(s"counter $name exceeded treshold")


}
V1
V2
- this variant will be chosen by implicitAwait


- value will be increment 3 times.
Automatic colouring: IO problems
def ctr(name:String) = async[IO] {


val counter = counters(name).getOrThrow()


val value = Memoize[counter(name).increment()]


if (await(value) % LOG_MOD == 0) then


log(s"counter $name is ${await(value)}”)


if (await(value) == THRESHOLD) then


log(s"counter $name exceeded treshold")


}
- Memoize:


- Future[T]: nothing


- IO: IO[IO[T]]


- Monix Task: Task[T]
Solution: memoize values. If you store something in val, then you want to reuse it.
Automatic colouring: IO problems
def ctr(name:String) = async[IO] {


val counter = counters(name).getOrThrow()


val valueMem = Memoize[counter(name).increment()]


val value = await(valueMem)


if (await(value) % LOG_MOD == 0) then


log(s"counter $name is ${await(value)}”)


if (await(value) == THRESHOLD) then


log(s"counter $name exceeded treshold")


}
- Memoize:


- We break referential transparency;


- But it was already broken when we remove awaits…
Solution: memoize values. If you store something in val, then you want to reuse it.
// currently in development, will be in the next version
How to help
https://github.com/rssh/dotty-cps-async

Ecosystem:

cps-async-connect

scala-gopher

Start using in examples and pet-project:

Discover techniques and bugs.

Di
ff
erent types of monads (probabilistic programming, streams, etc … )

Provide feedback.

More Related Content

What's hot

Java8 stream
Java8 streamJava8 stream
Java8 stream
koji lin
 
Advance features of C++
Advance features of C++Advance features of C++
Advance features of C++
vidyamittal
 

What's hot (20)

Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)
 
From Function1#apply to Kleisli
From Function1#apply to KleisliFrom Function1#apply to Kleisli
From Function1#apply to Kleisli
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
 
Operator overloading2
Operator overloading2Operator overloading2
Operator overloading2
 
Use Applicative where applicable!
Use Applicative where applicable!Use Applicative where applicable!
Use Applicative where applicable!
 
2014 computer science_question_paper
2014 computer science_question_paper2014 computer science_question_paper
2014 computer science_question_paper
 
ECMAScript 6
ECMAScript 6ECMAScript 6
ECMAScript 6
 
C++ TUTORIAL 4
C++ TUTORIAL 4C++ TUTORIAL 4
C++ TUTORIAL 4
 
Lambda Expressions in C++
Lambda Expressions in C++Lambda Expressions in C++
Lambda Expressions in C++
 
ES6 - Next Generation Javascript
ES6 - Next Generation JavascriptES6 - Next Generation Javascript
ES6 - Next Generation Javascript
 
Pads lab manual final
Pads lab manual finalPads lab manual final
Pads lab manual final
 
OOP v3
OOP v3OOP v3
OOP v3
 
Java8 stream
Java8 streamJava8 stream
Java8 stream
 
Functional Programming in Javascript - IL Tech Talks week
Functional Programming in Javascript - IL Tech Talks weekFunctional Programming in Javascript - IL Tech Talks week
Functional Programming in Javascript - IL Tech Talks week
 
The Ring programming language version 1.9 book - Part 40 of 210
The Ring programming language version 1.9 book - Part 40 of 210The Ring programming language version 1.9 book - Part 40 of 210
The Ring programming language version 1.9 book - Part 40 of 210
 
EcmaScript 6
EcmaScript 6 EcmaScript 6
EcmaScript 6
 
C++ Programming - 1st Study
C++ Programming - 1st StudyC++ Programming - 1st Study
C++ Programming - 1st Study
 
C++ file
C++ fileC++ file
C++ file
 
Advance features of C++
Advance features of C++Advance features of C++
Advance features of C++
 
Recursion to iteration automation.
Recursion to iteration automation.Recursion to iteration automation.
Recursion to iteration automation.
 

Similar to Svitla talks 2021_03_25

フレームワークなしでWSGIプログラミング
フレームワークなしでWSGIプログラミングフレームワークなしでWSGIプログラミング
フレームワークなしでWSGIプログラミング
Atsushi Odagiri
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
Dmitry Buzdin
 
Flask patterns
Flask patternsFlask patterns
Flask patterns
it-people
 

Similar to Svitla talks 2021_03_25 (20)

ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
F# Presentation for SmartDevs, Hereford
F# Presentation for SmartDevs, HerefordF# Presentation for SmartDevs, Hereford
F# Presentation for SmartDevs, Hereford
 
Things about Functional JavaScript
Things about Functional JavaScriptThings about Functional JavaScript
Things about Functional JavaScript
 
ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015
 
Talk - Query monad
Talk - Query monad Talk - Query monad
Talk - Query monad
 
FRP: What does "declarative" mean
FRP: What does "declarative" meanFRP: What does "declarative" mean
FRP: What does "declarative" mean
 
The State of JavaScript (2015)
The State of JavaScript (2015)The State of JavaScript (2015)
The State of JavaScript (2015)
 
Functional Programming Patterns for the Pragmatic Programmer
Functional Programming Patterns for the Pragmatic ProgrammerFunctional Programming Patterns for the Pragmatic Programmer
Functional Programming Patterns for the Pragmatic Programmer
 
フレームワークなしでWSGIプログラミング
フレームワークなしでWSGIプログラミングフレームワークなしでWSGIプログラミング
フレームワークなしでWSGIプログラミング
 
Fun with Lambdas: C++14 Style (part 1)
Fun with Lambdas: C++14 Style (part 1)Fun with Lambdas: C++14 Style (part 1)
Fun with Lambdas: C++14 Style (part 1)
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
 
Rethink programming: a functional approach
Rethink programming: a functional approachRethink programming: a functional approach
Rethink programming: a functional approach
 
掀起 Swift 的面紗
掀起 Swift 的面紗掀起 Swift 的面紗
掀起 Swift 的面紗
 
Functional Programming in the Wild
Functional Programming in the WildFunctional Programming in the Wild
Functional Programming in the Wild
 
Functional programming
Functional programmingFunctional programming
Functional programming
 
Tools for Making Machine Learning more Reactive
Tools for Making Machine Learning more ReactiveTools for Making Machine Learning more Reactive
Tools for Making Machine Learning more Reactive
 
Funkcija, objekt, python
Funkcija, objekt, pythonFunkcija, objekt, python
Funkcija, objekt, python
 
Mercury: A Functional Review
Mercury: A Functional ReviewMercury: A Functional Review
Mercury: A Functional Review
 
Flask patterns
Flask patternsFlask patterns
Flask patterns
 
Kotlin Developer Starter in Android projects
Kotlin Developer Starter in Android projectsKotlin Developer Starter in Android projects
Kotlin Developer Starter in Android projects
 

More from Ruslan Shevchenko

Jslab rssh: JS as language platform
Jslab rssh:  JS as language platformJslab rssh:  JS as language platform
Jslab rssh: JS as language platform
Ruslan Shevchenko
 

More from Ruslan Shevchenko (20)

Akka / Lts behavior
Akka / Lts behaviorAkka / Lts behavior
Akka / Lts behavior
 
Papers We Love / Kyiv : PAXOS (and little about other consensuses )
Papers We Love / Kyiv :  PAXOS (and little about other consensuses )Papers We Love / Kyiv :  PAXOS (and little about other consensuses )
Papers We Love / Kyiv : PAXOS (and little about other consensuses )
 
Scala / Technology evolution
Scala  / Technology evolutionScala  / Technology evolution
Scala / Technology evolution
 
{co/contr} variance from LSP
{co/contr} variance  from LSP{co/contr} variance  from LSP
{co/contr} variance from LSP
 
N flavors of streaming
N flavors of streamingN flavors of streaming
N flavors of streaming
 
Scala-Gopher: CSP-style programming techniques with idiomatic Scala.
Scala-Gopher: CSP-style programming techniques with idiomatic Scala.Scala-Gopher: CSP-style programming techniques with idiomatic Scala.
Scala-Gopher: CSP-style programming techniques with idiomatic Scala.
 
SE 20016 - programming languages landscape.
SE 20016 - programming languages landscape.SE 20016 - programming languages landscape.
SE 20016 - programming languages landscape.
 
Few simple-type-tricks in scala
Few simple-type-tricks in scalaFew simple-type-tricks in scala
Few simple-type-tricks in scala
 
Why scala is not my ideal language and what I can do with this
Why scala is not my ideal language and what I can do with thisWhy scala is not my ideal language and what I can do with this
Why scala is not my ideal language and what I can do with this
 
Scala jargon cheatsheet
Scala jargon cheatsheetScala jargon cheatsheet
Scala jargon cheatsheet
 
Java & low latency applications
Java & low latency applicationsJava & low latency applications
Java & low latency applications
 
Csp scala wixmeetup2016
Csp scala wixmeetup2016Csp scala wixmeetup2016
Csp scala wixmeetup2016
 
IDLs
IDLsIDLs
IDLs
 
R ext world/ useR! Kiev
R ext world/ useR!  KievR ext world/ useR!  Kiev
R ext world/ useR! Kiev
 
Jslab rssh: JS as language platform
Jslab rssh:  JS as language platformJslab rssh:  JS as language platform
Jslab rssh: JS as language platform
 
Behind OOD: domain modelling in post-OO world.
Behind OOD:  domain modelling in post-OO world.Behind OOD:  domain modelling in post-OO world.
Behind OOD: domain modelling in post-OO world.
 
scala-gopher: async implementation of CSP for scala
scala-gopher:  async implementation of CSP  for  scalascala-gopher:  async implementation of CSP  for  scala
scala-gopher: async implementation of CSP for scala
 
Programming Languages: some news for the last N years
Programming Languages: some news for the last N yearsProgramming Languages: some news for the last N years
Programming Languages: some news for the last N years
 
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
JDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation streamJDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation stream
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
 
Ruslan.shevchenko: most functional-day-kiev 2014
Ruslan.shevchenko: most functional-day-kiev 2014Ruslan.shevchenko: most functional-day-kiev 2014
Ruslan.shevchenko: most functional-day-kiev 2014
 

Recently uploaded

CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
giselly40
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 

Recently uploaded (20)

The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 

Svitla talks 2021_03_25

  • 1. SCALA3 & ASYNC: [BEHIND FUTURES] RUSLAN SHEVCHENKO <RUSLAN@SHEVCHENKO.KIEV.UA> @RSSH1 https://github.com/rssh/dotty-cps-async
  • 2. [Q]. Need 101 ? Can be functional concurrent programming be liberated from the monadic style ? // ScalaUA-2020, ScalaR-2020. (Year ago) - what’s after a year. - what’s interesting behind async/await
  • 3. Remind-01. : Monadic Effect E ff ect: [Something, which change the behaviour of program in non-functional way] Exceptions Context switching. IO  Monadic E ff ect Behaviour injected in monad. M(f), when we throw exception, we call method in M which suspect f and return exception You can look at e ff ect monads as an interpreter layer.
  • 4. Reminder: 02: reactivity def doBoringStaff(request: Request): Response = val query = buildQuery(request) val result = db.select(query) resut.toResponse App Server Web DB Thread blocked
  • 5. Reminder: 02: reactivity def doBoringStaff(request: Request): Response = val query = buildQuery(request) val result = db.select(query) resut.toResponse App Server Web DB Thread blocked Web DB Oops, all thread blocked Request will wait.
  • 6. Reminder: 02: reactivity def doBoringStaff(request: Request): Response = val query = buildQuery(request) val result = db.select(query) resut.toResponse Web DB Oops, all thread blocked Request will wait. WTF - operation system should care about this ? - context switching between kernel/user mode is slow - operation system research is died slow - language runtime should care about this ?
  • 7. Reminder: 02: reactivity def doBoringStaff(request: Request): Response = val query = buildQuery(request) val result = db.select(query) resut.toResponse WTF - language runtime should care about this ? - JVM interpreter is stack based, each switch to other thread means changing/ copying stack. - Project Loom: https://openjdk.java.net/projects/loom/ - Second attempt to do this on JVM - Virtual threads [Java Entity] with same API as native Thread - Work in progress ….
  • 8. Reminder: 02: reactivity def doBoringStaff(request: Request): Response = val query = buildQuery(request) val result = db.select(query) resut.toResponse def doBoringStaff(request: Request): F[Response] = query = buildQuery(request) for{ result <- db.select(query) } yield resut.toResponse Effect Monad
  • 9. Reminder: 03: Effect monads: for comprehension def doBoringStaff(request: Request): F[Response] = query = buildQuery(request) for{ result <- db.select(query) } yield resut.toResponse Effect Monad All monads are equal but some are more equal than others Future Cats-Effect IO ZIO Abstract F[_] Monix Task Roll you own … Haskell IO ScalaZ IO
  • 10. def doBoringStaff(request: Request): F[Response] = for{ optUser <- usersStorage.findUser(request.userId) user <- F.fromOption(optUser) _ <- monitoredAccess(user).ifM(logAccess(user), IO.void) query = buildQuery(user, request) result1 <- db1.select(query) result2 <- db2.select(query) result = merge(result1,result2) } yield resut.toResponse Effect monads: for comprehension: real world
  • 11. def doBoringStaff(request: Request): F[Response] = for{ optUser <- usersStorage.findUser(request.userId) user <- F.fromOption(optUser) _ <- monitoredAccess(user).ifM(logAccess(user), IO.void) query = buildQuery(user, request) result1 <- db1.select(query) result2 <- db2.select(query) result = merge(result1,result2) } yield resut.toResponse Effect monads: for comprehension: real world Sequential execution
  • 12. Effect monads: for comprehension: real world def doBoringStaff(request: Request): F[Response] = for{ optUser <- usersStorage.findUser(request.userId) user <- F.fromOption(optUser) _ <- monitoredAccess(user).ifM(logAccess(user), IO.void) query = buildQuery(user, request) (result1, result2) <- db1.select(query)|+|db2.select(query) result = merge(result1,result2) } yield result.toResponse DSL instead control flow. What to do if we need ‘real for’ ? Extra layer of complexity.
  • 13. Effect monads: async/await: real world def doBoringStaff(request: Request):F[Response] = async[F] { user = await(userStorage.findUser(request.userId).getOrElse( throw new IllegalArgumentException("user not found"))) if (await(monitoredAccess(user))) await(logAccess(user)) val query = buildQuery(user, request) val result1 = db1.select(query) val result2 = db2.select(query) merge( await(result1), await(result2) ).toResponse } Better, but: Manual colouring: Too many awaits:
  • 14. Effect monads: async/await: colouring def doBoringStaff(request: Request): F[Response] = async[F] { user = await(userStorage.findUser(request.userId)).getOrElse( throw new IllegalArgumentException("user not found")) if (await(monitoredAccess(user))) await(logAccess(user)) val query = buildQuery(user, request) val result1 = db1.select(query) val result2 = db2.select(query) merge( await(result1), await(result2) ).toResponse } Better, but: Manual colouring: Too many awaits:
  • 15. Effect monads: async/await: automatic colouring import cps.imlicitAwait def doBoringStaff(request: Request): F[Response] = async[F] { user = userStorage.findUser(request.userId).getOrElse( throw new IllegalArgumentException("user not found")) if (monitoredAccess(user)) await(logAccess(user)) val query = buildQuery(user, equest) val result1 = db1.select(query) val result2 = db2.select(query) merge( result1, result2 ).toResponse } [currently work only for Future, support for IO is in progress]
  • 16. Reminder: 02: reactivity def doBoringStaff(request: Request): Response = val query = buildQuery(request) val result = db.select(query) resut.toResponse def doBoringStaff(request: Request): F[Response] = async[F] { val query = buildQuery(request) val result = db.select(query) resut.toResponse }
  • 17. How to do this [?] https://github.com/rssh/dotty-cps-async Optimised monadic CPS [Continuation Passing Style] transform. Challenges: Size of the language. Monads interoperability. High-order functions. Automatic colouring.
  • 18. Optimised monadic CPS transform def doBoringStaff(request: Request): F[Response] = async[F] { val query = buildQuery(request) val result = db.select(query) resut.toResponse } def doBoringStaff(request: Request): F[Response] = { val m = summon[CpsMonad[F]] val query = buildQuery(request) m.map(db.select(query)){ x => val result = x resut.toResponse } } // implicitly[CpsMonad[F]]. In scala2
  • 19. Effect monads: async/await: automatic colouring import cps.imlicitAwait def doBoringStaff(request: Request): F[Response] = async[F] { user = userStorage.findUser(request.userId).getOrElse( throw new IllegalArgumentException("user not found")) if (monitoredAccess(user)) await(logAccess(user)) val query = buildQuery(user, equest) val result1 = db1.select(query) val result2 = db2.select(query) merge( result1, result2 ).toResponse }
  • 20. Effect monads: async/await: macro output def doBoringStaff(request: Request): IO[Response] = { m = summon[CpsMonad[F]] m.flatMap(userStorage.findUser(request.userId)){ x => m.flatMap(summon[AsyncShift[Option[User]]].getOrElse(m,u)( () => m.error(new IllegalArgumentException("user not found"))) ){ x => val user = x m.flatMap(monitoredAccess(user)){ x => m.flatMap(logAccess(user)){ x => val query = buildQuery(user, equest) val result1 = db1.select(query) val result2 = db2.select(query) m.flatMap(result1){ a1 => m.flatMap(result2){ a2 => merge( a1, a2 ) } } } } } } } // In principle, near the same code as written by hand with for-comprehension
  • 21. What we need from monad: Control flow: trait CpsMonad[F[_]]: def pure[A](x:A):F[A] 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] Try/catch: trait CpsTryMonad[F[_]] extends CpsMonad[F]: def error[A](e: Throwable): F[A] def flatMapTry[A,B](fa: [A])(f: Try[A]=>B) async[M] { await[Future](…) } trait CpsAsyncMonad[F[_]] extends CpsTryMonad[F]: def adoptCallbackStyle[A](source: (Try[A]=>Unit) => Unit): F[A] async[Future] { await[M](…) } trait CpsSchedulingMonad[F[_]] extends CpsAsyncMonad[F]: def spawn[A](op: F[A]): F[A] Automatic colouring: trait CpsMemoizingMonad[F[_]] extends CpsAsyncMonad[F]: transient inline def memoize[A](v: F[A])
  • 22. Monads interoperability def doPOST(uri: String, value: ujson.Value): Future[ujson.Value] = async[Future] { val request = HttpRequest.newBuilder() .uri(new URI(uri)) .header("Content-Type","application/json") .POST(HttpRequest.BodyPublishers.ofString(write(value))) .build() val response = await(client.sendAsync(request,HttpResponse.BodyHandlers.ofString())) parseResponse(response) } java.util.concurrent.CompletableFuture[HttpResponse[String]]
  • 23. Monads interoperability def doPOST(uri: String, value: ujson.Value): Future[ujson.Value] = async[Future] { val request = HttpRequest.newBuilder() .uri(new URI(uri)) .header("Content-Type","application/json") .POST(HttpRequest.BodyPublishers.ofString(write(value))) .build() val response = await(client.sendAsync(request,HttpResponse.BodyHandlers.ofString())) parseResponse(response) } java.util.concurrent.CompletableFuture[HttpResponse[String]] trait CpsMonadConversion[F[_],G[_]]: def apply[T](mf:CpsMonad[F],mg:CpsMonad[G],ft:F[T]):G[T] given toFutureConversion[F[_]](using ExecutionContext, CpsSchedulingMonad[F]): CpsMonadConversion[F,Future] = new CpsMonadConversion[F, Future] { override def apply[T](mf: CpsMonad[F], mg: CpsMonad[Future], ft:F[T]): Future[T] = val p = Promise[T]() val u = summon[CpsSchedulingMonad[F]].mapTry(ft){ case Success(v) => p.success(v) case Failure(ex) => p.failure(ex) } summon[CpsSchedulingMonad[F]].spawn(u) p.future }
  • 24. Monads interoperability def doPOST(uri: String, value: ujson.Value): Future[ujson.Value] = async[Future] { val request = HttpRequest.newBuilder() .uri(new URI(uri)) .header("Content-Type","application/json") .POST(HttpRequest.BodyPublishers.ofString(write(value))) .build() val response = await(client.sendAsync(request,HttpResponse.BodyHandlers.ofString())) parseResponse(response) } java.util.concurrent.CompletableFuture[HttpResponse[String]] given toFutureConversion[F[_]](using ExecutionContext, CpsSchedulingMonad[F]): CpsMonadConversion[F,Future] = new CpsMonadConversion[F, Future] { override def apply[T](mf: CpsMonad[F], mg: CpsMonad[Future], ft:F[T]): Future[T] = val p = Promise[T]() val u = summon[CpsSchedulingMonad[F]].mapTry(ft){ case Success(v) => p.success(v) case Failure(ex) => p.failure(ex) } summon[CpsSchedulingMonad[F]].spawn(u) p.future }
  • 25. High-order functions [1] 1-st approach: By-Name parameters as functions from 0 arguments: Local substitutions Private data Iterable[A] . map[B](f : A ⇒ B) : Iterable[B] AsyncShift[Iterable[A]] . map[F[_], B](m : CpsMonad[F], c : Iterable[A]) (f : A = > F[B]) : F[Iterable[B]] Option[T] . getOrElse(x := > T) AsyncShift[Option[T]] . getOrElse[F[_])(m : CpsMonad[F], c : Option[T])(x : () = > F[T])
  • 26. High-order functions [Local Substitutions] async[F]{ for{ log <- logs if (await(isMalicious(log.url)) } yield (await(report(log))) } async[F] { logs.withFilter( log => await(isMalicious(log.url)) ) .map(log => await(report(log))) } = We want navigate via list of logs once. With previous approach we will get filtered list of urls, and then iterate on it.
  • 27. High-order functions [Local Substitutions] Solution: create local WithFilter, which will run both filter and map in map. class DelayedWithFilter[F[_], A, C[_], CA <: C[A]](c: CA, m: CpsMonad[F], p:A=>F[Boolean], ) extends CallChainAsyncShiftSubst[F, WithFilter[A,C], F[WithFilter[A,C]] ]: // return eager copy def _origin: F[WithFilter[A,C]] = ... def map[B](f: A => B): F[C[B]] = ... def map_async[B](f: A => F[B]): F[C[B]] = ... def flatMap[B](f: A => IterableOnce[B]): F[C[B]] = ... def flatMap_async[B](f: A => F[IterableOnce[B]]): F[C[B]] = ... def withFilter(q: A=>Boolean) = DelayedWithFilter(c, m, x => m.map(p(x))(r => r && q(x)) ) def withFilter_async(q: A=> F[Boolean]) = ... ...
  • 28. High-order functions [Local Substitutions]: Categorical interpreation. Arrows: Functors: Left Kan Extension …
  • 29. High-order functions: private data class MyIntController: def modify(f: Int => Int): Int = … def modify_async[F[_]](m: CpsMonad[M])(f: Int => F[Int]): F[Int] = … - add a <x>_async (or <x>Async) method to you class…. class Select[F[_]]: def fold[S](s0:S)(step: S => S | SelectFold.Done[S]): S = … def foldAsync[S](s0:S)(step: S => F[S | SelectFold.Done[S]]): F[S] = … - F[_] can be in class. (Useful for DSL-s).
  • 30. Effect monads: async/await: automatic colouring import cps.imlicitAwait def doBoringStaff(request: Request): F[Response] = async[F] { user = userStorage.findUser(request.userId).getOrElse( throw new IllegalArgumentException("user not found")) if (monitoredAccess(user)) await(logAccess(user)) val query = buildQuery(user, equest) val result1 = db1.select(query) val result2 = db2.select(query) merge( result1, result2 ).toResponse } - when F = Future[_]. - all ok. - F = IO[_] — ambiguity
  • 31. All monads are equal but some are more equal than others Future: represent already started computation. cached by default. Cats-effect IO, ZIO, scalaz IO: represent computation, which are ‘not started’ not cached referential transparency val future = Future{ something } val a = await(future) val b = await(future) - something will be evaluated once. - the a and b will be the same object. val io = IO.delay{ something } val a = await(io) val b = await(io) - something will be evaluated twice. - the a and b will not be the same object.
  • 32. Automatic colouring: IO problems def ctr(name:String) = async[IO] { val counter = counters(name).getOrThrow() val value = counter(name).increment() if (value % LOG_MOD == 0) then log(s"counter $name is $value") if (value == THRESHOLD) then log(s"counter $name exceeded treshold") } def ctr(name:String) = async[IO] { val counter = counters(name).getOrThrow() val value = counter(name).increment() if (await(value) % LOG_MOD == 0) then log(s"counter $name is ${await(value)}”) if (await(value) == THRESHOLD) then log(s"counter $name exceeded treshold") } def ctr(name:String) = async[IO] { val counter = counters(name).getOrThrow() val value = await(counter(name).increment()) if (value % LOG_MOD == 0) then log(s"counter $name is $value") if (value == THRESHOLD) then log(s"counter $name exceeded treshold") } V1 V2 - this variant will be chosen by implicitAwait - value will be increment 3 times.
  • 33. Automatic colouring: IO problems def ctr(name:String) = async[IO] { val counter = counters(name).getOrThrow() val value = Memoize[counter(name).increment()] if (await(value) % LOG_MOD == 0) then log(s"counter $name is ${await(value)}”) if (await(value) == THRESHOLD) then log(s"counter $name exceeded treshold") } - Memoize: - Future[T]: nothing - IO: IO[IO[T]] - Monix Task: Task[T] Solution: memoize values. If you store something in val, then you want to reuse it.
  • 34. Automatic colouring: IO problems def ctr(name:String) = async[IO] { val counter = counters(name).getOrThrow() val valueMem = Memoize[counter(name).increment()] val value = await(valueMem) if (await(value) % LOG_MOD == 0) then log(s"counter $name is ${await(value)}”) if (await(value) == THRESHOLD) then log(s"counter $name exceeded treshold") } - Memoize: - We break referential transparency; - But it was already broken when we remove awaits… Solution: memoize values. If you store something in val, then you want to reuse it. // currently in development, will be in the next version
  • 35. How to help https://github.com/rssh/dotty-cps-async Ecosystem: cps-async-connect scala-gopher Start using in examples and pet-project: Discover techniques and bugs. Di ff erent types of monads (probabilistic programming, streams, etc … ) Provide feedback.