Finch 0.6.0
This release involves several improvements of the core
package as well as a new "easter egg" mode of Finch with codename "Your REST API as a Monad". Although, this release is 100% source-compatible with a previous version.
Please, note that while the old-style io.finch.Endopint
is not deprecated yet, it's highly recommended to consider migration to route combinators.
Deprecations
There is a new naming conversion for RequestReader
s that is heavily inspired by Scala standard library:
- Every
RequiredX
is now justx
, eg.param("a")
- Every
OptionalX
is nowxOption
, eg.headerOption("X-Secret")
There is just one exclusion from this scheme:
RequiredParams
is nowparamsNonEmpty
OptionalParams
is nowparams
New Features
- There is a secret Finch mode available: deal with types that matter (see details bellow)
- The
RequestReader
is now polymorphic in terms of request type (see details bellow) - There are two new
RequestReader
s:fileUpload
andfileUploadOption
that can read an upload (amultipart/form
) param from the request - Both
param
andparamOption
are also looking atmultipart/form
content for param values - Two new combinators
~>
and~~>
are available onRequestReader
(see details bellow) - The
/>
combinator onRouter
is now more useful (see details bellow) - There is router
**
that always matches an entire route
Your REST API as a Monad
There is a secret (ninja) mode available in this release. It's fully described in docs/micro.md. The basic idea is to replace Service
(a function) with RequestReader
(a monad), which basically does the same job but implies insane composability. While it's still an experimental approach, we believe that it solves the "Finch Puzzle" and dramatically simplifies the whole picture. We personally believe that his way is "the way" to write idiomatic and robust Finch code.
Polymorphic RequestReader[A]
a la PRequestReader[R, A]
While this tiny change doesn't affect API, this is a huge step towards flexibility. Code speaks for them self, here is the current picture:
trait PRequestReader[R, A] {
def apply(req: R): Future[A]
}
type RequestReader[A] = PRequestReader[HttpRequest, A]
That said, all the monadic and applicative operations are defined over the output type A
(right associative).
Magic behind ~>
, ~~>
and />
combinators
Both ~>
and ~~>
compositors allow to threat underlying type A ~ B .. ~ Z
as (A, B, ..., Z)
. So, the following code compiles:
def sum(i: Int, j: Int) = i + j
val r: RequestReader[Int] =
param("i").as[Int] ~ param("j").as[Int] ~> sum
Using the ~~>
we can make long calls to the underlying async API (considering that Micro
is RequestReader
):
def fetchUserFromDb(id: Int): Future[User] = ???
def getUser: Micro[User] = RequiredParam("id").as[Int] ~~> fetchUserFromDb
Compositor />
basically implies the same idea but for types A / B / C ... Z
.
By tradition, kudos to awesome contributors: @jenshaase, @imliar and @suls!