Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

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

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

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

alt text

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

Ответ 2

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

Реальный ответ заключается в том, что unsafePerformIO не является частью Haskell, более того, сборщик мусора или система времени выполнения являются частью Haskell. unsafePerformIO существует в системе, так что люди, которые строят систему, могут создать чистую функциональную абстракцию над очень эффективным оборудованием. У всех реальных языков есть лазейки, которые позволяют разработчикам системы сделать что-то более эффективным, чем отказ от кода C или кода сборки.

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

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

Ответ 3

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

Ответ 4

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

Рассмотрим скиписта. Для того, чтобы он работал хорошо, он требует доступа к RNG, нечистой функции, но это не делает структуру данных нечистой. Если вы добавите элемент и затем преобразуете его в список, тот же список будет возвращен каждый раз, когда вы добавите элемент.

По этой причине я думаю, что unsafePerformIO следует рассматривать как promPureIO. Функция, которая означает, что функции, которые имеют побочные эффекты и, следовательно, будут помечены как нечистые системой типов, могут быть признаны ссылочно прозрачными системой типов.

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

Ответ 5

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

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

Ответ 6

Safe Haskell, последнее расширение GHC, дает новый ответ на этот вопрос. unsafePerformIO является частью GHC Haskell, но не является частью безопасного диалекта.

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

Для получения дополнительной информации: Руководство GHC, Безопасная бумага Haskell

Ответ 7

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

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

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

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

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

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