A simple HTTP server can be represented with the following function:
Request => Response
However, we commonly need to perform an effectful operation such as retrieving data from database or call external service. Such cases we would define like this:
Request => F[Response]
http4s has own abstraction over Routes (Endpoints) called HttpRoutes[F] and it is type alias for Kleisli[OptionT[F, ?], Request, Response] as mentioned in the documentation.
As we know from cats documentation, Kleisli is just compositions of functions that return a monadic value like Option[A], Either[L, R], etc.
One of the most useful properties of functions is that they compose. That is, given a function A => B and a function B => C, we can combine them to create a new function A => C.
In this case http4s's Kleislei takes Request and returns F[Response], simply Request => F[Response]. F is an effect type.
With a bit of modification the above Request and Response types, we get the following:
type HttpRoutes[F] = Kleisli[OptionT[F, *], Request[F], Response[F]]
An HttpRoutes[F] is declared as a simple series of case statements. Each case statement attempts to match and optionally extract from an incoming Request[F]. The code associated with the first matching case is used to generate a F[Response[F]].
The simplest case statement matches all requests without extracting anything. The right hand side of the request must return a F[Response[F]].
We can see here simple demonstration of F[Response[F]]
scala> val getRoot = Request[IO](Method.GET, uri"/")
getRoot: org.http4s.Request[cats.effect.IO] = Request(method=GET, uri=/, headers=Headers())
scala> val io = service.orNotFound.run(getRoot)
io: cats.effect.IO[org.http4s.Response[cats.effect.IO]] = <function1>
Here cats.effect.IO[org.http4s.Response[cats.effect.IO]] is unresolved yet, this is aync result. It will be resolved in future at some point as simple Future/Promise. And we can enforce it to run:
scala> val response = io.unsafeRunSync
response: org.http4s.Response[cats.effect.IO] = Response(status=200, headers=Headers())
- Kleisli
- Http4s
- Practical FP in Scala by Gabriel Volpe