Ефекти и функционална обработка на грешки

Ефекти?

Описват влияния извън нормалното изпълнение на функциите:

  • Частичност/частични функции
  • Изключения/грешки
  • Недетерминизъм
  • Логване/мониторинг
  • Изменимо състояние
  • Вход/изход
  • Асинхронност и конкурентност
  • Ресурси

Ефекти

  • Ефекти в изрази, които не са референтно прозрачни, са странични ефекти
  • Езици като Scala, Haskel, Idris следят ефектите референтно прозрачно през типовата си система
    • Scala и Haskell – като return types
    • Idris – ефекти
    • Експериментално в Scala – capabilities

Роб Норис

Effects are good, side effects are bugs!

Functional Programming with Effects

Ефектни типове

  • Частичност/частични функции – Option
  • Изключения/грешки – Try/Either
  • Недетерминизъм – List
  • Логване/мониторинг – Writer
  • Изменимо състояние – State
  • Вход/изход – IO
  • Асинхронност и конкурентност – IO, Future
  • Ресурси – Resource

Всички следват структурата F[_]

Обработка на грешки. Изключения

Код когато прихващаме изключения

try {
    try {
    } finally {
    }
    try {
        try {
        } catch(...) {
        } catch (...) {
        }
    } catch(...) {
    } finally {
    }
} finally {
    try {
    } finally {
    }
}

Код когато прихващаме изключения

Проблеми с хвърлянето на изключения

  • Не са референтно прозрачни
    • Рефакторирането на код с тях е error-prone
  • Трудни за композиране
    • Прекъсват изчислението на кода
  • Не са стойност
  • Вързани са винаги към текущата нишка
    • не може грешката да се прехване от друга нишк

Стъпка 1: Option

От частични към тотални функции

val reciprocal: PartialFunction[Int,Double] =
  case x if x != 0 => 1.toDouble / x

reciprocal.lift(10) // Some(0.1)
reciprocal.lift(0) // None

Стъпка 2: Try

import scala.util.{Try,Success,Failure}

Success(1)
Failure(new RuntimeException("Something went wrong"))

Try("123".toInt) // Success(123)
Try("one-two-three".toInt) // Failure(java.lang.NumberFormatException: For input string: "one-two-three")

Стъпка 3: Either

val right: Either[String, Int] = Right(1)
val left : Either[String, Int] = Left("Something went wrong")

Either is what’s right or whatever’s left

Дефинирани от нас грешки

sealed trait ProcessingError extends
case class KeyNotFound(key: String) extends ProcessingError
case class NotNumeric(s: String) extends ProcessingError
case object DivisionByZero extends ProcessingError

Ефект за (синхронен) вход/изход – IO

Ползи

  • Реферетно прозрачна връзка с външния свят
  • IO-то е стойност
    • може да се трансформира
    • може да се предава между нишки
    • може да се преизползва – например за имплементация на retry, отложено изпълнение или изпълнение по график
  • Възможност за различни различни интерпретатори и инспекция
    • всички странични ефекти се имплементират на едно място, в преизползваем runtime
    • …който може да бъде оптимизиран и настройван за съответната среда
    • повече контрол при тестване, например пропускане на заложени time out-и
  • По-късно в курса ще видим как може да изразява асинхронност и канселации

Комбиниране на ефекти

Въпроси?