Как в образце Simple IO Type избавиться от побочных эффектов в «FP in Scala»? - PullRequest
0 голосов
/ 06 января 2019

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

object IO extends Monad[IO] {
  def unit[A](a: => A): IO[A] = new IO[A] { def run = a }
  def flatMap[A,B](fa: IO[A])(f: A => IO[B]) = fa flatMap f
  def apply[A](a: => A): IO[A] = unit(a)    
}

def ReadLine: IO[String] = IO { readLine }
def PrintLine(msg: String): IO[Unit] = IO { println(msg) }

def converter: IO[Unit] = for {
  _ <- PrintLine("Enter a temperature in degrees Fahrenheit: ")
  d <- ReadLine.map(_.toDouble)
  _ <- PrintLine(fahrenheitToCelsius(d).toString)
} yield ()

У меня есть пара вопросов относительно этого куска кода:

  1. Что делает функция def run = a в функции unit?
  2. В функции ReadLine что на самом деле делает IO { readLine }? Будет ли он действительно выполнять функцию println или просто вернет тип ввода-вывода?
  3. Что означает _ в понимании (_ <- PrintLine("Enter a temperature in degrees Fahrenheit: "))?
  4. Почему он устраняет побочные эффекты ввода-вывода? Я видел, что эти функции все еще взаимодействуют с входами и выходами.

Ответы [ 2 ]

0 голосов
/ 06 января 2019
  1. Определение вашего IO следующее:

    trait IO { def run: Unit }
    

    Следуя этому определению, вы можете понять, что написание new IO[A] { def run = a } означает инициализацию анонимного класса из вашей черты и назначение a в качестве метода, который запускается при вызове IO.run. Поскольку a является по имени параметра , на самом деле во время создания ничего не выполняется.

  2. Любой объект или класс в Scala, следующий за контрактом метода apply, может быть вызван как: ClassName(args), где компилятор будет искать метод apply в объекте / классе и конвертировать это на ClassName.apply(args) звонок. Более подробный ответ можно найти здесь . Таким образом, поскольку объект-компаньон IO обладает таким методом:

    def apply[A](a: => A): IO[A] = unit(a)    
    

    Расширение разрешено. Таким образом мы вместо этого на самом деле называем IO.apply(readLine).

  3. _ имеет много перегруженных областей применения в Scala . Это означает, что «меня не волнует значение, возвращаемое из PrintLine, откажитесь от него». Это так, потому что возвращаемое значение имеет тип Unit , к которому мы не имеем никакого отношения.

  4. Дело не в том, что IO тип данных удаляет часть выполнения ввода-вывода, а в том, что откладывает его на более поздний момент времени. Обычно мы говорим, что IO выполняется на «краях» приложения, в методе Main. Эти взаимодействия с внешним миром все еще будут происходить, но, поскольку мы инкапсулируем их в IO, мы можем рассуждать о них как значениях в нашей программе, что приносит большую пользу. Например, теперь мы можем составлять побочные эффекты и зависеть от успеха / неудачи их выполнения. Мы можем смоделировать эти эффекты ввода-вывода (, используя другие типы данных, такие как Const), и многие другие удивительно приятные свойства.

0 голосов
/ 06 января 2019

Самый простой способ взглянуть на IO монаду как на маленький кусочек определения программы.

Таким образом:

  1. Это определение IO, метод run определяет, что делает IO монада. new IO[A] { def run = a } - это способ создания экземпляра класса и определения метода run.
  2. Там происходит немного синтаксического сахара. IO { readLine } совпадает с IO.apply { readLine } или IO.apply(readLine), где readLine - функция вызова по имени типа => String. Это вызывает метод unit из object IO и, таким образом, это просто создание экземпляра класса IO, который еще не запущен.
  3. Поскольку IO является монадой, для понимания можно использовать. Требуется сохранение результата каждой монадной операции в синтаксисе, подобном result <- someMonad. Чтобы игнорировать результат, можно использовать _, поэтому _ <- someMonad читается как исполняющая монада, но игнорирует результат.
  4. Все эти методы являются IO определениями, они ничего не запускают и, следовательно, не имеют побочных эффектов. Побочные эффекты появляются только при вызове IO.run.
...