Pull to refresh

Comments 13

Если бы этот язык хорошо подходил для научных целей, то научное сообщество использовало бы его, а не Python. Чем Racket лучше Питона?

print('Hello, World!')

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

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

  • как на Racket зарабатывать (или что на этом можно делать)

  • чем Racket лучше Julia (раз заработать продажей приложений сложно)

  • прав ли автор в плане выразительности (multiple dispatch и прочие штучки Julia)

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

"Зачем?" - Ну потому что, перевод второй части о Racket уже был опубликован на хабре, а вот первой небыло, мне показалось что узнать начала этой темы тоже будет интересным. Во вторых, тема Лиспа мне очень близка, потому что я Лиспер. В третьих, мне надо было с чего то начать, ибо я планирую разместить на хабре серию статей о программировании в GIMP Script-fu, который имеет "некоторое" отношение к тому же семейству языков к которым принадлежит и Racket. И поверьте я разобрался, но не счёл возможным сокращать текст автора. Julia это отдельная большая тема, как бы в комментариях её невозможно обсудить, моё личное мнение(пока ещё временное и выработанное на основе поверхностного взгляда) макросы в Джулии чуть труднее будет писать. Не могу сказать про все штучки джулии, но например множественная диспетчиризация это что? зависимость выполняемого метода(или кода) от типа входных аргументов? В лиспе (в Racket то же что то есть, но точно не знаю надо искать) ему соответствует обобщённая функция(с методами) осуществляющая ровно такую же функцию, на её основе строится объектная система.

Всё это очень интересно, но хотелось бы увидеть какие-то простые практичекие примеры. Ну вот например, мне требуется распарсить текстовый файл, добыть из него какую-то строку, отправить её через HTTP запрос в формате JSON и написать в лог файл об успешности операции. Как выглядит work-flow настоящего липсиста ?

Код? Когда я буду писать о Script-fu GIMP там будет много кода, в нём ничего не обычного, обычный код со скобочками снаружи вызываемой функции. Твой пример понятный пример, который сводиться в функцию в рамках любого языка программирования. Сила Лисп(и да Джулии тоже) проявляется в другом, не сразу, по мере написания и развития проекта. Когда ты пишешь код, и начинаешь различать в нём повторяющиеся синтаскические конструкции, когда они начинают тебе надоедать ты можешь разработать синтаксис, который облегчает тебе работу с подобными шаблонами кода, уменьшает риск случайной ошибки или можно создать необходимую абстракцию в языке, которую не предусмотрели разработчики языка.

Я приведу простой пример, есть макрос for обеспечивающий проход по заданному индексу до последнего заданного индекса, он универсальныи и применим для любых действий:

;;(for (i 1 10)
;;   (prin1 "i: ") (print i))

(define-macro (for var . body)
   (let ((i     (car var))
         (start (cadr var))
         (end   (caddr var))
         (lp    (gensym)))
      `(let ,lp ((,i ,start))
          (cond
           ((<= ,i ,end)
            ,@body
            (,lp (succ ,i))
            )
           )
          )
      ))

Но иногда мне надо работать с векторами:

(let ((v1 #(12 13 14 15)))
  (for (i 0 (- (vector-length v1) 1))
     (prn "v1[" i "] = " (vector-ref v1 i) "\n")))

И вот этот -1 тебя постоянно достаёт, есть риск пропусить его. Что делает лиспер? он создаёт новый макрос:

(define-macro (for-vect var . body)
   (let ((i     (car var))
         (vect  (cadr var))
         (start 0)
         (end   (gensym))
         (lp    (gensym)))
      `(let ((,end (- (vector-length ,vect) 1)))
         (let ,lp ((,i ,start))
          (cond
           ((<= ,i ,end)
            ,@body
            (,lp (succ ,i))
            )
           )
          ))
      ))

Позволяющий писать так:

(let ((v1 #(12 13 14 15)))
  (for-vect (i v1)
     (prn "v1[" i "] = " (vector-ref v1 i) "\n")))

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

PS: И да, макроса for в script-fu изначально тоже не существовало.

И так вот развивая язык в процессе написания проекта, создаётся уникальный DSL, облегчающий написание кода проекта, уменьшающий его объём, облегчающий его чтение, а значит и сопровождение.

Подождите. Но получается, что только человек написавший код понимает смысл созданных им абстраукций, только он понимает синтаксис и семантику нового DSL, а для любого другого, даже самого ядрёного, лиспсиста этот DSL является криптограммой. Так откуда берется легкость сопровождения ? Стоит первоначальному автору забыть часть синтаксиса и семантики когда-то созданного им DSL (а время безжалостно дырявит нам память) и проект придется переписывать заново. Возможно в LISP интерпретаторах/средах имеются какие-то специальные средства позволяющие на ходу документировать вновь созданные абстрации и правила их использования ?

Вот например, стоит в Вашем примере заменить имя макроса for-vect на x123 и код с его использованием тут же превращается Филькину грамоту. Разве нет ? Получается, лиспсисту нужно очень грамотно подходить к выбору названий макросов, а учитывая что макросов приходится создавать неимоверно много, то красиво сделать это не просто. Ну мне так видится. Я не лиcпсист, но сочувстующий. :)

Хотелось бы побольше информации о ходе размышлений лиспсиста в процессе работы над задачей.

Ваше замечание совершенно справедливо! Вы прямо зрите в корень. Если я изобрёл какой то DSL, то другому программисту надо приложить много усилий что бы его освоить. НО это так с ЛЮБЫМ языком программирования. Мы же создали новый язык! И тут на первый план выходит ... тадам! Вы опять совершенно правильно заметили, Документация и примеры использования. Признаюсь честно, на документацию всегда не хватает времени. Как заметил Автор статьи, в Racket с этим обстоит дело очень хорошо. Но этот вопрос, документировани, надо изучать, я документированием в ракете не занимался. По вопросу именования функций/макросов, да опять верное замечание, имена надо выбирать сообразно выполняемой функции. Но код при этом не превратиться в "филькину грамоту" он будет работать, это обфускация кода, так иногда делают программисты на интерпретируемых языках.

Макросов приходиться писать много, но не так что бы очень. В основном это обычная работа программиста, написание функций. Лишь в чистой схеме(как это произошло со мной в Script-fu), этот процесс занимает немного большую долю, чем если бы мы работали например в Racket. И DSL это не сплошь одни сплошные макросы, синтаксическая абстракция идёт рука об руку с функциональной.

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

Например шаблон действий открытие закрытие файла, open и close заменяют макросом with-file, создающим конструкцию которая всегда закрывает файл(или осовобждает любой ресурс). Или как я привёл пример, конструкция котролирующая корректноость индексов массива.

Приведу ещё пример, в script-fu нет структур. но для работы желательно создавать абстракции точки, линий, фигур и т.п. всё это структуры которые хранят какие то поля, позволяют получить к ним доступ или перезаписать значение. И в рамках функциональной абстракции мы можем создавать такие абстракции создавая функции (для точки: конструктор make-p, функции доступа p-x, p-y, функции изменения p-x! p-y!) и тоже самое для других структурных абстракций. Но с помощью макроса (struct p (x y)) мы можем создать все эти функции для точки, и тоже самое для других структурных абстракций. Этот макрос будет создавать все функции(или необходимые макросы) сам, автоматически, представляете на сколько сокращается объём кода который надо написать программисту?

Всё, что Вы пишите очень сильно похоже на обьектно-ориентированное программирование и макросы в данном контексте это те же шаблоны в C++. Если это так, то хотелось бы понять, почем лисповый подход лучше (проще, изящней, позволяет делать меньше ошибок). В общем, ждем Вашу статью про script-fu, может быть с ней придёт понимание. :) В любом случае большое спасибо.

Вполне возможно что шаблоны в С++ вам напоминают макросы. Хотя на самом деле макросы в лиспе вам должны бы напоминать макросы в Си, это гораздо ближе. Но на самом деле и шаблоны и макросы в Си обладают гораздо меньшей выразительной силой. Шаблоны в С++ задумывались для решения одной проблемы: создания типонезависимого кода. Хотя сейчас некоторые утверждают, что сам язык шаблонов является полным по тьюрингу функциональным языком. Я не возьмусь утверждать или опровергать это утверждение. Но я никому бы не посоветовал программировать на этом языке, ну а если кто нибудь и программирует на нём я ему просто не завидую.

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

В C++ / python / whatever я тоже могу определить функцию delete_triangle(side_height, side_width), которая будет рисовать прямоугольник с шириной side_height и высотой side_width.

Кстати, недопонимание чем это лисп такой уж крутой возникает часто. Спросил чат гпт, чтоб он на пальцах показал примеры: https://chatgpt.com/c/6717ca20-8ed0-8013-8360-230627ce2073

на сколько ответ хорош или плох - не знаю, но познавательно

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

Но она была богата идеями, которые постепенно перетекают в мейнстрим.

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

Лисп - латынь языков программирования.

Sign up to leave a comment.

Articles