Авторы правил хотят писать age>=18=>allow, без пробелов и скобок. На вид изменение крошечное. Но на каком этапе решается, что здесь считается токеном?
В Racket начальное синтаксическое представление строит ридер. При обычном чтении этот фрагмент может прийти одним символом, а не цепочкой операторов, которую представляет себе автор. Макрос может нарочно разобрать написание символа, но тогда внутри нотации появляется маленький парсер, а лексические соглашения вокруг макроса он всё равно не переопределит. Собственный ридер уместен, когда языку действительно нужны другие границы токенов или правила разделителей.
У раскрытия макросов своя работа. Оно может отвергнуть некорректные формы правил, установить привязки и перевести правило в вызовы. А получить значение возраста, решить, что делать при его отсутствии, и выполнить разрешённое действие по-прежнему должен код времени выполнения. Если позже появится форма attempt, раскрытие может организовать вызов движка повторов. Повторяет ли он проверку, действие или и то и другое, определяет уже время выполнения, и нотация обязана это прописать.
Когда эти решения хорошо сочетаются, именованный язык может объединить точки входа ридера и экспандера, но ценой документации и инструментов. Если же просьба сводится к вызову существующего сравнения и возврату решения, обычный библиотечный вызов, возможно, уже выразит это ясно. У красивой записи тоже есть цена входа.