LINUX.ORG.RU

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


0

4

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

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

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


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

>Контроль над вводом/выводом в плагинах, которым был предоставлен интерфейс IO, если я правильно понял. Моё мнение — если плагины нужно контролировать, то предоставление им IO — уже ошибка.

А если плагину IO для дела надо? Но только то что можно и никакое другое. В чем проблема то?

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

>> Операционной семантикой, зафиксированной стандартом языка. Согласно которой, компилятор заменяет значения IO кодом, выполняющим ввод/ввывод.

Чего?

when :: bool -> IO () -> IO () when b f = if b then f else return ()

На какой ввод/вывод будет заменена функция when

when — не значение IO.

when True (putStr «hello») :: IO () — значение IO, и будет заменено на вывод строки hello.

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

>> Контроль над вводом/выводом в плагинах, которым был предоставлен интерфейс IO, если я правильно понял. Моё мнение — если плагины нужно контролировать, то предоставление им IO — уже ошибка.

А если плагину IO для дела надо? Но только то что можно и никакое другое. В чем проблема то?

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

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

>список - тоже какая-то особая монада, работа которой определена святым духом? selffix

Ну так что там с функцией

writeLine :: String -> ()

слабо сделать так чтобы ее не выкинуло?

Ведь ее возвращаемое значение в дальнейших вычислениях не используется.

Так же и со списком. Если мы не используем хвост списка, то он может быть не вычислен. Язык то ленивый.

Если мы пропустим начало списка, то хвост будет вычислено, а начало нет. Если мы после это используем начало списка, то начало будет вычислено позже хвоста.

В случае со списком нам пофигу ибо наблюдаемое поведение не меняется, а в случае с IO будет кавардак.

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

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

main = getContent >> putStrLn "I'm so lazy…"

Бесполезно. Хаскель такой ленивый, что даже монады не помогают.

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

> Ты программы на ассемблере видел? Вложенные блоки в коде на ассемблере видел? А про то, что любая Сишная программа легко транслируется в ассемблер, знаешь?

Бедняга, ты даже не понял о чем я. Вложенные блоки нельзя представить в виде последовательности хвостовых вызовов. У тебя рано или поздно выбьет нахуй стек, потому что вызов вложенного блока будет в общем случае нехвостовым.

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

> слабо сделать так чтобы ее не выкинуло?

А почему ее не нужно выкидывать? Такая ф-я возвращает () и больше ничего не делает. Почему бы ее не выкинуть? Я тебе более того скажу, ф-ю writeLine :: String -> IO () хаскель тоже выкидывает - потому что эта ф-я просто возвращает вычисление (и тоже больше ничего не делает - никаких строк она не выводит, например). И, наконец, совсем ломающие известия - хаскель вообще выкидывает ВСЕ ф-и, потому что результат любой ф-и в хаскеле известен на этапе компиляции. Это очевидная оптимизация - вычислить на этапе компиляции все, что возможно. Ты предлагаешь отказаться от оптимизации - какой в этом практический смысл? Зачем не выкидывать то, что можно выкинуть?

Так же и со списком. Если мы не используем хвост списка, то он может быть не вычислен. Язык то ленивый.

И?

В случае со списком нам пофигу ибо наблюдаемое поведение не меняется, а в случае с IO будет кавардак.

Где кавардак? Я никакого кавардака не вижу. >> сцепляет вычисления так же, как ++ - списки, так же как список имеет фиксированный порядок элементов подвычисления в вычислении так же имеют фиксированный порядок. Откуда взялся кавардак?

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

>Вложенные блоки нельзя представить в виде последовательности хвостовых вызовов.

Покажи мне код на языке С который нельзя транслировать в код с хвостовыми вызовами.

Вызовы функций ессно не считаются.

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

Как ты собрался затавить хаскель ее не выкинуть учитывая что он знает что тип () имеет одно значение.

Как ты, наверное, догадываешься, я буду использовать другие типы функций.

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

Где ты там увидил проблемы мне не ясно совершенно.

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

В модели с передачей RealWorld у тебя будут проблемы.

В моём варианте проблемы нет:

data MIO a = ... | Fork (MIO ()) (MIO a)

fork :: MIO () -> MIO ()
fork mio = Fork mio (return ())
и в рантайме:
run :: MIO x -> IO x
...
run (Fork mio1 mio2) = forkIO (run mio1) >> run mio2
Да, и я всё ещё жду ответа на поставленный мною выше вопрос: где в моей модели RealWorld?

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

делал для одного из языков сахар аналогичный do нотации

А, ну-ну. Выдающееся достижение, конечно, сахар писать. Невероятно сложно взять

do a <- action1
   b <- action2
   action3
и преобразовать в
action1 >>= \a ->
action2 >>= \b ->
action3
Конечно, без понимания, что такое монада, это никак не сделать.

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

Попробуйка одним IO запретить плагину открывать файлы за приделами некой директории.

Для этого будет написана другая монада, только и всего. И к ней рантайм, который перебросит её в IO.

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

Пример - применение do-нотации в других (не ИО) монадах. Там фиксируется порядок вычислений но без всяких ИО.

Не фиксируется.

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

Бесполезно. Хаскель такой ленивый, что даже монады не помогают.

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

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

Покажи мне код на языке С который нельзя транслировать в код с хвостовыми вызовами.

Вообще-то, любые вызовы можно сделать хвостовыми, через CPS.

Платой за это, правда, является рост кучи.

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

> Вообще-то, любые вызовы можно сделать хвостовыми, через CPS.

В языке С или вообще во всех языках? В языке C мне не понятно что делать с longjmp. А если говорить вообще о всех языках, то для того же Common Lisp полноценных CPS нет.

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

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

Ну то есть простой композиции тебе не хватит. Нужны присядания. ЧТД.

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

В языке C мне не понятно что делать с longjmp.

М-м-м, согласен, это не настолько просто. С другой стороны, setjmp/longjmp - это такой посредственный callCC, так что как-нибудь сделается.

А если говорить вообще о всех языках, то для того же Common Lisp полноценных CPS нет.

Э... не понял. CPS не есть кусок софта, это теоретическое понятие.

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

Ну то есть простой композиции тебе не хватит.

Где я это сказал?

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

> CPS не есть кусок софта, это теоретическое понятие.

Угу, которое ещё никто не показал, как применить к unwind-protect.

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

>Ну, попробуй в своей модели IO изобразить функцию fork типа IO () -> IO (),

Все равно не понимаю. Запускаем поток и в нем делаем unsafePerformIO для нашего значения.

unsafePerformIO :: IO a -> a unsafePerformIO (IO f) = let (!r, !_) = f newWorld in r

main запускается все тем же unsafePerformIO .

В чем проблема?

Да, и я всё ещё жду ответа на поставленный мною выше вопрос: где в моей модели RealWorld?

Внутри IO которой ты передал все управление.

Можно конечно все захардкодить в компилятор и говорить: Вот поглядите у меня тут гора императивного кода последовательно выполняет все действия и без всяких RealWorld.

Короче ты либо крестик сними либо трусы надень.

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

>Конечно, без понимания, что такое монада, это никак не сделать.

Про то, что я не только сахар, но и сами монады, в том числе те которые сложнее IO делал ты конечно «не заметил».

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

>Для этого будет написана другая монада, только и всего. И к ней рантайм, который перебросит её в IO.

Другими словами IO не справляется с контролем побочных эффектов.

ЧТД.

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

>Вообще-то, любые вызовы можно сделать хвостовыми, через CPS.

Про CPS я в курсе. Только это опять лицемерие. Стек просто переезжает в кучу и все.

Если алгоритму нужен стек то ты хоть что делай, а стек вынь да положь.

Я же писал про то что любую процедуру с любыми goto на языках типа С можно переписать в кучу процедур с хвостовыми вызовами без единого goto. Причем по честному. Без роста кучи.

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

не только сахар, но и сами монады

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

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

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

> Я же писал про то что любую процедуру с любыми goto на языках типа

С можно переписать в кучу процедур с хвостовыми вызовами без единого

goto. Причем по честному. Без роста кучи.



Подобные утверждения остро нуждаются в строгих формальных доказательствах. Поскольку таким доказательствам не место на ЛОРе, то лучше сразу выложить пруф на соответствующую научную статью. Если пруфа нет, то лучше об этом вообще не говорить. А аргументация типа «покажи мне код», очевидно, в качестве аргументации рассматриваться не может. А то получается типа «если Земля круглая, то покажи где она закругляется».

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

>а можно хоть что-нибудь подробнее? а то делал монады для меня звучит не содержательней, чем писал классы, или там, сочинял строчки кода. хочется проникнуться величием момента

Я уже несколько раз писал.

и зачем потребовался какой-то особый сахар

Примерно за тем же что и do нотация в хаскеле. Твой КО.

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

1) Станешь ли ты спорить с тем что любой сишный код можно переписать в виде кучи блоков двух видов:

а)

метка

ноль или несколько выражений/вызовов функций

goto метка

б)

метка

ноль или несколько выражений/вызовов функций

if условие then goto метка1 else goto метка2

2) Станешь ли ты спорить с тем что если эти блоки превратить в функции, а «goto метка» в вызовы функций то ничего не измениться?

Первое верно ибо процессор по другому не умеет. И если бы это было не верно то компиляторы бы не работали.

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

Вот тебе и все доказательство.

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

> Другими словами IO не справляется с контролем побочных эффектов.

С тем контролем, для которого она была создана, прекрасно справляется. А ты хочешь получить доступ к эффектам через IO, который эти эффекты создан скрывать.

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

Я уже несколько раз писал.

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

Примерно за тем же что и do нотация в хаскеле

примерно? различия в чём ты можешь сказать?

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

unsafePerformIO (IO f) = let (!r, !_) = f newWorld in r

Какой ещё newWorld?

Да, и я всё ещё жду ответа на поставленный мною выше вопрос: где в моей модели RealWorld?

Внутри IO которой ты передал все управление.

Т.е., только в рантайме. Итого ты признал, что в IO никакого RealWorld нет.

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

Про то, что я не только сахар, но и сами монады, в том числе те которые сложнее IO делал ты конечно «не заметил».

Ну, извини, что написано, на то и отвечаю.

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

Другими словами IO не справляется с контролем побочных эффектов.

Как раз справляется. Она отвечает на вопрос «есть ли побочные эффекты».

Более тонкий контроль легко строится на её основе.

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

Подобные утверждения остро нуждаются в строгих формальных доказательствах.

Ну блин, ну чего там доказывать, и так ведь очевидно.

1) Переделываем все циклы и все ветвления на метки, чтоб не мучиться.

2) Для каждого промежутка между метками сооружаем функцию, делающую всё то же самое, что делается в этом промежутке, и делающую return функция_для_следующего_промежутка().

3) Каждое «goto метка» заменяем на return функция_для_промежутка_после_метки().

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

> Вызовы функций ессно не считаются.

Это почему это не считаются? Очень даже считаются, ведь вложенный блок - это как раз аналог нехвостового вызова некоторой ф-и. Так ты умеешь легко и просто преобразовывать код с нехвостовыми вызовами в код без нехвостовых, не используя cps-преобразование? Прошу предоставить этот способ - потому что тянет как минимум на нобелевку. Это какая же экономия! Теперь вообще любую рекурсию, которая жрала O(n) памяти можно при помощи твоего алгоритма заставить работать за O(1)! Причем не тупо перенести информацию из стека в хип, а вообще исключить ее использование! настоящий прорыв.

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

> Не фиксируется.

Он там фиксируется в той же степени, в которой он фиксируется в ИО ;)

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

> Вообще-то, любые вызовы можно сделать хвостовыми, через CPS.

Ну тот баран говорит не про CPS, он как-то ловко и автоматически делает хвостовые вызовы из нехвостовых без малейших изменений кода - только метки понаставить.

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

> Угу, которое ещё никто не показал, как применить к unwind-protect.

Архимаг, ну зачем ты опять говоришь то, о чем не знаешь? CPS-трансформация - это просто преобразование кода, причем эквивалентное. Проблемы с unwind-protect у call/cc (хотя на самом деле давным-давно никаких проблем нет - ставишь барьер и все, но тебе-то откуда знать...), а не у CPS-преобразования.

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

> в том числе те которые сложнее IO делал

Так ведь не делал. Async - одна из самых простых монад.

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

> Я же писал про то что любую процедуру с любыми goto на языках типа С можно переписать в кучу процедур с хвостовыми вызовами без единого goto. Причем по честному. Без роста кучи.

Ну перепиши мне без роста кучи/стека нехвостовой факториал, умник.

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

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

А у тебя после:

эти блоки превратить в функции, а «goto метка» в вызовы функций

Окажется, что некоторые вызовы - нехвостовые. Облом-с.

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

>Какой ещё newWorld?

То который создает новое значение. Я же уже говорил что World можно заменить на BigInteger, updateWorld на increment, а newWorld будет 0.

И ничего не изменится. Только эффективность пропадет.

Т.е., только в рантайме. Итого ты признал, что в IO никакого RealWorld нет.

Ну да. Сделал код строгим... в С тоже никакого RealWorld нет ибо он строгий.

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

Это почему это не считаются? Очень даже считаются, ведь вложенный блок - это как раз аналог нехвостового вызова некоторой ф-и.

Если ты про это, то ты полный баран.

{
  foo1()
  {
    foo2()
  }
  foo3()
}
Во внутренний блок можно попасть только из одной точки кода и выйти из него только в одну точку кода. Адреса в обоих случаях известны на этапе компиляции и никакого стека не нужно.

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

>Так ведь не делал. Async - одна из самых простых монад.

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

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

>Окажется, что некоторые вызовы - нехвостовые. Облом-с.

Ты хоть раз видел ассемблерный код генерируемый компилятором С? Иди уроки учить детка. Тут большие дяди разговаривают.

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

То который создает новое значение. Я же уже говорил что World можно заменить на BigInteger, updateWorld на increment, а newWorld будет 0.

Полный код покажи, пожалуйста. А то непонятно ничего.

Вот у тебя определено:

data State
newtype IO a = IO (State -> (State, a))

Покажи реализацию функции

fork :: IO () -> IO ()

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

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

Конечно, нет. Во внутренний блок можно попасть откуда угодно и выйти куда угодно.

{
f1();
f2(){
if predicate then f2() else return;
}
if predicate then f2 else f3;
f3();
}

Здесь мы из вложенного блока выходим в начало того же блока и в последнее условие (то есть в две точки), а входим в него из трех точек - с конца вложенного блока, с точки после f1 и с условие в конце. И здесь вызовы f2() - нехвостовые, тебе выбьет стек. можешь сам написать подобный код в той же сишечке и убедиться в том, что стек действительно выбьет.

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