LINUX.ORG.RU

Глобальное состояние в ФП

 ,


2

3

А правильно ли я понимаю, что под «отсутствием состояния» в ФП понимается только отсутствие состояния переменных. У самого вычислителя, есть глобальное состояние, он в каждый момент времени находится в состоянии одной из редукций выражения, подобно тому, как машина Тьюринга в каждый момент вычисления «обозревает» строго определенную ячейку, на которой находится головка? Из этого следует, что ФП вычислитель относится к классу «Global State Machine» и у него, разумеется, ЕСТЬ глобальное состояние?

Ответ на: комментарий от quasimoto

Это ссылки на функции, а не на инты. И что значит неправильная? Одни и те же функции — одна и та же по значению структура.

По-этому оно не операция. Она должна принимать и возвращать одни и те же типы.

Это многократный вызов f someValue, f anotherValue и т.д. для некоторого сгенерированного набора значений, с проверкой что все вызовы возвращают одно и то же для одинаковых аргументов — если да, то тест пройден и функция скорее всего чистая

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

То есть хаскель это язык в котором явно разделены два слоя

Ну так вот, можно разделить эти два слоя не через монадическое ИО, а через комонадическое ОИ.

ну серьёзно — почему не прицепиться к С, мол, у него семантики нет

Ее и нет.

Как-то если посмотреть на публикации (Moggi, Gordon, Swierstra, SPJ на тему STG — посмотри, там мотивация не только в необходимости этого для реализации, но и для операционной семантики) — ни у кого ничего не заканчивается.

Так я же говорю - чтобы интерпретировать программы на хаскеле как нечто выполняющее полезные действия то надо придумать отдельную семантику для ИО, вот ее и придумывают. Функция readLn читает строку? Нет, не читает. Она возвращает IO String, никаких эффектов не совершая - такова ее реализация. Читает строку run readLn - но во-первых это не код на хаскеле, а во-вторых - это как раз и будет семантика ИО (мы описываем, что будет, если запустить на выполнение ИО-терм, генерируемый чистой readLn). Говорить что это якобы «тот же хаскель» - софистика.

anonymous
()
Ответ на: комментарий от quasimoto

Источник это ключевые слова moggi+monad, functional+specification+effects, lazy+io+operational+semantics, spj+publications, которые у меня с темой семантики IO ассоциируются — как-то нечего выкладывать :)

Ну так вот собрал бы статьи по всем темам, катологизировал, выложил - а мы бы тебе все вместе сказали, что ты - молодец!

anonymous
()
Ответ на: комментарий от anonymous

По-этому оно не операция. Она должна принимать и возвращать одни и те же типы.

Скалярное произведение векторов - не операция?

unC0Rr ★★★★★
()
Ответ на: комментарий от anonymous

Она должна принимать и возвращать одни и те же типы.

http://en.wikipedia.org/wiki/Operation_(mathematics)#General_description

Если хочешь одни и те же типы, то Add <: Signal/Callback, R(&)() <: Signal/Callback.

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

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

Ее и нет.

http://c-semantics.googlecode.com/files/2012-C-Semantics-Ellison-Rosu-POPL.pdf вместе с библиографией.

Функция readLn читает строку?

http://www.haskell.org/definition/haskell2010.pdf — там написано.

quasimoto ★★★★
()
Ответ на: комментарий от emulek

Ты что-то перепутал, я operator+ не писал, я писал f которая вместо вызова двух callback-ов возвращающих int и возвращения их суммы в виде int складывает эти callback-и в структуру Add и её возвращает, таким образом откладывает грязное вычисление и становится чистой.

quasimoto ★★★★
()
Ответ на: комментарий от unC0Rr

По-этому оно не операция. Она должна принимать и возвращать одни и те же типы.

Скалярное произведение векторов - не операция?

нет, не операция. Это свёртка. Результат «операции» не имеет никакого отношения к векторам, а является обычным числом. Ну вот смотри: какие операции можно произвести над файлом? Можно его сжать, можно скопировать, можно перекодировать, можно отправить другу и/или выложить в торрентах. Это всё операции. А размер файла? Или скажем список IP раздающих файл в торрентах? Или даже CRC32?

emulek
()
Ответ на: комментарий от quasimoto

я писал f которая вместо вызова двух callback-ов возвращающих int и возвращения их суммы в виде int складывает эти callback-и в структуру Add и её возвращает, таким образом откладывает грязное вычисление и становится чистой.

это называется «заметание мусора под ковёр». В твоей «структуре» грязный метод add, а твоя функция является простым селектором для callback'ов. Т.е. твоя функция ничего не откладывает, и вообще ничего не делает. Потому и является чистой. И это сразу будет раскрыто хотя-бы ещё одним сложением, которое ты уже не сможешь сделать чистым. И какой тогда смысл в λ-исчислении?

emulek
()
Ответ на: комментарий от emulek

Вот я и говорю, что то что предлагает anonymous это подобная ерунда — мол, любая f(x) = body[x] чистая, потому что возвращает не вычисленное body[x], а что конкретно за вычисления в body[x] нас типа не волнует.

То есть чистыми функциями можно строить некоторые выражения, эти чистые функции могут быть сколь угодно сложные (не как f, которая = id, как я уже говорил), но эффекты переносятся на сторонний вычислитель этих выражений. Само по себе это ок, но только не годится для обсуждения ЯП вообще, так как тут нам нужна именно полная картина, то есть f(x) = body[x] чистая или нет в зависимости от того можно ли ей сопоставить граф {(a, f(a)) | a \in Dom(f)} или нет ({(a, eval(f(a))) | a \in Dom(f)}, если кто-то упоролся :)).

В твоей «структуре» грязный метод add

Там нет такого метода, там есть Add::eval.

И это сразу будет раскрыто хотя-бы ещё одним сложением, которое ты уже не сможешь сделать чистым.

Просто Add::eval, туда перенесены непосредственные вычисления.

И какой тогда смысл в λ-исчислении?

Оно тут при чём? Заданный семантикой, видимо, в зависимости от того про какое λ-исчисление ты говоришь.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

То есть чистыми функциями можно строить некоторые выражения, эти чистые функции могут быть сколь угодно сложные

да. Можно делать сколь угодно сложные комбинации функций, которые ничего не делают.

Просто Add::eval, туда перенесены непосредственные вычисления.

не только. Вот здесь Глобальное состояние в ФП (комментарий) у тебя функция f() является типа чистой. Но это именно что «чистая». На самом деле ты очевидно плохо знаешь C++. Просто твоя f() гвоздями прибита к Add, в которой лежит грязный eval. Т.е. ты создал _видимость_ переноса сложения.

То, что ты хочешь сделать, делается чистыми виртуальными функциями в Add, что-бы f() возвращала Operator с чистой виртуальной функцией Operator::pure_operator()=0.Тогда f() может на входе принимать Operator'ы, и отдавать на выходе новый Operator, который можно уже запихать в другую f(). Далее Operator_add::pure_operator() может принимать два Operator, и сохранять например указатели на них. У нас получится классическое AST корнем которого является последний Operator дающий конечный результат. Тут ты можешь сделать редукцию, и вычислить своё выражение(редукцию лучше делать на прошлом этапе, при построении AST, т.е. если x+y, причём x,y нам уже известны, и x+y мы уже считали, то можно не делать новый объект, а взять старый). Это всё будет работать, но только до тех пор, пока все Operator'ы являются чистыми. Как только появляется грязный операнд вроде инкремента — всё ломается. Например ++x-x, тут совершенно непонятно, брать правый x до или после инкремента? Результат равен 0 или 1, причём оба результата равноправны. Не, ты конечно можешь костыли понаставить, как в java например, но эти костыли поломают оптимизацию, ибо компилятору не всегда выгодно выполнять так, как записано(например в этом случае, если сделать инкремент и брать старый правый x, то редукцию не сделать. А если наоборот — можно делать редукцию). Можно просто в стандарте написать «идите в попу, это UB», как в C/C++.

Оно тут при чём?

а как же!? См. выше: построение дерева из Operator'ов это λ-абстракция, ну а вычисление получившего выражения — аппликация. Всё вместе даёт нам β-редукцию. Самое главное тут: обеспечить чистоту функций. Т.е. из равенства указателей на Operator'ы должно следовать равенство самих Operator'ов⁽¹. А стало быть, указатели на Operator'ы должны быть константными(т.е. один раз создал, и всё, так он замороженным и остался). Иначе в процессе аппликации следствие (1) будет нарушено, и результат будет непредсказуем.

emulek
()
Ответ на: комментарий от quasimoto

спасибо за ссылку. Но эти «операции» нам не подходят, ибо мы их не можем связывать вместе в AST. Точнее они являются одноразовыми, и делят пути от вершину к листьям на две части: до оператора, и после. Например скалярное произведение под собой имеет векторы, а над собой — скаляры. И следовательно это минимум два дерева:

1. векторное, которое на выходе имеет скаляр

2. скалярное, один из листов которых — дерево №1.

Т.о. твой случай я разложил на два независимых случая. Зачем плодить сущности?

emulek
()
Ответ на: комментарий от emulek

функций, которые ничего не делают

Не ничего, а

строить некоторые выражения

Они это делают, то есть AST и т.п. строят.

Но это именно что «чистая».

Ну она чистая без всяких оговорок, по определению чистоты.

На самом деле ты очевидно плохо знаешь C++.

На самом деле достаточно такого примера, без Add : Expr, Fn : Expr, ..., Expr* f(Expr*, Expr*), просто потому что C++ не умеет в ADT¹, так что костыли наследованием и чистыми виртуальными функциями городить неохота, правильный пример я написал чуть выше (тут тебе и пример глобальной мутабельности, кстати).

То, что ты хочешь сделать, делается чистыми виртуальными функциями

¹) ADT в C++:

http://www.boost.org/doc/libs/1_55_0/doc/html/variant/tutorial.html

http://www.boost.org/doc/libs/1_55_0/libs/spirit/doc/html/index.html

http://www.boost.org/doc/libs/1_55_0/libs/spirit/example/qi/compiler_tutorial...

http://www.boost.org/doc/libs/1_55_0/libs/spirit/example/qi/compiler_tutorial...

а как же!? См. выше: построение дерева из Operator'ов это λ-абстракция, ну а вычисление получившего выражения — аппликация. Всё вместе даёт нам β-редукцию.

Это вряд ли, ты выше про такую грамматику говорил, как я понял

Op c ::= Op c + Op c | c

построение дерева это построение дерева, а не λ-абстракция, вычисление это редукция этого языка в c, не аппликация.

λ-исчисление начинается с чего-то вроде

T v ::= v | λ v . T v | (T v . T v)

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

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 2)
Ответ на: комментарий от emulek

Зачем плодить сущности?

Зачем топтаться на одном месте? AST/шмаесте, делай как хочешь, суть от этого не меняется, сделай надтип векторам со скалярами и будет тебе одно дерево и один плюс.

Вот бинарная операция (:+) :: Formula a -> Formula a -> Formula a, проехали.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

Они это делают, то есть AST и т.п. строят.

ничего они не строят. У тебя возможно только одно «дерево» из одного узла. Второй будет уже не прилепить.

Ну она чистая без всяких оговорок, по определению чистоты.

я же говорю: ты не знаешь C++. Если функция отдаёт класс, то содержимое класса тоже считается при определении чистоты. А ты делаешь вид, что не считается, как в лиспе и прочем ФП. Но в ФП всё проще, т.к. ты можешь любой выхлоп любой функции засунуть в любую другую(и ту же) функцию. В C++ увы не так.

На самом деле достаточно такого примера, без Add : Expr, Fn : Expr, ..., Expr* f(Expr*, Expr*), просто потому что C++ не умеет в ADT¹, так что костыли наследованием и чистыми виртуальными функциями городить неохота,

C++ умеет ADT, а если ты не осилил абстрактные классы — это твоя проблема, а не проблема языка. И абстрактные классы это не костыли. Костыли — твой мусор по имени boost.

правильный пример я написал чуть выше (тут тебе и пример глобальной мутабельности, кстати).

1. пример неправильный, и я уже устал объяснять почему.

2. в C++ это называется не «мутабельность», а «говнокод». Я думал, ты для простоты это говно написал, т.к. на практике такое говно не юзают.

λ-исчисление начинается с чего-то вроде T v ::= v | λ v . T v | (T v . T v)

а на русский перевести сможешь этот набор значков? Буду очень благодарен.

не, я понимаю, но очевидно не так как ты. Вот и хочу узнать, как правильно, и чего не хватает моему наброску с class Operator что-бы стать λ-исчислением?

emulek
()
Ответ на: комментарий от quasimoto

Зачем топтаться на одном месте? AST/шмаесте, делай как хочешь, суть от этого не меняется, сделай надтип векторам со скалярами и будет тебе одно дерево и один плюс.

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

v×v→a
a×a→a // ладно, скаляр на скаляр можно помножить как простое число из ℝ
a×v→? // WTF? какой смысл имеет скалярное произведение умноженное на вектор?
v×a→?
аж 4, и как половину из них реализовывать — я не знаю.

Вот бинарная операция (:+) :: Formula a -> Formula a -> Formula a, проехали.

ЯННП. А учить этот ваш хаскель мне лень. Не вижу профита.

emulek
()
Ответ на: комментарий от emulek

ничего они не строят. У тебя возможно только одно «дерево» из одного узла. Второй будет уже не прилепить.

«У меня» тут ничего нет, начнём с этого, ты сам что-то придумал и споришь, я написал только то что написал — f строит Add, очевидно, дерево из одного узла, да — Add r = r * r, f : r * r -> Add r, потому что простейший пример, любой другой на C++ будет выглядеть не очень (либо буст тащить), в примере на хескеле чуть лучше — (:+) : Formula r * Formula r -> Formula r и Cell : r -> Formula r строят Formula r, дерево из двух узлов — Formula r = Formula r * Formula r + r.

я же говорю: ты не знаешь C++.

Тогда ты мне приводишь пример как f возвращает разные значения для одинаковых аргументов.

C++ умеет ADT, а если ты не осилил абстрактные классы — это твоя проблема, а не проблема языка. И абстрактные классы это не костыли. Костыли — твой мусор по имени boost.

Проблема в твоём узком кругозоре :)

Нет, именно boost::variant реализует ADT, то есть добавляет суммы типов (собственно, boost::variant) к произведениям (которые уже есть — структуры, std::tuple). То о чём ты говоришь (абстрактные классы/интерфейсы, наследование) это классическое OOP — ADT и OOP на разных полюсах expression problem, так что то что ты говоришь даже смешно :) Что это за полюса — на одном у тебя есть фиксированные данные (например, AST) и легко добавляемые функции (обработки AST), это то что дают ADT — у этого одно применение, на другом полюсе — фиксированные интерфейсы и легко расширяемые иерархии классов (тут GUI может быть примером — типичные интерфейсы, но необходимость в расширении иерархии виджетов) это даёт OOP — другое применение. Но дело в том, что для AST нам нужно именно первое, делать это через второе может быть ок на безрыбье (если ADT нет), но вообще это через жопу (поэтому есть такие вещи как визиторы, например).

пример неправильный, и я уже устал объяснять почему.

Ты вообще на эту тему ничего ещё не сказал — объясняй что ты там объяснял :) А пример ок.

2. в C++ это называется не «мутабельность», а «говнокод». Я думал, ты для простоты это говно написал, т.к. на практике такое говно не юзают.

Какое говно на какой практике? IORef в структурки не засовывают? Ага, рассказывай.

а на русский перевести сможешь этот набор значков?

Я выше уже решил перейти на язык полиномиальных функторов, так что T v = v + v * T v + T v * T v :)

Это грамматика — посмотри на http://en.wikipedia.org/wiki/Backus–Naur_Form и производные, на нотацию из http://starling.rinet.ru/~goga/tapl/tapl.pdf, про типы произведения и суммы — http://en.wikipedia.org/wiki/Algebraic_data_type, http://en.wikipedia.org/wiki/Product_type, http://en.wikipedia.org/wiki/Tagged_union.

Вот и хочу узнать, как правильно, и чего не хватает моему наброску с class Operator что-бы стать λ-исчислением?

Добавить наследников Operator для переменных, абстракций (анонимных функций) и аппликаций (их применений) — v, (v, T v) и (T v, T v).

quasimoto ★★★★
()
Ответ на: комментарий от emulek

Ты перепрыгнул со сложения на умножение. Сложение скаляров есть, сложение векторов и матриц — есть, сложение векторов/матриц со скалярами — есть в любом мат. пакете, наверно, например в maxima:

(%i1) 1 + 2;
(%o1)                                  3
(%i2) [1, 2] + [3, 4];
(%o2)                               [4, 6]
(%i3) [1, 2] + 3;
(%o3)                               [4, 5]

Умножение в той же maxima:

(%i4) 2 * 3;
(%o4)                                  6
(%i5) [1, 2] * 3;
(%o5)                               [3, 6]
(%i6) [1, 2] * [3, 4];
(%o6)                               [3, 8]

ЯННП. А учить этот ваш хаскель мне лень. Не вижу профита.

Это универсальная запись. То есть имя_функции : множество -> (множество -> множество), что в категории Set то же самое, что имя_функции : множество x множество -> множество.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Тогда ты мне приводишь пример как f возвращает разные значения для одинаковых аргументов.

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

Что это за полюса — на одном у тебя есть фиксированные данные (например, AST) и легко добавляемые функции (обработки AST), это то что дают ADT — у этого одно применение, на другом полюсе — фиксированные интерфейсы и легко расширяемые иерархии классов (тут GUI может быть примером — типичные интерфейсы, но необходимость в расширении иерархии виджетов) это даёт OOP — другое применение. Но дело в том, что для AST нам нужно именно первое, делать это через второе может быть ок на безрыбье (если ADT нет), но вообще это через жопу (поэтому есть такие вещи как визиторы, например).

просто в C++ вовсе не классическое OOP, а своё, особое. Которое годно в частности и для обработки AST. Просто ты его не умеешь готовить.

Какое говно на какой практике? IORef в структурки не засовывают? Ага, рассказывай.

засовывают. Говнокодеры. А за ссылки спасибо, почитаю...

Вот и хочу узнать, как правильно, и чего не хватает моему наброску с class Operator что-бы стать λ-исчислением?

Добавить наследников Operator для переменных, абстракций (анонимных функций) и аппликаций (их применений) — v, (v, T v) и (T v, T v).

для переменных и функций — без проблем. А про аппликации, это как это?

emulek
()
Ответ на: комментарий от quasimoto

Ты перепрыгнул со сложения на умножение.

нет, ты перепрыгнул. Именно ты написал сложение, которое берёт ссылки на функции, а возвращает структуру в которой сложение. И обосновал такое «сложение» тем, что при _умножении_ векторов может получаться скаляр.

emulek
()
Ответ на: комментарий от emulek

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

Ещё раз — Глобальное состояние в ФП (комментарий), показываешь как вот эта функция Add f(int(&a)(), int(&b)()) возвращает разные значения для одинаковых аргументов, без всех этих фантазий про что-то там не относящееся к моему утверждению что данная конкретная f чистая.

просто в C++ вовсе не классическое OOP, а своё, особое. Которое годно в частности и для обработки AST. Просто ты его не умеешь готовить.

В C++ классическое ООП. Я не могу не уметь его готовить, я же показываю примеры, то что ты хочешь это вот так — http://llvm.org/docs/tutorial/LangImpl7.html#id1 (иерархия class ExprAST), а ADT это вот так — http://llvm.org/docs/tutorial/OCamlLangImpl7.html#id1 (type expr в ast.ml), или так — http://github.com/sdiehl/kaleidoscope/blob/master/src/chapter7/Syntax.hs (data Expr), в C++ вот так — http://www.boost.org/doc/libs/1_55_0/libs/spirit/example/qi/compiler_tutorial... (boost::variant-ы).

засовывают. Говнокодеры.

При этом ты даже не знаешь о чём речь :) IORef в структуре это как мутабельное поле в классе.

для переменных и функций — без проблем. А про аппликации, это как это?

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

-- | Типизрованное лямбда исчисление -- дерево с тремя узлами.
data Term :: * -> * where
  C :: a -> Term a                         -- ^ Переменные и примитивные значения с функциями.
  A :: Term (a -> b) -> Term a -> Term b   -- ^ Применение функции к выражению.
  L :: (a -> Term b) -> Term (a -> b)      -- ^ Анонимная функция.

-- | Интерпретатор.
eval :: Term a -> a
eval (C n) = n
eval (A e e') = eval e $ eval e'
eval (L e) = eval . e

test = L (\x -> C (+) `A` C x `A` C 3) `A` C 2
-- eval test
-- => 5

В test лямбда применяется к примитивной константе 2, в лямбде примитивная функция (+) дважды применяется к переменной лямбды x и примитивной константе 3. Тут используется HOAS, поэтому интерпретатор пишется легко без манипуляций с переменными и типизацией из коробки. Без HOAS (реализаций не пишу):

data Ty = B | Arr Ty Ty
data T = V | A T T | L (V, Ty) T

freeVars :: T -> [V]
subst :: V -> T -> T -> T
substV :: V -> V -> T -> T
alphaEq :: T -> T -> Bool
whnf :: T -> T
nf :: T -> T
betaEq :: T -> T -> Bool
check :: Env -> T -> Maybe Ty

Попробуй оба варианта HOAS / не-HOAS на C++ с наследованием / boost::variant.

нет, ты перепрыгнул. Именно ты написал сложение, которое берёт ссылки на функции, а возвращает структуру в которой сложение. И обосновал такое «сложение» тем, что при _умножении_ векторов может получаться скаляр.

Глобальное состояние в ФП (комментарий), конструктор узла дерева, иначе говоря. Ещё раз напишу — если дерево это Add r = r * r, то чистым конструктором будет r * r -> Add r, как я и написал. Почему дерево из одного узла? Ну дерево из нуля узлов это пустой тип — не интересно, из двух я написал выше — Formula r = r + Formula r * Formula r, так что чистый конструктор это уже Formula r * Formula r -> Formula r. Ну а в C++ это писать не досуг — итак смысл не в этом, а в том, что конструкторы AST чистые, его вычисление — нет, как анон хотел, только это всё нинужно в разговоре про чистые и нет функции в ЯП :)

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

Ещё раз — Глобальное состояние в ФП (комментарий), показываешь как вот эта функция Add f(int(&a)(), int(&b)()) возвращает разные значения для одинаковых аргументов, без всех этих фантазий про что-то там не относящееся к моему утверждению что данная конкретная f чистая.

чистая, не расстраивайся. Просто в C++ таким образом ничего не построить, с тем же успехом можно сказать, что

int f(int x, int y)
{
  return x+y;
}
является «чистой». Ну да, является. И что дальше? Она даже складывает два числа.

Я уже утерял нить дискуссии. Кто кому что доказывает?

emulek
()
Ответ на: комментарий от emulek

Кстати

eval :: Term a -> a

whnf :: T -> T
nf :: T -> T

являются интерпретаторами, но не трансляторами (как ни посмотри).

quasimoto ★★★★
()
Ответ на: комментарий от emulek

Дальше — чистых функций может быть не меньше грязных, их нужно различать на уровне семантики, документации, знания поведения. Дальше — pure это атрибут, такой же как const или noexcept, из его присутствия следует возможность делать CSE, его статические проверки были бы полезны.

Я уже утерял нить дискуссии. Кто кому что доказывает?

Нить про

int x = 0;

int f(int a) {
    return a + x;
}
x :: Ref Int
x <- new 0

f :: Int -> IO Int
f a = (a+) <$> get x -- a + x with generalized (+)

и вопрос являются ли такие f чистыми (очевидно, нет, в одном случае понятно почему, в другом — сфига ли оно тогда в IO).

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.