Действительно ли Haskell является чисто функциональным языком с учетом unsafePerformIO? - PullRequest
44 голосов
/ 26 июня 2010

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

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

Ответы [ 8 ]

115 голосов
/ 26 июня 2010

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

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

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

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

alt text

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

34 голосов
/ 26 июня 2010

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

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

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

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

4 голосов
/ 26 июня 2010

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

2 голосов
/ 04 января 2011

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

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

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

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

1 голос
/ 24 июля 2012

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

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

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

1 голос
/ 27 июня 2010

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

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

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

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

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

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

1 голос
/ 26 июня 2010

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

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

0 голосов
/ 14 апреля 2018

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

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

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

...