Play Framework integration

Freestyle programs are easy to use as a result of a Play Framework Action with the freestyle-http-play module. This module provides an implicit conversion FreeS[F, A] => Future[A] which allows a user to define a Free program as the result of any Play Action that expects a Future as a response.

To enable this integration you can depend on freestyle-http-play with Play framework 2.6:

libraryDependencies += "io.frees" %% "freestyle-http-play" % "0.3.1"

The regular imports for working with Freestyle and Cats:

import freestyle._
import freestyle.implicits._
import cats._
import cats.implicits._

And some imports for the freestyle-http-play module and Play itself:

import freestyle.http.play.implicits._

import javax.inject._

import play.api.mvc._
import play.api.http._

import scala.concurrent.ExecutionContext

import akka.actor.ActorSystem
import akka.stream.{ActorMaterializer, Materializer}

For demonstration purposes, we will create a very simple program that returns an OK http status response:

import freestyle._
// import freestyle._

object algebras {
  @free
  trait Noop {
    def ok: FS[String]
  }
}
// defined object algebras
import freestyle._
// import freestyle._

import freestyle.implicits._
// import freestyle.implicits._

import cats._
// import cats._

import cats.implicits._
// import cats.implicits._

object handlers {
  import algebras._

  implicit def noopHandler[M[_]](
      implicit MM: Monad[M]
  ): Noop.Handler[M] = new Noop.Handler[M] {
    def ok: M[String] = MM.pure("success!")
  }
}
// defined object handlers
import algebras._
// import algebras._

import handlers._
// import handlers._

def program[F[_]: Noop]: FreeS[F, Result] =
  for {
    msg <- Noop[F].ok
  } yield Results.Ok(msg)
// program: [F[_]](implicit evidence$1: algebras.Noop[F])freestyle.FreeS[F,play.api.mvc.Result]

Once our program is defined we may simply return it as a result of any Action without the need to explicitly interpret it to Future:

import algebras._
import handlers._

class AppController @Inject()(val controllerComponents: ControllerComponents)(implicit ec: ExecutionContext) extends BaseController {
  def endpoint = Action.async { _ => program[Noop.Op] }
}