LINUX.ORG.RU

имена экспортируемых функций в Racket

 ,


0

1

Как принято именовать экспортируемые функции в библиотеке.

Для CL, например, есть два варианта:

  • С префиксом: symbol-function, dllist-append
  • Без префикса (считается, что «префиксом» будет пакет): gtk:add, iterate:for

Согласно Google style guide предпочитается вариант без префикса.

----

В Racket без префикса получаем, например, невозможность одновременно импортировать racket/contract и ffi/unsafe (в обоих есть ->). Пакетов нет. Есть (require (prefix-in ...)), но требует дополнительных телодвижений от пользователя библиотеки.

И что будет, если в импортируемой библиотеке случайно перекрывается символ из racket? Программа, использующая этот символ молча ломается?

В общем, вопрос такой: есть какие-нибудь guidelines для именования символов библиотек?

★★★★★

3. If two modules export two different values with the same name, you must use (require (except-in modulename name)) in order not to import name (but import everything else)

An alternative for #3 is (require (prefix-in foo: some-module))

Взял из логов irc http://pastebin.com/1ayJbFeC

qaqa ★★
()

невозможность одновременно импортировать racket/contract и ffi/unsafe (в обоих есть ->).

(require racket/contract
         (except-in ffi/unsafe ->)
         (rename-in (only-in ffi/unsafe ->) [-> -->]))
qaqa ★★
()
Ответ на: комментарий от qaqa

Я это и имел в виду. В CL тоже есть shadow...

Когда столкнулся один символ — пережить можно. А вот если их десяток. Например, сделали бы в ffi/unsafe такую же развесистую систему стрелочек, то что делать...

Кроме того вот это ошибка:

#lang racket

(provide (contract-out (foo (-> integer? integer?))))

(define (foo x) x)

(require ffi/unsafe)

;; unsaved editor:3:28: ->: should be used only in a _fun context in: (-> integer? integer?)

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Предположим такой пример: надо сделать библиотеку: двусвязный список. Очевидно, что нужны типовые операции: first, last, rest, ...

Как будет наиболее корректно назвать функции, выполняющие эти операции?

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Получается, в данном случае, идентификатор определен на уровне языка (выше) и при импорте модуля перекрывается. А когда идентификатор содержится в нескольких одновременно импортируемых модулях, тогда конфликт. Еще один плюс в использовании raket/base. Ну а в случае своего языка, получается, надо осторожно использовать require других модулей, а лучше их вообще не использовать.

Вопрос кстати интересный. Ты вроде в рассылке общаешься. Задай его там, интересно, что ответят.

qaqa ★★
()
Ответ на: комментарий от qaqa

Нашёл два примера:

С префиксом: http://www.mail-archive.com/users@racket-lang.org/msg07771.html

Без префикса: http://planet.plt-scheme.org/package-source/krhari/pfds.plt/1/5/planet-docs/f...

Вот и думаю, что для потенциального пользователя очевиднее/удобнее.

monk ★★★★★
() автор топика
Ответ на: комментарий от qaqa

Наверное с префиксом,

Здесь есть проблема. Из-за чего в CL крайне не рекомендуется использовать префикс.

Предположим, я сделал библиотеку для двусвязных списков. Использую префикс dll- (double-linked- слишком длинно). Потом кто-нибудь делает библиотеку для работы с *.DLL файлами. С тем же префиксом. А потом в программе конструкции типа

(dll-cons (dll-open lib1) (dll-rest libraries))
; здесь dll-open открывает файл DLL, а остальное -- строит список

Читать такое почти невозможно.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Можно делать префиксы длиннее, но тогда быстро появляются имена наподобие command-line-read-remaining-arguments-for-partial-command (это из стандарта CLIM).

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Можно сделать без префиксов, а импортировать с префиксом и в документации об этом указать. Мне кажется тоже нормально.

qaqa ★★
()
Ответ на: комментарий от qaqa

Можно сделать без префиксов, а импортировать с префиксом и в документации об этом указать.

Спасибо. Похоже, это действительно лучший вариант.

monk ★★★★★
() автор топика

Лучший вариант - предоставить пользователю установить свой префик через prefix-in, это же в любом случае занимает всего одну строку. Какие проблемы?

anonymous
()
Ответ на: комментарий от buddhist

Google style guide

Не нужно.

Есть лучшие альтернативы?

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Какие проблемы?

В смысле, если явно не указать в документации, что пакет перекрывает функции Racket, могут быть странные ошибки (особенно, если вдруг у перекрытой функции контракт всё-таки совпадёт, а поведение — нет).

А явно указать не всегда можно, так как в Racket новые функции добавляются с версиями.

monk ★★★★★
() автор топика
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

Или в Racket принято всегда добавлять префикс для всех пакетов, кроме racket/* ?

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

В смысле, если явно не указать в документации, что пакет перекрывает функции Racket

А кто мешает указать?

anonymous
()
Ответ на: комментарий от anonymous

А кто мешает указать?

1. в racket новые функции добавляются с версиями
2. Их просто много, могу не заметить, как перекрыл что-нибудь типа set (который racket/set).

monk ★★★★★
() автор топика
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

вот если их десяток. Например, сделали бы в ffi/unsafe такую же развесистую систему стрелочек, то что делать...

А накатать свой «импорт», который бы пробегал по всем «публичным» именам библиотеки и приклеивал бы указанный префикс?

yyk ★★★★★
()
Ответ на: комментарий от yyk

А накатать свой «импорт», который бы пробегал по всем «публичным» именам библиотеки и приклеивал бы указанный префикс?

Так это и есть упомянутый выше prefix-in. Или в экспорте prefix-out.

Пока, по итогам обсуждения, склоняемся к варианту prefix-in + упоминание в документации, если какое-то имя из racket перекрыто осознанно.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

По идее можно макрос сделать, который пробегает по модулю и определяет все перекрытые идентификаторы.

anonymous
()
Ответ на: комментарий от anonymous

ну это упадет в любом случае на первом же тесте

Не всегда на первом

#lang racket
(module m racket/base
  (provide my-range test)
  (require (planet krhari/pfds:1:5/catenablelist))

  (define (my-range n)
    (reverse (let inner ([n (sub1 n)] [acc null])
               (if (< n 0) acc
                   (cons n (inner (sub1 n) acc))))))
  
  (define (test)
    (and (= (first (my-range 5)) 0)
         (= (first (rest (my-range 5))) 1))))

(require 'm)

;; запускаем
> (test)
#t

;; все тесты пройдены, используем
> (= (first (my-range 5)) 0)
. . first: contract violation
  expected: (and/c list? (not/c empty?))
  given: #<List>
monk ★★★★★
() автор топика
Ответ на: комментарий от anonymous

По идее можно макрос сделать, который пробегает по модулю и определяет все перекрытые идентификаторы.

В CL с такой идеей тоже носились :-)))

Пришли к выводу, что так делать нежелательно, так как если у тебя есть кусок кода типа

#lang racket
(require lib)

(define (my-foo x) (foo x)) ;; здесь foo из lib

то, если в очередной версии racket вдруг появится foo, программа должна продолжать работать, а не переименовывать тот foo, который нужен в lib:foo

На самом деле, если действительно ответственность переложить на пользователя библиотеки, то проблем нет: есть prefix-in если надо импортировать всё и есть (require lib name1 name2 ...) для явного импортирования того, что нужно с перезаписью поверх.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

то, если в очередной версии racket вдруг появится foo, программа должна продолжать работать, а не переименовывать тот foo, который нужен в lib:foo

Речь не о том, чтобы автоматом переименовывать, а о том, чтобы кинуть варнинг. Ну а дальше пользователь уже сам решит что ему надо - переименовать идентификатор, сделать prefix-in или еще что. Проблема то не в том что идентификаторы перекрываются, а в том, что это происходит незаметно. Подобный макрос решит проблему.

anonymous
()
Ответ на: комментарий от anonymous

Проблема то не в том что идентификаторы перекрываются, а в том, что это происходит незаметно.

Согласен.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

еще есть вариант сделать минимальный #lang, в котором _ничего_ уже не будет добавляться, а добавляться будет в библиотеки. у require же незаметного перекрывания нету.

anonymous
()
Ответ на: комментарий от anonymous

сделать минимальный #lang, в котором _ничего_ уже не будет добавляться

Угу. И заставить _всех_ пользователей им пользоваться. Они будут счастливы.

Ещё есть вариант блокировать символы из #lang, но тогда будет как в CL: локальный (define list ...) будет вызывать ошибку компиляции. А (except-in ...) или (prefix-in ...) у lang нету.

monk ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Речь не о том, чтобы автоматом переименовывать, а о том, чтобы кинуть варнинг.

Кстати, идеальный вариант — даже не макрос, а кнопка в DrRacket. Зачастую перекрытие символов сделано намеренно и осознанно. И при каждой компиляции получать предупреждение неприятно.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

ну удобнее всего сделать свой #lang и добавлять кавкой-нибудь ключ. типо #lang racket check - проверяется, без check - не проверяется. Надо подумать как конкретно макрос должен выглядеть.

anonymous
()
Ответ на: комментарий от anonymous

типо #lang racket check

Нет. Надо явно указывать список перекрываемых позже. Как в expect-in. Иначе никто эту проверку почти нигде использовать не будет (перекрывают хоть что-нибудь многие стандартные пакеты)

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Иначе никто эту проверку почти нигде использовать не будет (перекрывают хоть что-нибудь многие стандартные пакеты)

Да нет, для стандартных пакетов если один два символа -перекрывается - это уже редкость и исключение из правил. В 99% случаев вообще ничего не перекрывается.

anonymous
()
Ответ на: комментарий от anonymous

Я как раз про ситуацию «один два символа». Значит использовать «lang racket check» уже нельзя. Или надо в нём как то эти один-два символа явно указать как исключения.

А так:

ffi/unsafe: ->
srfi/1: assq, assv, car, ... 
srfi/13: string?, string-ref, ...

Их все будет нельзя использовать в #lang racket check (а srfi даже в lang racket/base check)

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Так стрелочки в #lang racket и так нету. А srfi тут как раз исключение, т.к. оно повторяет стандартную библиотеку.

anonymous
()
Ответ на: комментарий от anonymous

А srfi тут как раз исключение, т.к. оно повторяет стандартную библиотеку

Вот только в srfi/13 есть string-count, string-skip, ... В srfi/1 — break, span, ...

Поэтому не повторяет, а расширяет.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Я имел ввиду, что повторяет в перекрываемых идентификаторах.

anonymous
()
Ответ на: комментарий от anonymous

Ну так это в typed, а не в #lang racket.

-> provided from racket/contract/base, racket/contract, racket

Последний в списке просто racket. Он же в lang racket.

Ну ладно. Это уже придирки. По существу: идеальный вариант — просто инструмент (tool) показывающий список перекрытых идентификаторов (+ кем перекрыты). Добавлять что-то типа check малополезно, так как тогда придётся что-то делать с использованием srfi (не использовать — не вариант, использовать всегда с префиксом... srfi/13:string-count как-то не впечатляет).

monk ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.