From 09982ce7675e3ab0b2e23735edacb4cf3f67a572 Mon Sep 17 00:00:00 2001
From: Olivier Blanvillain <olivier.blanvillain@epfl.ch>
Date: Sun, 25 Apr 2021 15:00:01 +0200
Subject: [PATCH] Add exercises/exercise-4.md

---
 exercises/exercise-4.md | 81 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 81 insertions(+)
 create mode 100644 exercises/exercise-4.md

diff --git a/exercises/exercise-4.md b/exercises/exercise-4.md
new file mode 100644
index 0000000..5db6c4e
--- /dev/null
+++ b/exercises/exercise-4.md
@@ -0,0 +1,81 @@
+# Exercise 4
+
+Use the following commands to make a fresh clone of your repository:
+
+```
+git clone -b exercise-4 git@gitlab.epfl.ch:lamp/student-repositories-s21/cs206-GASPAR.git exercise-4
+```
+
+Update the README.md file with your solutions. Don't forget to list the group members's SCIPER numbers.
+
+# Problem 1: Implementing map and filter on Futures
+
+In this exercise, you will come up with an implementation of the `map` and `filter` methods of Futures. First of all, spend some time as a group to make sure that you understand what those methods are supposed to do. Then, complete the following code to implement the two methods:
+
+```scala
+trait Future[T] { self =>
+  def map[S](f: T => S): Future[S] =
+    new Future[S] {
+      def onComplete(callback: Try[S] => Unit): Unit = ???
+    }
+
+  def filter(f: T => Boolean): Future[T] =
+    new Future[T] {
+      def onComplete(callback: Try[T] => Unit): Unit = ???
+    }
+}
+```
+
+In the case of `filter`, if the original `Future` successfully returns a value which does not satisfy the predicate, the new `Future` should return a `Failure` containing a `NoSuchElementException`.
+
+# Problem 2: Coordinator / Worker
+
+In this exercise, you will have to implement a Coordinator / Worker actor system, in which one actor, the coordinator, dispatches work to other actors, the workers. Between the coordinator and the workers, only two kinds of messages are sent: `Request` and `Ready` messages.
+
+```scala
+case class Request(computation: => Unit)
+case object Ready
+```
+
+The coordinator actor sends `Request` messages to workers to request them to perform some computation (passed as an argument of `Request`). Upon reception of a `Request`, a worker should perform the computation. Workers should send a `Ready` message to their coordinator whenever they finish executing the requested computation, and also right after they are created.
+
+The coordinator actor itself receives requests through `Request` messages from clients. The coordinator actor should then dispatch the work to worker actors. The coordinator should however never send a request to a worker which has not declared itself ready via a `Ready` message beforehand.
+
+Implement the `Coordinator` and `Worker` classes.
+
+```scala
+class Coordinator extends Actor {
+  ???
+
+  override def receive = ???
+}
+
+class Worker(coordinator: Coordinator) extends Actor {
+  ???
+
+  override def receive = ???
+}
+```
+
+An example system using the Coordinator and Worker actors is shown below.
+
+```scala
+object Main extends App {
+  val coordinatorProps: Props = Props(new Coordinator())
+  def workerProps(coord: Coordinator): Props = Props(new Worker(coord))
+
+  val system = ActorSystem("coordinator/worker")
+
+  val coordinator = system.actorOf(coordinatorProps)
+  val workers = Seq.fill(10) {
+    system.actorOf(workerProps(coordinator))
+  }
+
+  // Now, clients should be able to send requests to the coordinator…
+  coordinator ! Request(println(3 + 5))
+  coordinator ! Request(println(67 * 3))
+  // And so on...
+}
+```
+
+*Hint*: In order to fulfill its job, the coordinator should remember which workers are ready and what requests are still to be allocated to a worker.
-- 
GitLab