読者です 読者をやめる 読者になる 読者になる

テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(scala report) sprayのMarshal/Unmarshalの基本原理

sprayは, StringとかXMLとかArray[Byte]とか, はたまたStream[T]までも, 自動的にHttp Entityに格納する仕組みがある. もちろんそれは, 以下の2つの情報が必要になる.

  1. データは何か
  2. それはいかにしてクライアントに読み出されるか

それが隠蔽されたのがMarshallingContextというクラスだと言ってよい. ざっくりと何を言ってるのかは, sprayのMarshallingに関するドキュメントを読む. http://spray.io/documentation/1.2.2/spray-httpx/marshalling/

遅延評価の関係するStreamのMarshallingは非常にやっかいなことをしていて, はっきりいうとまだ曖昧にしか理解してないのだけど, StringやらXMLやらをMarshalするコードの基本原理については理解したように思う. ポイントは, implicitlyで型を取得するところ. 実験をしたコードが以下. 読みやすいように, 型は敢えて冗長に書いてある.

  val m = mutable.Map[String, String]()

  trait Marshal[T] {
    def marshal(t: T): String
  }
  implicit val intMarshal = new Marshal[Int] {
    override def marshal(v: Int): String = v.toString
  }
  case class Write[T: Marshal](key: String) {
    def apply(v: T): Unit = {
      val str: String = implicitly[Marshal[T]].marshal(v)
      m += key -> str
    }
  }

  trait Unmarshal[T] {
    def unmarshal(v: String): T
  }
  implicit val intUnmarshal = new Unmarshal[Int] {
    override def unmarshal(v: String): Int = {
      v.toInt
    }
  }
  def read[T: Unmarshal](key: String): T = {
    val str: String = m(key)
    implicitly[Unmarshal[T]].unmarshal(str)
  }

  Write[Int]("age").apply(31)
  val age = read[Int]("age")
  println(age)

実はこれは以前に探求したscalazがいかにして型クラスを表現しているか(http://akiradeveloper.hatenadiary.com/entry/2015/06/23/221230)と基本的にはあまり変わらないのだけど, 実際に自分でシンプルなもので実験したことに意味がある.

Scalaは良い.