我尝试Scala的联合类型在这个万里萨宾的博客文章中定义:斯卡拉联合类型与封闭
http://www.chuusai.com/2011/06/09/scala-union-types-curry-howard/
,并在
How to define "type disjunction" (union types)?
讨论了简单的情况下,在那里定义它们工作正常,但我想要做的就是使用它们在Play Framework中创建一个只接受某些值(String,Boolean,Int,Undefined)的通用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,或者是否有我不在这里的东西?
任何想法我可以在这里做什么来获得这些工作,或者这是可能的吗?我对Scala更先进的类型和类型lambda非常陌生,但我正在尝试了解它们如何工作来创建更多的类型安全代码。
我还注意到,Miles Sabin有一个名为Shapeless的图书馆(),我正在研究这个图书馆,但是找不到任何可以做同样的事情的东西,我试图在这里实现。
我不知道这是为什么不编译,但即使它的类型擦除会使你所做的事情变得危险。你将引用'Option [String]'或'Option [Int]',并且由于类型擦除,你将无法将它们分开。这绝对不是“更安全的代码”。一个更好的解决方案是声明你自己的从一个公共子类降序的类集合,一个用于你正在联合的每个类型,一个用来替换'None'。这实际上具有更强的输入,因为你对这个用例有自己特定的类型,而不是重用'Option'。 – wingedsubmariner
你的意思是有一个UpdateType类,然后有像UpdateTypeString,UpdateTypeNone等子类?难道这不仅仅是一种不那么通用的方式来完成我想要在类型联合中完成的工作吗?在这两种情况下,我都需要在结尾模式中匹配类型。 – Klaus
是的,但是使用UpdateType类和孩子,模式匹配实际上可以工作。另外,如果UpdateType被标记为'sealed',编译器可以检查你没有忘记任何类型。 OO真的是在Scala中做到这一点的正确方式,类型级编程适合人们炫耀,但不属于生产代码。 – wingedsubmariner