Как может существовать функция времени в функциональном программировании?


646

Я должен признать, что я не знаю много о функциональном программировании. Я читал об этом здесь и там, и поэтому узнал, что в функциональном программировании функция возвращает один и тот же вывод для одного и того же ввода, независимо от того, сколько раз вызывается функция. Это в точности как математическая функция, которая оценивает один и тот же выход для того же значения входных параметров, которое входит в выражение функции.

Например, рассмотрим это:

f(x,y) = x*x + y; // It is a mathematical function

Независимо от того, сколько раз вы используете f(10,4), его значение всегда будет 104. Таким образом, где бы вы ни писали f(10,4), вы можете заменить его 104без изменения значения всего выражения. Это свойство называется ссылочной прозрачностью выражения.

Как говорит Википедия ( ссылка ),

И наоборот, в функциональном коде выходное значение функции зависит только от аргументов, которые вводятся в функцию, поэтому вызов функции f дважды с одним и тем же значением для аргумента x даст одинаковый результат f (x) оба раза.

Может ли функция времени (которая возвращает текущее время) существовать в функциональном программировании?

  • Если да, то как оно может существовать? Не нарушает ли это принцип функционального программирования? Это особенно нарушает ссылочную прозрачность, которая является одним из свойств функционального программирования (если я правильно понимаю).

  • А если нет, то как узнать текущее время в функциональном программировании?


15
Я думаю, что большинство (или все) функциональные языки не так строги и сочетают в себе функциональное и императивное программирование. По крайней мере, это мое впечатление от F #.
Алекс Ф

13
@ Adam: Откуда звонящий узнает текущее время в первую очередь?
Наваз

29
@ Адам: На самом деле это незаконно (как в: невозможно) в чисто функциональных языках.
sepp2k

47
@ Адам: Довольно. Чистый язык общего назначения обычно предлагает некоторую возможность достичь «состояния мира» (то есть, таких как текущее время, файлы в каталоге и т. Д.), Не нарушая прозрачность ссылок. В Haskell это монада IO, а в Clean это тип мира. Таким образом, в этих языках функция, которой необходимо текущее время, будет либо принимать его в качестве аргумента, либо будет возвращать действие ввода-вывода вместо фактического результата (Haskell), либо принимать состояние мира в качестве аргумента (Clean).
sepp2k

12
Думая о FP, легко забыть: компьютер - это большой кусок изменчивого состояния. FP не меняет этого, он просто скрывает это.
Даниил

Ответы:


176

Это можно объяснить еще одним способом: ни одна функция не может получить текущее время (поскольку оно постоянно меняется), но действие может получить текущее время. Допустим, getClockTimeэто константа (или нулевая функция, если хотите), которая представляет действие получения текущего времени. Это действие одинаково каждый раз, независимо от того, когда оно используется, поэтому оно является настоящей константой.

Аналогично, скажем print, это функция, которая принимает некоторое время и выводит его на консоль. Поскольку вызовы функций не могут иметь побочных эффектов на чистом функциональном языке, мы вместо этого представляем, что это функция, которая берет временную метку и возвращает действие ее печати на консоль. Опять же, это реальная функция, потому что, если вы дадите ей одну и ту же временную метку, она будет возвращать одно и то же действие при печати.

Теперь, как вы можете распечатать текущее время на консоли? Ну, вы должны объединить два действия. Так как мы можем это сделать? Мы не можем просто перейти getClockTimeк print, так как печать ожидает метку времени, а не действие. Но мы можем представить, что существует оператор, >>=который объединяет два действия: одно получает метку времени, а другое принимает аргумент и печатает его. Применение этого к ранее упомянутым действиям приводит к ... tadaaa ... новому действию, которое получает текущее время и печатает его. И это, кстати, именно так, как это делается в Haskell.

Prelude> System.Time.getClockTime >>= print
Fri Sep  2 01:13:23 東京 (標準時) 2011

Таким образом, концептуально вы можете посмотреть на это следующим образом: чисто функциональная программа не выполняет никаких операций ввода-вывода, она определяет действие , которое затем выполняет система времени выполнения. Действие одно и то же каждый раз, но результат его выполнения зависит от обстоятельств , когда оно выполняется.

Я не знаю, было ли это более понятным, чем другие объяснения, но иногда это помогает мне думать об этом таким образом.


33
Это не убедительно для меня. Вы удобно называете getClockTimeдействие вместо функции. Хорошо, если вы вызываете так, а затем вызываете каждое действие функции , тогда даже императивное программирование станет функциональным программированием. Или, может быть, вы хотели бы назвать это активным программированием.
Наваз

92
@Nawaz: Ключевым моментом здесь является то, что вы не можете выполнить действие из функции. Вы можете только комбинировать действия и функции вместе, чтобы делать новые действия. Единственный способ выполнить действие - это объединить его в свое mainдействие. Это позволяет отделить чистый функциональный код от императивного кода, и это разделение обеспечивается системой типов. Рассматривая действия как объекты первого класса, вы также можете передавать их и создавать свои собственные «управляющие структуры».
хаммар

36
Не все в Хаскеле является функцией - это полная чушь. Функция - это то, чей тип содержит ->- именно так стандарт определяет термин, и это действительно единственное разумное определение в контексте Haskell. Так - то, тип которого IO Whateverявляется не функцией.
sepp2k

9
@ sepp2k Итак, myList :: [a -> b] является функцией? ;)
fuz

8
@ThomasEding Я действительно опаздываю на вечеринку, но я просто хочу уточнить это: putStrLnэто не действие - это функция, которая возвращает действие. getLineпеременная, которая содержит действие. Действия - это значения, переменные и функции - это «контейнеры» / «метки», которые мы даем этим действиям.
Kqr

356

Да и нет.

Различные функциональные языки программирования решают их по-разному.

В Хаскеле (очень чистом) все это должно происходить в чем-то, что называется монадой ввода / вывода - смотрите здесь .

Вы можете думать об этом как о получении другого входа (и выхода) в вашу функцию (состояние мира) или просто как о месте, где происходит «нечистота», такая как получение изменяющегося времени.

В другие языки, такие как F #, просто встроена некоторая нечистота, и поэтому вы можете иметь функцию, которая возвращает разные значения для одного и того же ввода - как обычные императивные языки.

Как отметил в своем комментарии Джеффри Бурка: Вот хорошее введение в монаду ввода / вывода прямо из вики Haskell.


223
Ключевая вещь, которую нужно понять о монаде ввода / вывода в Haskell, состоит в том, что это не просто хак, чтобы обойти эту проблему; Монады - это общее решение проблемы определения последовательности действий в некотором контексте. Одним из возможных контекстов является реальный мир, для которого у нас есть монада IO. Другой контекст находится внутри атомарной транзакции, для которой у нас есть монада STM. Еще один контекст заключается в реализации процедурного алгоритма (например, Knuth shuffle) как чистой функции, для которой мы имеем монаду ST. И вы можете определить свои собственные монады тоже. Монады - это своего рода перегруженная точка с запятой.
Пол Джонсон

2
Я считаю полезным не называть такие вещи, как получение текущего времени, «функциями», а что-то вроде «процедур» (хотя можно утверждать, что решение Haskell является исключением из этого).
Сингполима

с точки зрения Haskell классические «процедуры» (вещи, которые имеют типы, такие как '... -> ()') несколько тривиальны, поскольку чистая функция с ... -> () вообще ничего не может сделать.
Карстен

3
Типичный термин Haskell - «действие».
Себастьян Редл

6
«Монады - это своего рода перегруженная точка с запятой». +1
user2805751

147

В Haskell каждый использует конструкцию, названную монадой, чтобы обработать побочные эффекты. Монада в основном означает, что вы инкапсулируете значения в контейнере и имеете некоторые функции для объединения функций из значений в значения внутри контейнера. Если наш контейнер имеет тип:

data IO a = IO (RealWorld -> (a,RealWorld))

мы можем безопасно осуществлять действия IO. Этот тип означает: действие типа IO- это функция, которая принимает токен типа RealWorldи возвращает новый токен вместе с результатом.

Идея заключается в том, что каждое действие IO изменяет внешнее состояние, представленное магическим токеном RealWorld. Используя монады, можно связать несколько функций, которые изменяют реальный мир вместе. Наиболее важная функция монады >>=, произносится как связать :

(>>=) :: IO a -> (a -> IO b) -> IO b

>>=принимает одно действие и функцию, которая берет результат этого действия и создает из него новое действие. Тип возвращаемого значения - это новое действие. Например, давайте представим, что есть функция now :: IO String, которая возвращает строку, представляющую текущее время. Мы можем связать это с функцией, putStrLnчтобы распечатать это:

now >>= putStrLn

Или написано в do-Notation, которая более знакома для императивного программиста:

do currTime <- now
   putStrLn currTime

Все это чисто, поскольку мы сопоставляем мутацию и информацию о внешнем мире с RealWorldтокеном. Поэтому каждый раз, когда вы запускаете это действие, вы, конечно, получаете разные выходные данные, но входные данные не совпадают: RealWorldтокен отличается.


3
-1: я недоволен RealWorldдымовой завесой. Тем не менее, наиболее важным является то, как этот предполагаемый объект передается по цепочке. Недостающий фрагмент находится там, где он начинается, где находится источник или связь с реальным миром - он начинается с главной функции, которая выполняется в монаде IO.
u0b34a0f6ae

2
@ kaizer.se Вы можете думать о глобальном RealWorldобъекте, который передается в программу при запуске.
fuz

6
По сути, ваша mainфункция принимает RealWorldаргумент. Только после казни это передается.
Луи Вассерман

13
Видите ли, причина, по которой они скрывают RealWorldи предоставляют только незначительные функции, чтобы изменить его так putStrLn, заключается в том, что некоторые программисты на Haskell не меняются RealWorldс одной из своих программ, так что адрес и дата рождения Haskell Curry таковы, что они становятся ближайшими соседями взросление (это может повредить пространственно-временной континуум таким образом, чтобы повредить язык программирования Haskell.)
PyRulez

2
RealWorld -> (a, RealWorld) не превращается в метафору даже в условиях параллелизма, если вы помните, что реальный мир может постоянно изменяться другими частями вселенной вне вашей функции (или вашего текущего процесса). Так (а) выбирает , предзадано не ломается, и (б) каждый раз , когда значение , которое имеет , RealWorldкак его типа передается функция, то функция должна быть повторно оценено, потому что реальный мир будет измениться в то же время ( который моделируется как объяснил @fuz, возвращая различное «значение токена» каждый раз, когда мы взаимодействуем с реальным миром).
Qqwy

73

Большинство функциональных языков программирования не являются чистыми, то есть они позволяют функциям не только зависеть от их значений. В этих языках вполне возможно иметь функцию, возвращающую текущее время. Для языков, на которых вы отметили этот вопрос, это относится к Scala и F # (а также к большинству других вариантов ML ).

В таких языках, как Haskell и Clean , которые являются чистыми, ситуация иная. В Haskell текущее время будет доступно не через функцию, а через так называемое IO-действие, которое является способом Haskell для инкапсуляции побочных эффектов.

В Clean это будет функция, но функция примет мировое значение в качестве аргумента и вернет значение нового мира (в дополнение к текущему времени) в качестве результата. Система типов будет гарантировать, что каждое мировое значение может быть использовано только один раз (и каждая функция, которая потребляет мировое значение, создаст новое). Таким образом, функция времени должна вызываться каждый раз с другим аргументом, и, следовательно, каждый раз будет разрешено возвращать другое время.


2
Это звучит так, как будто Haskell и Clean делают разные вещи. Из того, что я понимаю, они делают то же самое, только что Haskell предлагает более хороший синтаксис (?) Для достижения этой цели.
Конрад Рудольф

27
@Konrad: они делают то же самое в том смысле, что оба используют функции системы типов для абстрагирования побочных эффектов, но это все. Обратите внимание, что очень хорошо объяснить монаду ввода-вывода в терминах типа мира, но стандарт Haskell на самом деле не определяет тип мира, и на самом деле невозможно получить значение типа World в Haskell (хотя это очень возможно и действительно необходимо в чистоте). Кроме того, Haskell не использует уникальную типизацию как функцию системы типов, поэтому, если он действительно дает вам доступ к Миру, он не может гарантировать, что вы используете его чисто так, как это делает Clean.
sepp2k

51

«Текущее время» не является функцией. Это параметр. Если ваш код зависит от текущего времени, это означает, что ваш код параметризован по времени.


22

Это можно сделать абсолютно функционально. Есть несколько способов сделать это, но самый простой - заставить функцию времени возвращать не только время, но и функцию, которую вы должны вызвать для получения следующего измерения времени .

В C # вы можете реализовать это так:

// Exposes mutable time as immutable time (poorly, to illustrate by example)
// Although the insides are mutable, the exposed surface is immutable.
public class ClockStamp {
    public static readonly ClockStamp ProgramStartTime = new ClockStamp();
    public readonly DateTime Time;
    private ClockStamp _next;

    private ClockStamp() {
        this.Time = DateTime.Now;
    }
    public ClockStamp NextMeasurement() {
        if (this._next == null) this._next = new ClockStamp();
        return this._next;
    }
}

(Имейте в виду, что это пример, который должен быть простым, а не практичным. В частности, узлы списка не могут быть собраны сборщиком мусора, потому что они укоренены в ProgramStartTime.)

Этот класс ClockStamp действует как неизменный связанный список, но на самом деле узлы генерируются по требованию, поэтому они могут содержать «текущее» время. Любая функция, которая хочет измерить время, должна иметь параметр 'clockStamp' и также должна возвращать свое последнее измерение времени в своем результате (чтобы вызывающая сторона не видела старые измерения), например так:

// Immutable. A result accompanied by a clockstamp
public struct TimeStampedValue<T> {
    public readonly ClockStamp Time;
    public readonly T Value;
    public TimeStampedValue(ClockStamp time, T value) {
        this.Time = time;
        this.Value = value;
    }
}

// Times an empty loop.
public static TimeStampedValue<TimeSpan> TimeALoop(ClockStamp lastMeasurement) {
    var start = lastMeasurement.NextMeasurement();
    for (var i = 0; i < 10000000; i++) {
    }
    var end = start.NextMeasurement();
    var duration = end.Time - start.Time;
    return new TimeStampedValue<TimeSpan>(end, duration);
}

public static void Main(String[] args) {
    var clock = ClockStamp.ProgramStartTime;
    var r = TimeALoop(clock);
    var duration = r.Value; //the result
    clock = r.Time; //must now use returned clock, to avoid seeing old measurements
}

Конечно, немного неудобно проходить последнее измерение внутри и снаружи, внутри и снаружи, внутри и снаружи. Есть много способов скрыть шаблон, особенно на уровне языкового дизайна. Я думаю, что Хаскелл использует этот вид трюка, а затем скрывает уродливые части, используя монады.


Интересно, что i++цикл for не является прозрачным по
ссылкам

@ snim2 Я не идеален. : P Примите утешение в том факте, что грязная изменчивость не влияет на ссылочную прозрачность результата. Если вы дважды пройдете один и тот же «lastMeasurement», вы получите несвежее следующее измерение и вернете тот же результат.
Крейг Гидни

@ Strilanc Спасибо за это. Я думаю в императивном коде, поэтому интересно увидеть функциональные концепции, объясненные таким образом. Тогда я могу представить себе язык, где это естественно и синтаксически чище.
WW.

На самом деле вы также можете пойти монадой в C #, избегая явной передачи временных отметок. Вам нужно что-то вроде struct TimeKleisli<Arg, Res> { private delegate Res(TimeStampedValue<Arg>); }. Но код с этим все еще не выглядел бы так же хорошо, как Haskell с doсинтаксисом.
Оставлено около

@leftaroundabout вы можете сделать вид, что у вас есть монада в C #, реализовав функцию связывания как вызываемый метод SelectMany, который включает синтаксис понимания запроса. Вы все еще не можете программировать полиморфно над монадами, так что все это тяжелая битва против слабой системы типов :(
sara

16

Я удивлен, что ни один из ответов или комментариев не упоминает коалгебры или коиндукцию. Обычно при рассуждениях о бесконечных структурах данных упоминается коиндукция, но она также применима к бесконечному потоку наблюдений, такому как регистр времени в ЦП. Коалгебра моделирует скрытое состояние; и модели соиндукции, наблюдающие это состояние. ( Построение нормальных индукционных моделей состояние.)

Это горячая тема в Реактивном функциональном программировании. Если вы заинтересованы в подобных вещах, прочитайте это: http://digitalcommons.ohsu.edu/csetech/91/ (28 стр.)


3
И как это связано с этим вопросом?
Наваз

5
Ваш вопрос был о моделировании поведения, зависящего от времени, чисто функциональным способом, например, функцией, которая возвращает текущие системные часы. Вы можете либо пропустить что-то эквивалентное монаде IO через все функции и их дерево зависимостей, чтобы получить доступ к этому состоянию; или вы можете моделировать состояние, определяя правила наблюдения, а не конструктивные правила. Вот почему моделирование сложного состояния индуктивно в функциональном программировании кажется таким неестественным, потому что скрытое состояние действительно является коиндуктивным свойством.
Джеффри Агилера

Отличный источник! Есть что-нибудь более свежее? Сообщество JS все еще борется с абстракциями потоковых данных.
Дмитрий Зайцев

12

Да, для чистой функции возможно вернуть время, если оно указано в качестве параметра. Другой аргумент времени, другой результат времени. Затем формируйте и другие функции времени и объединяйте их с простым словарем функций (-временных) -преобразующих (высших порядков) функций. Поскольку подход не использует состояние, время здесь может быть непрерывным (независимым от разрешения), а не дискретным, что значительно повышает модульность . Эта интуиция является основой функционально-реактивного программирования (FRP).


11

Да! Ты прав! Now () или CurrentTime () или сигнатура любого метода такого типа не демонстрирует ссылочную прозрачность в одном отношении. Но по указанию компилятору он параметризуется входом системных часов.

По выводу Now () может выглядеть так, что не следует ссылочной прозрачности. Но фактическое поведение системных часов и функции над ними придерживается ссылочной прозрачности.


11

Да, функция получения времени может существовать в функциональном программировании с использованием слегка модифицированной версии функционального программирования, известной как нечистое функциональное программирование (по умолчанию или основной является чисто функциональное программирование).

В случае получения времени (или чтения файла, или запуска ракеты) код должен взаимодействовать с внешним миром, чтобы выполнить работу, и этот внешний мир не основан на чистых основах функционального программирования. Чтобы позволить чисто функциональному миру программирования взаимодействовать с этим нечистым внешним миром, люди внедрили нечистое функциональное программирование. В конце концов, программное обеспечение, которое не взаимодействует с внешним миром, ничем не полезно, кроме как выполнять некоторые математические вычисления.

Немногие функциональные языки программирования имеют эту встроенную особенность нечистоты, так что нелегко выделить, какой код нечистый, а какой - чистый (например, F # и т. Д.), И некоторые функциональные языки программирования должны убедиться, что когда вы делаете нечистые вещи этот код явно выделяется по сравнению с чистым кодом, таким как Haskell.

Еще один интересный способ увидеть это состоит в том, что ваша функция get time в функциональном программировании будет принимать «мировой» объект, который имеет текущее состояние мира, такое как время, количество людей, живущих в мире, и т. Д. Затем, получая время из какого мира объект всегда будет чистым, т. е. если вы перейдете в одно и то же состояние мира, вы всегда получите одно и то же время.


1
«В конце концов, программное обеспечение, которое не взаимодействует с внешним миром, ничем не полезно, кроме как выполнять некоторые математические вычисления». Насколько я понимаю, даже в этом случае ввод в вычисления будет жестко запрограммирован в программе, что также не очень полезно. Как только вы захотите прочитать входные данные для своих математических вычислений из файла или терминала, вам понадобится нечистый код.
Джорджио

1
@Ankur: Это точно так же. Если программа взаимодействует с чем-то другим, а не только с самим собой (например, с миром через клавиатуру), это все равно нечисто.
личность

1
@ Анкур: Да, я думаю, что вы правы! Даже если передача больших входных данных в командной строке может быть не очень практичной, это может быть простым способом сделать это.
Джорджио

2
Наличие «объекта мира», включая количество людей, живущих в мире, поднимает исполняющий компьютер до почти всезнающего уровня. Я думаю, что нормальным случаем является то, что он включает в себя такие вещи, как количество файлов на вашем HD и домашний каталог текущего пользователя.
Ziggystar

4
@ziggystar - «объект мира» на самом деле ничего не включает - это просто прокси для изменения состояния мира вне программы. Его единственная цель - явно поменять изменяемое состояние так, чтобы система типов могла его идентифицировать.
Крис Наттикомб

7

Ваш вопрос объединяет две взаимосвязанные меры компьютерного языка: функциональный / императивный и чистый / нечистый.

Функциональный язык определяет отношения между входами и выходами функций, а императивный язык описывает конкретные операции в определенном порядке выполнения.

Чистый язык не создает побочных эффектов и не зависит от них, а нечистый язык использует их повсюду.

На сто процентов чистые программы в основном бесполезны. Они могут выполнять интересные вычисления, но поскольку у них не может быть побочных эффектов, у них нет ввода или вывода, поэтому вы никогда не узнаете, что они рассчитали.

Чтобы быть вообще полезной, программа должна быть как минимум нечистой. Один из способов сделать чистую программу полезной - поместить ее в тонкую нечистую оболочку. Как эта непроверенная программа на Haskell:

-- this is a pure function, written in functional style.
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

-- This is an impure wrapper around the pure function, written in imperative style
-- It depends on inputs and produces outputs.
main = do
    putStrLn "Please enter the input parameter"
    inputStr <- readLine
    putStrLn "Starting time:"
    getCurrentTime >>= print
    let inputInt = read inputStr    -- this line is pure
    let result = fib inputInt       -- this is also pure
    putStrLn "Result:"
    print result
    putStrLn "Ending time:"
    getCurrentTime >>= print

4
Было бы полезно, если бы вы могли решить конкретную проблему получения времени и немного объяснили, в какой степени мы считаем IOценности и результаты чистыми.
AndrewC

На самом деле, даже 100% чистые программы нагревают процессор, что является побочным эффектом.
Йорг Миттаг

3

Вы изучаете очень важный предмет в функциональном программировании, то есть выполнение ввода-вывода. Многие чистые языки делают это с помощью встроенных доменных языков, например, подъязыка, задачей которого является кодирование действий. , которые могут иметь результаты.

Например, среда выполнения Haskell ожидает, что я определю действие под названием main , которое состоит из всех действий, составляющих мою программу. Затем среда выполнения выполняет это действие. В большинстве случаев при этом выполняется чистый код. Время от времени среда выполнения использует вычисленные данные для выполнения операций ввода-вывода и возвращает данные обратно в чистый код.

Вы можете жаловаться, что это звучит как мошенничество, и в некотором роде: определяя действия и ожидая, что среда выполнения их выполнит, программист может делать все, что может делать обычная программа. Но система строгого типа Хаскелла создает сильный барьер между чистыми и «нечистыми» частями программы: вы не можете просто добавить, скажем, две секунды к текущему времени ЦП, и распечатать его, вы должны определить действие, которое приведет к текущему Время процессора и передайте результат другому действию, которое добавляет две секунды и печатает результат. Однако написание слишком большой части программы считается плохим стилем, поскольку из-за этого сложно определить, какие эффекты вызваны, по сравнению с типами языка Haskell, которые сообщают нам все, что мы можем знать о значении.

Пример: clock_t c = time(NULL); printf("%d\n", c + 2);в C, против main = getCPUTime >>= \c -> print (c + 2*1000*1000*1000*1000)в Haskell. Оператор >>=используется для составления действий, передавая результат первого в функцию, приводящую ко второму действию. Это выглядит довольно загадочно, компиляторы Haskell поддерживают синтаксический сахар, который позволяет нам писать последний код следующим образом:

type Clock = Integer -- To make it more similar to the C code

-- An action that returns nothing, but might do something
main :: IO ()
main = do
    -- An action that returns an Integer, which we view as CPU Clock values
    c <- getCPUTime :: IO Clock
    -- An action that prints data, but returns nothing
    print (c + 2*1000*1000*1000*1000) :: IO ()

Последнее выглядит весьма императивно, не так ли?


1

Если да, то как оно может существовать? Не нарушает ли это принцип функционального программирования? Это особенно нарушает ссылочную прозрачность

Он не существует в чисто функциональном смысле.

А если нет, то как узнать текущее время в функциональном программировании?

Вначале может быть полезно узнать, как время извлекается на компьютере. По сути, есть бортовая схема, которая отслеживает время (именно поэтому компьютеру обычно требуется небольшая батарея). Тогда может быть какой-то внутренний процесс, который устанавливает значение времени в определенном регистре памяти. Который по существу сводится к значению, которое может быть получено процессором.


Для Хаскелла существует концепция «действия ввода-вывода», которая представляет тип, который может быть выполнен для выполнения некоторого процесса ввода-вывода. Таким образом, вместо ссылки на timeзначение мы ссылаемся на IO Timeзначение. Все это было бы чисто функционально. Мы не ссылаемся на timeчто-то вроде «прочитайте значение регистра времени» .

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.