Рассмотрим недавно вошедшую в Gambit Scheme возможность по переопределению семантики скобок.
Gambit Scheme – используемый автором диалект Scheme, имеющий очень быстрый интерпретатор и компилятор с рядом полезных расширений, строящиеся из исходного кода без внешних зависимостей, а также в полной мере поддерживающие интернациональные символы UTF-8.
Разумеется, речь здесь не пойдёт о священных круглых Скобках, образующих большую часть текста программ на Лиспе и его диалектах. Мы поговорим о квадратных и фигурных скобках.
Традиционно, квадратные и фигурные скобки не имели в Scheme никакого специального применения, и это было закреплено в стандартах до R5RS включительно. В R6RS квадратные (но не фигурные) скобки объявили эквивалентом круглых скобок, но это, по всей видимости, вызвало сопротивление общественности, и в R7RS квадратные и фигурные скобки снова не имеют определённого значения.
В большинстве диалектов Scheme, как в R6RS, квадратные (и заодно фигурные) скобки можно использовать вместо круглых. Такого же подхода придерживался и Gambit Scheme в старых версиях (в версии 4.7 это ещё так). Однако в современной версии 4.9 реализован более интересный подход, заимствованный из Kawa, которым мы и воспользуемся.
Gambit Scheme интерпретирует конструкцию [list]
как синтаксический сахар для (|[...]| list)
, а {list}
– как синтаксический сахар для (|{...}| list)
. Здесь |[...]|
и |{...}|
- имена атомов, которые, поскольку стоят в первой позиции в форме, интерпретируются как имена функций. Напомним, что вертикальные палочки по правилам Scheme означают экранирование для необычных имён атомов, которые без этих палочек интерпретировались бы как составные конструкции (в данном случае имена функций сами включают те скобки, которые они в обычном лексическом контексте раскрывали бы).
Как это можно использовать?
Автор довольно много усилий посвящает обмену UDP-сообщениями в коде на Gambit Scheme, и там приходится постоянно использовать байтовые массивы типа u8vector. Довольно обременительным при этом является постоянное обращение к функциям для получения одного элемента массива (u8vector-ref v i)
и сечения массива (subu8vector v i j)
. Возникает вопрос: зачем писать имена функций, тем более такие длинные, если можно просто добавить скобочек в программу на Лиспе? Давайте переобозначим эти функции как [v i]
и [v i j]
соответственно! А заодно, для общности, можем длину массива обозначить как [v]
. Поскольку Scheme поддерживает полиморфизм, реализуем такую нотацию сразу для типов u8vector, u16vector, u32vector, u64vector, vector и string:
(define (|[...]| v . i)
(cond
((u8vector? v)
(case (length i)
((0) (u8vector-length v))
((1) (u8vector-ref v (first i)))
((2) (subu8vector v (first i) (second i)))
(else (raise "Subscript error [u8vector ...]"))))
((u16vector? v)
(case (length i)
((0) (u16vector-length v))
((1) (u16vector-ref v (first i)))
((2) (subu16vector v (first i) (second i)))
(else (raise "Subscript error [u16vector ...]"))))
((u32vector? v)
(case (length i)
((0) (u32vector-length v))
((1) (u32vector-ref v (first i)))
((2) (subu32vector v (first i) (second i)))
(else (raise "Subscript error [u32vector ...]"))))
((u64vector? v)
(case (length i)
((0) (u64vector-length v))
((1) (u64vector-ref v (first i)))
((2) (subu64vector v (first i) (second i)))
(else (raise "Subscript error [u64vector ...]"))))
((vector? v)
(case (length i)
((0) (vector-length v))
((1) (vector-ref v (first i)))
((2) (subvector v (first i) (second i)))
(else (raise "Subscript error [vector ...]"))))
((string? v)
(case (length i)
((0) (string-length v))
((1) (string-ref v (first i)))
((2) (substring v (first i) (second i)))
(else (raise "Subscript error [string ...]"))))
(else (raise "Subscript error [type? ...]"))))
Проверим:
> (define v #u8(1 2 3 4 5))
> [v]
5
> [v 1]
2
> [v 0 3]
#u8(1 2 3)
Удобно. Хотя в случае с длиной есть некоторые вопросы, так как изобилие сокращённых вызовов через скобочки может сделать код программы сложно читаемым. Но нам ли бояться скобочек?
impwx
Имхо обобщить получение одного или нескольких элементов - логичный шаг, а вот прилепить туда же длину - нет.
vadimr Автор
Не буду спорить с вашей позицией по существу, так как я сам отчасти с ней согласен, она основывается на вполне разумной прагматике. Однако с чисто синтаксической точки зрения раздражают также и бессмысленные языковые конструкции. В целом, синтаксис и идиоматика Лиспа тяготеют к тому, чтобы максимально обобщать функции, в том числе и на разное количество фактических параметров. Поэтому встаёт вопрос, как можно обобщить квадратные скобки на один параметр (и более чем на 2, кстати).