I use the Scala type constructor template safe for a simple rest request. This works great as a seamless api.
sealed abstract class Method(name: String) case object GET extends Method("GET") case object POST extends Method("POST") abstract class TRUE abstract class FALSE case class Builder[HasMethod, HasUri]( method: Option[Method], uri: Option[String]) { def withMethod(method: Method): Builder[TRUE, HasUri] = copy(method = Some(method)) def withUri(uri: String): Builder[HasMethod, TRUE] = copy(uri = Some(uri)) } implicit val init: Builder[FALSE, FALSE] = Builder[FALSE, FALSE](None, None) //Fluent examples val b1: Builder[TRUE, FALSE] = init.withMethod(GET) val b2: Builder[TRUE, TRUE] = init.withMethod(GET).withUri("bar")
I would like to make it more DSL-like by allowing the instance of Method converted to an instance of Builder . However, when I add an attempt to implicitly include the constructor init combination of implicit conversion parameters and the type confuse the compiler.
implicit def toMethod[HasUri](m: Method) (implicit builder: Builder[_, HasUri]): Builder[TRUE, HasUri] = builder.withMethod(m) // ** ERROR **: could not find implicit value for parameter builder: // Builder[_, HasUri] val b3: Builder[TRUE, TRUE] = GET withUri "foo" // However the implicit parameter is discovered fine when function is called directly val b4: Builder[TRUE, FALSE] = toMethod(GET) val b5: Builder[TRUE, TRUE] = toMethod(GET) withUri "foo"
All lines are compiled except b3. When the toMethod function toMethod called explicitly, the builder parameter can be found implicitly. Also, if I remove the general arguments (and the security type), the code works as expected.
Is this a limitation in Scala implicit conversions? Or am I missing the correct syntax to achieve this?
I want to open an instance of the initial instance implicitly, to allow users to provide their own initial builder with default values ββfor some fields of the builder.
Update
I left a piece of code to simplify the example, since this is just an implicit conversion that I'm trying to fix.
A line type type template is very well described here: http://blog.rafaelferreira.net/2008/07/type-safe-builder-pattern-in-scala.html
Then you can call the build method only after the Builder has a method and uri.
The reason I want to open the builder as an implicit parameter is to support the following case in DSL.
url("http://api.service.org/person") apply { implicit b => GET assert(Ok and ValidJson) GET / "john.doe" assert(NotFound) POST body johnDoeData assert(Ok) GET / "john.doe" assert(Ok and bodyIs(johnDoeData)) }
In these cases
- A new constructor is created with the specified uri using
url - Then it is reused on the close side as
implicit b => - The
assert method is available only because the uri and method are specified / joins the current uri, this is only available because the constructor has uri specified.
Another example where the method and uri are indicated
GET url("http://api.service.org/secure/person") apply { implicit b => auth basic("harry", "password") assert(Ok and ValidJson) auth basic("sally", "password") assert(PermissionDenied) }