LINUX.ORG.RU

[FP] Помогите с выбором языка для ФП


0

4

Здравствуйте, пожалуйста порекомендуйте какой язык выбрать из списка и по возможности книгу/туториал по нему, скажите какие у него перед остальными плюсы: - Erlang - Scala - Clojure - Scheme - OCaml Или что-то другое. Python не предлагать, как впрочем и Haskell. Если язык не из списка - то интересует именно инопланетный синтаксис и возможность писать на чистом ФП. Желательно, чтобы язык был не чисто академическим, имелась возможность работы с тулкитами (Qt, GTK, Tk).

Изучать собрался пока ради удовольствия, потом буду использовать в работе и реализации пары OpenSource проектов «для себя». Заранее спасибо.

p.s.: А Refal еще жив? Может он шевелится и имеет связки с тулкитами и прочим?


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

>В таком случае утверждение просто бессмысленно. Либо я просто не понимаю, что ты имеешь в виду. Раскрой утверждение подробнее, пожалуйста.

Я имею в виду ровно то что я сказал.

Добавлять фичу которая не решает никаких практических задач глупо.

Как это сказать по-другому я не знаю.

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

Добавлять фичу которая не решает никаких практических задач глупо.

разумно. но какая фича имеется в виду в контексте беседы?

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

ОК, надеюсь, я правильно понял тебя. Давай к следующему пункту чтоли.

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

Теперь предположим что создатели хескеля не идиоты.

Значит, монада IO решает сугубо практическую задачу.

Какую?

Почему монада IO не нужна в окамле, скале, немерле, С, С++, паскале,… но нужна в хаскеле?

С чем боролись создатели хаскеля?

Чем отличается хаскель от перечисленных языков?

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

> Значит, монада IO решает сугубо практическую задачу.

Какую?

Сказано уже много раз. Задача: представление работы в внешним миром в терминах чистых функций. Один из возможных путей решения задачи — монада IO. В хацкеле выбрали этот путь.

Почему монада IO не нужна в окамле, скале, немерле, С, С++, паскале,… но нужна в хаскеле?

Ну это даже не смешно, назвал бы хоть миранду или клин. А то ни один из названных языков не является чисто функциональным.

Чем отличается хаскель от перечисленных языков?

Слишком много отличий, чтобы даже начинать разговор.

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

Гм. А, собственно, что ещё может быть целью разработки некоторого языка, кроме некоторого набора фич?

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

> И вообще я сам пишу на функциональных языках. Компиляторы по большей части.

Моё почтение и мои поздравления. Но этот факт разве как-то оберегает тебя от ошибок при рассуждениях про IO?

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

Почему монада IO не нужна в окамле, скале, немерле, С, С++, паскале,… но нужна в хаскеле?

Почему же не нужна? Очень даже нужна. Только не сделали. Почему не сделали - ну, мозгов, наверное, не хватило.

Чем отличается хаскель от перечисленных языков?

IQ авторов.

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

P.S. Чуть погорячился: в C, пожалуй, не нужна - уж больно низкоуровневый язык, и насчёт немерля не знаю - недостаточно им занимался, но уже в плюсах, например, очень пригодилась бы.

Зачем нужна - для контроля над сайд-эффектами.

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

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

Главная причина в том что перечислинные языки строгие, а хаскель ленивый.

В строгом языке нет проблемы порядка вызовов.

Нет проблемы с тем что функцию не вызвали.

Именно эти две проблемы решает монада IO.

Их можно решать по разному но все решения семантически эквивалентны протаскиванию значения типа RealWorld через все вызовы функций реализующих ввод/вывод.

Так сделано во всех языках в которых не определен порядок вызова функций.

Для тех кто до сих пор в танке кусок документации: If the I/O computation wrapped in unsafePerformIO performs side effects, then the relative order in which those side effects take place (relative to the main I/O trunk, or other calls to unsafePerformIO) is indeterminate.

Что думаете зря написано про «relative order»?

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

Какую?

ну, например, выразить операции с IO в системе типов для повышения удобства работы (локализация работы с окружением полезна хоть в Haskell, хоть в C). в OCaml и Scala этого не сделали, удобства меньше; в Clean и Disciple сделали, но с помощью других механизмов

то, что при этом можно ещё и спрятать в IO всякую низкоуровневую реализацию - тем лучше, абстракция наш друг

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

Чистота — это крайне серьёзная причина для введения непрозрачной обёртки над вводом-выводом. А вот с ленью как раз можно бороться и без монад.

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

>Зачем нужна - для контроля над сайд-эффектами.

Контролировать сайд-эффекты можно гораздо проще.

Достаточно ввести неизменяемые типы данных и исключить глобальные переменные включая те что прячутся за функциями типа fopen и протаскивать сервис имен явно.

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

А вот с ленью как раз можно бороться и без монад.

Как?

Только не забывай про то что я уже написал:

Их можно решать по разному но все решения семантически эквивалентны протаскиванию значения типа RealWorld через все вызовы функций реализующих ввод/вывод.

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

Контролировать сайд-эффекты можно гораздо проще.

Только почему-то нигде не сделано.

Достаточно ввести неизменяемые типы данных и исключить глобальные переменные включая те что прячутся за функциями типа fopen и протаскивать сервис имен явно.

Ты только что предложил другой способ реализации монады IO.

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

но все решения семантически эквивалентны протаскиванию значения типа RealWorld через все вызовы функций реализующих ввод/вывод.

Боюсь, что твоё «семантически эквивалентны» требует более подробного объяснения. Выше по топику я привёл (не ghc-шную) реализацию небольшого кусочка IO. Где там RealWorld?

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

>Только почему-то нигде не сделано.

Например так сделано в сингулярити.

Ты только что предложил другой способ реализации монады IO.

Я думаю ты чего-то недопонял.

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

Твой

data MIO x =

Он протаскивается через все вызовы ввода/вывода и каждый вызов порождает новое значение основываясь на предыдущем.

Чем это отличается от протаскивания RealWorld не ясно.

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

Например так сделано в сингулярити.

Я имел в виду «нигде из перечисленного тобой».

Я думаю ты чего-то недопонял.

Ну, почему же? Более того, посмотри упомянутую тут много раз IO_inside, и увидишь, что в некоторых компиляторах Хаскеля IO так и реализуется.

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

Поздравляю. Таким образом ты сделаешь хаскель строгим.

Только там, где это нужно. В этом и заключалась задача, не?

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

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

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

Давай выкинем IO, заменим его World и ты продемонстрируешь эти проблемы, ОК? Есть getLine :: World -> (String, World), putLine :: String -> World -> World, есть main :: World -> World.

Пример,

main = (\s -> putLine ("Text: " ++ s)) . getLine . (putLine "Type some text, please: ")

Какая функция будет не вызвана? Какой порядок вызовов будет нарушен?

Их можно решать по разному но все решения семантически эквивалентны протаскиванию значения типа RealWorld через все вызовы функций реализующих ввод/вывод.

Операционная семантика у некоторых из них эквивалентна, да, и то не у всех (что демонстрирует мигель). А денотационная — нет, нет и нет.

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

Если ты не будешь протаскивать строгость до всех мест где ты делаешь IO то поимеешь проблемы аналогичные тем которые создает unsafePerformIO.

Соответствующий кусок документации я уже цитировал.

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

Он протаскивается через все вызовы ввода/вывода и каждый вызов порождает новое значение основываясь на предыдущем.

Чего-чего-чего??? А это чем-то отличается от стандартного поведения IO?

Чем это отличается от протаскивания RealWorld не ясно.

Ну, наверное, тем, что MIO x не является функцией от RealWorld. И вообще функцией не является.

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

Клиника.

Давай выкинем IO, заменим его World и ты продемонстрируешь эти проблемы, ОК? Есть getLine :: World -> (String, World), putLine :: String -> World -> World, есть main :: World -> World.

Я уже приводил это:

type IO a = World -> (a, World)

И мы получаем:

getLine :: World -> (String, World)

putLine :: String -> World -> ((), World)

main :: World -> ((), World)

Другими словами ты просто написал то что происходит в монаде руками.

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

>Чего-чего-чего??? А это чем-то отличается от стандартного поведения IO?

Это я тебя спрашиваю чем поведение твоего кода отличается от поведения стандартного IO. :)))

Ну, наверное, тем, что MIO x не является функцией от RealWorld. И вообще функцией не является.

Ну да. MIO является RealWorld.

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

То есть ты признаешь, что фиксация порядка действий и избежание потери функций решаются не монадой? Ну чтд тогда.

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

Это я тебя спрашиваю чем поведение твоего кода отличается от поведения стандартного IO. :)))

Ничем. Так где там RealWorld?

MIO является RealWorld.

То есть, IO = RealWorld?

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

Я с самого начала пишу что порядок вызова функций задается протаскивание значения RealWorld через все вызовы функций ввода/вывода.

В хаскеле этим занимается монада IO.

Ты в своем сообщении с точностью до мелочей рассахарил то что происходит в монаде IO.

И чем же после всего этого занимается монада IO как не фиксацией порядка действий и избежание потери вызовов функций?

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

MIO = RealWorld

Минуточку, чуть раньше ты упоминал «значения (типа) RealWorld». Отсюда с необходимостью следует, что RealWorld имеет кайнд *. С другой стороны, MIO имеет кайнд * -> *. Как же они могут быть равны?

Далее, я утверждаю, что MIO является точным аналогом IO (только с меньшим количеством возможностей). Если ты говоришь, что MIO - это RealWorld, то либо согласись, что IO - тоже RealWorld, либо объясняй, в чём разница.

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

> Ты в своем сообщении с точностью до мелочей рассахарил то что происходит в монаде IO.

Да не сахар это. Это возможная замена. Можешь еще комонаду OI посмотреть :) Ей тоже заменить можно.

И чем же после всего этого занимается монада IO как не фиксацией порядка действий и избежание потери вызовов функций?

Еще раз. Фиксацией порядка действий и избежанием потери вызовов функций занимается композиция, а не монада. Заметил (.) в том куске кода?

Я с самого начала пишу что порядок вызова функций задается протаскивание значения RealWorld через все вызовы функций ввода/вывода.

Ты точно понимаешь, что такое ввод/вывод?

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

> Например так сделано в сингулярити.

Это интересно. Можно ссылку? У меня не получается сходу нагуглить.

sanuda
()

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

quantum-troll ★★★★★
()

Mercury

Не заметил в этом треде упоминания Mercury

http://www.mercury.csse.unimelb.edu.au/
http://en.wikipedia.org/wiki/Mercury_(programming_language)
http://mercurylanguage.ucoz.ru/

Меж тем, к описанию топикстартера подходит идеально.

В двух словах: чистый (как haskell) функционально-логический ЯП, «очищенный» статически-типизированный пролог.

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

> И чем же после всего этого занимается монада IO как не фиксацией порядка действий и избежание потери вызовов функций?

Winduzyatnik, смотри. Предположим, ты пишешь алгоритм для игры в шахматы через константу прочестьХод: IO Ход и функцию сделатьХод: Ход -> IO (). Твоя программа будет чистым математическим объектом - стратегией. Конечно, можно то же самое написать, потаскав повсюду RealWorld - но опять же, RealWorld - это не бессмысленная эстафетная палочка из 0 байт, а математически осмысленная вещь - позиция. В данном случае монада IO занимается тем, что позволяет тебе описать стратегию, не упоминая прямо позицию. Функции прочестьХод и сделатьХод тоже имеют понятный математический смысл и не содержат никаких инструкций ввода/вывода.

А теперь у нас есть интерпретатор, который позволяет пользователю поиграть в шахматы. Все инструкции ввода-вывода расположены именно в нем. Может быть, он действительно реализован так, что функция сделатьХод при вызове сама двигает фигуру и перерисовывает доску. А может быть, там все сделано как-то по-другому; какая разница? Ты по сути утверждаешь, что второй ход делается после первого только потому, что в интерпретаторе какая-то оптимизация происходит поздно и поэтому какой-то вызов происходит после другого. Может быть, там внутри действительно все так и есть. Но снаружи-то мы не знаем ничего про оптимизации, порядок вызовов и т.д.; мы просто описали осмысленный математический объект - стратегию - и в этом нам помогли монады.

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

Начнем разбираться с самого начала. Вот так выглядит полноценная реализация монады IO: Обрати внимания кода меньше чем у тебя. Да и хак в компиляторе нужен маленький и универсальный, а не только для IO.

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

Модуль раз:

data State
updateState :: State -> State
Собственно это единственный хакерский модуль. Хак нужен для того чтобы создать тип с бесконечным количеством значений и размером 0 байт. Сам хак происходит только на последней стадии. Кодогенератор просто не генерирует код для updateState и не передает и не возвращает значение типа State в/из функции. Делаем его отдельно ибо такой тип может быть и людям полезен. Например для реализации монады State.

Модуль два:

{-# LANGUAGE BangPatterns #-}
newtype IO a = IO (State -> (a, State))

instance Monad IO where
  return x   = IO (\w1 -> let !r = x; !w2 = updateState w1 in (r, w2))
  IO f >>= h = IO (\w1 -> let (!x, !w2) = f w1; IO (!next) = h x; (!r, !w3) = next w2 in (r, w3))
Делаем строгим все. Ибо лень нам тут не нужна. Совсем. Экспортируем из него только IO a и instance Monad IO. Конструктор IO (State -> (a, State)) не эспортируем. Таким образом пользователь не сможет ни добраться до состояния ни создать IO a самостоятельно.

Модуль три:

unsafeReadLine :: String

readLine :: IO String
readLine = return unsafeReadLine

unsafeWriteLine :: String -> ()

writeLine :: String -> IO ()
writeLine s = return (unsafeWriteLine s)
Собственно это пример реализации функций ввода/вывода. unsafeReadLine и unsafeWriteLine грязные с побочными эффектами. Реализованы через какой-нибудь FFI. Их ессно не экспортируем. Вопросы есть?

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

Яж говорю, вы народ за деревьями леса не видите.

Вот ты тут развел кучу текста про «математическую абстракцию», а зачем она вообще нужна?

Или ее просто так ввели? Если просто так значит создатели хаскеля идиоты. Или она таки решает некоторую практическую задачу?

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

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

> а зачем она вообще нужна?

Она, как и любая другая математическая абстракция, нужна за тем, чтобы можно было применять для анализа мат. методы. Например, ты можешь считать на пальцах и на палочках, но адекватные люди используют в таких случаях абстракцию под названием «числа» и считают в столбик, что существенно экономит время. Вот понадобилось людям сделать язык чистым и развести чистый/не чистый код (профиты такого подхода очевидны), а тут опа! Оказывается есть в математике такая штука, как «монады», в рамках которых как раз и можно получить изящное и эффективное решение. Можно было, конечно, придумать свой велосипед - но зачем, если уже есть годное готовое решение, с которым в комплекте идет куча готовых библиотек (матаппарат теорката).

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

> А нужна она для того чтобы зафиксировать порядок вычислений и не потерять вызовы функций.

Порядок вычислений и вызовы ф-й в хаскеле фиксируются следующим образом: (h(g(f(x)))) и никаких монад. конкретно монада ИО нужны за тем, чтобы: 1. язык был чистым (соответственно можно применять много вкусных вещей в плане анализа кода и оптимизации) 2. компилятор знал где чистый код, а где - нет (опять-таки широкие возможности для оптимизаций, котоыре без аналога ИО по-просту были бы невозможны)

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

> Яж говорю, вы народ за деревьями леса не видите.

Это ты уперся в три дерева и не видишь леса. Зачем ты отождествляешь понятия «IO в коде на языке Haskell» и «реализация IO средствами FFI, фантомного типа World и unsafePerformIO»?

Зачем пытаться определить IO посредством остальных языковых средств? IO — это один из базисов, который не определяется средствами языка, а постулируется. Почему ты не с таким же упорством не пытаешься выразить (let ... in ...) остальными средствами? Ну или (data ... = ...), например.

Или ты считаешь, что в терминах IO невозможно описать взаимодействие с внешним миром?

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

ЛОЛ!

IO — это один из базисов, который не определяется средствами языка, а постулируется.

Ты просто шикарно подставляешься.

Мигель на второй странице показал, как реализовать обрубок IO. Я показал, как реализовать полноценное IO.

Причем в моем случае даже хак не нужен. (Он только для эффективности) Можно просто в качестве эстафетной палочки использовать безразмерное целое. В этом случае IO будет реализовано вообще без хаков на чистейшем хаскеле пусть и не столь эффективно.

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

Ну так покажи, где в стандарте та хуйня, что ты написал? Конечно IO можно реализовать, но это не забота языка или программиста, это забота компилятора и рантайма. Или для тебя программа на языке и рантайм, в котором ее выполняют, вещи эквивалентные?

sanuda
()

Извеняюсь, что не обозначил сразу. А как на счет языков Mercury, Curry, Oz?

z0rk
() автор топика
Ответ на: комментарий от winduzyatnik

Вопросы есть?

Есть. За каким хреном ты это всё писал?

Что ты, собственно говоря, пытаешься доказать этим кодом?

Что можно реализовывать IO через передачу RealWorld? Я согласен, можно. Даже, наверное, удобно. В каких-то случях, по крайней мере.

Что оно так реализовано? Да, реализовано. Кое-где.

Что оно ВЕЗДЕ так реализовано? Нет, это неправда. Не везде, читай свою любимую IO_inside.

Что кому-то, кроме разработчиков компиляторов Хаскеля есть дело до того, как оно реализовано? Очень вряд ли.

Miguel ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.