Как реализовать короткое замыкание с IO monad в Scala
Я использую стандартную монаду ввода-вывода.
и в какой-то момент, мне нужно укорачивать. При данном условии я не хочу запускать следующую 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-й строки Мне нужно повторить то же условие.
есть ли лучший способ ?
1 ответов
вы на самом деле ничего не закоротили. Вы все еще используете IOs; вы просто не фиксируете значения.
кроме того, стандартная монада IO не определяет filter (или withFilter), поэтому вы не можете использовать охранников в своем понимании.
теперь, если вы хотите только то, что вы сказали (та же логика, только более сухая), вы всегда можете назначить временную переменную в для понимания:
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 объект companion имеет метод 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, стоит отметить штраф, который вы платите за хранение вещей, завернутых в монады; без этого такая же логика была бы (например):
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 дополнительными методами, которые немного помогают, но стандарт withFilter не будет работать, поэтому вы не сможете использовать синтаксический сахар для понимания.