Task#apply versus Task#delay - scala

Given the following scala.concurrent.Task instance created via Task#delay:
val t =
Task.delay { println(Thread.currentThread); Thread.sleep(5000); 42 }
I wrote a method that will run t asynchronously.
def f = t.runAsync {
case \/-(x) => println(x)
case -\/(e) => println(e.getMessage)
}
Running it shows that f evaluates entirely, i.e. waits 5 seconds, and then evaluates again. In other words, the second f appears to wait until the first f had completed
scala> {f; f; }
Thread[run-main-0,5,run-main-group-0]
42
Thread[run-main-0,5,run-main-group-0]
42
Then, I re-wrote t using Task#apply:
val u =
Task { println(Thread.currentThread); Thread.sleep(5000); 42 }
Again, I defined a method that executes u with runAsync:
def g = u.runAsync {
case \/-(x) => println(x)
case -\/(e) => println(e.getMessage)
}
Finally, I ran two g's.
scala> {g; g}
Thread[pool-3-thread-2,5,run-main-group-0]
Thread[pool-3-thread-3,5,run-main-group-0]
scala> 42
42
However, in the above result, the g's, more or less, ran at the same time.
I had expected that {f; f; } would've run asynchronously, i.e. in the same way as g. But, it seems to me that calling f resulted in a block.
EDIT
Task's docs note on runAsync:
Any pure, non-asynchronous computation at the head of this Future will be forced in the calling thread.
Since t's body is non-asynchronous, I suppose that the above comment explains why it blocked, i.e. "forced in the calling thread."
When is the right time to use Task#delay versus Task#apply?

You can think of Task.delay as a fancy version of something like () => Try[A]. It suspends evaluation of the computation, but doesn't have anything to say about what thread that evaluation is eventually going to run on, etc. (which means it's just going to run on the current thread).
This is often exactly what you want. Consider a definition like this:
val currentTime: Task[Long] = Task.xxx(System.currentTimeMillis)
We can't use now because that would evaluate the time immediately (and only once, on definition). We could use apply, but forcing an asynchronous boundary for this computation is wasteful and unnecessary—we actually want it to run in the current thread, just not right now. This is exactly what delay provides.
In general when you're modeling your computations, if something is always going to be computationally expensive, you might want to consider Task.apply, which means the evaluation will always happen on a thread determined by the current implicit ExecutorService. This may make usage a little cleaner, at the expense of flexibility—you're baking something you know about the runtime characteristics of the evaluation of the computation into its definition.
The nice thing about using delay to define your asynchronous computations is that you can always force an asynchronous boundary by wrapping your Task with Task.fork, which gets you essentially the same thing you'd have if you'd defined the computation with Task.apply. It's not possible to go in the other direction—if you use Task.apply, the implicit strategy is going to determine where the computation is evaluated and that's all there is to it.

Related

What is meant by "effectively tail recursive"?

Chapter 7 in FP in Scala deals with creating a purely functional library for handling concurrency. To that end, it defines a type
type Par[A] = ExecutorService => Future[A]
and a set of useful functions such as fork
def fork[A] (a: => Par[A]): Par[A] =
es => es.submit(new Callable[A] {
def call = a(es).get
})
One of the exercises is about the function sequence with the following signature
def sequence[A](ps: List[Par[A]]): Par[List[A]]
The solution using foldRight is straightforward. However the authors included two other versions as answers, one of which states the following
// This implementation forks the recursive step off to a new logical thread,
// making it effectively tail-recursive. However, we are constructing
// a right-nested parallel program, and we can get better performance by
// dividing the list in half, and running both halves in parallel.
// See `sequenceBalanced` below.
def sequenceRight[A](as: List[Par[A]]): Par[List[A]] =
as match {
case Nil => unit(Nil)
case h :: t => map2(h, fork(sequenceRight(t)))(_ :: _)
}
I am not quite sure what is meant by "effectively tail recursive". From the definition of fork it is clear that it accepts a by name parameter and so map2(h, fork(sequenceRight(t)))(_ :: _) will not evaluate the second parameter (until the executor service is provided). But that doesn't tell me how and why it is "effectively tail recursive".
Let's take some List(a, b, c). After passing it into sequenceRight it will turn into:
map2(
a,
fork(
map2(
b,
fork(
map2(
c,
fork(unit(Nil)
)(_ :: _)
)
)(_ :: _)
)
)(_ :: _)
This isn't tail recursive at all and compiler cannot treat it as one. However, when you would evaluate how it would be executed:
fork would make whatever you pass to it async, so it would return immediately,
map2 implementation will not block the execution until fork is executed to apply the function passed to map2, instead it would asynchronously transform the result calculated in fork to prepend the value
since recursion is done asynchronously, posting things to ExecutorService and appending operation to Future let you treat ExecutorService+Future like a trampoline
As a result what actually happens is:
sequenceRight(List(a, b, c)) call `map2(a, fork(sequenceRight(List(b, c))(_ :: _)
a will complete when it will complete but we can hold it as value even now
fork(sequenceRight(List(b, c)) is scheduled, but we aren't waiting until it complete, we can pass it around already
we can create a Future that will combine the result of the 2 above (and return it) without waiting for any of them completing!
as a result, this Future is returned immediately! It still runs, but this one call is finished!
same is true for recursively created Futures
once c and fork(unit(Nil)) completes, rResult :: Nil is computed
this allows completion of bResult :: cResult :: Nil
and this finally allows computation of the final result
In other words tail-recursive refers to recursive calls being rewritten into while loop to avoid stack overflow. But stack overflow is only an issue if recursive calls are being made synchronously. If you are returning something async, then the backtracing is shoved to ExecutionServices and Futures' observers, so they are hidden in a heap. From that point of view they solve the same issue of stack-overflow as tail-recursive calls, so "in spirit" they could be considered somewhat similar.
This is certainly not tail-recursion, and I think, they know it – that's why they added "effectively" :).
What they must mean is that this implementation does not create additional frames on stack for recursive invocations, which is true, since those invocations happen asynchronously, as you noted.
Now, whether this situation is even deserves to be called a "recursion" at all is a good question. I don't think there is a single accepted answer to that. I personally lean toward "yes", because the definition of recursion as "referencing itself" definitely includes this situation, and I don't really know how else to define it so that async invocations are excluded, but tail-recursion is not.
BTW, I am not much of an expert in Javascript, but I hear that the term "asynchronous recursion" is used fairly widely there.

Monads being a mechanism for sequencing computations, is the below list still a monad though they are printed in a random order Scala

for {
i <- 1 to 5
} yield Future(println(i))
Desugared to:
List(1,2,3,4,5).map {i => Future(println(i))}
The above code prints numbers in random order.
Now, if we see the multiple definitions of Monad:
a) Monad is a wrapper over an object
b) Monad is a mechanism for sequencing computations
The question that I'm trying to answer is that shouldn't map operation on List monad wait for the first element in the list to be printed and only then go for the computation of the second element regardless of Future?
Sorry, it might be simple and I'm complicating it but it gets trickier for me to find simple reasoning. Answers will be much appreciated:)
Compare:
for {
_ <- Future(println(1))
_ <- Future(println(2))
_ <- Future(println(3))
_ <- Future(println(4))
_ <- Future(println(5))
} yield ()
or
Future(println(1)).flatMap { _ =>
Future(println(2))
}.flatMap { _ =>
Future(println(3))
}.flatMap { _ =>
Future(println(4))
}.flatMap { _ =>
Future(println(5))
}
with
List(
Future(println(1)),
Future(println(2)),
Future(println(3)),
Future(println(4)),
Future(println(5))
)
The first two create the next Future only after the former completed and made the result available. The last one creates all Futures at once (and it doesn't differ much in this regard from your example with List[Future]).
Future (as opposed to IO from Cats Effect, Monix's Task or ZIO) is eager, so it starts execution the moment you create it. For that reason you have sequential result in the first two examples, and random order (race condition) in the third example.
If you used IO instead of Future it would be more apparent because you wouldn't be able to just have List[IO[Unit]] and execute side effects - you would have to somehow combine the different IOs into one, and the way you would do it would make it obvious whether the effects will be sequential or parallel.
The bottom line is - whether or not Future is a monad depends on how the .flatMap behaves (and how it behaves with combination with Future.successful), so your results doesn't invalidate the claim that Future is a monad. (You can have some doubts if you start checking its behavior with exceptions, but that is another topic).
The execution of map is sequential indeed, but when you wrap it to a Future it gets executed in an asynchronous manner, I mean it is evaluated in another thread and because of that, it is not possible to know what thread is going to finish earlier because it depends also in the thread management of the operating system and other considerations.
Both of your code snippets are still Monads in loose terms. When you did .map() on your object, the map picks element one by one in orderly fashion (from index 0 to index 4). And then it passes on that to an operation block (which is body of map - map is a higher order function that accepts a function of type f:This => That).
So monad operation's responsibility is picking it up and passing it as paramater to a function.
In your case the actual function type is:
f: Int => Future[Unit]
For clarity, your function actually looks like this:
def someFunction(i: Int): Future[Unit] = {
Future {
println(i)
}
}
So, what the map operation did here is that it picked on item from your object (in sequence, one by one) and called the someFunction(i). And that's all a monad does.
Now to answer why your println are random, it's because of JVM threads.
If you re-define the body of you map like this
List(1,2,3,4,5)
.map {i =>
println(s"Going to invoke the println in another thread for $i")
Future(println(i))
}
You'll see that the first println will be in sequence - always! It proves that .map() picks your elements in sequence. While the next println may or may not be out of sequence. This out of order fashion is not because of monad operation map but because of multithreading nature in multi core CPUs.

Regarding Scala Futures being eager

I have been trying to understand why Scala Futures are regarded as eager and violate referential transparency. I think I understand this part reasonably. However, I have trouble understanding what this means:
(A => Unit) => Unit
With respect to a Future.
I am not sure if this is the right forum, but ELI5 answers appreciated
The reason why Future is regarded as eager (and as such violates referential transparency) is because it evaluates as soon as the value is defined. Below is the ELI5 and non-ELI5 explanation for this.
As for (A => Unit) => Unit, it's a signature for the callback-driven asynchronous computation. In a synchronous computation, you evaluate the Future[A] to A, even if it means sitting in place and waiting a long time for the evaluation to finish. But with asynchronous computation, you don't sit and wait; instead, you pass a function of type A => Unit, and you immediately get the Unit back. Later, when the computation has finished in the background and value A has been produced, function A => Unit will be applied to it. So basically you tell the Future "once you obtain A, here's what I want you to do with it", and it responds "OK, will do, here's a Unit for you, leave now and do other stuff".
TBH I wouldn't overthink this signature too much because that's not what your mental model of working with Future should be. Instead, just become familiar with the notion of mapping and flatMapping. When you have a value wrapped in a Future, you shouldn't try to get that value out of the Future context because that would be a blocking synchronous operation. But what you can do is map over it and say "alright Future, I don't need this value A right now, I just want to describe a function A => B to you which turns it to another value B, and you make sure to apply it to once you have the original A". And if B is wrapped in a yet another Future, meaning your function is not A => B but A => Future[B], instead of mapping you should use flatMap. This is how you chain asynchronous operations. Imagine a database query which as a parameter needs something returned in the previous query.
And that's it. Somewhere at the end of the world, e.g. when you're done processing an http request and are ready to send some response payload over the wire, you will finally unwrap that future in a synchronous way (you can't send a payload if you don't know what to put in it).
Now, about referential transparency in Future:
ELI5:
Imagine you have two daughters, Anna and Betty. You tell them that their task will be to count to 20 out loud. You also tell them that Betty should start only after Anna is done. Whole process is hence expected to take about 40 seconds.
But if they evaluate their task eagerly (like Future does), as soon as you explain the task to them, they will each start counting right away. Whole process will hence last about 20 seconds.
In the context of programming, referential transparency says that you should always be able to replace (pseudocode):
// imagine >> as a pipe operator which starts the next function
// only after previous one has terminated
count(20) >> count(20)
with
anna = count(20)
betty = count(20)
anna >> betty
but that's not true in this situation because of eager evaluation (the girls start counting as soon as their task is explained to them, so in the second case the program will last only 20 seconds regardless of the pipe).
non-ELI5:
Let's prepare an execution context for Future and a function that will be evaluated. It simply sleeps for two seconds before printing "hi".
import scala.concurrent.ExecutionContext.Implicits.global
def f = {
Thread.sleep(2000)
println("hi")
}
Let's now write a for comprehension which will create two Futures one after another:
val done = for {
f1 <- Future(f)
f2 <- Future(f)
} yield (f1, f2)
import scala.concurrent.duration._
Await.result(done, 5000 millis)
As expected, after two seconds we'll get the first "hi" (from f1), and after additional two seconds we'll get the second "hi" (from f2).
Now let's do a small modification; we will first define two Future values, and then we'll use those in the for comprehension:
val future1 = Future(f)
val future2 = Future(f)
val done = for {
f1 <- future1
f2 <- future2
} yield (f1, f2)
import scala.concurrent.duration._
Await.result(done, 5000 millis)
What happens this time is that after approximately two seconds you get two simultaneous "hi" printouts. This is because both future1 and future2 started getting evaluated as soon as they were defined. By the time they got chained in the for comprehension, they were already running alongside each other on the given execution context.
This is why referential transparency is broken; normally you should be able to replace:
doStuff(foo)
with
val f = foo
doStuff(f)
without having any consequence on the behaviour of the program, but in the case of Future, as you can see above, that's not the case.

Why sometimes do I have to call compute() twice on dask delayed functions?

I'm working with dask delayed functions and I'm getting familiar with the do's and don'ts when using the #dask.delayed decorator on functions. I realized that sometimes I will need to call compute() twice to get the result despite the fact that I thought I followed the best practices. i.e. don't call a dask delayed function within another dask delayed function.
I've run into this problem in two scenarios: when there are nested functions, and when calling a member function in a class that uses class members that are delayed objects.
#dask.delayed
def add(a, b):
return a + b
def inc(a):
return add(a, 1)
#dask.delayed
def foo(x):
return inc(x)
x = foo(3)
x.compute()
class Add():
def __init__(self, a, b):
self.a = a
self.b = b
#dask.delayed
def calc(self):
return self.a+self.b
a = dask.delayed(1)
b = dask.delayed(2)
add = Add(a, b)
add.calc().compute()
In the first example, x.compute() does not return the result but another delayed object, and I will have to call x.compute().compute() to get the actual result. But I believe inc is not a delayed function and therefore it's not against the rule of not calling a delayed function within another delayed function?
In the second example, again I will have to call add.calc().compute().compute() to get the actual result. In this case self.a and self.b are just delayed attributes and there are no nested delayed function anywhere.
Can anyone help me understand why I need to call compute() twice in these two cases? Or even better, could someone briefly explain the general 'rule' when using dask delayed functions? I read the documentation and there's not so much to be found there.
Update:
#malbert pointed out that the examples require calling compute() twice because there is delayed results involved in a delayed function and therefore it counts as 'calling delayed function within another delayed function'. But why something like follows only requires calling compute() once?
#dask.delayed
def add(a,b):
return a+b
a = dask.delayed(1)
b = dask.delayed(2)
c = add(a,b)
c.compute()
In this example, a and b are also both delayed results, and they are used in a delayed function. My random guess would be what actually matters is where the delayed result is in a delayed function? It's probably only fine if they are passed in as arguments?
I think the key lies in understanding more precisely what dask.delayed does.
Consider
my_delayed_function = dask.delayed(my_function)
When used as a decorator on my_function, dask.delayed returns a function my_delayed_function which delays the execution of my_function. When my_delayed_function is called with an argument
delayed_result = my_delayed_function(arg)
this returns an object which contains all the necessary information about the execution of my_function with the argument arg.
Calling
result = delayed_result.compute()
triggers the execution of the function.
Now, the effect of using operators such as + on two delayed results is that a new delayed result is returned which bundles the two executions contained in its inputs. Calling compute on this object triggers this bundle of executions.
So far so good. Now, in your first example, foo calls inc, which calls a delayed function, which returns a delayed result. Therefore, computing foo does exactly this and returns this delayed result. Calling compute on this delayed result (your "second" compute) then triggers its computation.
In your second example, a and b are delayed results. Adding two delayed results using + returns the delayed result of bundling the execution of a,b and their addition. Now, since calc is a delayed function, it returns a delayed result on getting a delayed result. Therefore again, its computation will return a delayed object.
In both cases, you didn't quite follow the best practices. Specifically the point
Avoid calling delayed within delayed functions
since in your first example the delayed add is called within inc, which is called in foo. Therefore you are calling delayed within the delayed foo. In your second example, the delayed calc is working on the delayed a and b, therefore again you are calling delayed within a delayed function.
In your question, you say
But I believe inc is not a delayed function and therefore it's not
against the rule of not calling a delayed function within another
delayed function?
I suspect you might be understanding "calling delayed within delayed functions" wrongly. This refers to everything that happens within the function and is therefore part of it: inc includes a call of the delayed add, therefore delayed is being called in foo.
Addition after question update: Passing delayed arguments to a delayed function bundles the delayed executions into the new delayed result. This is different from "calling delayed within the delayed function" and is part of the intended use case. Actually I also didn't find a clear explanation of this in the documentation, but one entry point might be this: unpack_collections is used to process delayed arguments. Even if this should remain somewhat unclear, sticking to the best practices (interpreted this way) should produce a reproducible behaviour regarding the output of compute().
The following codes result when sticking to "Avoid calling delayed within delayed functions" and return a result after a single call of compute:
First example:
##dask.delayed
def add(a, b):
return a + b
def inc(a):
return add(a, 1)
#dask.delayed
def foo(x):
return inc(x)
x = foo(3)
x.compute()
Second example:
class Add():
def __init__(self, a, b):
self.a = a
self.b = b
##dask.delayed
def calc(self):
return self.a+self.b
a = dask.delayed(1)
b = dask.delayed(2)
add = Add(a, b)
add.calc().compute()

How to compose two parallel Tasks to cancel one task if another one fails?

I would like to implement my asynchronous processing with
scalaz.concurrent.Task. I need a function (Task[A], Task[B]) => Task[(A, B)] to return a new task that works as follows:
run Task[A] and Task[B] in parallel and wait for the results;
if one of the tasks fails then cancel the second one and wait until it terminates;
return the results of both tasks.
How would you implement such a function ?
As I mention above, if you don't care about actually stopping the non-failed computation, you can use Nondeterminism. For example:
import scalaz._, scalaz.Scalaz._, scalaz.concurrent._
def pairFailSlow[A, B](a: Task[A], b: Task[B]): Task[(A, B)] = a.tuple(b)
def pairFailFast[A, B](a: Task[A], b: Task[B]): Task[(A, B)] =
Nondeterminism[Task].both(a, b)
val divByZero: Task[Int] = Task(1 / 0)
val waitALongTime: Task[String] = Task {
Thread.sleep(10000)
println("foo")
"foo"
}
And then:
pairFailSlow(divByZero, waitALongTime).run // fails immediately
pairFailSlow(waitALongTime, divByZero).run // hangs while sleeping
pairFailFast(divByZero, waitALongTime).run // fails immediately
pairFailFast(waitALongTime, divByZero).run // fails immediately
In every case except the first the side effect in waitALongTime will happen. If you wanted to attempt to stop that computation, you'd need to use something like Task's runAsyncInterruptibly.
There is a weird conception among java developers that you should not cancel parallel tasks. They comminate Thread.stop() and mark it deprecated. Without Thread.stop() you could not really cancel future. All you could do is to send some signal to future, or modify some shared variable and make code inside future to check it periodically. So, all libraries that provides futures could suggest the only way to cancel future: do it cooperatively.
I'm facing the same problem now and is in the middle of writing my own library for futures that could be cancelled. There are some difficulties but they may be solved. You just could not call Thread.stop() in any arbitrary position. The thread may perform updating shared variables. Lock would be recalled normally, but update may be stopped half-way, e.g. updating only half of double value and so on. So I'm introducing some lock. If the thread is in guarded state, then it should be now killed by Thread.stop() but with sending specific message. The guarded state is considered always very fast to be waited for. All other time, in the middle of computation, thread may be safely stopped and replaced with new one.
So, the answer is that: you should not desire to cancel futures, otherwise you are heretic and no one in java community would lend you a willing hand. You should define your own executional context that could kill threads and you should write your own futures library to run upon this context