当我正在设计模型时,我在两种不同的指示类型参数List必须的方法之间徘徊nonEmpty.我开始使用List[Int]附带的require声明来验证List是nonEmpty.
case class A(name: String, favoriteNumbers: List[Int]) {
require(favoriteNumbers.nonEmpty, "favoriteNumbers must not be empty")
}
Run Code Online (Sandbox Code Playgroud)
然后我需要使列表可选.如果List提供,它必须是nonEmpty.我正在使用Option[List[Int]]附带的require声明来验证,如果Option是nonEmpty,则列表也必须是nonEmpty.
case class B(name: String, favoriteNumbers: Option[List[Int]]) {
require(
favoriteNumbers.isEmpty || favoriateNumbers.get.nonEmpty
, "when defined, favoriteNumbers.get must be nonEmpty"
)
}
Run Code Online (Sandbox Code Playgroud)
但是,我需要List在我正在建模的系统中使用这个非空的.这意味着我的代码在require任何地方都有相同的语句重复.是否有一种(非ScalaZ)方式来获得一个新类型,比如说NeList,它定义并且行为与List相同,只有在NeList尝试实例化没有元素的情况下才会引发异常?
我试图谷歌为此而找不到一组搜索术语来磨练这个领域.我要么非常简单List,要么对ScalaZ的NEL(非空列表)进行各种引用.所以,如果有一个链接可以帮助解决这个问题,我很乐意看到它.
如果你
def foo[A](x: ::[A]) = "List has length "+x.length
Run Code Online (Sandbox Code Playgroud)
然后你坚持认为清单是非空的.但是当然你的列表都是打字的List,所以你需要一个帮助方法给你一个非空的列表:
implicit class NonEmptyList[A](private val underlying: List[A]) {
def ifNonEmpty[B](f: ::[A] => B): Option[B] = {
underlying match {
case x: ::[A @unchecked] => Some(f(x))
case _ => None
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在您可以安全地应用该操作来获取Option.(您也可以在类似foreach的方法中运行副作用函数.)
现在,这是非惯用的Scala.但它在编译时是安全的(@unchecked尽管如此 - Scala的编译器还不够智能,无法实现类型参数没有改变).
您可以使用 List[A] 和 Nel[A] 之间的隐式转换自己实现一个非空列表:
case class Nel[A](val head: A, val tail: List[A] = Nil)
implicit def list2Nel[A](list: List[A]): Nel[A] = {
require(!list.isEmpty)
Nel(list.head, list.tail)
}
implicit def nel2List[A](nel: Nel[A]): List[A] = nel.head :: nel.tail
Run Code Online (Sandbox Code Playgroud)
然后您可以在需要的地方定义您的函数,以便它们将 Nel[A] 作为参数:
def f(l: Option[Nel[String]]) = { ... }
Run Code Online (Sandbox Code Playgroud)
并使用普通列表调用它们(假设隐式 defs 在范围内):
f(Some(List("hello", "world")) // works
f(Some(Nil)) // throws IllegalArgumentException
f(None) // works
Run Code Online (Sandbox Code Playgroud)
编辑:应该注意,这不提供编译时保证传入的 List[A] 不会为空。如果这就是您想要的,那么摆脱implicit def list2Nel并要求您的函数的客户端显式传入 Nel[A],从而保证在编译时列表不为空。
此外,这是一个非常基本的 NonEmptyList 实现。在 scalaz 中找到了更完整的解决方案(当然,在不使用 scalaz 的问题中特别要求它):https : //github.com/scalaz/scalaz/blob/series/7.2.x/core/src/main/ scala/scalaz/NonEmptyList.scala