単純な休息リクエストに 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
インスタンスをインスタンスに変換できるようにすることで、これをより DSL に似せたいと思いますがBuilder
、ビルダーを暗黙的にインクルードしようとすると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
暗黙のパラメーターとしてビルダーを発見したい理由は、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 が指定されているためにのみ使用できます。
method と 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)
}