如何在Scala中实现IO monad的短路

Yan*_*san 7 monads scala

我使用标准的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 …
Run Code Online (Sandbox Code Playgroud)

例如,对于第3行,第4行和第5行,我需要重复相同的条件.

有没有更好的办法 ?

Rex*_*err 2

你实际上并没有在那里短路任何东西。您仍在运行 IO;你只是没有捕捉到价值观。

\n\n

另外,标准 IO monad 没有定义filter(或withFilter),因此您不能在 for 理解中使用防护。

\n\n

现在,如果您想要的只是您所说的(相同的逻辑,只是更干燥),您可以随时在 for 理解中分配一个临时变量:

\n\n
for {\n  a <- io\n  b <- shortCircuit(io, a == 1)\n  continue = b.map(_ == 1).getOrElse(false)\n  c <- shortCircuit(io, continue)\n  d <- shortCircuit(io, continue)\n  e <- shortCircuit(io, continue)\n} yield \xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n\n

但如果你真的想短路,你就必须以某种方式拆开外壳。这是一种可能性,假设您只想将所有内容打包到一个数组中,以便返回类型很简单,并且您的IO伴生对象有一个 apply 方法,您可以使用该方法创建只返回值的对象:

\n\n
io.flatMap(a =>\n  if (a == 1) IO(() => Array(a))\n  else io.flatMap(b =>\n    if (b == 1) IO(() => Array(a,b))\n    else for {\n      c <- io\n      d <- io\n      e <- io\n    } yield Array(a,b,c,d,e)\n  )\n)\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您的返回类型更复杂,您可能需要更加努力地指定类型。

\n\n

FWIW,值得注意的是你为将东西包裹在 monad 中而付出的惩罚;如果没有,相同的逻辑将是(例如):

\n\n
io() match {\n  case 1 => Array(1)\n  case a => io() match {\n    case 1 => Array(a, 1)\n    case b => Array(a, b, io(), io(), io())\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您允许退货,您将得到:

\n\n
val a = io()\nif (a == 1) return Array(a)\nval b = io()\nif (b == 1) return Array(a, b)\nArray(a, b, io(), io(), io())\n
Run Code Online (Sandbox Code Playgroud)\n\n

原则上也可以用额外的方法来装饰 IO monad,这些方法会有所帮助,但标准withFilter不起作用,因此您将无法使用 for-communion 语法糖。

\n