LINUX.ORG.RU

Про возможности макросов в lisp-подобных языках

 


0

4

Котаны, я тупой, и уменя есть вопрос к лисперам!

Вот в Ruby есть такая фича:

irb(main):001:0> v = [1, 2, 3,]
=> [1, 2, 3]
irb(main):002:0> [20, 30, 40, *v, 0]
=> [20, 30, 40, 1, 2, 3, 0]
Унарная псевдооперация * подставляет содержимое аргумента-контейнера в рантайме туда, где она находится.

Может ли в принципе такая же фича быть в лиспе? Пусть у нас есть:

(some-function a b c (unbox v) d)
, где (unbox v) обозначает то же самое, что *v в Ruby. Для такого синтаксиса мы не сможем написать макрос, который сделает этот код работоспособным.

Можно сделать вот так:

(do-unbox some-function a b c (unbox v) d)
Такая форма записи позволит определить макрос do-unbox и скрыть все кишки внутри него. Но это как-то длинно и нелаконично.

★★

Последнее исправление: geekless (всего исправлений: 1)

Котаны

Это еще что за херня?! Ты где находишься?! «Уважаемые лисперы» надо говорить.

anonymous
()

(some-function a b c (unbox v) d)

`(... ,@v ...) не подойдет?

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

бтв, что такое «псевдооперация»? Операция на псевдомножестве? Почти-операция? Притворяется операцией, а сама потихоньку наркотики продает?

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

Притворяется операцией, а сама потихоньку наркотики продает?

Да. Она _выглядит_ как унарная операция над v, а на самом деле это операция над всем этим [...] целиком.

geekless ★★
() автор топика

Возьмем за основу:

(do-unbox some-function a b c (unbox d) e)
Пусть это раскроется в:
(let ((v (make-array (+ 4 (length d))))
      (i -1))
  (setf (aref v (incf i)) a)
  (setf (aref v (incf i)) b)
  (setf (aref v (incf i)) c)
  (loop for x across d do (setf (aref v (incf i)) x))
  (setf (aref v (incf i)) e)
  v)
Сделать такое преобразование, в целом, несложно.

dmitry_vk ★★★
()
Последнее исправление: dmitry_vk (всего исправлений: 5)

Ну и, конечно, это примерно то же самое, что и `( ... ,@ ... )

dmitry_vk ★★★
()

Когда закроешь свою последнюю 13-ю ')' подряд идущую скобку, тогда и поговорим, «котан»

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

Вы о чём? В ОП вроде все скобки сбалансированы.

naryl ★★★★★
()

В CL есть сахар для конструирования списков в компайл-тайме: `,,@.

`(a b ,c ,@d e)

,c вставляет значение с.
,@d вставляет содержимое списка d.

Для рантайма можно сделать руками, но не нужно.

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

Да ладно, есть, например paredit: иногда наоборот при редактировании «традиционного» кода быстрее «оскобить» выражения -> paredit-raise-sexp / transpose-sexp.

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

В CL есть сахар для конструирования списков в компайл-тайме

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

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

(use-outer-macro (
   (def-outer-macro unbox do-unbox)
   (def-outer-macro another-unbox do-another-unbox)
   (весь)
   (остальной)
   (код)
   (программы)))

В результате работы макроса use-outer-macro, везде, где в глубинах «всего остального кода» встечаются unbox или another-unbox, их внешний список будет завернут в соответственно do-unbox и do-another-unbox. Т.е. (some-function a b c (unbox v) d) превращается после подстановки в (do-unbox (some-function a b c (unbox v) d)). Ну а потом получившееся уже обрабатывается как нормальная лисп-программа.

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

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

geekless ★★
() автор топика
Последнее исправление: geekless (всего исправлений: 1)

Унарная псевдооперация * подставляет содержимое аргумента-контейнера в рантайме

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

В идеале, хотелось бы, чтобы backquote не было, а функция его выполнялась.

И как вы предлагаете различать, что выполнять при компиляции, а что в рантайме?

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

А что в руби уже можно подставлять что-то в компайл-тайме?

Нет, но не все ж знакомы с семантикой Ruby. Поэтому уточнил.

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

Да, но разворачивается она на уровень выше по синтаксическому дереву, чем дано в исходнике. Я хочу проэмулировать это поведение: макрос указан во внутреннем списке, а макроподстановка выполняется во внешнем списке.

> В идеале, хотелось бы, чтобы backquote не было, а функция его выполнялась.
И как вы предлагаете различать, что выполнять при компиляции, а что в рантайме?

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

Да, это всё из разряда «хочу странного». :} Но хочу.

geekless ★★
() автор топика
Последнее исправление: geekless (всего исправлений: 1)
Ответ на: комментарий от geekless

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

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

no-such-file ★★★★★
()
Ответ на: комментарий от staseg

Для рантайма можно сделать руками, но не нужно.

`,@ это и есть в рантайме

(defun f (v)
  (apply #'+ `(1 2 3 ,@v 7)))

Например, в случае ` (https://github.com/sbcl/sbcl/blob/master/src/code/backq.lisp):

(defun backq-list (&rest rest)
  (declare (truly-dynamic-extent rest))
  (apply #'list rest))

Алсо — https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node367.html.

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

Да я потом сам понял, что ошибся. `,@ во время компиляции раскрывается в код сборки списка, который выполняется в рантайме.

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

В руби есть операция +

Ты удивишься, но в лиспе есть функция + которая тоже выполняет сложение. Причем можно сделать (reduce #'+ '(1 2 3)). А как с этим в руби?

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

АПВС?

Причем можно сделать (reduce #'+ '(1 2 3)). А как с этим в руби?

vadim@aquila:~$ irb
irb(main):001:0> [1, 2, 3].reduce(&:+)
=> 6
irb(main):002:0> 
geekless ★★
() автор топика
Ответ на: АПВС? от geekless

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

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Проснись, Нео, операция — это сахар над функцией.

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

Лол.

Чего «лол»-то, ооп-костыль же. Это ведь даже не синтаксический сахар, а _специальная конструкция для передачи «self» в качестве первого аргумента для :empty или я ошибаюсь?

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

:empty? — это Symbol строки «empty?». А & перед аргументом передаёт аргумент как блок.

Попытка от-yield-дить Symbol приводит к посылке данного атома как сообщения к первому аргументу yield-а. Например:

irb(main):001:0> def f(a) ; yield a ; end
=> nil
irb(main):002:0> f("qwerty", &:upcase)
=> "QWERTY"
irb(main):003:0> 
geekless ★★
() автор топика
Последнее исправление: geekless (всего исправлений: 1)

возможности макросов в lisp-подобных языках

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

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

Вот я сразу знал, что начнётся срач. :/

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

:empty? — это Symbol строки «empty?

На это моих познаниний еще хватает. Был вопрос про амперсанд.

аргумент как блок

Это уже интереснее, в («a»..«f»).map(&:upcase) -> («a»..«f»).map{|e| e.send :upcase} можно понимать как «удаление внешних скобок и раскрытие в блок?
А в f(...) как можно формализовать?

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

Мне не нравится именно сама необходимость в явном виде заворачивать конструкцию в квотирование.

Но в руби то ты в квотирование заворачиваешь вполне явно. Непонятно в чем проблема. Ну можешь #%app тогда перегрузить или скобки. Но зачем? Во всех ящыках используется явное Квотирование и всех это устраивает.

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

Да, но разворачивается она на уровень выше по синтаксическому дереву, чем дано в исходнике

ЭТо с чего ты взял? В руби макрос - открывающая [, которая указывает, что гдето внутри формы можеь быть звездочка. Все в точности как с лисповыми ' и ,.

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

ЭТо с чего ты взял? В руби макрос - открывающая [, которая указывает, что гдето внутри формы можеь быть звездочка. Все в точности как с лисповыми ' и ,.

Звёздочка действует везде. [ ни при чем.

puts(*v)
a, b, c = *v

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

Звёздочка действует везде.

Это разные звезочки.

[ ни при чем.

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

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

Это разные звезочки.

А в двух других приведенных тобой случаях она интерпретируется иначе.

4.2

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

Для осоьо одаренных - та звездочка что в оп-посте вообще ее имеет смысла вне []. Так что если бы она интерпретировалась одинаково, то последние два примера были бы ошибкой. У splat есть 4 совершенно разных смысла и в каком именно смысле он используется - зависит от контекста. в квадратных скобках - слайсинг, в определении метода - аналог лиспового &rest, в вызове метода - apply, в присваивании - множественое присваивание. Соответственно и макроом будет не сама звездочка, а внешние задабщие конекст формы: квадратные скобки, определение/вызов метода, присваивание. Я полагал, что человек знающий руби, должен быть в курсе таки. очевидных вещей

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

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

Сколько понтов-то.

Для осоьо одаренных - та звездочка что в оп-посте вообще ее имеет смысла вне [].

Для особо одаренных «знающих руби»: [blabla] — это синтаксический сахар над вызовом class-метода [] класса Array. На, кури:

irb(main):004:0> Array.send(:[], 1, 2, 3)
=> [1, 2, 3]

Поэтому звездочка в [] имеет ровно тот же самый смысл, что и внутри круглых скобок при вызове любого другого метода. Потому что это засахаренный вызов метода, блеать.

И ровно то же самое относится и к присваиванию, потому что рубиевское присваивание a, b, c = *v в lisp-форме эквивалентно записи а ля (do-unbox (= '(a b c) (unbox(v)))) . Только с поправкой, что присваивание как прямой вызов метода нам из сорцов на руби не доступно.

а внешние задабщие конекст формы

Ага, а в примерах из стартового поста формы (do-unbox (function1 a b c (unbox v))), (do-unbox (function2 a b c (unbox v))), (do-unbox (function3 a b c (unbox v))) следовало бы считать построенными с использованием разных макросов, потому что после формирования списка аргументов каждый раз вызывается разная функция.

Включай мозги и учись правильно различать различные уровни абстракции, «знаток руби».

geekless ★★
() автор топика
Последнее исправление: geekless (всего исправлений: 2)
Ответ на: комментарий от geekless

На самом деле, специальный случай, когда звёздочка имеет иное значение — это случай, когда она используется как l-value:

Либо в левой части присваивания:

irb(main):021:0> a, *v, b = 1, 2, 3, 4, 5; v
=> [2, 3, 4]

Либо в аргументах функции. Либо в аргументах блока.

Что вполне закономерно.

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

Что в лоб что по лбу. Еще раз, для совсем тупых - макрос это не звездочка, а обрамляющая форма, в которой эта звездочка используется. Если хочешь чтобы внутри аппикации можно было делать слайсинг аргументов - переопределяй макрос аппликации, если хочешь чтобы при определении ф-и можно было делать слайсинг аргументов - переопределяй дефайн, если хочешь при присваивании - переопределяй присваивание. Собственно первые два пункта в лиспе сделаны - rest-аргументы и apply+слайсинг в списках, ну а множественного присваивания какбы и нет. Именно такая семантика в руби, и другой она быть не может в принципе. Только в руби макросов нету, просто ы грамматике для соответствующих нетерминалов указанг, что внутри может быть звездочка.

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

Что в лоб что по лбу.

Действительно.

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

Еще раз для совсем тупых: в руби нет макросов, и все сабжевые фичи являются частью синтаксиса. Поэтому рассуждать на тему, что было бы если бы у бабушки был ..., можно сколько угодно.

Если хочешь чтобы внутри аппикации можно было делать слайсинг аргументов - переопределяй макрос аппликации, если хочешь чтобы при определении ф-и можно было делать слайсинг аргументов - переопределяй дефайн, если хочешь при присваивании - переопределяй присваивание

Задача стартового поста состояла в том, чтобы выполнять трансформацию AST на уровень выше вхождения в него соответствующего макроса. То, о чем ты пишешь, вообще к теме треда отношения не имеет, потому что является решением другой задачи. Для неисправимо тупых: синтаксис руби был приведен в качестве наглядного примера.

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

geekless ★★
() автор топика
Последнее исправление: geekless (всего исправлений: 1)
Ответ на: комментарий от geekless

Какой смысл приводить в качестве трансформации аст на уровень выше руби, если в руби таких трансформаций нет? И вообще нигде нет?

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

Задача стартового поста состояла в том, чтобы выполнять трансформацию AST на уровень выше вхождения в него соответствующего макроса.

Для начала надо как бы определиться с тем, осмысленно ли это преобразование. Выполнение преобразования на уровень или несколько уровней выше требует хотя бы построить это самое AST _полностью_. Вопрос: если рядом с макросом есть макрос (или вызов макрос находится внутри другого макроса):

(a
  (b)
  (macro-1)
  (macro-2)
  (macro-3
    (c)
    (macro-4)
    (d)))
то какой макрос какое AST должен увидеть?

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

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

для макросов, видимо, да — но макросы это прошлый четвертьвек

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

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

но макросы это прошлый четвертьве

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

а в практических задачах

ну так в практических задачах это и дедается безо всяких проблем точно также как в руби - внешняя форма переопределяет семантику внутренней.

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

Ну это же ложь.

чувак, ты жжошь

Макросы как раз самая перспективная область исследований

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

примерно как обзорчик Uniprocessor Garbage Collection Techniques

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

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

опять жжошь

вообще противопоставление генеративного подхода (это я так о макросах) и систем типов — несколько надуманное

з.ы. и еще я не считаю системы типов верхом совершенства, вот так

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 4)
Ответ на: комментарий от www_linux_org_ru

чувак, ты жжош

Тл есть не ложь? Хоть один факт можешь в подтверждение предоставить?

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

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

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

Я и не противопоставлял. Система типов - это не более чем попытка ограниченного метапрограммирования, при расширении выразительности системы типов приходят к макросам (бестиповая лямбда с гигиеническими макросами - это по сути минимальное construction of calculus с подтипированием, типами являются алгоритмические подмножества термов, макросы - полиморфными функциями с ограниченной квантификацией), так что макросистема - это система типов сделанная правильно

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

Тл есть не ложь? Хоть один факт можешь в подтверждение предоставить?

я щас добрый, и мне лень тебя на место ставить любопытный, и вдруг ты все-таки обзорчик предоставишь? (не обязательно pdf)

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

нет

ты не понимаешь, что такое результаты научных или околонаучных исследований

для шаблонов с++, скажем, это выработка понимания, что нужны концепты

для системы типов хаскеля — это, скажем, выработка понимания того, что все-таки нужны семейства типов (бугага, не прошло и 20 лет после появления шаблонов в плюсах, как они с помпой сделали что-то похожее)

вот это результаты

что такое сделано для макросов?

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

з.ы. и еще я не считаю системы типов верхом совершенства, вот так

Да я просто пример привел, как самую непутевую часть CS. Полезные вещи тоже, конечно, делаются, но об этом почти никто не знает, зато о том что вышла книга по гомотопической теории типов уже каждая собака в курсе.

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

Система типов - это не более чем попытка ограниченного метапрограммирования

ты категорически неправ, осиль хотя бы первые 20 страниц TAPL

то, что метапрограммирование, скажем, в с++ прикручивают к системе типов — это не совсем правильно (хотя, конечно, «макросы» должны уметь оперировать с типами), и *точно* система типов делалась не под это

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

вот это результаты

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

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