私には、Scala で暗黙的な依存性注入をより簡単にするための素晴らしいアイデアがありました (まあ、それには議論の余地がありますが、私にアイデアがあったとしましょう)。私が抱えている問題は、暗黙的な依存関係を必要とするメソッドを呼び出す場合、その具体的な依存関係が最終的にスコープに入るまで、呼び出し元のメソッドを同じ依存関係で装飾する必要があることです。私の目標は、具象クラスに混在するときに暗黙のグループを必要とする特性をエンコードできるようにすることでした。これにより、暗黙を必要とするメソッドを呼び出すことができますが、その定義は実装者に委ねられます。
これを行う明白な方法は、この疑似スカラのようなある種の自己型を使用することです:
object ThingDoer {
def getSomething(implicit foo: Foo): Int = ???
}
trait MyTrait { self: Requires[Foo and Bar and Bubba] =>
//this normally fails to compile unless doThing takes an implicit Foo
def doThing = ThingDoer.getSomething
}
その優れた構文を得るために実際に atrait and[A,B]
を実装しようと勇敢に何度か試みた後、私は shapeless から始めて、それでどこにでも行けるかどうかを確認する方が賢明だと思いました。私はこのようなものに着陸しました:
import shapeless._, ops.hlist._
trait Requires[L <: HList] {
def required: L
implicit def provide[T]:T = required.select[T]
}
object ThingDoer {
def needsInt(implicit i: Int) = i + 1
}
trait MyTrait { self: Requires[Int :: String :: HNil] =>
val foo = ThingDoer.needsInt
}
class MyImpl extends MyTrait with Requires[Int :: String :: HNil] {
def required = 10 :: "Hello" :: HNil
def showMe = println(foo)
}
これが実際にコンパイルされたとき、私はかなり興奮していたと言わざるを得ません。しかし、実際に をインスタンス化すると、 と の間でMyImpl
無限の相互再帰が発生することがわかります。MyImpl.provide
Required.provide
shapeless で犯した間違いが原因だと思う理由は、ステップスルーすると、それに到達してselect[T]
から HListOps にステップインし (HListOps にはselect[T]
メソッドがあるため、理にかなっています)、その後跳ね返るように見えるからです。への別の呼び出しにRequires.provide
。
私の最初の考えは、それを明示的に保護していないため、からSelector[L,T]
暗黙的に取得しようとしているということでした。しかし、provide
provide
- コンパイラは、 を取得するつもりはないことを認識し、
Selector
別provide
の候補を選択するか、コンパイルに失敗する必要がありました。 provide
暗黙的に受け取ることを要求してガードすると (この場合、を取得するSelector[L,T]
だけで済みます)、 が原因でコンパイルされなくなります。これは、アドレス指定の方法がよくわかりません。apply
Selector
T
diverging implicit expansion for type shapeless.ops.hlist.Selector[Int :: String :: HNil]
私の考えがそもそも間違っている可能性があるという事実は別として、人々が通常、この種の神秘的で核心を突いたものをどのようにデバッグしているのか知りたいです。ポインタはありますか?