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 isF[Response[F]]
.It has to be wrapped with
F
because your are using.flatMap
onF
.Response
is parametrized withF
because it will produce the result returned to caller usingF
.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 fromStatuses
trait is provided extension methods withhttp4sOkSyntax
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
toEntityResponseGenerator
class which is providing you the functionality you used (inapply
) returningF[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).