Skip to content

Instantly share code, notes, and snippets.

@igstan
Last active August 29, 2015 13:58
Show Gist options
  • Save igstan/10022319 to your computer and use it in GitHub Desktop.
Save igstan/10022319 to your computer and use it in GitHub Desktop.
object Example {
trait HttpParser[A] {
// For the sake of simplicity, parse doesn't take any params.
// Returning an Option is to reflect the fact that parse may
// fail.
def parse: Option[A]
}
trait HttpRenderer[B] {
def render(b: B): String
}
object HttpRenderer {
implicit val StringRenderer = new HttpRenderer[String] {
override def render(s: String) = s
}
}
case class HttpResponse[A](
code: Int,
body: A,
renderer: HttpRenderer[A]
)
object Ok {
def apply[A : HttpRenderer](body: A) =
HttpResponse(200, body, implicitly[HttpRenderer[A]])
}
object BadRequest {
def apply[A : HttpRenderer](body: A) =
HttpResponse(400, body, implicitly[HttpRenderer[A]])
}
case class UserRequest(email: String)
object UserRequest {
implicit val UserRequestParser = new HttpParser[UserRequest] {
override def parse: Option[UserRequest] =
// Hardcoding for the sake of example.
Some(UserRequest("some@email.com"))
}
}
case class UserResponse(email: String)
object UserResponse {
implicit val UserResponseRenderer = new HttpRenderer[UserResponse] {
override def render(user: UserResponse): String = s"User[email = ${user.email}]"
}
}
def parse[A: HttpParser, B: HttpRenderer](fn: A => HttpResponse[B]): HttpResponse[B] = {
implicitly[HttpParser[A]].parse match {
case Some(a) => fn(a)
case None => BadRequest("Cannot parse body.") // <-- doesn't type check
}
}
def exampleUsage =
// This handler can return both a UserResponse as well as a String,
// which is why it won't type check. However, both of these types have
// an HttpRenderer typeclass instance. So, ideally, parse would be able
// to specify that the type wrapped by HttpResponse is *some* type for
// which an instance of HttpRenderer exists.
parse[UserRequest, UserResponse] { user =>
if (user.email != "some@email.com") {
BadRequest("Invalid user email") // <-- doesn't type check
} else {
Ok(UserResponse(email=user.email))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment