我正在使用 Scala 类型安全构建器模式来处理简单的休息请求。这作为一个流利的 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")
我想通过允许将Method
实例转换为Builder
实例来使其更像 DSL,但是当我添加尝试隐式包含init
构建器时,隐式转换和类型参数的组合会使编译器感到困惑。
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"
除 b3 外,所有行都编译。当toMethod
函数被显式调用时,可以隐式找到 builder 参数。此外,如果我删除通用参数(和类型安全),代码将按预期工作。
这是 scala 隐式转换的限制吗?还是我错过了实现这一目标的正确语法?
我想隐式地发现初始构建器实例,以使用户能够为他们自己的初始构建器提供一些构建器字段的默认值。
更新
我留下了一些代码以使示例保持简单,因为它只是我要修复的隐式转换。
类型安全的构建器模式在这里得到了很好的概述:http: //blog.rafaelferreira.net/2008/07/type-safe-builder-pattern-in-scala.html
之后,您只能在具有方法和 uri时调用该build
方法。Builder
我想发现builder作为隐式参数的原因是为了支持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))
}
在这些情况下
- 使用指定的 uri 创建一个新的构建器
url
- 然后在闭包中重用它
implicit b =>
- 该
assert
方法仅可用,因为已指定 uri 和方法 - 附加到当前 uri,这
/
仅在构建器指定了 uri 时才可用。
指定方法和 uri 的另一个示例
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)
}