Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finch: not enough arguments for method 'toService'

I've made a pretty simple rest-method using Finch and Finagle:

val getUsers:Endpoint[List[User]] = get("users") {Ok(getAllUsers())}
Http.serve(":8080", getUsers.toService)

and got this error:

Error:(50, 32) not enough arguments for method toService: (implicit ts: io.finch.internal.ToService[List[DAL.Instances.User.User]])com.twitter.finagle.Service[com.twitter.finagle.http.Request,com.twitter.finagle.http.Response].
Unspecified value parameter ts.
  Http.serve(":8080", getUsers.toService)
                               ^

Any idea on how to fix it?

like image 630
NikitaAbrosimov Avatar asked Feb 26 '16 07:02

NikitaAbrosimov


1 Answers

The compiler error is a little better in the most recent version of Finch (0.10). If we have the following build config:

scalaVersion := "2.11.7"

libraryDependencies += "com.github.finagle" %% "finch-core" % "0.10.0"

And this setup:

import com.twitter.finagle.Http
import io.finch._

case class User(id: String, name: String)

def getAllUsers(): List[User] = List(User("111", "Foo McBar"))
val getUsers: Endpoint[List[User]] = get("users") { Ok(getAllUsers()) }

Then when we try to use toService, we get the following:

<console>:18: error: You can only convert a router into a Finagle service if the
result type of the router is one of the following:

  * A Response
  * A value of a type with an EncodeResponse instance
  * A coproduct made up of some combination of the above

List[User] does not satisfy the requirement. You may need to provide an
EncodeResponse instance for List[User] (or for some  part of List[User]).

       Http.serve(":8080", getUsers.toService)
                                    ^

The problem is that you haven't told Finch how to translate instances of your User type into HTTP responses. Finch's EncodeResponse is an example of a type class, which is an approach to polymorphism that's widely used in Scala (including the standard library) and many other statically-typed functional programming languages.

The easiest way to provide the appropriate EncodeResponse instances is to add Finch's Circe compatibility module to your build:

libraryDependencies ++= Seq(
  "io.circe" %% "circe-generic" % "0.3.0",
  "com.github.finagle" %% "finch-circe" % "0.10.0"
)

And then all you need is the following imports:

import io.finch.circe._, io.circe.generic.auto._

And toService will work just fine:

scala> Http.serve(":8080", getUsers.toService)
Feb 26, 2016 8:32:24 AM com.twitter.finagle.Init$$anonfun$1 apply$mcV$sp
INFO: Finagle version 6.33.0 (rev=21d0ee8b5070b735eda5c84d7aa6fbf1ba7b1635) built at 20160203-202859
res2: com.twitter.finagle.ListeningServer = Group(/0:0:0:0:0:0:0:0:8080)

Now if you go to http://localhost:8080/users, you'll see the following:

[{"id":"111","name":"Foo McBar"}]

This looks like magic, but what's happening is fairly principled. Circe is a JSON library that provides generic codec derivation that figures out at compile-time how to represent your case classes as JSON values (see my blog post here for more context).

The Finch cookbook is a great resource for learning more about questions like this, and the first section goes into detail about other ways to provide the EncoderResponse instances that toService needs. If you have any other questions or if any of the above isn't clear, feel free to ask either here or on Gitter.

like image 84
Travis Brown Avatar answered Nov 03 '22 13:11

Travis Brown