There are several components to the type-safety of spray. Here's a selection
- more-or-less complete model for HTTP data structures including many headers
- type-class-based (un)marshalling infrastructure
- type-safe data extraction in the routes, type-safe combination of route parts
To explain a bit more
A complete model for HTTP means that after parsing (or constructing) an HTTP message follows a certain fixed structure and certain invariants are met. E.g. this means user-level parsing of headers is usually not needed and runtime errors in code dealing with those headers can be ruled out to some degree.
The (un)marshalling infrastructure ensures statically that values of domain types can only then be converted into HTTP data structures if a marshaller for that type is statically available (through implicits). That means that in a route you can usually work on the level of your domain types with all the logic bridging the types being tucked away in Marshaller definitions.
In spray's route structure you can extract values from a request. E.g. you can use parameters('name.as[String], 'age.as[Int])
to access the name
and age
query parameter of a request. Using as[T]
means that the extracted value will be converted into the type T
at runtime. Inside the parameters
route you know that name
and age
will be of the right types (otherwise spray will generate an appropriate error message for the client).
spray's routes are composed of "directives". Each directive has a type Directive[T1 :: T2 :: ... :: HNil]
where the T1...
are types of values that are extracted by a directive. Directives can be freely composed, that means that instead of parameters('name.as[String], 'age.as[Int])
(which is of type Directive[String :: Int :: HNil]
) you could also write parameters('name.as[String]) & parameters('age.as[Int])
. This allows you to extract common patterns in your route into your own custom directives that you can re-use anywhere.
See the spray documentation on directives for more information about this topic.
It would also make sense to talk about points where spray isn't type-safe (some of those points may be addressed in the future, others are inherent in the architecture). E.g.
- You can miss the
~
operator while building a route.
- It can be hard to see if a content-type is allowed/generated for a route or not.
- It is currently possible not to handle a request in the route.
- A service you "ask" for an answer to a request (see the Akka ask pattern) may answer with an unexpected kind of message.
- You can violate the message protocol to spray actors (which is common to all message flow with Akka actors which is untyped).
- ...
spray.io
provides type safety. I appreciate your insight, and I agree with the question asked in your link. Not being defensive here, but I genuinely want to know how spray.io delivers type safety. Could you please recommend how to re-word this question (or move it)?