LINUX.ORG.RU

Вопрос про Лисп


0

0

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

1. функции высших порядков (функции, которые возвращают как результат, и принимают в качестве аргумента другие функции, например map foldl и тд)

2. карринг (преобразование функции к функции с меньшем числом аргументов)

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

anonymous

1. Есть, конечно же.

2. Нет, его явным образом надо делать, например (для Scheme): (define (curry2->1 f a1) (lambda (a2) (f a1 a2)))

И потом использовать так: (map (curry2->1 + 2) ...)

3. Нет из коробки, но как и в любом другом языке можно легко эмулировать.

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

> Нет, его явным образом надо делать

Как Вы без этого живете? Явно делать это же не удобно!

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

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

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

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

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

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

> Честно говоря, не совсем понимаю, что это могут быть за задачи в которых важен динамизм?

Практически любой скриптинг. Метапрограммирование. Реализация в том числе и статически типизированных языков (целевой то язык всегда будет или безтиповым, или с тривиальной типизацией, и карринг должен раскрываться гораздо ранее - см. например lambda core в Helium, и аналогичные промежуточные языки в любых других реализациях Хаскелля).

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

>Хм... мне одному кажется, что надо тоньше?

Нет, мне тоже.

Кстати, а вот питон...

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

> Практически любой скриптинг. Метапрограммирование.

Интересно, а Emacs как классифицировать? Скрипт, метапрограмма или еще что???

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

> Скриптинг типичнейший. Прям таки хрестоматийный пример.

Так вроде почти весь Emacs написан на диалекте лиспа? Чё прям это все один большой скрипт? А тогда чем в сущности скрипт отличается от не скрипта?????

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

> Чё прям это все один большой скрипт?

Нет. Это очень много маленьких скриптов. Для которых в 99% случаев на фиг не надо интероперабельность друг с другом поддерживать.

> А тогда чем в сущности скрипт отличается от не скрипта?????

Назначением и размером.

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

> Нет. Это очень много маленьких скриптов. Для которых в 99% случаев на фиг не надо интероперабельность друг с другом поддерживать.

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

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

> Любая хорошо спроектированная программа состоит из очень маленьких изолированных кусочков,

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

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

> Изолированных? Только в идеальном случае. И тогда эти кусочки разумнее всего делать в виде скриптов. А в реальности как не изолируй, а взаимодействие будет, и тогда уже нужны тяжелые злые языки со строгой типизацией и статическим анализомю

Т.е. лисп не является конкурентом таких языков как C++ Java .NET, лисп занимает свою и при том много меньшую нишу?

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

> Т.е. лисп не является конкурентом таких языков как C++ Java .NET, лисп занимает свою и при том много меньшую нишу?

Нет. Лисп занимает гораздо более широкую нишу, включающую в себя ниши всего пречисленного, поскольку это метаязык. Он не для того, чтобы на нём писать приложения, он для того, чтобы на нём писать языки.

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

И, да, кстати, C++, Java, C# и прочие тоже не относятся к строгим статически типизированным языкам, по степени динамизма от элементарного Лиспа не шибко далеко ушли.

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

> Нет. Лисп занимает гораздо более широкую нишу, включающую в себя ниши всего пречисленного, поскольку это метаязык. Он не для того, чтобы на нём писать приложения, он для того, чтобы на нём писать языки.

Значит можно на лиспе написать лисп со строгой типизацией, каррингом и ленивыми вычислениями (и не таким ручным способом как описано выше, а автоматически как в хаскелле где не нужно ни чего писать дополнительно ни при объявлении функции ни при ее использовании)??

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

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

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

Qi
Clojure

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

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

Нет пути :)

Зачем?

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

> Нет пути :) Зачем?

Вот я тоже начинаю думать "зачем?". Может С++ часть тоже на лиспе переписать раз он такой Ъ? :))) Короче, познакомлюсь поближе, а там видно будет. Время есть.

Такие вопросы:

1. Как в cl обстоят дела с многопоточностью, синхронизацией?

2. Что можно использовать для построения GUI? При чем интересует не только использование уже готовых компонентов, но и построение на базе существующих своего набора компонентов в традициях ООП?

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

> Как в cl обстоят дела с многопоточностью, синхронизацией? Что можно использовать для построения GUI?

Может не будем о грустном? :) Хреново, если кратко. Нет, есть куча тех же биндингов к графическим фреймворкам, но вот юзабельного...

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

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

Каких именно, кстати? А то я недавно посокрушался было, что нет никакого более-менее явного интерфейса с Garbage Collector-ом, а потом взял - и нашёл. В официальной доке к GHC (ну да, других компиляторов мы не знаем).

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

> Значит можно на лиспе написать лисп со строгой типизацией,

Конечно же можно. Тривиально.

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

>2. Что можно использовать для построения GUI?

Unix way. Отдельным процессом программку на Tcl/Tk, с которой общаться по простому текстовому протоколу.

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

> Отдельным процессом программку на Tcl/Tk, с которой общаться по простому текстовому протоколу

, и о быстром гуе можно не мечтать.

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

> Уж всяко быстрее чем Qt или GTK+ с перделками-свистелками.

А т.е. вы таки предлагаете использовать Tk, а я подумал что это было только в качестве примера. Можно использовать и Tk, но тогда может получиться нечто вроде Ткаббер, от внешнего вида которого 90% пользователей блюет, а остальные привыкли :)

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

Пользователи, которым не нравится внешний вид Tk - это, как показывает практика, дураки. А дураки не нужны.

ЗЫ: сам я вообще WinForms использую. Из Лиспа.

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

> Каких именно, кстати?

Тут соображения общего характера. Хаскель уж больно сильно функционален! :) Логика приложения пишется на нем на ура, однако, как дело доходит до "Реального Мира"... Хоть вешайся... И нет желания разводить зоопарк языков. По этому обратился в сторону всяких гибридов.

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

> Может не будем о грустном? :) Хреново, если кратко. Нет, есть куча тех же биндингов к графическим фреймворкам, но вот юзабельного...

А можно подробнее?

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

> Хаскель - лучший в мире императивный язык (c) Simon Peyton-Jones

А добило меня вот что. Может я конечно чё не понимаю в Хаскеле... Есть биндинг gtk2hs, возникла такая задача: создать на основе абстрактного виджет свой виджет, а на основе последнего еше один. В ООП эта задача решается как 2 пальца... А в Хаскеле максимум, что я смог сделать так это подцепить сигнал перерисовка, подцепить к нему через IORef состояние, в итоге получилось полное Г...! Может Вы объясните как все организовать???

anonymous
()

>Подумываю изучить Лисп

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

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

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

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

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

> возникла такая задача: создать на основе абстрактного виджет свой виджет, а на основе последнего еше один.

Гм. А можно подробнее? Я что-то не шибко понял, чего ты пытаешься достичь.

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

> 1. Как в cl обстоят дела с многопоточностью, синхронизацией?

http://www.cliki.net/concurrency

Хотя сам я не трогал...

> 2. Что можно использовать для построения GUI? При чем интересует не только использование уже готовых компонентов, но и построение на базе существующих своего набора компонентов в традициях ООП?

http://www.cliki.net/Graphics%20Toolkit

http://www.cliki.net/CLIM

http://www.cliki.net/clg

http://www.cliki.net/cells-gtk

Есть еще крутая экзотика типа Lambda-GTK (http://common-lisp.net/project/lambda-gtk/), но я про нее только слышал...

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

Насчёт карринга - вопрос оказался непрост. Я не говорю про Scheme, там карринг наверняка можно сделать с помощью макросов и он будет ничем не хуже, чем в Хаскеле. Я не согласен, что карринг как-то связан с типизацией - мы просто подставляем несколько первых параметров и нам совершенно по барабану, типизированные они или нет.

Я пишу на Common Lisp. Scheme и Common Lisp - это, на самом деле, вообще совершенно разные языки. Scheme потенциально мощнее, т.к. там есть call/cc. Тем не менее, Scheme - это язык скорее учебный и научный, а CL - промышленный.

Дальше я говорю только про CL. В CL первая засада состоит в том, что есть flet. Та "функция", которая определяется с помощью flet - это и не функция вовсе, а существующая только во время компиляции абстракция. Самый простой способ сделать карринг - был бы вот такой:
(defun curry (fun arg) (lambda (&rest args) (apply fun arg args)))

Но дальше "закаренную" функцию нужно вызывать с помощью apply/funcall (в Cl функцию можно звать либо по имени, либо с помощью apply/funcall)

> (setf new-fun (curry '+ 1))
#<interpreted closure (LAMBDA (&REST ARGS) (APPLY FUN ARG ARGS))>
> (funcall new-fun 2)

А "функции", определённые с помощью flet, так вызвать нельзя.

Если хотим истинной красоты, то можно сделать примерно так. Для глобальных функций curry-m будет определять глобальную же функцию с новым именем.
>(defmacro curry-m (new-name fun &rest args-to-subst)
> `(defmacro ,new-name (&rest args)
> `(,',fun ,,@args-to-subst ,@args)))

Тогда
> (curry-m format-t-message format t "message: ~A~%")
FORMAT-T-MESSAGE

> (format-t-message "привет")
message: привет
NIL

Для локального использования нужно другое определение (следуя дизайну CL)
>(defmacro curry-m-let (((new-name fun &rest args-to-subst)) &body body)
> (let ((args (gensym)))
> `(macrolet ((,new-name (&rest ,args)
> `(,',fun ,,@args-to-subst ,@,args)))
> ,@body)))

и тогда
> (curry-m-let ((plus1 + 1)) (plus1 2))
3

Хотя это определение недоделано, но и ладно.
Но плюс к тому, ещё есть symbol-macrolet. Короче, основные трудности преодолены, дальше доделывать некогда. "Доказательство предоставляется читателю". И возможно, появятся ещё какие-то грабли. Например, попытка закаррить макрос может привести к каким-то непреодолимым трудностям (но макросу нельзя сделать и apply; любой программист на лиспе никогда не должен забывать, что именно перед ним - функция или макрос).

Отмечу, что в лиспе можно и принято заводить глобальные переменные, кроме того, есть необязательные аргументы у функций, аргументы - ключи
( подобно слову find в команде юникса #find . -name foo.bar )
Им можно придать значения по умолчанию. Есть макросы, которые позволяют гораздо больше, чем просто карринг. Так что нет такой острой нужды в карринге.

Насчёт ленивости - я бы не сказал, что можно прямо вот так взять и эмулировать её. Это можно, но совсем не легко, если пытаться делать ленивым весь язык. Практически, придётся написать свой транслятор из ленивого лиспа в обычный. Конечно, писать транслятор с лиспа на лисп на лиспе особенно легко, но всё же, это - всё равно большая работа. Это будет несложно для Scheme, но сложно для CL (просто ввиду большого размеров). Хотя, есть проект screamer, добавляющий в лисп недетерминизм, думаю, что ленивость добавить - это примерно такой же объём работы. Однако, есть всякие альтернативные ленивости варианты. Очень многое можно делать во время компиляции. Например, есть вот такая вещь www.ap5.com - in-process реляционный сервер, который при компиляции лисп-функции строит план выполнения реляционных запросов.

Мне кажется, что не получится взять и перенести идеологию Хаскеля на лисп. У лиспа есть своя идеология - в неё нужно вникнуть. Но она покрывает гораздо более широкий класс задач, чем чистое ФП. Common Lisp и Scheme я вообще не считаю ФП языками, т.к. там нет врождённого примитива, отличающего, например, чистую функцию от грязной или декларатора, ограничивающего допустимые побочные эффекты по отношению к аргументам (такого, как const в С). Хотя это тоже можно построить, но это будет совсем нелегко ввиду большого объёма работы.

Насчёт lisp vs java. Если использовать декларирование типов, то лисп становится статически типизированными и тогда по производительности он практически не уступает java даже на простых задачах, см. http://shootout.alioth.debian.org/gp4sandbox/benchmark.php?test=all&lang=...
В более сложных случаях, где используются разного рода метапрограммные вещи, я так думаю, что лисп сделает яву. Хотя это нужно проверять.

Касаемо же библиотек - в то время как в Яву вкладывались деньги корпорации Sun, в лисп их никто не вкладывал, не было единого центра и плана развития, не было идеологии совместимости и свободного использования. В CL есть несколько родных средств построения гуя, но, по-моему, все они - платные или связаны с какими-то античными системами, или связаны с конкретной реализацией. Взамен этого есть биндинги, но лично я ещё ничего гуёвого на лиспе не писал.

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

помнится пользовался ltk (там же, на cliki можно найти, биндинг к тк) вполне приятные воспоминанися, но в моем случае гуй был простой очень и было это давно, но проект жив, посмотреть стоит

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

Эх, неправильно я определил карринг. Параметры вычисляются в момент макрорасширения, а должны вычислять в момент исполнения. Ща буду думать, как исправить.

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

Вот так вроде правильно:

(defun quote-all-in-list (list) 
  (mapcar (lambda (x) `',x) list))

(defmacro curry-m (new-name fun &rest args-to-subst) 
  (let ((args (gensym))) 
    `(defmacro ,new-name (&rest ,args)
       `(,',fun ,,@(quote-all-in-list args-to-subst) ,@,args))))


(defmacro curry-m-let (((new-name fun &rest args-to-subst)) &body body) 
  (let ((args (gensym)))
    `(macrolet ((,new-name (&rest ,args) 
                  `(,',fun ,,@(quote-all-in-list args-to-subst) ,@,args))) 
       ,@body)))

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

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

Не по барабану - нам надо знать, сколько их там ещё осталось. На их тип конечно же плевать, а вот на количество - нет. Пользоваться функциями с произвольным количеством аргументов - не спортивно и производительность резко пострадает, если все аргументы списками передавать.

> Насчёт ленивости - я бы не сказал, что можно прямо вот так взять и эмулировать её. Это можно, но совсем не легко, если пытаться делать ленивым весь язык. Практически, придётся написать свой транслятор из ленивого лиспа в обычный.

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

А так - есть и извращенческие решения, вроде того же http://dslengine.sourceforge.net/ (осторожно, код написан идиотом)

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

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

> Гм. А можно подробнее? Я что-то не шибко понял, чего ты пытаешься достичь.

Да в сущности для ООП языков задача тривиальна, в сущности мне нужно на основе абстрактного виджета построить свой виджет (класс), например, AbstractPlotter, который будет реализовывать основной функционал построения всяческих графиков (рисование коорд. сетки, отслеживание координат и тд.). Далее класс AbstractPlotter расширить, например, 1) для рисования функций (класс FuncPlotter) 2) для рисования гистограмм (класс GistPlotter). Иными словами получается такая иерархия классов графических объектов, каждый из которых расширяет функционал базового класса. При этом, аккурат, все классы хранят свои состояния, которые можно модифицировать с помощью методов (сюрприз :) ).

Вроде все просто и удобно, но самое интересное происходит когда от ООП мы переходим в функциональный стиль. И тут у меня возникает очень большой вопрос: как что-то подобное можно сделать в Хаскеле, я не говорю что также (в стиле ООП), я хочу узнать как аналогичные вопросы решаются в хаскелле (желательно, применительно к gtk2hs). Честно говоря, у меня закралось такое смутное подозрение, что красивого решения подобных вопросов при построении гуи в Хаскелле НЕТ.

А вообще получается как в анекдоте: рассказываю Чепаеву как устроен автомобиль, а он говорит "Ну, вот все понял, а где спрятана лошадь не понял!".

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

> Пользоваться функциями с произвольным количеством аргументов - не спортивно и производительность резко пострадает, если все аргументы списками передавать

Можно обойтись и без &rest-ов. Есть port:arglist из clocc. Только возиться нужно, чтобы этот список потом разобрать. Правда, это достаточно сделать один раз и потом будет много пользы в других случаях тоже. И ещё - я надеюсь, что компилятор умеет преобразовывать &rest там, где число аргументов с вызывающей стороны известно. Нет у него причины так не делать. Т.е., пока в коде нет apply/funcall, можно пользоваться и &rest -ом. А когда есть apply/funcall, то всё равно теряем. Хотя я это не проверял.

> Я вообще с подозрением отношусь к ленивости, из неё слишком много неприятных последствий лезет

Я тоже не фанат ленивости (наелся в Mathematica по горло), но будучи честным с собой, я не хочу принимать такую точку зрения, что "всё, чего мы не можем, нам не нужно". Я своих окружающих давно пытаюсь убедить, что им нужно пользоваться лиспом, но они мне отвечают "МП нам не нужно", "расширяемый компилятор нам не нужен" и т.п. Потому что сами они пишут на C или на ocaml.

Ленивость иногда всё же нужна. Иногда хочется сделать именно mapcar по списку форм в файле, не читая сразу весь файл. И этого сделать нельзя. Для этого понадобится всё же другой mapcar, т.е. транслятор огромного количества встроенных функций. Можно, впрочем, обойти это методологичнски, например, с помощощью iterate. А ленивость по требованию требует ещё и дизайнерских усилий, чтобы язык не испортить.

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

А и кстати! У меня вообще &rest стоит в определении макроса, т.е., он существует только во время компиляции. Так что тут никаких потерь по быстродействию нет.

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

> И ещё - я надеюсь, что компилятор умеет преобразовывать &rest там, где число аргументов с вызывающей стороны известно.

Ага. Но ведь без apply то тут как раз и не обойдёшься.

> Я тоже не фанат ленивости (наелся в Mathematica по горло),

Там то она как раз явная только, неявной нет. Меня скорее пугает ленивость Хаскелля.

> Ленивость иногда всё же нужна.

Именно. Иногда. В явном виде.

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

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

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