In my previous , we explained how some features of the Scala programming languages overlap with the ones we can find in the C# programming language. But and are not the only features both languages share. post implicits type extensions The Actor Model As presents Wikipedia The actor model in computer science is a mathematical model of concurrent computation that treats “actors” as the universal primitives of concurrent computation: in response to a message that it receives, an actor can make local decisions, create more actors, send more messages, and determine how to respond to the next message received. It has been used both as a framework for a theoretical understanding of computation and as the theoretical basis for several practical implementations of concurrent systems. Actors are a huge deal on the Scala world and C# really lacks of this pattern by default, but there is Akka.NET to save the day. Because Actors is just a pattern that can be implemented in any language we are going to skip it and jump directly to the core of multithreading in each of the languages. Futures vs Tasks Scala presents the idea of futures which work as a placeholder for a value that will be available at some point in the future. Futures are normally defined as generic place holders to hold these values. Let’s see how we can use them. val aFuture = Future {1 to 1000000} Here, holds the value of the sequence once it becomes available. It is also strongly typed so we can define it with a different syntax just to show its definition. It looks like this aFuture val aFuture: Future[Seq[Int]] = Future {1 to 1000000} Futures do not block the execution on the current thread, so it will be executed asynchronous in a different thread. Let’s suppose we have a long task to execute async, for example, downloading a file from the web, we can use Futures to asynchronously do this operation. Here is how it goes val aFuture = Future {val file = downloadFileFromTheWeb()file} In this case, we just start downloading the file and in the meantime, we can still do some other interesting operations after the Future definition. But what happens at the point we need the result of the async operation? Futures present a mechanism to do this that in my opinion is something from the past, but let’s review it. case class File(content: String) object File{def apply(content: String) = new File(content)} val aFuture = Future {val file = downloadFileFromTheWeb()file} aFuture onSuccess {case File(content) => println(content)} aFuture onFailure {case error => println("Error Downloading from the web: " + error.printStackTrace())} The and functions are defined so they will be executed based on the result of the long time computation and then we can act accordingly. However, if we need to wait for the result of the long time operation things get even more weird. onSuccess onFailure val aFuture = Future {val file = downloadFileFromTheWeb()file} val file = Await.result(aFuture, 0 nanos) Here we just wait for the computation to finish, however, the Future object has not control over this call since it is called on the execution context and not in the operation itself. C# offers a better API to solve these problems even though it models these ideas in the same way Scala does. However, the .NET API is easier to use. The same problem would look like this public class File{public string Content { get; set; }} var task = Task<File>.Factory.StartNew(() =>{var file = downloadFileFromTheWeb(); return file; }); Now if we want to wait for the result of this task we only say var file = task.Result;Console.WriteLine(file.Content) This will block until the task ends in the same way works. However, the control action is executed by the Task itself and not by a global executor context that needs to be imported into our execution environment. Await.result C# also has better support for . They are not called anymore but . Let’s implement the same we did in Scala using C# continuations. callbacks callbacks task continuation var taks = Task<File>.Factory.StartNew(() =>{var file = downloadFileFromTheWeb(); return file; }) .ContinueWith(t => { Console.WriteLine(t.Result); }, TaskContinuationOptions.OnlyOnRanToCompletion); This would be the same as for Futures, however, method returns a Task, too, so it could be chained to other operations. decides in what case we execute the continuation so there is not need for different callbacks. onSuccess ContinueWith TaskContinuationOptions As we can see, C# Tasks are a more intuitive way to execute long time running tasks with a better support to solve what will come after the task has been completed. Still, there is another problem related to chaining task. C# awaitable model The async/await model was introduced to solve the problem of continuations and I truly believe that C# is far more advanced than other programming languages in this area. Let’s review the following pseudo code: var taks = Task<File>.Factory.StartNew(() =>{var file = downloadFileFromTheWeb();// <-- here want to do some other async operation such as writing the file content to a database// <-- then here we want to log to a file the db resultreturn file;}) We can achieve this by doing var taks = Task<File>.Factory.StartNew(() =>{var file = downloadFileFromTheWeb(); return file; }) .ContinueWith(t => { Task<string>.Factory.StartNew(() => { string fileContent = t.Result.Content; return saveToDb(fileContent); }).ContinueWith(x => { logToFile(x.Result); }) ; }, TaskContinuationOptions.OnlyOnRanToCompletion); Note how complicated the chaining of continuations can gets, and the same happens in Scala, but there is a feature that C# has that kills Scala in this aspect. The last code can be rewritten in the following way using the async/await pattern from C# void main(...){var task = Do(); RefreshUIOrDoSomethingElse(); //if we need to do something with the file we do File file = task.Result; // or File file = await task; } private static async Task<File> Do(){var file = await Task<File>.Factory.StartNew(downloadFileFromTheWeb); var messageFromDb = await saveToDbAsync(file.Content); await logToFileAsync(messageFromDb); return file; } static Task logToFileAsync(string s) { return Task.Factory.StartNew(()=>Console.WriteLine(s)); } private static List<string> db = new List<string>(); static Task<string> saveToDbAsync(string fileContent) { return Task<string>.Factory.StartNew(() => { db.Add(fileContent); return fileContent; }); } Now, we are making use of the C# async/await API in order to get the code as clean as it is. Let’s analyze it just to make sure we all are in the same page. First, we call the method which basically returns a Task or Future in Scala, but the interesting part is what happens inside it. The most important part is that does not block. Do() Do() Second, inside we are downloading the file async, but that is so at the moment we call Do() awaitable var file = await Task<File>.Factory.StartNew(downloadFileFromTheWeb); the control flow returns to the function. Once the file has been downloaded, the system selects an available thread and the task from where it was. Yes! from where it was, so the next to execute in that task will be main resume Do() var messageFromDb = await saveToDbAsync(file.Content); but that sentence is also awaitable, so the control is returned once again to the until the the DB has finished it’s work. When DB finishes, another thread is selected (doesn’t has to be the same thread as before) and the task resumes. The same happens to main await logToFileAsync(messageFromDb); When it finished the task is resume again then the file is returned. Note that is strongly typed, so the result of its execution will be the generic type of the awaitable task. For example, await static Task<string> saveToDbAsync(string fileContent){...} returns a so if we this task, the result will be a Task<string> await string To be fair with Scala, something similar to async/await can be implemented using . Let’s see an example. map aFuture onSuccess {case File(content) => println(content)} The previous code can be implemented as follow aFuture.map(println) Let’s look at a more complicated example I found online val firstLove = future {Thread.sleep(500)"i love you"} val thenBetray = firstLove map { case loveLetter => { Console.println(loveLetter) Thread.sleep(500) "not really" } } thenBetray onSuccess { case partingWords => Console.println(partingWords) } We can see how helps to chain the Future(s) but still the code can get quite messy. There are other Scala constructs that work along with but they are not comparable to how C# solves this problem. map map Endings We saw how Scala implements long time running operation without blocking the calling thread. We also saw how the same can be implemented in C# by the use of class . We took a look at how continuations need to be implemented in Scala using callbacks and in C# by chaining (s). Ultimately we saw how C# solves the problem of chaining by the use of the async/await pattern and how natural it comes to developers. Task Task I hope this comparison helps to synchronize developers in both worlds when talking about asynchronous programming. When someone talks about you can them to or the other way around. Futures map Tasks Have fun, both languages are great, I enjoy them both. Read next: Implicit conversions in Scala for C# developers is how hackers start their afternoons. We’re a part of the family. We are now and happy to opportunities. Hacker Noon @AMI accepting submissions discuss advertising & sponsorship To learn more, , , or simply, read our about page like/message us on Facebook tweet/DM @HackerNoon. If you enjoyed this story, we recommend reading our and . Until next time, don’t take the realities of the world for granted! latest tech stories trending tech stories
Share Your Thoughts