43

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

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

+0

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

+1

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

+5

См. Также http://stackoverflow.com/questions/1916692/are-side-effects-possible-in-pure-functional-programming и http://stackoverflow.com/questions/1916692/are-side-effects-possible -in-pure-functional-programming и http: // stackoverflow.com/вопросы/2488646/why-are-side-effects-modeled-as-monads-in-haskell –

ответ

114

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

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

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

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

alt text

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

+0

Правильно ли я понимаю вас, что люди должны более точно сказать: «Haskell 98 - это чистый функциональный язык», а не «Haskell - это чисто функциональный язык»? –

+22

@Stefan Schmidt: больше похоже на «Haskell * - это чисто функциональный», но «GHC - это реализация супер-набора Haskell, который включает интерфейс внешней функции, который не является чисто функциональным». – yairchu

+1

Спасибо за интересную графику! –

4

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

1

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

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

+1

К сожалению? Я бы сказал «к счастью» :) Мы больше не «избегаем успеха любой ценой». – Artyom

34

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

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

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

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

+0

http://www.haskell.org/ghc/docs/6.12.2/html/libraries/ Что такое «часть Haskell» в вашем понимании? –

+3

@StefanSchmidt: То, что было определено в стандарте языка. Просто потому, что что-то является частью ghc, это не часть haskell больше, чем что-либо в gcc, является частью языка C. – sepp2k

+9

@ Stefan: Для таких вопросов «Haskell» означает стандарт * Haskell 2010 * или, возможно, стандарт * Haskell 98 *, опубликованный Cambridge University Press в 2003 году. В других контекстах многие программисты используют слово «Haskell» для описать, что произойдет с GHC на этой неделе. –

0

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

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

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

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

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

Желаю всего этого было не так. К сожалению, это так.

+3

Я хотел поддержать ваш ответ, но я думаю, что это то, что означает «небезопасный PERformIO-хак» или почему он необходим в реальных библиотеках IO, что делает ваш ответ полезным. Большая часть вашего ответа - просто ручная работа. –

+0

Я предполагаю, что «небезопасный PerformIO-хак» - это то же самое, что описано в http://www.haskell.org/haskellwiki/Top_level_mutable_state – hvr

2

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

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

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

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

1

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

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

Для получения дополнительной информации: GHC manual, Safe Haskell paper

Смежные вопросы