我正在尝试了解如何使用效果monad(cats.effect.IO
或scalaz.IO
并不重要)。想象一下,我有以下方法:
def extract(str: String): String = {
if(str.contains("123"))
"123"
else
throw new IllegalArgumentException("Boom!")
}
由于此方法不纯(抛出异常)并且我需要将其结果与另一个有效计算(network-IO)相结合,因此将它包装到IO
中是一个很好的做法,如下所示:
def extract(str: String): IO[String] = IO {
if(str.contains("123"))
"123"
else
throw new IllegalArgumentException("Boom!")
}
它是Effect monads的常见用例吗?
答案 0 :(得分:2)
这是否合适取决于你想表达什么。
它不像你的第一个def extract(str: String): String
- 方法定义在某种程度上是无效的:你只是在地毯下扫除所有异常和副作用,这样它们就不可见了签名。如果这与您当前的项目无关,并且如果程序只是因为抛出的异常的长堆栈跟踪而崩溃,那么就可以做到,没问题(很容易想象一次性)抛弃脚本,这是合适的。)
如果您声明def extract(str: String): IO[String] = IO { ... }
,那么您至少可以在签名中看到函数extract
可以做一些不纯的事情(在这种情况下抛出异常)。现在问题变成:谁负责处理此异常,或者你想在哪里处理这个异常?考虑一下:如果抛出异常,它将出现在你的代码中调用yourProgram.unsafeRunSync()
之类的行。在那里处理这个例外是否有意义?也许它确实如此,也许它没有:没有人可以告诉你。如果您只想在main
的顶级抓住异常,请记录它,然后exit
,那么它是合适的。
但是,如果您想立即处理异常,那么您有更好的选择。例如,如果您正在编写一个提示输入文件名的方法,那么尝试使用此名称extract
,最后对文件执行一些IO
,然后返回类型IO[String]
可能太不透明了。您可能希望使用其他一些monad(Option
,Either
,Try
等)来表示失败的提取。例如,如果您使用Option[String]
作为返回类型,则不再需要在main
方法中处理千里之外的模糊异常,而是可以立即处理它,例如,重复提示输入新文件名。
整个练习有点类似于提出如何处理异常的策略,只有在这里你才能在方法的类型签名中明确表达它。