LINUX.ORG.RU

Почему на Common Lisp пишут в императивном стиле?


0

1

Недавно начал читать PCL (до этого изучал Scheme) - не могу привыкнуть к императивщине. По-моему, Лисп просто создан для того чтобы быть функциональным языком программирования :)

Связано ли это как-нибудь с тем фактом, что CL является «промышленным стандартом» Лиспа?


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

> Я вижу это как: «тело цикла с предусловием выполняется даже

в том случае, если условие изначально ложно».


Что за странный взгляд? iterate это не цикл, это значительно более мощная и гибкая конструкция. Ты пытаешься интерпретировать эту конструкцию как цикл, но это не верно. iterate это мета-цикл.

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

> А если функция инлайнится?

А зачем ей инлайнится?

По-моему, на примере вышеприведенного кода вполне ясно, что без

макросов Virgil, из Doors получился бы стокилометровый копипаст



iolib обходится простым CFFI и прекрасно себя чувствует. Ну да, там же нет говно-COM, ну да из говна конфетку и не сделаешь.

Я не вижу недостатков в моем случае.


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

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

Даже если не учитывать то, что это кривое говно и с тем, что он может, плохо справляется, то:

Как только он научится из

BOOL WINAPI CreateProcess(
  LPCTSTR lpApplicationName,
  LPTSTR lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL bInheritHandles,
  DWORD dwCreationFlags,
  LPVOID lpEnvironment,
  LPCTSTR lpCurrentDirectory,
  LPSTARTUPINFO lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);
Генерировать такое:
(define-external-function
    (#+doors.unicode "CreateProcessW"
     #-doors.unicode "CreateProcessA"
                   create-process)
    (:stdcall kernel32)
  ((last-error bool) rv process-information)
  "Creates a new process and its primary thread. The new process runs in the security context of the calling process."
  (application-name (& tstring :in t))
  #-doors.unicode
  (command-line (& astring :in t) :key void)
  #+doors.unicode
  (command-line pointer :key)
  (process-attributes (& doors.security:security-attributes :in t)
                      :key void)
  (thread-attributes (& doors.security:security-attributes :in t)
                     :key void)
  (inherit-handles boolean)
  (creation-flags process-creation-flags)
  (environment pointer :key)
  (current-directory (& tstring :in t) :key)
  (startup-info (& (union ()
                          (info* startup-info*)
                          (info startup-info)))
                :key (make-startup-info))
  (process-information (& process-information :out) :aux))

То есть, в данном конкретном случае, узнавать из документации, что

  • lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, lpEnvironment и lpCurrentDirectory - необязательные параметры
  • а lpProcessInformation аж выходной, и его надо занести не в список аргументов, а в список возвращаемых значений функции
  • что lpStartupInfo может быть указателем на STARTUPINFOEX
  • что флаги в dwCreationFlags это комбинация констант из вполне четко заданного списка, и что его желательно маршаллить как список из имен этих констант, а не как тупой dword
  • что нам не надо два варианта функции(CreateProcessW и CreateProcessA), а надо одну, имя который определялось бы при компиляции
  • что lpCommandLine в юникод-варианте может изменяться вызванным процессом, и что, соответственно, его нельзя маршалить как строку, память под которую освобождалась бы после вызова функции, а надо передавать как указатель
  • и что при возврате функцией нуля надо обязательно проверить GetLastError и кинуть соответствующее исключение

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

>ничего сложнее int foo(const char *bar) ты из лиспа не вызывал.

Смешно.

http://github.com/Lovesan/doors

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

>А зачем ей инлайнится?
Инлайнинг это оптимизация такая, часто применяемая к небольшим функциям, если что.

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

Угу, прекрасный пример того, как одна сложность пораждает другие. Впрочем, про Win API кажется уже давно известно. И да, я много писал под винду, так что знаю размер и степень вонючести.

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

>Зато я основательно задолбался fasl-ы чистить.
Это просто в ASDF не допилена пересборка систем. По идее, при изменении в системах, от которых :depends-on, всю зависимую систему надо перекомпилировать.

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

> Это просто в ASDF не допилена пересборка систем

Какая разница почему это? Факт в том, что это очень сильно мешает на практике.

По идее, при изменении в системах, от которых :depends-on,

всю зависимую систему надо перекомпилировать.



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

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

> Опять факториалы понеслись. Пипец.

дык ФП == Факториалов Программирование

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

Ну и с плюсовкой, само собой, без swig'а лучше даже не связываться.

С крестами лучше вообще не связываться.

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

> Инлайн функции?

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

Классы/типы/?


Не надо.

пакеты


А пакеты здесь при чём? Нет, не надо.

Замечательным свойством CL является интерактивная разработка, но макросы ей препятствуют, увы.

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

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

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

>С крестами лучше вообще не связываться.

Ниасиляторам — бесспорно. Любой мало-мальски сообразительный сишник как минимум оценит гарантированные вызовы деструкторов и все ништяки, отсюда проистекающие.

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

>Я так и не понял, что с инлайн функциями?
Они инлайнятся.
К.о.

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

Классы/типы/?

Не надо.


За классы не скажу, там действительно все динамично. А вот при изменениях типов(deftype) и структур(defstruct) перекомпиляция обязательна.

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

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

Линуксфэна поймали бешенные скобки, затащили в тёмный угол и жестоко изнасиловали каким-то предметом, отдалённо походящим на греческую букву «лямбда». И когда он, надруганный, лежал в дворовой грязи и горько рыдал, на соседней стройке сорвалась с крана реляционная база данных и упала на него, горемыку. Картину завершал болтающийся на зимнем ветру моргающий фонарь, и пушистые, но холодные вызовы WinAPI, тихо падающие по спирали с неба.

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

> И если их определение изменилось(баги пофиксили, и т.п.), то все

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

будет использоваться старое определение.



Так я не понял, почему я с таким никогда не встречался?

А вот при изменениях типов(deftype) и структур(defstruct)

перекомпиляция обязательна.



Ну вот и славно, что я ими никогда не пользуюсь.

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

Ты не понял, дурачок.
На CL и так можно ехать как на сях, если использовать winapi как на сях. То есть, ехать на телеге. А я создаю библиотеку, с помощью которой можно ездить как на поршкайен. Врубаешься?

И что там про SWIG? Когда он научится читать документацию к библиотекам? Или ты признаешься, что пернул в лужу?

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

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

Любой мало-мальски сообразительный сишник додумается бросать исключения из конструктора. А чо?

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

>А я создаю библиотеку, с помощью которой можно ездить как на поршкайен. Врубаешься?

Жаль тебя разочаровывать, но .NET придумали уже довольно давно. Хватит уже писать красноглазые биндинги. Бери инструмент, готовый к решению промышленных задач, а не клепай велосипед из хлама, подобранного на свалке истории.

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

>Любой мало-мальски сообразительный сишник додумается бросать исключения из конструктора.

А в этом есть что-то плохое? В Objective C для этого даже отделили аллокацию от инициализации — все для того, чтобы можно было обработать ошибку при создании объекта.

И уж кто-кто, а сишник сумеет подчистить выделенные ресурсы, прежде чем throw в конструкторе делать. И вообще, нахрена нужен убогий throw, когда есть return codes с такими-то setjmp/longjmp? Сишник будет писать на плюсах как на C с классами и в 99% случаев будет прав, успешен и не допустит утечек ресурсов.

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

Да настоящий сишник забухает, ёпт! Нах.й ему эти программы не нужны.

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

Это дотнет то поршкайен?
Посмеялся, спасибо.

Из него толком даже и winapi то не подергать - либо дрочим как в сях(unsafe в руки и поплясали), либо тормозим - в плане разработки как на жабе, а в плане исполнения кода как на питоне (System.Runtime.InteropServices.Marshal и друзья)

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

> Понятия не имею.

Ладно, подскажу, реализации сохраняют связи между функциями, так что реализация знает где и как какая функция используется. Для макросов такой связи нет.

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

Вот пример:

(declaim (inline foo))

(defun foo (x)
  (print x))

(defun moo (y)
  (foo y))

(moo 4)
=> 4

(defun foo (x)
  (print (1+ x)))

(moo 4)
=> 4
ander-skirnir
()
Ответ на: комментарий от archimag

Попробуй на досуге такой пример:

(declaim (inline plus1))
(defun plus1 (x)
  (+ x)) ;;bug

;;и где-нибудь в другом файле:
(defun foo (x)
  (plus1 x))

;;(foo 1) ==> 1

;;теперь, допустим, баг в первом файле поправили:
(declaim (inline plus1))
(defun plus1 (x)
  (1+ x))
;;его перекомпилировали, но второй файл - нет
;;(plus1 1) ==> 2
;;теперь пробуем вызывать foo:
;;(foo 1) ==> 1
;;нифига не изменилось.
;;Потому что функция объявлена инлайн и теперь
;;второй файл тоже надо перекомпилировать

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

> Попробуй на досуге такой пример:

А! Так ведь это же только по специальной дериктиве, ну извините, мне дорого моя время, я подобным не занимаюсь. Если мне нужен будет inline, то я лучше возьму C++.

archimag ★★★
()
Ответ на: комментарий от Love5an
(defun fact (n) (fold-left #'* 1 (range 1 n)))

;;; или
(defun fact (n) (product (range 1 n)))

Циклы или рекурсия это то что должно быть под капотом. А вот fold/map/filter/iterate - это комбинаторы, ими можно выразить любые алгоритмы, а то во что их превращать, это уже пусть реализация думает.

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

На CL пользоваться reduce/map/remove[-if] гораздо менее удобно, чем макросами для циклов. И выглядит оно зачастую гораздо менее приятно, чем последнее.

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

Кстати, у меня по этому поводу даже возникла мысль:
Дело в том, что такие клевые макросы для циклов, как loop и iterate, есть только в CL. Так может, одепты штанги потому и восхваляют конбенаторы, потому что ничего лучше и удобенее в их любимых языках нет(понятно, что сишный for или жабовский/сишарповский foreach гораздо менее удобны, чем оные комбинаторы, но ведь loop/iterate еще удобнее)?

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

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

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

> cl-sql - это унылая мерзость.

Мне в скором будущем предстоим морду к бд написать и cl-sql был основным кандидатом на эту задачу. Можешь вкраце пояснить почему его использовать не надо? Беглое чтение документации каких-то серьёзных недостатков не выявило.

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

Эти товарищи (т.е. стандартные ФВП) имеют набор оптимизаторов в SBCL - потенциально они могут быть столь быстрыми, как и макросы, раскрывающиеся в циклы tagbody/go. Но вообще да - пока что iterate выглядит во всех отношениях лучше и именно потому что CL это не ФП (а то в ФП эти товарищи как кирпичи, небольшим общим числом, из которых строятся любые функции, работающие с любыми АТД).

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

Глючит, тормозит и имеет траблы с кодировками.

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

loop или iterate это практические инструменты и работают так как работают «просто потому что», без формализма. А комбинаторы - это уже теория, она может пригодится для генерации лучшего кода компиляторами. (Тут ведь нет спору? Если где CS вещи и нужны так это в таких вот сферах вроде разработки конпеляторов (+ есть ещё некоторые другие ,немногочисленные, практические области)).

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

> loop или iterate это практические инструменты и

работают так как работают «просто потому что», без формализма.


Не! В iterate больше смысла, чем может показаться. Это не «просто потому что», это очень тонкая и продуманная система. Так сказать, мощный императивный ответ оголтелой функциональщине ;)

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

>Можешь вкраце пояснить почему его использовать не надо?

1) Глюки с кодировками

2) Не поддерживает (и не будет поддерживать) параметры в запросах (ни позиционные, ни именованные)

3) (как следствие 2) Глюки с экранированием строк

4) (как следствие 2) Дикое выделение памяти

5) (как следствие 2) Дикие тормоза из-за постоянного склеивания строк

Например, вот простенькое сравнение cl-sqlite и cl-sql: http://dmitry-vk.livejournal.com/14363.html. Я думаю, что десятикратное замедление работы будет заметно даже для простеньких веб-приложений.

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

>2) Не поддерживает (и не будет поддерживать) параметры в запросах (ни позиционные, ни именованные)

Тут имеется в виду, что вместо передачи значений параметров в нормальном виде, cl-sql каждый раз создает запрос в виде текстовой строки и отправляет его в таком виде.

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

Ясно. Благодарю за развёрнутый ответ.

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

это очень тонкая и продуманная система.

Как и всякий хороший *практический* инструмент - делает то, делает это, делает и то и это вместе; и всё это удобно использовать. А «просто потому что» потому что никто PhD не защищает по iterate :) (а то по комбинаторам - могут).

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

Ответ-то мощный, но если уметь пользоваться cl'вскими фвп, где-то эдак в половине случаев они оказываются удобнее. Вообще, под mapcar и reduce скрываются две фундаментальные алгоритмические идиомы, очень часто встречающиеся в программировании. Что касается рекурсии, существует множество задач, которые можно, но очень сложно представить итерацией, и при этом очень легко и просто - рекурсией. А часто наиболее удобным и интуитивным решением является комбинирование рекурсии и итерации. Тот же flatten, например:

(defun flatten (list)
  (loop :for x :in list
        :if (atom x) :collect x
        :else :append (flatten x)))

ander-skirnir
()
Ответ на: комментарий от ander-skirnir

Кстати, не могу не заметить, что в хаскелле flatten на гетерогенных списках - phd-level-topic.

ander-skirnir
()
Ответ на: комментарий от ander-skirnir

> но если уметь пользоваться cl'вскими фвп, где-то эдак в

половине случаев они оказываются удобнее.


А обосновать?

Вообще, под mapcar и reduce скрываются две фундаментальные

алгоритмические идиомы, очень часто встречающиеся в


программировании.



Отлично, но iterate их включает.

Что касается рекурсии, существует множество задач, которые можно,

но очень сложно представить итерацией



Есть, никто же не говорит, что рекурсия не нужна, просто не так часто.

Одно из главных преимуществ iterate перед map/reduce/filter - он более гибок и расширяем с точки зрения редактирования кода, внести изменения в него обычно значительно проще. Но тут опять надо оторваться от абстрактного рассмотрения кода с точки зрения эстетики и смотреть на него с точки зрения его разработки.

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

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

Раз за разом меня называют невеждой, когда я указываю на очевиднейшие недостатки. Да просто попробуй: сядь и напиши на хваленом лиспе какую-нибудь простенькую утилитку, которой так не хватает в повседневной жизни. Ощутишь и его излишнюю многословность, и тормозность (по крайней мере clisp очень медленно подгружает пакеджи), и отсутствие библиотек.

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

Отлично, но iterate их включает.

mapcar - да, но слишком громоздко и появляется дополнительная логика. reduce - нет.

Одно из главных преимуществ iterate перед map/reduce/filter - он более гибок и расширяем с точки зрения редактирования кода, внести изменения в него обычно значительно проще.

В одних ситуациях - да, в других - нет. Одним из преимуществ фвп в этом плане является чёткое разделение входных данных и функций, которые их обрабатывают. Кстати, то же самое можно сказать про format - хотя управляющая строка из себя представляет кошмар в лучших традициях J, зато она инкапсулирует в себе всю логику вывода, и данные с ней не перемешиваются, что видно уже по первому примеру из PCL:

(loop :for cons :on list
      :do (format t "~a" (car cons))
      :when (cdr cons) :do (format t ", "))

(format t "~{~a~^, ~}" list)

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