【发布时间】:2014-04-30 01:15:22
【问题描述】:
我正在试用 Miles Sabin 的博文中定义的 Scala 联合类型:
http://www.chuusai.com/2011/06/09/scala-union-types-curry-howard/
也讨论过
How to define "type disjunction" (union types)?
对于在那里定义的简单案例,它们可以正常工作,但我想做的是使用它们在 Play Framework 中创建一个通用 JSON 解析器,它只接受某些值(字符串、布尔值、整数、未定义),然后顺利通过。
这是我的代码:
type UpdateType = Option[String] |∨| Option[Int] |∨| Option[Boolean] |∨| Option[List[Int]]
def withValue[T : (UpdateType)#λ](request: Request[JsValue])(block: (String, T) => Future[SimpleResult]) = {
val field = request.body \ ("field")
val value = request.body \ ("value")
(field, value) match {
case (x: JsString, y: JsString) => block(x.value.toString, Some(y.value.toString))
case (x: JsString, y: JsNumber) => block(x.value.toString, Some(y.value.intValue))
case (x: JsString, y: JsBoolean) => block(x.value.toString, Some(y.value.booleanValue))
case (x: JsString, y: JsUndefined) => block(x.value.toString, None)
case _ => Future.successful(BadRequest(s"Incorrect field, value pair for ${request.body}."))
}
}
然后我想以这种方式使用它:
def update(code: String) = Action.async(parse.json) {
request =>
withValue(request) { (field, value) =>
// Code that does something with the value
Future(Ok)
}
}
但是我的 withValue 函数给出了编译错误:
[error] found : Some[String]
[error] required: T
[error] case (x: JsString, y: JsString) => block(x.value.toString, Some(y.value.toString))
不应该是 UpdateType 应该接受 Some[String] 还是有什么我没有得到这里?
知道我可以在这里做些什么来使这些工作正常进行吗?或者有可能吗?我对 Scala 中更高级的类型和类型 lambdas 很陌生,但我正在尝试了解它们如何工作以创建更多类型安全的代码。
我还注意到 Miles Sabin 有一个名为 Shapeless (https://github.com/milessabin/shapeless) 的库,我正在研究它,但在那里找不到任何可以与我在这里尝试实现的功能相同的东西。
【问题讨论】:
-
我不确定为什么不编译,但即使它确实类型擦除也会使你正在做的事情变得危险。您将引用
Option[String]或Option[Int],并且由于类型擦除,您将无法区分它们。这绝对不是“更安全的代码”。更好的解决方案是声明您自己的从一个公共子类继承的类集合,一个用于您要联合的每种类型,一个用于替换None。这实际上具有更强的类型,因为您有自己的特定类型用于此用例,而不是重用Option。 -
您的意思是拥有一个 UpdateType 类,然后拥有像 UpdateTypeString、UpdateTypeNone 等子类吗?这难道不是一种不太通用的方式来完全按照我在此处尝试使用类型联合执行的操作吗?在这两种情况下,我都需要在最后匹配类型。
-
是的,但是对于 UpdateType 类和子类,模式匹配实际上会起作用。此外,如果 UpdateType 标记为
sealed,编译器可以检查您是否忘记了任何类型。 OO 确实是在 Scala 中做到这一点的正确方法,类型级编程是供人们炫耀的,但不属于生产代码。 -
我不知道。这行得通,但最后现在有一个样板文件遍布我的代码,我只想要一些简单地确保我只能将 String、Int 或 Boolean 传递给方法的东西。
-
无形中的 Coproduct 在这种情况下会有所帮助github.com/milessabin/shapeless/wiki/… 吗?