Hoes do Spray Parameters work?

289 Views Asked by At

I'm trying to wrap my head around how Spray has implemented their Directives, and in particular the Parameter extraction DSL.

I understand the magnet pattern (barely) but am stuck on how the ParamDefMagnet and ParamDefMagnet2 work together.

def parameter(pdm: ParamDefMagnet): pdm.Out = pdm()

trait ParamDefMagnet {
  type Out
  def apply(): Out
}

trait ParamDefMagnet2[T] {
  type Out
  def apply(value: T): Out
}

type ParamDefMagnetAux[A, B] = ParamDefMagnet2[A] { type Out = B }
  def ParamDefMagnetAux[A, B](f: A ⇒ B) = new ParamDefMagnet2[A] { type Out = B; def apply(value: A) = f(value) }

I'm trying to work out how a ParamDefManget2 is implicitly converted to a ParamDefMagnet by the the below implicit method.

object ParamDefMagnet {
  implicit def apply[T](value: T)(implicit pdm2: ParamDefMagnet2[T]) = new ParamDefMagnet {
    type Out = pdm2.Out
    def apply() = pdm2(value)
  }
}

If i call parameter("name"), how is "name" implicitly converted to a ParamDefMagnet? And if it converts it to a ParamDefMagnet2 first, then where does value: T come from in order to convert it to a ParamDefMagnet?

1

There are 1 best solutions below

1
Upio On

So after digging around with examples, I think i've finally got to the bottom of how the parameter function works:

def parameter(pdm: ParamDefMagnet): pdm.Out = pdm()

An example for extracting a parameter of type String:

val p: Directive1[String] = parameter("name")

// we can then apply the function with the extracted name
p { name => 
   // stuff
}

Spray uses a bunch of implicit conversions but basically, if you have a String and a String => Directive1[String], you can construct a () => Directive1[String]:

// Our String => Directive1[String]
val pdm2: ParamDefMagnet2[String] { type Out = Directive1[String] } = ParamDefMagnet2.fromString

// Our () => Directive1[String]
val pdm: ParamDefMagnet { type Out = Directive1[String] } = new ParamDefMagnet {
  type Out = Directive1[String]
  def apply() = pdm2("name")
}
val directive: Directive1[String] = pdm()
// equivalent to:
val directive2: Directive1[String] = parameter("name")

All of this is what constitutes the simple parameter("name") call:

val p: Directive1[String] = parameter("name")

For how a Directive1[String] is applied in a DSL-ey way, see How do directives work in Spray?