You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
And then of course, everything you do with that result must be explicitly `flatMap`ped into the `Either`, and higher-order control flow libraries like Fs2 will often need some extra coaxing in order to make everything work the way you want it to. This gets old in a hurry, which often results in reaching for alternatives like `EitherT`. That way lies madness.
27
+
And then of course, everything you do with that result must be explicitly `flatMap`ped into the `Either`, and higher-order control flow libraries like Fs2 will often need some extra coaxing in order to make everything work the way you want it to. This gets old in a hurry, which often results in reaching for alternatives like `EitherT`. That way lies frustration and woe.
28
28
29
29
## Capabilities
30
30
31
-
The good news is that we now have a better answer here, and one which composes perfectly with the existing (and future) ecosystem, maintains all relevant concurrency properties, and which type-infers extremely well, particularly in Scala 3. The answer has been to double down on the relatively little-used implicit capabilities library for Cats, known under the very misleading name of Cats MTL.
31
+
The good news is that we now have a better answer here, and one which composes very nicely with the existing (and future) ecosystem, maintains all relevant concurrency properties, and which type-infers extremely well, particularly in Scala 3. The answer has been to double down on the relatively little-used implicit capabilities library for Cats, known under the very misleading name of Cats MTL.
32
32
33
33
The name "Cats MTL" comes from Haskell's MTL package, which in turn was pretty aptly named: "Monad Transformer Library". Haskell's MTL is entirely oriented around making it easier and more ergonomic to manipulate monad transformer *stacks*, which is to say, multiple layers of datatypes like `EitherT`, `Kleisli`, and so on. Monad transformer stacks are extremely difficult to work with, both in Scala and in Haskell, and so over time people progressively evolved techniques involving typeclasses in Haskell and implicits in Scala to more ergonomically manipulate composable effect types. Cats MTL was rooted in an adaptation of some of these ideas.
34
34
@@ -68,9 +68,12 @@ val program: IO[Unit] = Handle.allow[ParseError]:
68
68
_ <-IO.println(s"successfully parsed $x and $y")
69
69
yield ()
70
70
.rescue:
71
-
caseParseError.UnclosedBracket=>IO.println("you didn't close your brackets")
72
-
caseParseError.MissingSemicolon=>IO.println("you missed your semicolons very much")
IO.println("you missed your semicolons very much")
75
+
caseParseError.Other(msg) =>
76
+
IO.println(s"error: $msg")
74
77
```
75
78
76
79
There's a lot to unpack here! At the very beginning we define a custom error type, `ParseError`. This is just a domain error like any other, and you'll note that it *doesn't* extend `Exception` or `Throwable` or similar. Without Cats MTL, we would generally have to wrap this error up in `Either` in all our function's result types, if we wanted to use it (similar to what Circe does). In this case though, instead of adding the error to the result type, we added a `using` parameter to our `parse` function!
@@ -121,9 +124,12 @@ val program: IO[Unit] = Handle.allowF[IO, ParseError] { implicit h =>
121
124
_ <-IO.println(s"successfully parsed $x and $y")
122
125
} yield ()
123
126
} rescue {
124
-
caseParseError.UnclosedBracket=>IO.println("you didn't close your brackets")
125
-
caseParseError.MissingSemicolon=>IO.println("you missed your semicolons very much")
0 commit comments