Last active
August 29, 2015 13:58
-
-
Save igstan/10022319 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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