Implementing the CSP concurrency style on the JVM is not easy, whether for Java or for Scala. The reason is that CSP is based on threads with a reduced context, which are often called green threads. The reduced context consumes less memory, which means that you can run much greener threads than OS threads or Java threads (1 Java thread corresponds to 1 OS thread). I once tried: with 4 GB of RAM, you can run about 80,000 gorts (the green thread option in Go) compared to about 2,000 Java threads.
Now, why does it matter? The idea in CSP is that if there is no data in any channel, there is “only” one green stream, lost, which now sits on that channel until it receives an input. Say you have access to a web application of 40,000 users. 80,000 Goroutines, which can be run on a machine with 4 GB of RAM, can process these 40,000 connections on the spot (1 incoming connection and one outgoing connection). Without green threads, you need much more memory or more servers.
Another point in green streams is that you just don't need to worry if the green stream is on the channel, since you have so many. Now with channel-oriented code, you can look at the code that is behind the asynchronous one, as if it were synchronous. The flow of messages through pipes is as simple as any other method calls. Robert Pike explains this well in this Youtube-Video at around 29:00. This makes CSP-style parallel code much easier from the start, as well as easier to find concurrency related errors.
Another question is the continuation. Let's say you have a function that consumes data from two channels in a row and somehow calculates the data. Now the first channel has data, and the second - no. Therefore, when the second channel receives data, the language runtime should skip inside the function to the place where the second channel transmits the data to the function. In order to be able to do this, the runtime must remember where to jump, and he needed to store the data taken from the first channels and restore them, because they are calculated along with the data of the second channel. This can be done on the JVM using continuation libraries that use byte code injection to stitch intermediate results and remember where to go. One library for Java nad Kotlin that can do this is Quasar: http://docs.paralleluniverse.co/quasar/ Quasar also has fibers that serve as a means for something like green threads on the JVM. Quasar’s developer is Ron Presler, who was hired by Oracle to work on Projekt Loom: http://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html The idea of this project is to support fibers and extensions at the JVM level, which would make the fibers more efficient, and introducing the byte code to continue is less cumbersome.
Then Kotlin also has Coroutines: https://kotlinlang.org/docs/reference/coroutines.html Kotlin Couroutines also implements the fibers and extensions provided by the Kotlin compiler, so the developer does not need to help the CSP library (e.g. Quasar), knowing which function is required to enter a byte code.
Unfortunately, Kotlin Couroutines are for Kotlin only and cannot be used outside of it. Therefore, they are not available for other JVM languages. Quasar does not work with Scala, since inserting byte code for Scala for continuations will be much more complicated than for Java or Kotlin, since Scala is a much more complex language. At least these are the arguments made by the Quasar developer.
So, the best thing to do, like Scala, is to stick with Akka or wait for Project Loom to complete. Then, some Scala people can begin to implement CSP for Scala at a level that really implements CSP. At the time of writing, Project Loom works, but is not yet officially approved by Oracle. Therefore, it is still unclear whether some future JDK will contain those things that are necessary for a full-blown CSP.