For the third year in a row, I was attending Scala.io in Lyon at the end of October. Like in 2018, the conference spanned on 3 days, the first one being dedicated to workshops. I only had a ticket for the 2 days of talks. As always, this conference is the place to meet the Scala community in France. For this edition, I might regret that it was "too French". But anyway, Scala.io remains a place were every aspects of Scala blend together: pure FP, spark related stuff, akka, tooling...
The first thing to note about this edition is that the keynotes were given by women keynoting for the first time. The first one being done by Oli, the runner of the Scala Love podcast and the second one by Daniela Sfregola, the initiator of the #scalaThankYou campaign and person full of good energy !
Among the talks I attended some of them really caught my attention completely so that I decided to write down a short summary here.
ZIO & Error Management by François Armand
First talk, right after Oli's keynote, François gives a talk on both on architectural and implementation levels. François states on a few slides how we, developer work: we solve problems by creating models which reflect a reality, the real world. Consequently, we make shortcuts, we have bias, and so, we mainly solve the nominal case. Remains bugs, misunderstanding of the problem,.. But our work goes beyond the nominal case. As developers, we must address failure. We must consider errors as a mean to transfer meaningful and useful intelligence to the receiver of said error. In the context of modern web applications, we have to take care of developers (our team), the ops (the team in charge of running the app, meaningful errors will benefit them firs), and eventually a customer. The responsibility of the developer then, is to find the right balance between errors & nominal case and to give some semantic to both. The question is : "how does this translate in real life" ? François states five points to answer:
- Using pure functions
- Using a dedicated chanel for errors
- Distinguish Errors & Failures
- Define strict interfaces and protocols & stick to them
- Use composition, and get the most of tooling
The speaker highlight how these point may be applicable for several layers of an application (architecture, ...)
The first point is obvious for functional developers: we must not lie in a function definition, and in correlation with the second point, using types to their full potential helps in clarifying and expressing the exact contract of a function. In Scala this often translate to things like this:
// A total function def totalPrice(c: Cart): Price = // some implementation of this // A common trait trait MyAppError // A dedicated error chanel using Either type PureResult[A] = Either[MyAppError, A] // Function in pure code, which might fail def computeInvoice(c: Cart): PureResult[Invoice] = // whatever // Function performing effect def sendInvoice(c: Customer, i: Invoice): IO[MyAppError, Summary] = // ...
François explains the third point by showing an example extracted from his use case at Rudder.io : the case of
java.lang.SecurityException . As soon as we deal with the file system, you can get this exception. So, is it an error, or a failure. In Rudder.io, François tells us that it depends. In some parts of the system, it is an error (something recoverable, expected, a signal for users, reflected in types) while in some other areas it is a failure (something unexpected, leading to an unknown state, not reflected in types) . To determine the limit, François invites us to make diagrams of ours systems and show interactions between them, clearly stating boundaries.
Systems have horizon & horrors lie beyond
The forth point is addressed by carefully designing subsystems. In functional programming we have powerful tools to do so: ADT. The idea for every subsystem is to build around a pure core and add side effects around it, keeping a stable interface for other subsystems to be used.
Regarding the fifth point, François reminds the audience of checked exception in Java, which were quite disliked at the time but also were a good signal for users of an API on what to expect. As Exceptions are a pain to deal with, we need a system to manage errors which allows composition.
With all this on the table, François states how ZIO is a library allowing to fulfill the aforementioned points. ZIO provides an effect system with a dedicated channel for errors, and also some goodies to analyze faillures.
I especially liked this talk, not because it was about ZIO, but it highlighted a way of thinking. It just appears that ZIO fits the requirements. In the end, this talk invites us to think better when we design a system, to take care of our users (ops, other devs and customers), in this aspect, it reminded me of the talk François Teychene did at Devfest Toulouse.
By the way, the slides are here.
Much ado about testing by Nicolas Rinaudo
Almost like François's talk this talk could have take place in any conference targeting developers and not only Scala developers.
Using the FizzBuzz example, Nicolas talks about testing, and more accurately about Dynamic (running the software and analyzing its behaviour) Verification (are we building the product right ?) . He gives the three phases composing testing:
- creating input (leading to the test case generation problem)
- evaluating the program
- analysing the output (leading to the oracle problem)
Then Nicolas talks about example testing, which is what we do in Java with JUnit. With this technique, the input is data for which we know the output. Often, it results in a test that only tested what the developer implemented: this is the tester bias. To overcome this, we can use decision tables, this clariy the specification. Showing examples Nicolas cames to the conclusion that example based testing is great at the oracle problem, but is also weak at test case generation.
This leads him to Generative testing. The concept is to run a test on random data. This solves the generation problem, what about the oracle problem ? The idea is to test properties. But getting this right takes time and there are traps on the way :
- we can easily fall in the trap when we actually rewrite the system under test (in a worst form, because of the code needed for test ceremony)
- almost as easily, we can force an oracle
Fortunately Nicolas has tips to share. Properties fall in a set of known categories. In case of refactoring or optimisation, we can use the old implementation as an oracle. Also, we have to keep in mind that errors are outputs too. Another category is invertible properties : like substraction is the inverse of addition. This works greatly for encoder/decoder pairs. Idempotence and Invariance are also properties that can be tested. But the most interesting one is metamorphic relation.
This is harder to get and the best way to learn more, is to watch Nicolas presenting it !
And the slides are here.
Scala.io is also a place to focus on the basics and the talk "10 tips to write fast stack-safe functional code in Scala: the automatic-batching library experience report" by Christophe Calvès was one of them. In this deep dive into tail recursion, Christophe gives useful tips to avoid the dreadful
StackOverflowException. In this talk I especially learned about
cats.Defer and the
tailRecM function. To quickly sum up,
Defer works for types that are implemented with a trampoline.
def tailRecM[A, B](a: A)(f: A => F[Either[A, B]]): F[B] allows to embed recursive calls of a function
f into a safe (in terms of stack) manner. I found out the FAQ section of cats has an entry about it.
As lots of talks were about effects, I had to attend the one by Wiem Zine Elabidine : Flying Futures in the same sky can make the moon sun at midnight . In her talk, she highlights the biggest issue with Futures: Referential Transparency. And she introduces how ZIO can help. I really liked how she presented IO and Fiber:
- IO models an effect
- Fiber models a running computation
She also talked about STM and how it can be used to solve transaction issues.
To keep going on the effects topic, I attended a talk by Nicolas Francois about cats-retry. This library is designed to apply retry policy on IO type from cats-effect. I rembered this project as I did an implementation of it in Kotlin (with Arrow, of course) a while back.
Of course, Scala.io had its share of talks about parsers, introductions to category theory, ADTs, akka, and functional programming in general. On the content side, I wasn't disapointed, although it wasn't as advanced as in previous editions.
Scala.io is an indie conference. The ticket is cheap, and the focus is clearly made on talks. It is worth mentioning that organizers kept great care on diversity and to state a few examples, we can say that there were food for vegetarians, that the food was halal, and that the keynote speakers were women. Some will regret the amount of food, and the lack of space to settle and eat. I'm more disapointed by the 2 hours break for lunch and I have to say, it was better in 2018 with lightning talks.
During the community party, I overheard the organizition team had some issues to get sponsors on board, so the budget came late and was limited. Well, regarding this fact, the staff did an amazing job. I mean, I come to Scala.io to learn, share & connect with the Scala community and I think the conference completed these goals. So congratulations again to the volunteers shaping up Scala.io. I really want to thank them for giving the opportunity to speak for first time speakers, to be independant and to be very close to #noBullshit. Special thanks also to Linkfluence, my employer, for sending me there !
I hope there will be a Scala.io 2020, in Lyon or Paris or elsewhere. And sure, I won't miss it.