OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
Principled io in_scala_2019_distribution
1. Developing a principled
approach to I/O in Scala
cats-effect
Target audience: You are likely a Haskell programmer whose got an interest in Scala and you wish to use it with a functional style library like Cats, Scalaz though this talk
focuses on the cats ecosystem.
2. What
• Provide a standard I/O for the Cats ecosystem
• Cats ecosystem is a Haskell-inspired Scala library ☺
• Abstractions work in JVM and JavaScript
Develop higher-order combinators as you normally would:
1/ develop a function : f
2/ develop a function : g
3/ compose them : f . g
4/ develop other HOFs
F . G should work in the JVM and JavaScript worlds - why would I re-write it again?
3. Why
• The project aims to standardise the way functional
programmers work with I/O in Scala
•Each team and/or individual was using
various approaches to develop their
solutions - not having a “common
language” means that more time is spent
on understanding.
The I/O abstraction leverages from Java with the usual try-catch-finally but they require embellishments to make them composable functions. What if there was a way to
do this in a functional style? We are functional programmers and we refuse to accept that we cannot change the situation. So let’s see what we can do…
4. A first IO typeclass
“run” would return
would return nothing
i.e. Unit
We started with crafting the IO typeclass (note that its not a monad yet !) and we seem to have develop something but its not quite useful yet. Why? Because we wanted
to compose , combine IO actions into something bigger.
5. An improved IO typeclass (1)
“++” would run
itself followed by
the other IO-action.
“empty” creates a
run that does
nothing.
This improved IO typeclass is much better and I can do much more things with it
6. An improved IO typeclass (2)
“++” allows us to
chain IO actions.
Here’s how we can possibly leverage our new IO typeclass to run 2 (potentially more) IO actions together. But Its not too useful because we cannot define what’s the
input to the IO action; we can solve this but we need more work !
7. IO Monad (1/3)
-“unit” aka pure
-“flatMap” aka >>=
-“map” aka “map” or “<$>"
Over here, I’ve pre-developed the Functor and Monad - its bare minimum but suffice for this example. As you are trying to understand what’s going on - don’t fret over
the syntax of this example and instead try to focus on the bigger idea here which is our IO monad can now return a meaningful value which is something our previous IO
implementation could not do.
8. IO Monad (2/3)
The process of encoding an
IO Monad using our
previous definitions.
Let’s see it in action next and beware that our IO Monad can now return a meaningful value and you can see this when comparing this version and the previous.
9. IO Monad (3/3)
Now that IO Monad returns a meaningful value, we can now proceed to do something more useful with it. Let’s dissect a little. You would notice that the type signature of
“demo” is “IO[Unit]” which returns nothing and the reason is because I suppressed the value to be returned in the definition of “demo” via the “yield ( )” expression.
10. Quantum leap to the future :
cats-effect !
That was a long story to get to this part of the talk where we start talking about cats-effect which is IO monad designed for the cats ecosystem. The cats-effect library
leverages the cats library which is type-class based approach to functional programming inspired none other than Haskell.
11. Cats-effect
Monadic comprehension in
action. In effect, its the
same as “ioa >> ioa”.
An example using cats-effect https://github.com/typelevel/cats-effect and this example serves to highlight the benefits of leveraging the efforts presented by this library.
The main takeaway from the presentation till now is the realisation that cats-effect (via Scala) gives Scala programmers a way to standardise the approach of performing
IO i.e. principled approach to IO in Scala. ☺
12. Concurrency
How IS concurrency linked to the IO monad in cats-effect? Well, the reason is that the library has a abstractions that allows the programmer to develop your own
concurrent abstractions; they come in a few forms: MVar, Ref, Semaphore & Deferred. Over here, I’m going to use an Haskell example found in this paper “Concurrent
Haskell” by SPJ et al which I will develop a construct concurrency example known as a Channel. Since we are all students of Haskell, you would immediately see the
resemblance to [Control.Concurrent.Chan] package.
13. Concurrency
• Ideas => Cross pollination 【异花传粉】
• From papers, journals, conference slides,
talks etc
• Self learning, validation of ideas
• You gotta be something of a polyglot
But that paper’s code is written in Haskell ? How are we (haskell students) suppose to translate it successfully? That’s the
It’s an incredible skill to be able to translate ideas from different sources, internalise them and cross-pollinate them. Ideas are often more easily understood if you can see
it in action (e.g. papers in Haskell) and when you internalise them (you understand why it works, how it works) and finally you might want to try them out in another
environment (e.g. Scala) and it would be best if you didn’t have to re-invent everything ;)
It helps when something like cats-effect allows you to just “translate” from Haskell idea/source Scala source
14. A buffer variable
As in the paper entitled “Concurrent Haskell”, this is the code presented in that paper. The encoding here is straightforward. Might be a good idea about now to read up
or refresh your knowledge about [Control.Concurrent.MVar]. Here’s how we can translate it to Scala via cats-effect.
15. Concurrency - Example
Source of image: https://typelevel.org/cats-effect/concurrency/mvar.html
This slide is from the project’s website and my purpose was to see if I could translate the code from one of my favourite Haskell papers of all time (aka Concurrent
Haskell) using only MVars and this codebase is housed in [Control.Concurrent.MVar].
16. Channel Variable via MVar
An MVar can very nearly be used to mediate a producer/consumer connection: the producer puts items into the MVar and the consumer takes them out. The fly in the
ointment is, ofcourse, that there is nothing to stop the producer overrunning, and writing a second value before the consumer has removed the first.
This problem is easily solved, by using a second MVar to handle acknowledgements from the consumer to the producer. We call the resulting abstraction a CVar (short for
channel variable).
17. Channel Variable - How to use
There are a number of concurrent abstractions highlighted in that paper by SPJ et al and I’ve implemented them all w/o too much trouble (e.g. Quantity Semaphore, Skip
Channel (though the source is readily available in Haskell doc which is wonderous).
Final thing to do is to execute it: CVarDemo.run(null) and you should see a message like
Result of writing and reading a buffer-variable is : (0, 1, 2, 3).
18. Questions
• I’d be happy if you connect with me on Twitter and
LinkedIn, see below:
• @RaymondTayBL
• https://www.linkedin.com/in/raymondtayboonleong/
• I did two books on developing HPC solutions using
OpenCL (GPGPU) and programming Actors using Akka.