Haskell является действительно чисто функциональным языком, рассматривая unsafePerformIO?

На Haskell обычно ссылаются как пример чисто функционального языка. Как это может быть выровнено по ширине, учитывая существование System.IO.Unsafe.unsafePerformIO ?

Править: Я думал с "чисто функциональным", это было предназначено, что невозможно ввести нечистый код в функциональный компонент программы.

44
задан fuz 25 May 2011 в 16:36
поделиться

5 ответов

Языки, которые мы называем Haskell

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

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

Язык, известный как Haskell 98, указан прямо посередине, допуская полные и частичные функции. Agda (или Эпиграмма ), где разрешены только общие функции, еще менее выразительна, но «более чиста» и более безопасна. В то время как Haskell в том виде, в котором мы его используем сегодня, включает все, что связано с FFI, где находится unsafePerformIO. То есть вы можете написать что угодно в современном Haskell, хотя, если вы используете вещи из внешних колец, будет сложнее установить гарантии безопасности, упрощенные внутренними кольцами.

alt text

Итак, программы Haskell обычно не строятся из 100% ссылочно прозрачного кода, однако это единственный умеренно распространенный язык, который по умолчанию является чистым .

116
ответ дан 26 November 2019 в 21:38
поделиться

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

Хотя это правда, что unsafePerformIO был официально добавлен к языку как часть приложения FFI, причины этого в основном исторические, а не логические. Он существовал неофициально и широко использовался задолго до того, как в Haskell появился FFI. Он никогда официально не входил в состав основного стандарта Haskell, потому что, как вы заметили, это было слишком большим затруднением. Я предполагаю, что надежда была на то, что в какой-то момент в будущем это как-то исчезнет. Что ж, этого не произошло и, на мой взгляд, не будет.

Разработка приложения FFI предоставила удобный предлог для того, чтобы unsafePerformIO укоренился в официальном языковом стандарте, поскольку это, вероятно, не кажется таким уж плохим по сравнению с добавлением возможности вызова внешнего (IE C) кода (где в любом случае все ставки на статическое обеспечение чистоты и типовой безопасности сняты). К тому же было очень удобно разместить его здесь по, по сути, политическим причинам. Это породило миф о том, что Haskell был бы чистым, если бы не весь этот грязный «плохо спроектированный» C, или «плохо спроектированные» операционные системы, или «плохо спроектированное» оборудование или ..что угодно .. Конечно, unsafePerformIO регулярно используется с кодом, связанным с FFI, но причины этого часто больше связаны с плохим дизайном FFI и, действительно, с самим Haskell, а не с плохим дизайном любой чужой штуки, которую Haskell тоже пытается использовать. .

Итак, как говорит Норман Рэмси, официальная позиция заключалась в том, что использовать unsafePerformIO можно при условии соблюдения определенных обязательств по доказательству того, кто его использовал (в первую очередь, это не отменяет важные преобразования компилятора, такие как встраивание и общие подгруппы). исключение выражения). Пока все хорошо, по крайней мере, можно подумать. Настоящая проблема заключается в том, что эти обязательства по доказательству не могут быть удовлетворены тем, что, вероятно, является единственным наиболее распространенным вариантом использования unsafePerformIO, который, по моей оценке, составляет более 50% всех unsafePerformIOs, существующих в дикой природе. Я говорю об ужасающей идиоме, известной как «взлом unsafePerformIO», которая доказуемо (на самом деле очевидно) совершенно небезопасна (при наличии встраивания и cse).

У меня действительно нет времени, места или желания вдаваться в суть «взлома unsafePerformIO» или почему он нужен в реальных библиотеках ввода-вывода, но суть в том, что люди, которые работают над инфраструктурой ввода-вывода Haskells, обычно " застрял между камнем и наковальней ". Они могут либо предоставить безопасный по своей сути API, который не имеет безопасной реализации (в Haskell), либо они могут предоставить небезопасный по своей сути API, который может быть безопасно реализован, но то, что они могут сделать редко, - это обеспечить безопасность как при проектировании API , так и реализация.Судя по удручающей регулярности, с которой «взлом unsafePerformIO» появляется в реальном коде (включая стандартные библиотеки Haskell), кажется, что большинство выбирают первый вариант как меньшее из двух зол и просто надеются, что компилятор не испортит дела обстоят с inlining, cse или любым другим преобразованием.

Мне бы очень хотелось, чтобы все это было не так. К сожалению, это так.

1
ответ дан 26 November 2019 в 21:38
поделиться

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

Хорошо то, что вы можете (и должны) ограничить использование этого «нестандартного» кода несколькими конкретными, хорошо документированными частями вашей программы.

1
ответ дан 26 November 2019 в 21:38
поделиться

Я думал, что под "чисто функциональным" подразумевается невозможность внедрения нечистого кода...

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

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

unsafePerformIO - это способ получения эффектов небезопасным способом; где "unsafe" означает "безопасность должна быть гарантирована программистом" - компилятор ничего не проверяет. Если вы опытный программист и готовы взять на себя серьезные обязательства по доказательству, вы можете использовать unsafePerformIO. Но в этот момент вы уже не программируете на Haskell; вы программируете на небезопасном языке, который очень похож на Haskell.

34
ответ дан 26 November 2019 в 21:38
поделиться

Язык / реализация являются чисто функциональными. Он включает в себя пару «аварийных люков», которые вам не нужно использовать, если вы не хотите.

4
ответ дан 26 November 2019 в 21:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: