我使用标准的IO monad。
在某些时候,我需要短路。在给定条件下,我不想运行以下ios。
这是我的解决方案,但我发现它太冗长而且不优雅:
def shortCircuit[A](io: IO[A], continue: Boolean) =
io.map(a => if (continue) Some(a) else None)
for {
a <- io
b <- shortCircuit(io, a == 1)
c <- shortCircuit(io, b.map(_ == 1).getOrElse(false))
d <- shortCircuit(io, b.map(_ == 1).getOrElse(false))
e <- shortCircuit(io, b.map(_ == 1).getOrElse(false))
} yield …
例如,对于第3行,第4行和第5行,我需要重复相同的条件。
有更好的方法吗?
答案 0 :(得分:1)
你实际上并没有将任何东西短路。你还在运行IO;你只是没有抓住价值观。
此外,标准的IO monad并没有定义filter
(或withFilter
),所以你不能在你的理解中使用守卫。
现在,如果你想要的只是你所说的(相同的逻辑,只是更多的干),你总是可以在for comprehension中分配一个临时变量:
for {
a <- io
b <- shortCircuit(io, a == 1)
continue = b.map(_ == 1).getOrElse(false)
c <- shortCircuit(io, continue)
d <- shortCircuit(io, continue)
e <- shortCircuit(io, continue)
} yield …
但如果你真的想要短路,你将不得不以某种方式分解案件。这是一种可能性,假设您只想将所有内容打包到一个数组中,以便返回类型很简单,并且您的IO
伴随对象有一个apply方法,您可以使用该方法创建只返回值的内容:
io.flatMap(a =>
if (a == 1) IO(() => Array(a))
else io.flatMap(b =>
if (b == 1) IO(() => Array(a,b))
else for {
c <- io
d <- io
e <- io
} yield Array(a,b,c,d,e)
)
)
如果您的返回类型更复杂,您可能需要更加努力地指定类型。
FWIW,值得注意的是你要把东西包裹在monad中的惩罚;没有,相同的逻辑(例如):
io() match {
case 1 => Array(1)
case a => io() match {
case 1 => Array(a, 1)
case b => Array(a, b, io(), io(), io())
}
}
如果你允许退货,你会得到:
val a = io()
if (a == 1) return Array(a)
val b = io()
if (b == 1) return Array(a, b)
Array(a, b, io(), io(), io())
原则上也可以使用额外的方法来装饰IO monad,但标准withFilter
不会起作用,所以你不能够使用for-comprehension syntactic sugar。