#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
немедленно вызвала бы функцию (если бы это не был пустой список).