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

6fc7682c7e2ae977783c239484e62faf.png

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

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

© Habrahabr.ru