finch

A freestyle program can easily be used with Finch.

You can add the freestyle-finch module as follows:

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

Integration

The freestyle-finch module allows you to return a FreeS[F, io.finch.Output[A]] value if there is either a F.Handler[Id] or F.Handler[Future] in scope (where Future is Twitter’s Future) within an Endpoint#apply (eg get(string) { ... }).

Example

The standard freestyle imports:

import freestyle._
import freestyle.implicits._

In this example, we will create a Finch Endpoint which will be able to calculate the greatest common divisor (GCD) of two natural numbers:

@free trait Calc {
  def gcd(a: Int, b: Int): FS[Int]
}
// defined trait Calc
// defined object Calc

The greatest common divisor can be computed using Euclid’s algorithm:

def gcd(a: Int, b: Int): Int =
  if (b == 0) a.abs else gcd(b, a % b)
// gcd: (a: Int, b: Int)Int

A version returning a twitter Future:

import com.twitter.util.Future
// import com.twitter.util.Future

def gcdFuture(a: Int, b: Int): Future[Int] =
  Future.value(gcd(a, b))
// gcdFuture: (a: Int, b: Int)com.twitter.util.Future[Int]

We can now create a Calc.Handler[Future]:

import io.catbird.util._
// import io.catbird.util._

implicit val futureHandler = new Calc.Handler[Future] {
  def gcd(a: Int, b:Int): Future[Int] = gcdFuture(a, b)
}
// futureHandler: Calc.Handler[com.twitter.util.Future] = $anon$1@7150e9a1

Importing the freestyle-finch module, Finch, and shapeless’ :: used by Finch:

import freestyle.http.finch._

import io.finch._

import shapeless.::

We can use a freestyle program within Endpoint#apply:

cats.Monad[Future]
// res2: cats.Monad[com.twitter.util.Future] = io.catbird.util.FutureInstances$$anon$1@40291745

val gcdEndpointTwo = get(int :: int) { (a: Int, b: Int) =>
  Calc[Calc.Op].gcd(a, b).map(Ok(_))
}
// gcdEndpointTwo: io.finch.Endpoint[Int] = GET /:int :: :int

val gcdEndpointOne = get(int) { (a: Int) =>
  Calc[Calc.Op].gcd(a, 12).map(Ok(_))
}
// gcdEndpointOne: io.finch.Endpoint[Int] = GET /:int

val gcdEndpointZero = get(/) {
  Calc[Calc.Op].gcd(2209, 94).map(Ok(_))
}
// gcdEndpointZero: io.finch.Endpoint[Int] = GET /

We can combine the three endpoints and check the responses if we access the three endpoints:

import shapeless.:+:
// import shapeless.$colon$plus$colon

val endpoint = gcdEndpointTwo :+: gcdEndpointOne :+: gcdEndpointZero
// endpoint: io.finch.Endpoint[Int :+: Int :+: Int :+: shapeless.CNil] = (GET /:int :: :int :+: (GET /:int :+: GET /))

endpoint(Input.get("/83/2671")).awaitValueUnsafe()
// res3: Option[Int :+: Int :+: Int :+: shapeless.CNil] = Some(Inl(1))

endpoint(Input.get("/30")).awaitValueUnsafe()
// res4: Option[Int :+: Int :+: Int :+: shapeless.CNil] = Some(Inr(Inl(6)))

endpoint(Input.get("/")).awaitValueUnsafe()
// res5: Option[Int :+: Int :+: Int :+: shapeless.CNil] = Some(Inr(Inr(Inl(47))))