Problems with Router (Kleisli vs PartialFunction)
The first problem is that PartialFunction doesn't compose well. Example:
@ val f1: PartialFunction[Int, Int] = { case 1 => 2 }
f1: PartialFunction[Int, Int] = <function1>
@ val f2: PartialFunction[Int, Int] = { case 1 => 3 }
f2: PartialFunction[Int, Int] = <function1>
@ f1.andThen(f2)
res2: PartialFunction[Int, Int] = <function1>
@ res2.isDefinedAt(1)
res3: Boolean = true
@ res2(1)
scala.MatchError: 2 (of class java.lang.Integer)
...
And this leads us to the problem. I want to log the state before and after toState call. Of cause I can write it directly, but what is FP for? :-) I want to do is as middleware and compose it. Maybe it's better to use Kleisli with OptionT here? It composes well.
The Second problem again with PartialFunction and toState. The signature of the function is PartialFunction[Router.Path, S => F[S]]. And if I want to make a decision about handling the path based on the old state, I just can't do it in the pattern matching. All I can do is to return the old state.
What if change Router definition like this?
final case class Router[F[_]: Async, S](
fromState: Kleisli[Option, S, Router.Path] = ???
toState: Kleisli[OptionT[F, *], (Router.Path, S), S] = ???
)
You offer is appropriate. Korolev will never depend on cats or scalaz directly beacuse of zero dependencies policy. However we can add your Kleisli-router to cats-effects-support module. New release is coming soon, so I waiting for pull request.