Существуют два метода, которые используются чисто функциональными языками программирования для моделирования побочных эффектов:
1) Тип мира, представляющий внешнее состояние, где каждое значение этого типа гарантируется системой типов для использования только один раз.
На языке, который использует этот подход, функции print
и read
могут иметь типы (string, world) -> world
и world -> (string, world)
соответственно.
Их можно использовать так:
let main w =
let w1 = print ("What's your name?", w) in
let (str, w2) = read w1 in
let w3 = print ("Your name is " ^ name, w2) in
w3
Но не так:
let main w =
let w1 = print ("What's your name?", w) in
let (str, w2) = read w in
let w3 = print ("Your name is " ^ name, w2) in
w3
(потому что w используется дважды)
Все встроенные функции с побочными эффектами будут принимать и возвращать мировое значение. Поскольку все функции с побочными эффектами являются либо встроенными, либо вызывают другие функции с побочными эффектами, это означает, что все функции с побочными эффектами должны принимать и возвращать мир.
Таким образом, невозможно вызвать функцию с побочными эффектами дважды с одинаковыми аргументами, и ссылочная прозрачность не нарушается.
2) Монада ввода-вывода, в которой все операции с побочными эффектами должны выполняться внутри этой монады.
При таком подходе все операции с побочными эффектами будут иметь тип io something
. Например, print
будет функцией с типом string -> io unit
, а read
будет иметь тип io string
.
Единственный способ получить доступ к значению выполняемой операции - это использовать операцию «monadic bind» (называемую >> = в haskell, например) с операцией ввода-вывода в качестве одного аргумента и функцией, описывающей, что делать с результатом как другой операнд.
Пример сверху выглядел бы так с монадическим IO:
let main =
(print "What's your name?") >>=
(lambda () -> read >>=
(lambda name -> print ("Your name is " ^ name)))