I've got an API that looks like this:
object Comics {
...
def impl[F[_]: Applicative]: Comics[F] = new Comics[F] {
def getAuthor(slug: Authors.Slug): F[Option[Authors.Author]] =
...
and a routing that looks like this:
object Routes {
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap {
case Some(author) => Ok(author)
case None => NotFound()
}
So when there is a None
, it gets converted to a 404. Since there are several routes, the .flatMap { ... }
gets duplicated.
Question: How do I move this into a separate .orNotFound
helper function specific to my project?
My attempt:
To make things simple for me (and avoid parameterisation over F
initially), I've tried to define this inside comicsRoutes
:
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
def orNotFound[A](opt: Option[A]): ???[A] =
opt match {
case Some(value) => Ok(value)
case None => NotFound()
}
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap(orNotFound)
But what's ???
here? It doesn't seem to be Response
or Status
. Also, the .flatMap { ... }
was made under import dsl._
, but I'd like to move this further out. What would a good place be? Does it go into the routes file, or do I put it in a separate ExtendedSomething
extension file? (I expect that ???
and Something
might be related, but I'm a little confused as to what the missing types are.)
(Equally importantly, how do I find out what ???
is here? I was hoping ???
at the type level might give me a "typed hole", and VSCode's hover function provides very sporadic documentation value.)
The type returned by Http4sDsl[F]
for your actions is F[Response[F]]
.
It has to be wrapped with F
because your are using .flatMap
on F
. Response
is parametrized with F
because it will produce the result returned to caller using F
.
To find that out you can use IntelliJ and then generate the annotation by IDE (Alt+Enter and then "Add type annotation to value definition"). You can also:
Ok
object imported from Statuses
trait is provided extension methods with http4sOkSyntax
implicit conversion (Ctrl+Alt+Shift+Plus sign, you can press it a few times to expand implicits more, and Ctrl+Alt+Shift+Minut to hide them again)http4sOkSyntax
by pressing Shift twice to open find window, and then pressing it twice again to include non-project symbols,OkOps
to EntityResponseGenerator
class which is providing you the
functionality you used (in apply
) returning F[Resposne[F]]
.So if you want to move things around/extract them, pay attention to what implicits are required to instantiate the DSL and extension methods.
(Shortcuts differ between Mac OS - which sometime use Cmd instead of Ctrl - and non-Mac OS systems so just check them in documentation if you have an issue).