有一个程序,我希望将一组整数的范围限制在5到15之间。
有没有办法定义允许这种情况的类型?
如何使用此示例:
// Define type Good X as range from 5 to 15
class Foo(val x: GoodX)
{
//blah blah
}
我还想保留" Int-iness" of GoodX。
val base:GoodX=5
val f=Foo(base+4)
答案 0 :(得分:6)
看看https://github.com/fthomas/refined。它允许您在类型级别细化(约束)现有类型。例如。正整数,它仍然与整数有一个子类型关系。
语法有点冗长,它将填充原语(详见下文)。但除此之外,它完全符合您的要求。
这是一个简短的演示。使用精炼类型定义细化和方法:
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._
type FiveToFifteen = GreaterEqual[W.`5`.T] And Less[W.`15`.T]
type IntFiveToFifteen = Int Refined FiveToFifteen
def sum(a: IntFiveToFifteen, b: IntFiveToFifteen): Int = a + b
将它与常量一起使用(注意好的编译错误消息):
scala> sum(5,5)
res6: Int = 10
scala> sum(0,10)
<console>:60: error: Left predicate of (!(0 < 5) && (0 < 15)) failed: Predicate (0 < 5) did not fail.
sum(0,10)
^
scala> sum(5,20)
<console>:60: error: Right predicate of (!(20 < 5) && (20 < 15)) failed: Predicate failed: (20 < 15).
sum(5,20)
^
如果有变量,则在编译时不知道它们是否在范围内。因此,从Int到精简int的向下转换可能会失败。抛出异常在功能库中不被认为是好的风格。因此refineV方法返回一个Either:
val x = 20
val y = 5
scala> refineV[FiveToFifteen](x)
res14: Either[String,eu.timepit.refined.api.Refined[Int,FiveToFifteen]] = Left(Right predicate of (!(20 < 5) && (20 < 15)) failed: Predicate failed: (20 < 15).)
scala> refineV[FiveToFifteen](y)
res16: Either[String,eu.timepit.refined.api.Refined[Int,FiveToFifteen]] = Right(5)
答案 1 :(得分:0)
我认为Partial Function会有所帮助。
case class GoodX(x: Int)
object GoodX {
def apply: PartialFunction[Int, GoodX] =
{ case i if i > 5 && i < 15 => new GoodX(i) }
}
// implicits to remain int-fulness
implicit def goodXToInt(goodX: GoodX): Int = goodX.x
GoodX(5) // throw Match Error
GoodX(10) // GoodX(10)
此解决方案不需要库。 希望这有帮助。
答案 2 :(得分:0)
如examples section of refined library所示,我们可以定义一个自定义的精简类型,其值在7到77之间
// Here we define a refined type "Int with the predicate (7 <= value < 77)".
scala> type Age = Int Refined Interval.ClosedOpen[W.`7`.T, W.`77`.T]
此外,如果在 scala 2.13.x 上,也可以使用基于文字的单例类型,如下所示,因为不需要来自无形的见证;)
import eu.timepit.refined.numeric.Interval.Closed
type AgeOfChild = Int Refined Closed[2, 12]
case class Child(name: NonEmptyString, age:AgeOfChild = 2)
有关更多详细信息,请参阅SIP和official documentation。
答案 3 :(得分:-1)
当然......
object FiveToFifteen extends Enumeration {
val _5 = Value(5)
val _6,_7,_8,_9,_10,_11,_12,_13,_14,_15 = Value
}
修改如果您想“保留内容”,您还可以添加如下转换:
implicit def toInt(v: Value) = v.id
implicit def fromInt(i: Int) = apply(i)
但是,显然,这不会使你的类型更加“完整”然后它已经是(几乎没有),因为像
val v: Value = _15 - _10
或val v: Value = _5 * 3
甚至val v = _15 * _5
都可以使用,但val v: Value = _5 - 1
等其他人会崩溃