Как на самом деле работает монада ввода-вывода с эффектом cats?

#scala #functional-programming #io-monad #cats-effect #referential-transparency

#скала #функциональное программирование #io-монада #кошки-эффект #ссылочный-прозрачность

Вопрос:

Я новичок в функциональном программировании и Scala, и я проверял фреймворк Cats Effect и пытался понять, что делает монада ввода-вывода. Пока что я понял, что написание кода в блоке ввода-вывода — это просто описание того, что нужно сделать, и ничего не происходит, пока вы явно не запустите, используя unsafe предоставленные методы, а также способ сделать код, который выполняет побочные эффекты, ссылочно прозрачным, фактически не запуская его.

Я попытался выполнить приведенный ниже фрагмент текста, просто чтобы попытаться понять, что это значит:

 object Playground extends App {
  var out = 10
  var state = "paused"

  def changeState(newState: String): IO[Unit] = {
    state = newState
    IO(println("Updated state."))
  }

  def x(string: String): IO[Unit] = {
    out  = 1
    IO(println(string))
  }

  val tuple1 = (x("one"), x("two"))

  for {
    _ <- x("1")
    _ <- changeState("playing")
  } yield ()

  println(out)
  println(state)
}
 

И результат был:

 13
paused
 

Я не понимаю, почему присваивание state = newState не выполняется, но выполняются выражения increment и assign out = 1 . Я упускаю что-то очевидное из того, как это должно работать? Мне действительно не помешала бы помощь. Я понимаю, что я могу запустить это, используя unsafe методы.

Комментарии:

1. Просто в качестве примечания, если вы собираетесь использовать чисто функциональное программирование var , в вашем коде нет места. Весь смысл IO Monad в том, чтобы содержать побочные эффекты. Если вы хотите поддерживать изменяемое состояние, подобное этому, cats предоставьте вам несколько вариантов, таких как Ref

2. @sinanspd Да, понял! Я просто пытался понять, что означает, что IO не запускает побочные эффекты, а только когда они обернуты внутри.

3. Это функция, которую вы вызываете при выполнении ввода-вывода (…): github.com/typelevel/cats-effect/blob /… Он принимает значение в качестве параметра по имени. Вы должны знать, что это такое и как это работает, чтобы понять, как работает ввод-вывод.

Ответ №1:

В вашем конкретном примере, я думаю, происходит то, что обычный императивный код Scala не зависит от IO монады — он выполняется, когда обычно выполняется по правилам Scala.

Когда вы запускаете:

 for {
  _ <- x("1")
  _ <- changeState("playing")
} yield ()
 

это немедленно вызывает x . Это не имеет ничего общего с IO монадой; это просто то, как for определяются понимания. Первым шагом является оценка первого оператора, чтобы вы могли вызвать flatMap его.

Как вы заметили, вы никогда не «запускаете» монадический результат, поэтому аргумент to flatMap , монадическое продолжение, никогда не вызывается, что не приводит к вызову to changeState . Это специфично для IO монады, так как, например, List монада flatMap немедленно вызвала бы функцию (если бы это не был пустой список).