Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
А новая версия кложуры несёт ещё исключения с данными. То есть Вы сможете принимать решения исходя не только из типа исключения, но и используя дополнительные данные.
Удачно кто-то подметил: после того как ПРИВЫКАЕШЬ к иммутабельным данным, не можешь понять зачем тебе нужны были мутабельные.
Гораздо интересней посмотреть на conditions в Common Lisp.
Лично у меня складывается ощущение что автор языка создавал синтаксис не по принципу «как будет удобнее» а по принципу «как нет ещё ни у кого».
Зачем? Какая тут реальная польза и удобство для программиста?
В скобках то главная сила и заключается. Кстати, я давно уже вывел критерий, как плохого программиста от хорошего отличить: плохой программист заморачивается синтаксисом, а хорошему на синтаксис наплевать, он интересуется только семантикой.
Главная фича скобочек — это то, что ты программируешь в чистом AST. Соответственно, ты так же легко можешь программно код прочитать и код сгенерить — и это и есть наиболее мощная разновидность метапрограммирования.
По поводу LISP — не только слышал о нём, но и изучал некоторое время.
По поводу неважности синтаксиса — это выражение из той же серии что «главное — это душа».
Безусловно, семантика важнее внешнего вида языка, но ломать себе голову пытаясь читать ужасное скопление символов ради не очень понятно чего — тоже не дело.
Возможность создавать свои eDSL можно реализовать и в си-подобном синтаксисе, не так ли?

на лиспах делает тоже, что и все — запросы в БД пуляет, что-то делает с результатами и отрисовывает
Скобочки тоже отстой, так как синтаксис имеет значение для читабельности. Дело тут не в привычке, а в том, что называется cognitive load, а не в странности.
Graphics[{White, Riffle[NestList[Scale[Rotate[#, 0.1], 0.9] &, Rectangle[], 40], {Purple, Red}]}]
(Graphics{White, #(Riffle(NestList(Scale(Rotate %, 0.1), 0.9), (Rectangle), 40), [Purple, Red])})
И только Лисперы везде со своим неизменным «синтаксис не имеет значения».
(do ((x 1 (1+ i))
(xs () (conj x xs)))
((= x 10) xs))
(loop for x from 1 to 10 collect x)
(loop {for x from 1 to 10} {collect x})
with open('/path/to/file') as f:
f.read()
;; определение новой фичи языка
(defmacro with-open-file [filename mode alias body]
`(progn
(let [~alias (open ~filename ~mode)]
~@body
(close ~alias))))
;; и пример использовнаия
(with-open-file "/path/to/file", "r", 'f
(read f))
nil
:yes
true
(+ 3 5)
(def inc [x] (+ x 1))
(inc 41)
Я это не по наслышке знаю, я пишу на Scala, в библиотеках часто используют макросы, так вот каждый раз, когда изучаешь такую библиотеку, ощущение такое, что учишь новый язык.
def objectExitss: Route = get {
pathPrefix(ApiVersion) {
pathPrefix("group" / IntNumber) { gId =>
path("objects" / IntNumber) { objId =>
traceName(Traces.ObjectExists) { ctx =>
// handler code goes here
}
}
}
}
}
(defroutes myapp
(GET "/v1/group/:groupId/objects/:objId" [groupId objId] <handler code goes here>))
def objectExitss = get { path(ApiVersion / "group" / IntNumber / "objects" / IntNumber) { (groupId, objId) =>
<handler code goes here>
}}При желании на любом языке можно написать бяку.
json4s со своим DSL
akka-streams
Макросы в Лиспе не ухудшают ситуацию, не вводят нового непонятного синтаксиса, а наоборот унифицируют подход, делая код понятнее.
При условии, что вы не пытаетесь сопротивляться префиксной нотации и скобкам, конечно.
Я для него написал макрос, по аналогии с макросами Json.format[CaseClassName] из Play. Зачем там DSL вообще использовать не представляю.
Для той задачи, что они решают, вроде довольно вменяемо.
in ~> f1 ~> bcast ~> f2 ~> merge ~> f3 ~> out
bcast ~> f4 ~> merge
Лично я бросил clojure после двух решающих моментов:
Не зная во что развернутся макросы, даже не представляешь итоговое AST
«Расслабьтесь и получайте удовольствие»?
на официальном сайте около половины страницы посвящено именно DSL-ю
нужно было разобрать JSON, в который никак не имеет и не должен иметь соответсвующего case class, на который этот JSON можно отобразить
Есть такая штука как `macroexpand`, которая снимает все вопросы.
Естественно, если вы изначально настроены против чего-то и активно сопротивляетесь новым концепциям
Если ваши json нельзя внятным образом отобразить на вменяемого вида структуру данных, то это проблема не DSL, а того, кто json составлял.
И к чему тут это? Предлагаете мне весь код, прежде чем его читать, прогнать через macroexpand?
Последний раз я так делал чтоб потестить самописный макрос для pattern matching xml — у меня одна строка развернулась в 180 строк. Почитаешь такое.
(defmacro time [expr]
`(let [start (current-time)] ;; берём текущее время
(let [result ~expr] ;; выполняем выражение
(println (- (current-time) start)) ;; считаем, сколько прошло, и печатаем на консоль
result))) ;; возвращаем результат
(let [start (current-time)]
(let [result (foo)]
(println (- (current-time) start))
result))
Я утверждаю, что все заявления про отсутствие синтаксиса и «написание программ на AST» — «маркетинговый булшит»
просто надо как-то оправдать нечитаемость кода.
Понавешиваем ярлыков?
единственный источник новых концепций
Вы спросили, как узнать AST после развёртывания макроса, я вам ответил.
А возьмём какой-нибудь макрос из Lisp, например, `time`
макросы открытия/закрытия ресурсов
def time[T](f: => T): T = {
val start = currentTime()
val res = f
val end = currentTime()
// use start and end here
res
}
time { longOperation() }
Так что при правильном подходе макросы абсолютно прозрачны.
И кому тогда, по вашему мнения, лисперы пытаются «продать» свой язык?
К Лиспу люди приходят сами, и только сами.
Для меня гораздо проще понять, что проиходит в исходном коде Compojure, чем в коде Spray, даже при том, что на Clojure я писал немного меньше и довольно давно.
Но вы взяли себе за аксиому, что у Lisp тяжёлый синтаксис, и делаете все выводы на основе этого.
Речь шла про абстрактного лирического героя
Давайте так. Вы напишете или возьмёте существующий кусок кода на вашем любимом языке программирования, я напишу аналогичный кусок на Clojure, и вы мне пальцем покажете, какая часть этого кода нечитаема.
Эти два примера оба используют достаточное количество magic.
P.S. и таки да, вам уже правильно написали, коды действительно неэквиваленты. Код на Scala делает больше — он привносит типипзацию как минимум.
И совершенно не ясно как defroute работает внутри. В spray можно хотя бы по сигнатурам функция посмотреть что происходит. У вас нечто, что генерит на выходе другое нечто.
Повторю еще раз, я ЗА макросы в популярных библиотеках, я против активного использования, в частности написания своих макросов.
Как это не новый? Макросы по определению — это расширение языка.
Это я и называю «учить новый язык». Когда я юзаю обычные функции у меня не возникает мыслей смотреть как оно там реализовано. Никакой магии нет, главно прочитать сигнатуру, сразу понятно что передать и что вернется. А когда надо лезть внутрь, это уже плохо.
Человеку, который будет работать с вашим кодом придется смотреть этот ваш macroexpand, а потом в голове держать то, что он увидел.
(doc myfunction)
(doc mymacro)
>просто исполняется она на этапе компиляции.
Это вносит целый слой абстракции в код и усложняет понимание.
Мне как стороннему наблюдателю приходится читать определение макроса всегда.
Мой опыт говорит о том, что нужно ограничивать использование продвинутых штук в прикладном коде вроде макросов
потратить 4 час на поиск implicit параметра, отвечающего за таймаут запроса
Вы действительно 4 часа имплисит искали или 4 часа выясняли, что проблема в таймаутах?
Если вы знаете быстрый способ найти некий параметр неизвестного типа передаваемый неявно неизвестно откуда и неизвестно куда, пожалуйста сообщите.
Idea «Ctrl Shift P» — показ имплиситов в данном вызове.
class ParentClass {
public ResultType method(Type1 param1, TimeoutType timeout) {...}
public ResultType method(Type1 param1) {
return method(param1, getDefaultTimeout())
}
}class ParentClass {
public method(param1: Type1)(implicit timeout: TimeoutType): ResultType = ???
}
object ParentClass {
implicit def defaultTimeout: TimeoutType = ???
}class ChildClass extends ParentClass {
def myMethod = {
...
method(param1)
...
}
}(defroutes myapp
(GET "/v1/group/:groupId/objects/:objId" [groupId objId] <handler code goes here>))Такую мелочь в нормальных языках делают без макросов.
Замените «нормальные» на «обычные». Могу даже уточнить «в языках, не декларирующих отсутствие синтаксиса одной из основных фич».
Макросы подключают для чего-то более существенного.
Если да, то зачем дублирование ":groupId/objects/:objId" и "[groupId objId]"?
Та же функция time или автозакрытие ресурсов во всех языках с лямбдами (C#, Java, JS, etc) пишется тривиально.
Зачем тут вообще могут понадобиться макросы? Что в лиспе мешает определить функцию?
(defn my-defroutes [name & routes] (let [new-routes (map trace-name routes)] (defroutes name new-routes)))
Scala есть необязательный синтаксис, который позволяет опустить дополнительные скобки и выражение
Вопрос стиля — в Clojure не принято убирать все «очевидные» элементы синтаксиса.
А как насчёт генерации N вложенных циклов или переписывания выражания над матрицами на команды BLAS?
В макросе `name` и `routes` — это выражения, в функции — аргументы, которые вычислятся ровно перед передачей их в функцию.
Если это легкость понимания человеком итогового AST — а именно с таким аргументом я обычно сталкивался, то именно этот аргумент разбивается о засилие макросов.
Разница с макросами, как ранее вам уже кто-то писал, что достаточно прочитать сигнатуру, чтоб понять принцип работы.
И задача разработчиков языка определить круг часто используемых фич, покрываемых синтаксисом.
делал проверку на совпадения имен,
Вы только что аргументировали к тому, что макросы не создают нового языка и не вносят проблем в понимание. Сейчас же вы предлагаете делать нечто довольно запутаное.
где там засилие макросов
И как я уже ответил, кто вам мешает прочитать сигнатуру макроса?
А почему они должны совпадать?
Сгенерировать несколько вложенных циклов или заменить `X += z * Y` на `axpy!(z, Y, X)` — это что-то запутанное?
Аргумент к тому, что юз-кейсы макросов в Lisp не сводятся к анонимный функциям в Scala.
Начнем с defn. Даже чтоб определить именованную функцию уже используется макрос.
Какая сигнатура макроса? Нет типизации. Читать надо весь макрос.
Не должны, конечно. Чтоб быть более честным там надо написать "/v1/group/:?/objects/:?" просто чтоб не вводить никого в заблуждение.
Да, потому, что не прочитав ваш макрос не поймешь что он делает и какие замены производит. Он вносит новый не определенный до этого набор допущений и договоренностей.
В случае же того макроса любой кто знает XPath, знает как работает итоговый код
У меня очень сильное подозрения, что все достаточно простые макросы в Lisp сводятся к синтаксическим конструкциям популярных языков
JS
на самом деле сразу догадались, что это за аргументы
Макрос, генерирующий N вложенных циклов
Макрос, заменяющий обычные операции над матрицами на соответсвующие операции из BLAS
По вашим словам, макросы вносят полную неразбериху
На вскидку:
Единственное из-за чего я ввязался в разговор — совершенно кошмарное использование скалы, выдаваемое за вменяемый пример кода на ней (приведенный для сравнения).
Вы назвали систему типов (фактически принудительные юнит-тесты) вкусовщиной. Ок, это ваши фломастеры.
Мои тезисы: в «обычных языках» синтаксис вносит общий словарь. Макросы же используются только когда этого словаря оказывается не достаточно, что сразу сигнализирует о сложности происходящего.
Но мне (и не только мне) он не удобен. Это субъективно. Просто примите существование такой точки зрения.
Это не отменяет того факта, что использовать надо именно макрос.
Если ваши json нельзя внятным образом отобразить на вменяемого вида структуру данных, то это проблема не DSL, а того, кто json составлял.
На самом деле синтаксис есть и код имеет крайне отдаленное отношение к AST, просто надо как-то оправдать нечитаемость кода.
Это какие-то бедненькие макросы. Макросы нужны как раз когда нужен новый язык, например.
Такую мелочь в нормальных языках делают без макросов. Макросы подключают для чего-то более существенного.
См. выше. В JS очень лаконичный общий словарь, на котором общаются разработчики. И обычно если они выходят за его пределы, то это видно очень явно.
Способ описания переменных и их диапазонв. А так же возможность указания зависимости диапазона вложенного цикла от текущего значения внешнего.
Как минимум список поддерживаемых операций и сложность преобразуемых выражений.
Макросы не вносят полную неразбериху, они увеличивают сложность кода.
В лиспе можно встретить большие куски кода, где макросов больше, чем обычных функций и это будет нормально для лиспа.
Юнит-тесты тут не причём.
Ошибки типизации составляют от силы 1% от ошибок,
причём все они прекрасно отлавливаются одним запуском функции в REPL.
Мой антитезис: не сигнализирует.
если всё это — ваше мнение
В чём разница между сигнатурой макроса и сигнатурой функции в JS?
Это входные параметры для макроса, которые описываются в его сигнатуре.
Список поддерживаемых операций описан в спецификации BLAS
сложность обрабатываемых выражение, как и в любом оптимизирующем инструменте, зависит от качества реализации
Вы ведь не считаете, что оптимизатор Scala вносит дополнительные допущения и договорённости?
Вы снова забыли добавить «я считаю, что...».
ущербность динамической типизации
уродливость синтаксиса и сложность макросов
Откуда статистические данные, да еще и с такой точностью?
Но это надо просто брать и пробовать, переубедить вас сторонними аргументами мне вряд ли удастся, да и не очень хочется.
У вас 1 запуск в REPL дает покрытие по путям в 100%?
Не сигнализирует в том случае, если манипуляции с AST используются постоянно для всего. Тот же time совершенно не обязательно реализовывать макросом.
«Удобство» и «сложность» в любом случае субъективны.
В том, что ни какая функция в JS не меняет порядок вычисления своих аргументов — все аргументы вычислятся до вызова. Если в вызове есть лямбда, то при чтении кода она видна.
Можно пример такой сигнатуры из которой будет понятно как устанавливать соотношения между значением во внешнем цикле и диапазоном во вложенном?
(macro nloops [r f1 f2 & fs] ...)
"""
Generate nested loops.
r - range or collection, constituting top level of loops
f1, f2, fs - function to get current range from a higher-level loop variable, e.g. (fn [i] (range 1 i))
"""
Меня интересуют не выходные операции, а то, что на вход. Список функций clojure.
То есть мы пишем некий код и надеемся, что он как-то будет оптимизирован? Это хоть как-то допустимо для оптимизаций, работающих неявно. Но мы-то говорим о макросе, вызываемом явно! Человек сам его вызывает и надеется на какой-то результат. И проверить результат он может только эмпирически. Так почему бы результат macroexpand не вставить вместо кода, просто чтобы гарантировать, что ни какие последующие изменения, кажущиеся незначительными, не угробят оптимизацию? В этом, кстати, разница с макросом для XPath — он работает детерминировано, такой «оптимизатор» работает «как-то». В следующей версии может заработать по другому. А тесты на производительность крайне трудозатратны.
И, кстати, это причина почему Scala — единственный язык, в котором я считаю возможным использовать рекурсию вместо цикла. Языков, оптимизирующих хвостовую рекурсию, много. Но только Scala (насколько я знаю) позволяет гарантировать такую оптимизацию.
Если к какому-то вашему утверждению про сложность можно добавить «это не мнение, это факт», то я не знаю как вообще можно обсуждать субъективные понятия.
Если вы считаете, что удобность синтаксиса и простота работы с макросами — это не ваше личное мнение, а факт, то дискуссию закрыть просто необходимо.
Но вы никаких метрик не приводите, так что да, у вас сложность субъективная.
В Lisp можно (явно) передать выражение в макрос
method1(incrementAndGet())
method2(function() { return incrementAndGet(); })(method1 (incrementAndGet))
(method2 (incrementAndGet))А я нигде и не утверждал, что какая-то фича является сложной и её использования нужно избегать.
Я считаю, что к любому синтаксису можно достаточно быстро привыкнуть и перестать комплексовать.
Но вы никаких метрик не приводите, так что да, у вас сложность субъективная.
Как и у вас.
Сами отличие поймете? Если нет, то на этом точно стоит окончить этот разговор.
ни у кого ни с макросами, ни со скобками не возникает проблем
И многие другие участники этого треда вам об этом сказали.
с Лиспом работали весьма недолго
с макросами так и того меньше
статистики такой не имеете и во всех своих суждениях опираетесь на собственные ощущения
В конце концов, вы всегда можете договориться о конвенции для имён макросов
Но в этом треде я был не единственным, кому макросы и скобки создают проблемы со сложностью кода.
с макросами так и того меньше
Нет.
Не только. А вы свою статистику набирали по опытным лисперам? Или по тем, кто с лиспом только столкнулся?
Да, при этом, что неудивительно, проблемы возникают именно у людей, которые Лиспом в своей жизни (почти) не пользовались.
например, из вопросов на Stackoverflow. Я сейчас просмотрел первые 4 страницы результатов по запросу «clojure», но не нашёл ни одного вопроса по использованию или даже написанию макросов
никто ни разу не спросил «как это работает?» или «как это написать?»
Я сейчас просмотрел первые 4 страницы результатов по запросу «clojure»
но не нашёл ни одного вопроса по использованию или даже написанию макросов (что немного сложней).
запрос "[scala] implicit error" выдаёт 12 результатов только за последнюю неделю.
Вы причину со следствием не путаете?
Я посчитал Stackoverflow не репрезентативным по clojure
Первый вопрос по обоим тегам scala и clojure и я сходу не понял что человек хочет.
Второй вопрос, 12 часов назад: Making a macro nested in macros using variable
arguments work. Что это, если не «как это написать?»?
рекомендую искать [clojure] — это тег, а не любое замыкание
implicit — ключевое слово в scala, причем часто используемое. Поищите "[scala] class error" — найдете вдвое больше. У людей проблемы с OOP?
Еще 2 вопроса про implicit в контексте макросов, один из них вида «у меня всё работает, но почему именно так?».
Я сегодня объяснял
Предложите другую выборку.
вопросы по конкретным и сложным макросам, конечно, встречаются
Замыкание пишется через «s».
Ну замените слово «implicit» на тег "[implicit]", а чтобы было честно, в запросе на Clojure тоже добавьте «macro» — соотношение не сильно изменится.
Вот это вообще эпично.
Было пара вопросов, почему в ядре Clojure макросы написаны именно так
Я целенаправленно достаточно глубоко его изучил, чтобы сравнить с другими инструментами и сделать выбор.
Нет, опыт не добавляет ему плюсов по сравнению с другими языками.
Лисперов действительно так мало?
Если человек задает вопрос, значит у него уже проблемы. Добавление error ничего не менияет по сути.
Вот это вообще эпично.
Ну вы же отмели
Было пара вопросов, почему в ядре Clojure макросы написаны именно так
Но ок, добавьте error, получите 259 vs 95. Только не забудьте отнормировать на соотношение количества вопросов 4/1.
«Достаточно глубоко» в вашем понимании подразумевает написание макросов?
Про плюсы Clojure я написал ещё в одном из первых комментариев
количества скобок и непонятных макросов. Тфу-тфу-тфу, вроде разобрались, что к этому можно привыкнуть.
А вот фраза «всё работает, но я не понимаю как» — это реально эпично.
Даже при таком раскладе получается, что макросы в Clojure чуть сложнее имплистов в Scala.
Скринг
Да, исплиситы создают накапливаемую сложность, которую не оценить по SO (так как каждая часть этой сложности мала). И макросы создают накапливаемую сложность.
Имплиситы действительно привносят накапливаемую сложность, потому в проектах ограничивают их использование.
[...]
В clojure же, как я это увидел, никто не испытывает ни малейших комплексов по поводу макросов. Это считается настолько же удобным, как функции.
Можно ссылку? Я, кажется, уже писал, что все плюсы съедаются привнесенной сложностью макросов. Но может какой-то из плюсов упустил.
Вы ведь говорите, что даже писали макросы.
доступа к библиотекам JVM
немутабельность
Богатая коллекция персистентных коллекций. Одни вектора чего стоят!
Лаконичность. Программы на Clojure, как правило, гораздо короче аналогов на Scala и Java
Простота.
Сам язык, кстати, тоже очень небольшой
Инструменты сборки проекта, а именно Leiningen.
Та же Scala берёт корни как минимум в Java, Ruby, Haskell и Erlang, что иногда делает сложным комбинировать библиотеки, написанные в разных стилях
multiple dispatch по любому условию
Написание макросов не связано с их использованием. Накапливаемая сложность не проявляется в заметных историях, просто со временем растет количество багов и сложность поддержки кодовой базы.
Проблема в том, что совместимость однонаправленная. Чем-то из clojure уже в Java не воспользоваться
Не так плохо, что (def name ...) мутабелен
как то, что еще и скоупов нет
(let [my-local-func (fn [x] (+ x 1))]
(my-local-func 42))
Вот к этому все мои рассуждения про сложность, привносимую макросами.
Clojure не имеет смысла рассматривать без стандартного набора макросов
Пробовал. По началу хотелось кого-нибудь придушить
ООП + ФП.
От руби я увидел только влияние RoR на Play, но это не относится к самому языку.
Лично мне pattern matching гораздо больше нравится, особенно учитывая возможность кастомных матчеров (по любому условию) и проверку компилятором на покрытие всех вариантов.
def func(x) = x match {
case foo => ...
case bar => ...
}
func foo = ...
func bar = ...
Предлагаю на этой позитивной ноте окончим данный не конструктивный диалог.
Меня, в общем-то, интересует только чем вам не понравился Leiningen, а в остальном я не против.
Мутабелен в том смысле, что он добавляет новое определение в пространство имён? Ну с таким подходом даже Haskell весь мутабельный.
Хотя сравнение было в другую сторону, о чём я уже сказал выше.
Ну да: «смотрите какой ужасный код на Scala и как миленько то же самое на Clojure». При том, что код на scala ужасен только из-за автора, но не инструмента.
def objectExitss: Route = get {
pathPrefix(ApiVersion) {
pathPrefix("group" / IntNumber) { gId =>
path("objects" / IntNumber) { objId =>
traceName(Traces.ObjectExists) { ctx =>
// handler code goes here
}
}
}
}
}
def objectExitss = get { path(ApiVersion / "group" / IntNumber / "objects" / IntNumber) { (groupId, objId) =>
<handler code goes here>
}}
val route: Route = {
(path("search") & get) {
// handler code
}
}
val route: Route = {
(path("search") & get) {
parameters("q"!, "filter" ? "all") { (query, filter) =>
...
}
}
}
Зато в аргументе `path` мы делим строчку на строчку на какую-то константу на строчку на константу. Океееей.
Не придумывайте, даже вы, жалуясь на непонятность, на самом деле сразу догадались
What the ***?!
path(«search») & get
(path("search") & get) {
parameters("q"!, "filter" ? "all") { (query, filter) => (get & path("search") & parameters("q"!, "filter" ? "all")) { (query, filter) => понятный фреймворк — Finatra
но после примера со Spray (...) говорить о том, что макросы в Clojure делают код запутанным — это уже моветон.
Directive — единица роутинга — условие, накладываемое на запрос. get — условие на метод, path — условие на url, parameters — условие на CGI параметры. Уверен, что там есть еще условия на тело, на куки. Можно написать кастомные условия на сессию, на IP отправителя, на что хотите.
Цитируя вас:
При этом, если запрос совпадает с образцом, то совпашкая часть «потребляется» директивой
Такого идиотизма я давно не слышал.
Я видел Ruby, Python, JS, Perl и, внезапно, Clojure. Во всех этих языках IDE беспомощнее слепого котенка и способна только форматирование поправить.
Такая примитивная операция, как переименование чего-либо
Во всех динамически типизированных языках, с которыми у меня есть шанс столкнуться
Рефакторинг это лишь одна из многих возможностей IDE.
Если не изучать — шанс не будет увеличиваться, если шанс не будет увеличиваться то зачем изучать?
Еще есть intellisense
Smalltalk у меня в планах на изучение как источник идей для многих других языков, но точно не как нечто, на чем я планирую писать когда-либо. Примерно как Io — один раз попробовал, понравилось, закрыл навсегда.
Routes.create("myapp")
.get("/v1/group/:groupId/objects/:objId", (groupId, objId) -> { <handler code goes here> })
.post(...)val router = Router.from {
case GET(p"/hello/$to") => Action {
Ok(s"Hello $to")
}
}Макросы нужны как раз когда нужен новый язык
Кстати да, я даже, кажется, знаю почему у вас такой scala код.
то ли трейсили разные части пути по разному
заставлять делать пути вида `/group//objects` вместо простого `/`
для нашего совсем не сложного юз кейса
(defmacro my-defroutes [name & routes]
(let [new-routes (map trace-name routes)]
(defroutes name new-routes)))
я не пытался доказать некрасивость кода на Scala, а наоборт, пытался показать, что макросы в Lisp ничем не хуже, а то и лучше
Естественно, вместо использования нового имени можно просто перекрыть старый `defroutes` и использовать свой «прокачанный» макрос как drop-in replacement для старого кода.
(defn my-defroutes [name & routes]
(let [new-routes (map trace-name routes)]
(defroutes name new-routes)))я пишу на Scala, в библиотеках часто используют макросы, так вот каждый раз, когда изучаешь такую библиотеку, ощущение такое, что учишь новый язык.Не троллинга ради, но может проблема не в макросах самих по себе, а в Scala?
С макросами по сути придется учить свой диалект в каждом случае
Все это детсад и фантазерство.
if (boolVar)
method1();
else
method2();
(if boolVar
(method1)
(mothod2))(let ((x 1)
(y 2))
(+ x y))(let [x 1
y 2]
(+ x y))В то время как в си-подобных языках скобки нужны только в особых случаях, вроде группировки арифметических или логических операцийА еще для вызова функций. Вот уж действительно «особый случай».
в си-подобных языках скобки нужны только в особых случаяхПри этом скобки нужны на каждый вызов функции, на каждый
if, for, while и т.п.



От PHP к Clojure