Как стать автором
Обновить

Gambit Scheme: переопределяем скобки

Уровень сложностиПростой
Время на прочтение3 мин
Количество просмотров491

Рассмотрим недавно вошедшую в 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)

Удобно. Хотя в случае с длиной есть некоторые вопросы, так как изобилие сокращённых вызовов через скобочки может сделать код программы сложно читаемым. Но нам ли бояться скобочек?

Теги:
Хабы:
Всего голосов 5: ↑5 и ↓0+8
Комментарии2

Публикации

Истории

Ближайшие события

11 – 13 февраля
Epic Telegram Conference
Онлайн
27 марта
Deckhouse Conf 2025
Москва
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань