I've got some code that returns an IO but I need a Effect in http4s.
import cats.effect.{Effect, IO}
class Service[F[_]: Effect] extends Http4sDsl[F] {
val service: HttpService[F] = {
HttpService[F] {
case GET -> Root =>
val data: IO[String] = getData()
data.map(d => Ok(d))
}
}
}
gives
[error] found : cats.effect.IO[F[org.http4s.Response[F]]]
[error] required: F[org.http4s.Response[F]]
[error] data.map(d => Ok(d))
[error] ^
One way we can get around using a concrete IO[A]
is using LiftIO[F]
:
class Service[F[_]: Effect] extends Http4sDsl[F] {
val service: HttpService[F] = {
HttpService[F] {
case GET -> Root =>
getData().liftIO[F].flatMap(Ok(_))
}
}
}
LiftIO
lifts will lift: IO[A] => F[A]
, but this yields us F[F[Response[F]
. In order to get things to compile, we'll flatten
on F
since it has a Monad
(or FlatMap
) instance in cats due to our Effect
context bounds requirement.
If we want more detail, this is the -Xprint:typer
result:
cats.implicits.catsSyntaxFlatten[F, org.http4s.Response[F]](
cats.effect.LiftIO.apply[F](Service.this.evidence$1)
.liftIO[F[org.http4s.Response[F]]](
data.map[F[org.http4s.Response[F]]](
((d: String) => Service.this.http4sOkSyntax(Service.this.Ok)
.apply[String](d)(Service.this.evidence$1,
Service.this.stringEncoder[F](
Service.this.evidence$1, Service.this.stringEncoder$default$2[F]))))))(Service.this.evidence$1).flatten(Service.this.evidence$1)
And at the end of the world when you want to give a concrete effect, for example Service[IO]
, we get:
val serv: Service[cats.effect.IO] =
new Service[cats.effect.IO]()(effect.this.IO.ioConcurrentEffect)
Where ioConcurrentEffect
is the Effect[IO]
instance.
It seems that all the good stuff is defined at org.http4s.syntax.AllSyntax
trait.