#scala cats-mtl submarine error handling .. inspired by
Thanh Le (lenguyenthanh)'s talk at #scaladays .. here is a working #scastie
https://scastie.scala-lang.org/KcQYZKLgRM6NIwAtdPiYpg
Uses scalacli features and a 3.8.nightly (also tested with 3.7.4)
references:
1. https://github.com/lenguyenthanh/talks/blob/main/submarine/code/example5.scala
2. https://www.youtube.com/watch?v=nNqx2HiL7cc

Scastie - An interactive playground for Scala.
//> using scala 3.8.1 //> using option -language:experimental.saferExceptions //> using option -language:experimental.captureChecking // Describes computations that can fail with error `E` final class CanThrow[-E] // Internal exception used to jump to the catch block private final case class Break[E](value: E, label: CanThrow[E]) extends RuntimeException(null, null, false, false) // Library version of `throw` def failWith[A](a: A)(using l: CanThrow[A]): Nothing = throw Break(a, l) // Provides a way to catch errors. object Catch: def apply[A, E](op: CanThrow[E] ?=> A)(default: E => A): A = given label: CanThrow[E] = new CanThrow() try op catch case Break(value, `label`) => default(value) // Error types sealed trait Error extends Exception case class NegativeNumber(i: Int) extends Error case class InvalidInput(s: String) extends Error // sqrt can fail if its argument is negative. def sqrt(i: Int)(using CanThrow[NegativeNumber]): Double = if i >= 0 then math.sqrt(i) else failWith(NegativeNumber(i)) // parse can fail if its argument is not a valid int. def parse(s: String)(using CanThrow[InvalidInput]): Int = scala.util.Try(s.toInt).getOrElse(failWith(InvalidInput(s))) // Composing both widens the type of the error to the parent of `NegativeNumber` and `InvalidInput`. def composed(s: String)(using CanThrow[Error]): Double = sqrt(parse(s)) // I like this better - auto-widening is nice, but note how `Error` is not sealed: `composedSubtype` // forces me to deal with error cases that don't exist. def composedBetter(s: String)(using CanThrow[NegativeNumber | InvalidInput] ): Double = sqrt(parse(s)) def printSqrt(s: String): Unit = Catch(println(composedBetter(s))): case NegativeNumber(i) => println(s"Negative number: $i") case InvalidInput(s) => println(s"Invalid input: '$s'") @main def run = printSqrt("abc") printSqrt("-4") printSqrt("4")









