我是Scala的新手。我想要一个表达类似于以正整数测量的坐标的类。 (这是一个简化的例子;请不要指出是否存在其他坐标类;假设我真的需要一个新类。)
我的第一次尝试是简单地将require(x >= 0 && y >= 0)
放入课堂,但我的团队成员不喜欢这会引发异常。
我的第二次尝试返回Option[Coordinate]
。所以现在每次我创建这样一个坐标时,我都有4行,我通常只有一行。更糟糕的是,Option
ality感染了我正在构建这些坐标的所有内容。现在我有Option[Row]
,Option[Rectangle]
,Option[Cube]
,Option[Report]
......
其他人建议我通过建立一个始终为正的Natural
数字类型来解决问题。但现在这推动了问题是从一个可能为负的常规整数中创建Natural
。
我是Scala的新手,但在我看来,如果
,选项,任何一个和尝试都有意义(a)操作可能会失败,因为您正在进行I / O
(b)有一个合理的替代方案,缺少某些东西
似乎这些都没有抓住我在这里要做的事情,这实际上是为了防止某人在编码时犯了数学错误。
么?
答案 0 :(得分:0)
在我看来,如果
,选项,任何一个和尝试都有意义
您提到的课程都负责某个效果。您在某些需要效果的上下文中描述它们是正确的,例如执行IO,或者与可能引发异常的方法(可能是Java API)交互,或者想要描述一个或多个操作可能会失败的操作链
我认为这可以归结为你想要去的兔子洞的深度。我的意思是Scala的强大之处在于它的类型系统,这就是你可以而且应该利用的优势。我同意你的观点,使用Option[Coordinate]
并不自然,但对我来说当然不行。立即出现的问题是“我如何利用类型系统来帮助我只接受自然数?”。好吧,似乎其他人也和你有同样的担忧。
例如,有一个名为shapeless的库,其下面有一个代表自然数的Nat
类型。例如,给出以下案例类:
import shapeless.Nat
case class Coordinate(x: Nat, y: Nat)
如果我使用自然数字创建它,一切都会编译:
def main(args: Array[String]): Unit = {
val cord = Coordinate(1, 1)
}
但是一旦我给它一个非自然数,编译器会抱怨!
def main(args: Array[String]): Unit = {
val cord = Coordinate(-1, 1)
}
使用:
Error:(13, 28) Expression -1 does not evaluate to a non-negative Int literal
val cord = Coordinates(-1, 1)
IMO是你应该利用的类型系统的力量。如果您和您的团队愿意正确地对这些类型进行编码,那么您可以为您的团队和用户提供一个非常有力的保证:“如果这样编译,那就完成了您的意思。”
答案 1 :(得分:0)
你有一个非常好的问题。
我的第一次尝试是简单地将require(x> = 0&& y> = 0)放入 上课,但是我的团队成员并不喜欢这样做 异常。
那是对的。如果你选择这种方式,你必须以某种方式处理异常。
我是Scala的新手,但在我看来,选项,任何一个和尝试 如果有意义
(a)操作可能会失败,因为您正在进行I / O
(b)除了某种东西之外,还有一个合理的替代方案
半真的。如果您域中的不变量表示某个元素可能为空,那么您可以使用Option
来表达该行为,并且您也可以获得表达性。特别是Option
的使用并不局限于I / O操作。
在我解决(b)关注之前,让我解释一下。
Scala有上下文(我不会在这里使用'M-word')表示效果,正如@Yuval Itzchakov之前所说。那就是:你有一个值,它包含一些提供一些组合器的东西,并提供一些行为处理。 Either
,Try
和Option
就是其中的一个示例,当您选择使用时,您将承诺处理包含在这些上下文中的数据。它们的作用是你必须处理这种效果。
请记住,数据可能会产生影响:异常(Try
,Either
),空值(Option
),异步计算({{1 }}) 等等。您的代数将根据这些上下文进行编码,因为它可以帮助您处理预期的行为。
似乎这些都没有抓住我在这里尝试做的事, 这是为了防止出现数学错误的人 在他们的编码中。
那就是说,在你的具体情况下,抛弃无形的Future
选项,不,编译器不支持你想要实现的目标。请查看this。