5

部分関数があるとします (Akka のアクターの receive メソッドに似ています)。

def receive: PartialFunction[Any, Unit] = {
    case SomeCaseClass(params) => println("whatever")
}

この関数のすべての定義済みパラメーターを取得する方法はありますか?

JSON RPC のようなサービス構造を実装しています。私は基本的に、クライアントが部分関数を介してサービスを定義できるようにしたいと考えています。

def clientService = {
    case Connect(login, password) =>.....
    case SomeMessage => ...
    case SomeMethod(bla) => ..
}

たとえば、最初のメソッドは次のように変換されます

{method: "connect", params:{login: "asdsad", password: "adsada"}}

(この部分はすでに機能しています)

クライアントがサービスを定義し、別のクライアントがサービスの利用可能なメソッドについて知りたい場合、要求しているクライアントに伝えることができるように、サービスが受け入れるケース クラスの種類を知る必要があります。オブジェクトの通常のメソッドでこれを簡単に実行できることはわかっていますが、JSON を解析してケース クラスに変換する方法のおかげで、部分関数によって API が簡素化され、美しいコードが気に入っています ;)

さらに、コンパイル後/実行時に部分関数がどのように表現されるかはわかりませんが、リフレクションを介した方法が必要であると確信しています。

4

2 に答える 2

3

サービスについてどれだけ多くの仮定を立てることができるか、または喜んで行うかにもよりますが、オプションになる可能性のあるまったく単純なアプローチを次に示します。基本的には、1) 考えられるすべてのメッセージ タイプが既知であること、および b) 部分関数が 1 つの次元でのみ部分的であること (詳細は後述) に依存しています。

可能なメッセージ タイプの有限セットが必要です。

sealed trait Message

case class Hello(who: String) extends Message
case class Lunch(withWhom: String) extends Message
case class Dinner(withWhom: String) extends Message
case class Goodbye(who: String) extends Message

そして、いくつかのサービス例:

val service0: PartialFunction[Any, Unit] = {
  case Hello(who) => ()
  case Goodbye(who) => ()
}

val service1: PartialFunction[Any, Unit] = {
  case Hello(who) => ()
  case Lunch(withWhom) => ()
  case Goodbye(who) => ()
}

var services = List(service0, service1)

次に、受け入れられたメッセージの設計図として機能するメッセージ インスタンスのリストも定義します。

val simpleInstances = List(Hello("*"), Lunch("*"), Dinner("*"), Goodbye("*"))

最後に、部分関数から受け入れられた引数と可能な引数のリストを返すメソッドを定義します。

def supportedArguments[F, T, A <: F]
                      (pf: PartialFunction[F, T], args: Iterable[A]) =

  args filter pf.isDefinedAt

きれいなプリンター:

def printSupportedArguments(services: Iterable[PartialFunction[Any, Unit]],
                            messages: Iterable[Message]) {

  services.zipWithIndex.foreach {case (s, i) =>
    val supported = supportedArguments(s, messages)
    println(s"Service $i supports $supported")
  }
}

さあ行こう:

printSupportedArguments(services, simpleInstances)
  // Service 0 supports List(Hello(*), Goodbye(*))
  // Service 1 supports List(Hello(*), Lunch(*), Goodbye(*))
  // Service 2 supports List(Goodbye(*))

サービスが受け入れるメッセージ タイプに関して部分的であるだけでなく、受け入れるメッセージ コンテンツに関しても部分的である場合、つまり、サービスが複数の方向で部分的である場合、事態はさらに複雑になります。

val service2: PartialFunction[Any, Unit] = {
  case Hello("Thomas") => ()
  case Hello("Laura") => ()
  case Goodbye(who) => ()
}

このようなサービスには、ブループリント インスタンスのリストの適応も必要です。

val moreInstances = simpleInstances ++ List(Hello("Thomas"), Hello("Laura"))

その結果:

printSupportedArguments(services :+ service2, moreInstances)
  // Service 0 supports List(Hello(*), Goodbye(*), Hello(Thomas), Hello(Laura))
  // Service 1 supports List(Hello(*), Lunch(*), Goodbye(*), Hello(Thomas), 
  //                         Hello(Laura))
  // Service 2 supports List(Goodbye(*), Hello(Thomas), Hello(Laura))

このアプローチの一連の欠点には、明らかに次のようなものがあります。

  • メッセージの種類を知っておく必要があります

  • サービスが複数の次元で部分的である場合、考えられるすべてのメッセージ コンテンツも把握する必要があります。

  • Hello("*")の任意のインスタンスを示すためにインスタンスを使用すると、受け入れるサービスと文字通りのみ受け入れるサービスHelloを区別することができなくなります。Hello(_)Hello("*")

マクロまたはリフレクション (またはマジック) を使用した実際の解決策を見つけた場合は、ここに投稿してください。

于 2013-05-15T09:08:49.517 に答える
1

Any一致する引数のすべての入力のリストを取得できるかどうかを尋ねていますか? それがあなたの質問なら、答えはisDefinedAtノーだと思います。引数がこの部分関数に指定された場合に一致するかどうかを伝えるだけですが、それはおそらくあなたが探していたものではありません。

于 2013-05-14T13:23:48.273 に答える