Julia: Проверка согласованности API

Созданный на 2 февр. 2017  ·  131Комментарии  ·  Источник: JuliaLang/julia

Я начинаю с этого места, чтобы оставлять заметки о вещах, которые следует обязательно учитывать при проверке согласованности API в Julia 1.0.

  • [x] Приоритет конвенций. Перечисление и расстановка приоритетов наших соглашений о том, что приходит в первую очередь с точки зрения аргументов функций для do-блоков, аргументов ввода-вывода для функций, которые печатают, вывода для функций на месте и т. Д. 19150).

  • [] Позиционные аргументы против ключевых слов. Давным-давно у нас не было аргументов ключевого слова. Их по-прежнему иногда избегают из соображений производительности. Мы должны делать этот выбор, основываясь на том, что делает API лучшим, а не на таком историческом багаже ​​(необходимо также решить проблемы с производительностью ключевых слов, чтобы это больше не рассматривалось).

  • [] Инструменты метапрограммирования. У нас есть много инструментов, таких как @code_xxx которые работают в паре с такими базовыми функциями, как code_xxx . Они должны вести себя последовательно: одинаковые подписи, если есть функции с похожими подписями, убедитесь, что у них похожие версии макросов. В идеале все они должны возвращать значения, а не одни возвращающие значения и другие результаты печати, хотя это может быть сложно для таких вещей, как код LLVM и код сборки.

  • [] IO <=> эквивалентность имени файла. Обычно мы разрешаем передавать имена файлов в виде строк вместо объектов ввода-вывода, и стандартным поведением является открытие файла в соответствующем режиме, передача полученного объекта ввода-вывода той же функции с теми же аргументами, а затем обеспечение того, чтобы объект ввода-вывода после этого закрывается. Убедитесь, что все соответствующие функции приема-вывода следуют этому шаблону.

  • [] API редукторов. Убедитесь, что редукторы имеют последовательное поведение - все перед редукцией принимают функцию карты; аргументы конгруэнтной размерности и т. д.

  • [] Аргументы размеров. Последовательная обработка входных аргументов «вычислить по этому [этим] измерению [ам]», какие типы разрешены и т.д.

  • [] Мутирующие / не мутирующие пары. Убедитесь, что неизменяющие функции связаны с изменяющими функциями там, где это имеет смысл, и наоборот.

  • [] Кортеж против vararg. Убедитесь, что существует общая согласованность между тем, принимают ли функции кортеж в качестве последнего аргумента или vararg.

  • [] Объединения против значений NULL против ошибок. Согласованные правила относительно того, когда функции должны выдавать ошибки, а когда они должны возвращать значения Nullables или Unions (например, синтаксический анализ / анализ, сопоставление и т. Д.).

  • [] Поддерживайте генераторы как можно шире. Убедитесь, что любая функция, которая может разумно работать с генераторами, работает именно так. Мы уже неплохо разбираемся в этом, но я предполагаю, что мы упустили несколько.

  • [] Выбор типа вывода. Будьте последовательны в том, должны ли API "выходного типа" быть с точки зрения типа элемента или общего типа контейнера (ref # 11557 и # 16740).

  • [x] Выберите имя. Есть несколько функций / операторов с псевдонимами. Я думаю, что это нормально в тех случаях, когда одно из имен не является ASCII и предоставляется версия ASCII, поэтому люди все еще могут писать код в чистом ASCII, но есть также такие случаи, как <: который является псевдонимом для issubtype где оба имени - ASCII. Мы должны выбрать одно и исключить другое. Мы устарели is в пользу === и должны поступить аналогичным образом.

  • [] Согласованность со структурами данных . Это несколько выходит за рамки Base Julia, но мы должны убедиться, что все коллекции в DataStructures имеют согласованные API с API, предоставляемые Base. Связь в другом направлении заключается в том, что некоторые из этих типов могут повлиять на то, как мы закончим разработку API в Base, поскольку мы хотим, чтобы они расширялись плавно и последовательно.

  • [] NaN против DomainErrors. См. Https://github.com/JuliaLang/julia/issues/5234 - разработайте политику, определяющую, когда что делать, и убедитесь, что она соблюдается последовательно.

  • [] Сборник <=> генератор. Иногда вам нужна коллекция, иногда вам нужен генератор. Мы должны пройти через все наши API и убедиться, что есть вариант для обоих, где это имеет смысл. Давным-давно существовало соглашение использовать имя в верхнем регистре для версии генератора и имя в нижнем регистре для версии, которая стремится и возвращает новую коллекцию. Но никто никогда не обращал на это внимания, так что, возможно, нам нужна новая конвенция.

  • [] Функции высшего порядка на ассоциативах. В настоящее время некоторые функции высшего порядка перебирают ассоциативные коллекции с сигнатурой (k,v) - например, map , filter . Другие перебирают пары, то есть с сигнатурой kv , требуя, чтобы тело явно деструктурировало пару на k и v - например, all , any . Это следует пересмотреть и согласовать.

  • [x] Преобразование против построения. При необходимости разрешите преобразование. Например, было несколько проблем / вопросов по поводу convert(String, 'x') . В общем, преобразование подходит, когда есть одно каноническое преобразование. Преобразование строк в числа в общем случае нецелесообразно, потому что существует множество текстовых способов представления чисел, поэтому вместо этого нам нужно выполнять синтаксический анализ с параметрами. Однако существует единственный канонический способ представления номеров версий в виде строк, поэтому мы можем их преобразовать. Мы должны применять эту логику осторожно и повсеместно.

  • [] Проверить полноту API коллекций. Нам следует взглянуть на стандартные библиотечные функции для коллекций, предоставляемых другими языками, и убедиться, что у нас есть способ выразить общие операции, которые они выполняют. Например, у нас нет функции flatten или concat . Наверное, стоит.

  • [] Нижний аудит.

deprecation

Самый полезный комментарий

Нижний аудит

Ниже приводится анализ всех символов, экспортированных из Base, которые содержат подчеркивания, не являются устаревшими и не являются строковыми макросами. Здесь главное отметить, что это только экспортированные имена; сюда не входят неэкспортированные имена, которые мы говорим людям называть квалифицированными.

Я разделил вещи по категориям. Надеюсь, это больше полезно, чем раздражает.

Отражение

У нас есть следующие макросы с соответствующими функциями:

  • [] @code_llvm , code_llvm
  • [] @code_lowered , code_lowered
  • [] @code_native , code_native
  • [] @code_typed , code_typed
  • [] @code_warntype , code_warntype

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

  • [x] module_name -> nameof (# 25622)
  • [x] module_parent -> parentmodule (# 25629, см. # 25436 для предыдущей попытки переименования)
  • [x] method_exists -> hasmethod (# 25615)
  • [x] object_id -> objectid (# 25615)
  • [] pointer_from_objref

pointer_from_objref возможно, можно было бы использовать с более описательным именем, может быть, что-то вроде address ?

Псевдонимы для взаимодействия с C

Псевдонимы типов, содержащие символы подчеркивания: C_NULL , Cintmax_t , Cptrdiff_t , Csize_t , Cssize_t , Cuintmax_t и Cwchar_t . Те, которые заканчиваются на _t должны остаться, так как они названы в соответствии с их соответствующими типами C.

C_NULL здесь необычный, поскольку это единственный псевдоним C, содержащий подчеркивание, которое не отражается в C (поскольку в C это всего лишь NULL ). Мы могли бы назвать это CNULL .

  • [] C_NULL

Подсчет бит

  • [] count_ones
  • [] count_zeros
  • [] trailing_ones
  • [] trailing_zeros
  • [] leading_ones
  • [] leading_zeros

Обсуждение их переименования см. # 23531. Я очень поддерживаю удаление подчеркиваний для них, а также некоторые из предлагаемых замен в этом PR. Я думаю, это нужно пересмотреть.

Небезопасные операции

  • [] unsafe_copyto!
  • [] unsafe_load
  • [] unsafe_pointer_to_objref
  • [] unsafe_read
  • [] unsafe_store!
  • [] unsafe_string
  • [] unsafe_trunc
  • [] unsafe_wrap
  • [] unsafe_write

Вероятно, это нормально - оставить все как есть; уродство подчеркивания еще больше подчеркивает их небезопасность.

Индексирование

  • [] broadcast_getindex
  • [] broadcast_setindex!
  • [] to_indices

Судя по всему, существуют broadcast_getindex и broadcast_setindex! . Я не понимаю, что они делают. Может быть, они могли бы использовать более описательное имя?

Интересно, что версия с одним индексом to_indices , Base.to_index не экспортируется.

Следы

  • [] catch_backtrace
  • [x] catch_stacktrace -> stacktrace(catch_backtrace()) (# 25615)

Предположительно, это catch блочные эквиваленты backtrace и stacktrace соответственно.

Задачи, процессы и сигналы

  • [] current_task
  • [] task_local_storage
  • [] disable_sigint
  • [] reenable_sigint
  • [] process_exited
  • [] process_running

Потоки

  • [] redirect_stderr
  • [] redirect_stdin
  • [] redirect_stdout
  • [x] nb_available -> bytesavailable (# 25634)

Было бы неплохо иметь более общую функцию перенаправления IO -> IO в которой все это можно было бы объединить, например, redirect(STDOUT, io) , тем самым удалив как подчеркивания, так и экспорт.

Продвижение

  • [] promote_rule
  • [] promote_shape
  • [] promote_type

См. # 23999 для соответствующего обсуждения promote_rule .

Печать

  • [x] print_with_color -> printstyled (см. # 25522)
  • [] print_shortest (см. № 25745)
  • [] escape_string (см. # 25620)
  • [] unescape_string

escape_string и unescape_string немного странны в том смысле, что они могут печатать в поток или возвращать строку. См. # 25620 для предложения переместить / переименовать их.

Загрузка кода

  • [] include_dependency
  • [] include_string

include_dependency . Используется ли это вообще вне базы? Я не могу представить себе ситуацию, когда вы бы хотели это вместо include в любом типичном сценарии.

include_string . Разве это не просто официально санкционированная версия eval(parse()) ?

То, что я не стал категоризировать

  • [x] gc_enable -> GC.enable (# 25616)
  • [] get_zero_subnormals
  • [] set_zero_subnormals
  • [] time_ns

get_zero_subnormals и set_zero_subnormals можно использовать с более информативными именами. Их нужно экспортировать?

Все 131 Комментарий

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

Нет, это хорошее место для этого. И да, мы должны стремиться исключить все имена, в которых необходимо подчеркивание :)

  • согласованный подход к входным аргументам «вычислить по этому [этим] измерению [ам]», какие типы разрешены и т. д., подумайте, может ли быть желательно их использование в качестве аргументов ключевого слова
  • перечисление и определение приоритетов наших соглашений о том, что приходит в первую очередь с точки зрения аргументов функций для do-блоков, аргументов ввода-вывода для функций, которые печатают, выходов для функций на месте и т.Д. (редактировать: подумал, что для этого уже может быть один открытый)

Для второго пункта @tkelman см. Https://github.com/JuliaLang/julia/issues/19150

Также недавно был Джулеп относительно API для find и связанных функций: https://github.com/JuliaLang/Juleps/blob/master/Find.md

Должны ли мы отказаться от put! и take! на каналах (и, возможно, сделать то же самое для фьючерсов), поскольку на них есть push! и shift! ? Просто предлагаю удалить 2 лишних слова в API.

Я подозреваю, что shift! удобен для пользователя. Кандидат fetch! у нас уже есть fetch который является неизменяемой версией take!

исх # 13538 # 12469

@amitmurthy @malmaud

Изменить: имеет смысл даже повторно использовать send и recv на каналах. (Я удивлен, что на данный момент они используются только для UDPSockets)

+1 за замену put! / take! на push! / fetch!

Я добавлю переименование @inferred в @test_inferred .

Еще раз проверьте, соответствуют ли специализации более универсальным функциям, т.е. не что-то вроде # 20233.

Просмотрите все экспортированные функции, чтобы проверить, можно ли исключить какие-либо из них, заменив их множественной отправкой, например print_with_color

Типичное сочетание push! и shift! при работе со структурой данных, подобной очереди.

Если мы не собираемся использовать типичное сочетание имен для такой структуры данных, потому что мы обеспокоены тем, что операция влечет за собой накладные расходы на связь, которые неадекватно передаются этими именами, тогда я не думаю, что push! тоже имеет смысл. send и recv действительно могли бы быть лучше.

Возможно, еще раз проверьте, есть ли общая согласованность между тем, принимают ли функции кортеж в качестве последнего аргумента или vararg.

Возможно, слишком велик для этой проблемы, но было бы хорошо иметь последовательные правила, определяющие, когда функции должны выдавать ошибки и когда они должны возвращать Nullable s или Union s (например, parse / tryparse , match и т. Д.)

Нет проблем, @simonbyrne - это список для стирки.

Кстати: это не совсем для конкретных изменений (например, переименование определенных функций) - это больше о том, что мы можем проверить. Для конкретных предлагаемых изменений просто откройте вопрос, предлагающий это изменение.

У нас есть много инструментов, таких как @code_xxx, которые связаны с базовыми функциями, такими как code_xxx

Не уверен, что вы об этом говорите, но посмотрите CreateMacrosFrom.jl

  • Должны ли API "типа вывода" быть с точки зрения типа элемента или общего типа контейнера (ref # 11557 и # 16740)
  • Документируйте все экспортированные функции (включая тесты)

Документируйте все экспортированные функции (включая тесты)

Если это часть этого, то, возможно, также: не забудьте пометить свои тесты номером проблемы / PR. Так становится намного проще понять, зачем нужен этот тест. Я знаю, как работает git blame, но при добавлении тестовых наборов (просто для примера) иногда остается загадкой, что именно тестируется, и было бы здорово, если бы всегда был номер проблемы / PR.

@dpsanders : и экспортированные макросы! например, @fastmath не имеет строки документации.

Это очень незначительно, но функции string и Symbol делают почти одно и то же и имеют разные заглавные буквы. Я думаю, что symbol будет иметь больше смысла.

@amellnik Разница в том, что Symbol - это конструктор типа, а string - обычная функция. IIRC у нас был symbol но он устарел в пользу конструктора типа. Я не уверен, что для этого необходимы изменения, но я думаю, что мы должны использовать конструктор String вместо string .

во всяком случае, я думаю, мы должны использовать конструктор String вместо строки.

Нет, это разные функции, и их не следует объединять

julia> String(UInt8[])
""

julia> string(UInt8[])
"UInt8[]"

Нет, это разные функции, и их не следует объединять

Это похоже на ситуацию, когда string(args...) следует просто устареть в пользу sprint(print, args...) , тогда - наличие string и String сбивает с толку. Мы могли бы специализироваться на sprint(::typeof(print), args...) чтобы восстановить любую потерянную производительность. В этой связи также может иметь смысл отказаться от repr(x) для sprint(showall, args...) .

Звучит нормально, хотя вызов string для преобразования чего-либо в строку кажется довольно стандартным ....

вызов строки для преобразования чего-либо в строку кажется довольно стандартным

Да, но именно здесь возникает разрыв между String и string .

sprint(print, ...) кажется лишним. Если мы избавимся от string , мы можем переименовать sprint в string чтобы получить string(print, foo) и string(showall, foo) что, на мой взгляд, хорошо читается. .

Это может быть тот случай, когда согласованность переоценена. Я думаю, что это нормально, если string(x) означает «просто дайте мне строковое представление x». Если это будет сложнее, например, требуется указать, какую функцию печати использовать, тогда имеет смысл использовать другое имя, например sprint .

Также было бы нормально переименовать String(UInt8[]) во что-нибудь другое и использовать String вместо string . string дает нам немного больше гибкости в будущем, чтобы изменить тип возвращаемой строки, но это маловероятно.

Имеет ли смысл reinterpret(String, ::Vector{UInt8} вообще, или это каламбур на reinterpret ?

В этом есть смысл.

Проблема в том, что эта функция иногда копирует, поэтому это название вводит в заблуждение.

Верно, но предполагается, что строки должны быть неизменяемыми, так что нам, вероятно, это сойдет с рук.

Существует также метод String(::IOBuffer) , но похоже, что он может быть устаревшим до readstring .

Я тоже думал о предлагаемом вами изменении API, но интерфейс string(a, b...) заключается в том, что он структурирует и объединяет свои аргументы, и это приведет к раздражающему исключению ошибок для вызываемых первых аргументов. Если мы удалим конкатенацию из string тогда она может заработать.

Да, согласен; последовательность и избегание ошибок является наиболее важным.

Обратите внимание на проблемы № 18326 и № 3893 в категории «аргументы измерения».

Если я могу заняться другим вопросом: убедиться, что поведение контейнеров изменяемых файлов задокументировано и согласовано.

@ JaredCrean2 : не могли бы вы

Я конечно надеюсь, что это не связано с созданием множества «защитных копий».

Например, если у меня есть массив изменяемых типов и я вызываю на нем sort , указывает ли возвращаемый массив на те же объекты, что и входной массив, или он копирует объекты и делает возвращаемый массив точкой на их?

Те же объекты. Я почти уверен, что все наши методы сортировки коллекций, getindex, фильтрации, поиска и т. Д. Следуют этому правилу, не так ли?

Я не думаю, что в этом вопросе есть недостаток ясности или последовательности - это всегда одни и те же объекты.

Фактически, я думаю, что единственной стандартной функцией, где это не так, является deepcopy где все дело в том, что вы получаете все новые объекты.

Это где-то задокументировано?

Нет, мы могли бы, но я не уверен, где лучше всего это задокументировать. Зачем функциям делать копии без надобности? Откуда у вас создалось впечатление, что они могут?

Здравствуйте. Я не встречал никаких замечаний по поводу сериализации данных.

Рано или поздно программы julia будут написаны и запущены публично, данные начнут иногда расслаиваться на долгие годы. Сериализация данных, например. цепочка: объект в байты, управляемая типом (возможно, через json или ...), должна быть построена, чтобы быть устойчивой по времени. Подумайте о семантическом управлении версиями и веб-API.

Можно ли ожидать, что сериализация пользовательских данных останется близкой к https://github.com/JuliaLang/julia/blob/v0.5.1/base/serialize.jl ?

Зачем функциям делать копии без надобности? Откуда у вас создалось впечатление, что они могут?

Не знаю, знают они или нет. Насколько я могу судить, поведение не определено. Из комментария @JeffBezanson видно , что есть люди, которые выступают за создание защитных копий, а он выступает против. Так что документация должна где-то касаться вопроса защитных копий.

Кажется, вы подразумеваете какой-то принцип наименьшего действия, но в зависимости от деталей алгоритма, что такое наименьшее действие, становится неоднозначным. Я думаю, что для обеспечения единообразия API требуется более конкретное руководство.

@ o314 : это проблема проверки согласованности API, я не уверен, как связана сериализация.

@ JaredCrean2 : необходимо

Я говорю о том, что более глубокие объекты никогда не копируются, кроме как методом deepcopy (очевидно).

Недавно это обсуждалось в контексте copy для некоторых оболочек массива, например SubArray и SparseMatrixCSC но также Symmetric , LowerTriangular . Мне кажется, что в соответствии с вышеупомянутой политикой copy не будет для таких типов оболочки. Правильный ли уровень абстракции здесь упоминается в политике? Например, я думаю, это подразумевает, что если Array s были реализованы в Julia (упаковка буфера), поведение copy на Array s должно измениться на noop.

Если существует соглашение, что более глубокие объекты никогда не копируются, то остается только задокументировать это. Документация - действительно важная часть API. Такое поведение может показаться вам очевидным (возможно, потому, что вы написали часть кода), но со стороны это не так очевидно.

Изменить: не видел сообщение Андреаса. Это интересное соображение.

@StefanKarpinski Я согласен с вашей точкой зрения.
И все основные темы, затронутые здесь, очень хорошие и умные.

Но иногда я немного опасаюсь балансировки между процессом и данными в Julia:

Мы можем легко позвонить в fortran или c,
Но будет ли код так же легко развернут в современном центре обработки данных, например. aws лямбда с его функцией как шаблон службы. будет ли код легко вызываться через Интернет, откройте API?

Иногда нужно снизить функциональную нагрузку до масштабирования (без общих, без vaargs в сигнатуре функции, без высокого порядка в общедоступном API) и более систематически связывать данные позади (схема json / openapi).

Я видел, как вот так тонет очень хорошая библиотека Python, и это очень жаль.

Я считаю, что для языка 1.0 крайне важно сохранять сбалансированность и модульность данных и функций, чтобы их можно было легко развертывать в Интернете. И для этой функции интерфейс должен быть меньше ориентирован на домашних животных и больше ориентирован на скот, когда это необходимо.

Может быть, суть не в этом.

@StefanKarpinski Возможно, я неправильно понял ваш пост. Когда ты сказал

копируется ли объект верхнего уровня или нет, безусловно, необходимо задокументировать

что означает «объект верхнего уровня»? Если у меня есть x::Vector{MyMutableType} , это объект верхнего уровня x или элементы x ?

Объект верхнего уровня относится к самому x , а не к элементам x .

@andreasnoack Понятие объекта верхнего уровня должно относиться к реализованной абстрактной структуре, а не к деталям реализации.

может быть, добавить float и другую аналогичную функцию, которая действует как на типы, так и на значения?

Если заглянуть в примечания к выпуску 0.6, то кажется странным, что добавлено iszero(A::Array{T}) , в то время как многие другие функции (например, sumabs , isinteger , isnumber ) над массивами устарело в пользу all(f,A) .

Есть нулевые массивы, и они являются нулевыми элементами в своем векторном пространстве. iszero общем случае проверяет, является ли что-то аддитивным обратным, каковыми являются нулевые массивы.

Re. согласованность символов подчеркивания в именах функций, вот навигационная цепочка к count_ones и count_zeros .

Мне кажется, что если вы хотите, чтобы Julia API была согласованной, вам понадобится какое-то программное обеспечение, которое позволит вам (а) указать правила / соглашения API, (б) выполнить статический анализ кода Julia для обнаружения отклонения от этих правил / соглашений и (c) предложения. Такой инструмент принесет пользу как Julia Base, так и всем Julia Packages. Новый пакет Julia может помочь. (Язык в щеку: первое, что должен сделать этот пакет, - это предложить свое собственное имя; APICheck.jl, ApiCheck.jl, API_Check.jl, APIChecker.jl, JuliaAPIChecker.jl и т. Д.) Я новичок в Джулии, так что не хочу брать на себя инициативу в таких вещах. Однако я не прочь внести свой вклад. Есть предложения, как это сделать?

Нам бы очень хотелось, чтобы это было в Lint.jl!

num2hex и hex2num (# 22031 и # 22088)

Я тоже считаю, что Lint.jl - правильный пакет для проверки согласованности API Julia. Но если мы пойдем по этому пути, первоначальный список Стефана должен быть намного точнее. Например, когда он пишет «мы должны убедиться, что все коллекции в DataStructures имеют согласованные API», на ум приходят следующие вопросы:

  • Что делает структуру данных коллекцией? (Список коллекций в базе)
  • Какие функции ДОЛЖНА поддерживать коллекция? (Таблица функций по коллекциям)
  • Какой должна быть сигнатура каждой из этих функций?
  • Согласованы ли уже API, предоставляемые Base для коллекций?

Чтобы управлять такими инвентаризацией и анализом, мы могли бы добавить проект в репозиторий Julia (проект № 8), или в репозиторий JuliaPraxis (предложенный TotalVerb в автономном режиме), или в репозиторий Lint. В этом случае нам нужно будет разобраться, кому будет принадлежать такой проект, какие люди должны быть задействованы с самого начала и кто должен принимать окончательные решения о том, каковы на самом деле соглашения Джулии (для целей линтинга).

Но прежде чем продолжить в этом направлении, я хотел бы спросить

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

Действительно ли нам нужны Base.datatype_module и Base.function_module?

Унифицированный функциональный «модуль» (возможно, getmodule), рассылающий по типу данных и функции, мне кажется более последовательным.

А как насчет подчеркивания в @code_typed и друзьях?

Это хорошая возможность для рефакторинга (указанная причина запрета подчеркивания). У вас может быть макрос @code с первым аргументом, который является типом кода, который вы хотите.

( @bramtayl , пожалуйста, не забудьте поставить обратные кавычки вокруг макросов, так как это пингует "код" пользователя github, если нет; @code )

FWIW, завершение табуляции работает только с именами функций. Уметь делать @code_<TAB> - это хорошо ....

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

Недостаточный аудит.

Контрпредложение: мы по-прежнему проверяем эти имена, но вместо этого добавляем дополнительные подчеркивания там, где это упростит чтение кода (codetyped vs code_typed, isos2 vs is_os2).

Я не сторонник абсолютизма. Я думаю, что code_typed в порядке, полезно использовать табуляцию, как указывает @KristofferC , и на самом деле нет ничего очевидного, что можно было бы передать в качестве аргумента для выбора, какой результат вы хотите.

Мне кажется, что корабль поплыл с добавлением дополнительных подчеркиваний, так как нам пришлось бы отказаться от почти половины Base. Например, есть 74 функции-предиката, которые начинаются с is и только 6 начинаются с is_ . Что имеет больше смысла, отказаться от 6 или 74?

Хорошо, здесь несколько противоречивых целей:

1) Делаем имена более читабельными
2) Уменьшение оттока кода
3) Поощрение рефакторинга

Устранение подчеркивания путем объединения слов не удается на всех трех фронтах.

То, что методы show принимающие поток, не являются ! -завершенными, кажется несовместимым с обычным соглашением? Ref. https://github.com/JuliaLang/julia/pull/22604/commit/db9d70a279763ded5088016d9c3d4439a49e3fca#r125115063. Лучший! (Изменить: я полагаю, это соответствует write методам, принимающим поток.)

Несоответствия с API трейтов. Некоторые черты вычисляются путем вызова такой черты, как
TypeArithmetic(Float64)
в то время как другие эту функцию нужно писать в нижнем регистре:
iteratorsize(Vector{Float64})

Попробуйте переименовать size -> shape (xref # 22665)

Array{T,1}() вероятно, тоже следует устареть:

julia> Array{Int,1}()                                                                                                                  
0-element Array{Int64,1}                                                                                                               

julia> Array{Int,2}()                                                                                                                  
WARNING: Matrix{T}() is deprecated, use Matrix{T}(0, 0) instead.                                                                       

Долго думали о коллекциях. В основном у нас есть три вида:

  • Простые коллекции в виде наборов (или пакетов), которые содержат только некоторые значения.
  • Коллекции, похожие на массивы, которые добавляют индекс вместе с данными.
  • Dict-подобные коллекции, в которых индексы являются частью данных, то есть пары k=>v .

Я стал скептически относиться к диктатному поведению. Канарейка в угольной шахте - это map , где работа с парами "ключ-значение" неестественна, поскольку обычно вы не хотите менять набор ключей. Для массивов и dicts также возможно реализовать один и тот же интерфейс:

  • keys соответствует eachindex
  • mapindexed и filterindexed будут полезны как для словарей, так и для массивов. Они похожи на map и filter, за исключением того, что также передают вашей функции индекс рассматриваемого элемента.
  • Массивы и словари (и именованные кортежи и, возможно, другие вещи) могут использовать итератор pairs , который в основном является ярлыком для zip(keys(c), values(c)) .

Подумайте о переименовании ind2sub и sub2ind , которые, по-видимому, являются матлабизмами и имеют не-юлианское и странное имя для пользователя, не использующего Matlab. Возможные имена: indice и linearindice соответственно. Я не решился делать пиар, потому что не уверен, что люди об этом думают, но сделаю, если будет поддержка.

То же самое с rad2deg и deg2rad .

Ref. # 22791 ( select -> partialsort ). Лучший!

Я не видел здесь одного: идут ли необязательные позиционные аргументы первыми или последними? Иногда необязательные позиционные аргументы идут первыми, как в sum(f, itr) и rand([rng,] ..) . Но в других местах они идут последними, например, в median(v[, region]) или split(s::AbstractString[, chars]) . Иногда они могут идти первым или последним, но не одновременно! (Например, mean может первой принимать функцию или последнее измерение, но не оба сразу.)

Текущая семантика языка заставляет необязательные аргументы идти последними: вы можете написать f(a, b=1) но не f(b=1, a) . Но если все необязательные аргументы идут последними, что происходит с удобными блоками do?

По крайней мере, небольшая проблема в том, что язык должен определять такие методы, как rand.jl : shuffle!(a::AbstractVector) = shuffle!(GLOBAL_RNG, a) . Синтаксис необязательного позиционного аргумента должен учитывать именно этот вариант использования.

Может быть, стоит выделить отдельную тему, но кажется возможным переместить необязательные аргументы куда угодно. Так, например, f(a = 1, b, c = 2) будет определять f(x) = f(1, x, 2) и f(x, y) = f(x, y, 2)

xref # 22460 для (непопулярной) попытки включить аргументы по умолчанию в любой позиции.

Возможно, переименуйте warn в warning (Matlab также использует это), не имеет большого значения, но подумал, что упомянул?

Мне нравится warn потому что это глагол, например throw .

Меня это очень смутило:

julia> f(;a=1,b=1) = a+b                                                                                                                              
f (generic function with 1 method)                                                                                                                    

julia> f(a=4,5)            # I intended to write f(a=4,b=5)                                                                                                                           
ERROR: MethodError: no method matching f(::Int64; a=4)                                                                                                
Closest candidates are:
  f(; a, b) at REPL[13]:1

Я предлагаю разрешать только ключевые слова последними при вызове функций, аналогично определению функций ключевых слов.

Я предлагаю разрешать только ключевые слова последними при вызове функций, аналогично определению функций ключевых слов.

Существует множество API-интерфейсов, в которых передача ключевых слов в другие позиции одновременно полезна и эргономична.

Из любопытства, существует ли определенный порядок оценки для позиционных аргументов и аргументов ключевых слов? Я видел несколько более старых проблем, и https://docs.julialang.org/en/latest/manual/functions/#Evaluation -Scope-of-Default-Values-1 говорит об объемах, но ничего из того, что я нашел, не указывает, например, аргументы оцениваются слева направо, или все позиционные аргументы оцениваются перед всеми аргументами ключевого слова, или если нет определенного порядка оценки (или что-то еще).

@yurivish , ключевые слова см. в документации (также https://github.com/JuliaLang/julia/issues/23926). Для необязательных история немного сложнее, может быть, почитайте здесь . (Обратите внимание, что вопросы лучше задавать на https://discourse.julialang.org/)

Кажется, это не стоит того, но мне всегда кажется странным, что bits(1) возвращает String , похоже, это должно быть BitVector или Vector{Bool} .

Я хотел бы предложить обзор таблиц методов, которые отправляются по Function или Callable . Давайте попробуем убедиться, что эти API-интерфейсы являются такими, какими мы их хотим… и что мы не упускаем возможности разрешить реструктуризацию таблиц таким образом, чтобы мы могли позволить утиный вызов любого объекта.

Что касается all и any , их кажется несложным для устаревания до all(f(x) for x in xs) , которое уже сокращено до all(Generator(f, xs)) и поэтому не должно иметь накладных расходов.

Не уверен , если это то , что вы имели в виду , но я понял , что стоит о том , на всякий случай: я хардкорный против протестующий любые API - интерфейсы функционального стиля для генераторов. У нас есть any(f, x) и all(f, x) и они широко используются; -10000000 за удаление тех (или каких-либо подобных методов, правда).

Я про генератор. Похоже на фундаментальный строительный блок ленивого программирования, и его следует экспортировать. all(Generator(f, xs)) иногда удобнее для определенных функций, чем all(f(x) for x in xs) . Также +10000000 для восстановления баланса

Я предпочитаю использовать здесь меньше синтаксиса, если это возможно. Если легко, легко и эффективно выразить идею «взять xs, применить f ко всему и вернуть истину, если все это истинно», то почему мы должны объединить ее в один глагол?

Если вас беспокоит ненужное существительное x в f(x) for x in xs , тогда предложение @bramtayl об экспорте Generator (возможно, с использованием лучшего имени, например Map ?) имеет смысл.

Такие вещи, как all(isnull, x) , намного проще, чем all(isnull(v) for v in x) . Мы исключили функцию allnull из NullableArrays в пользу all(isnull, x) ; если бы этот синтаксис исчез, нам, вероятно, пришлось бы его снова ввести.

Как насчет переименования strwidth в stringwidth (я думаю, что это единственная экспортируемая функция манипулирования строкой, которая имеет сокращенную строку до str)

На самом деле он был переименован в textwidth (https://github.com/JuliaLang/julia/pull/23667).

ИМО, эта проблема слишком обширна, чтобы включать в себя отметку 1.0, учитывая, что мы хотим в ближайшее время заморозить функцию. Нам может потребоваться несколько владельцев и назначить для проверки наборы функций, если мы захотим это сделать.

Кроме того, это еще одно место, где FemtoCleaner может автоматически обновлять многие вещи после версии 1.0, хотя было бы неплохо все исправить.

просто комментарий по ширине текста:

INFO: Testing Cairo
Test Summary:   | Pass  Total
Image Surface   |    7      7
Test Summary:   | Pass  Total
Conversions     |    4      4
Test Summary:   | Pass  Total
TexLexer        |    1      1
WARNING: both Compat and Cairo export "textwidth"; uses of it in module Main must be qualified
Samples        : Error During Test
  Got an exception of type LoadError outside of a <strong i="6">@test</strong>
  LoadError: UndefVarError: textwidth not defined
  Stacktrace:
   [1] include_from_node1(::String) at .\loading.jl:576
   [2] include(::String) at .\sysimg.jl:14
   [3] macro expansion at C:\Users\appveyor\.julia\v0.6\Cairo\test\runtests.jl:86 [inlined]
   [4] macro expansion at .\test.jl:860 [inlined]
   [5] anonymous at .\<missing>:?
   [6] include_from_node1(::String) at .\loading.jl:576
   [7] include(::String) at .\sysimg.jl:14
   [8] process_options(::Base.JLOptions) at .\client.jl:305
   [9] _start() at .\client.jl:371
  while loading C:\Users\appveyor\.julia\v0.6\Cairo\samples\sample_pango_text.jl, in expression starting on line 28

Сообщение об ошибке кажется довольно ясным, в чем проблема? Cairo нужно расширить базовый метод.

help?>  Base.textwidth
  No documentation found.

  Binding Base.textwidth does not exist.

julia> versioninfo()
Julia Version 0.6.0
julia> Compat.textwidth
textwidth (generic function with 2 methods)

Я не сомневался, что сообщение (которое я получил через Travis) в порядке, но почему Compat экспортирует ширину текста, если его нет в 0.6?

Потому что в этом смысл Compat? Тем не менее, это обсуждение очень сильно выходит за рамки, поэтому я предлагаю продолжить его на дискурсе или расслаблении.

Я предлагаю заменить copy на shallowcopy и deepcopy на copy поскольку недавно мне потребовалось довольно много времени, чтобы понять, что копия является «поверхностной» копией, а Функция, которую я написал, изменяла массив массивов. Думаю, было бы гораздо более интуитивно понятным, если бы copy выполнял бы "глубокую" копию, а что-то вроде shallowcopy использовалось для мелких копий? Теперь я знаю. когда использовать deepcopy , но я думаю, что многие другие пользователи столкнутся с той же проблемой.

Давайте постараемся сохранить эту проблему для согласованности API, а не для сбора «конкретных вещей, которые мне не нравятся».

Associative имя кажется совершенно несовместимым со всеми другими именами типов в Julia.

Во-первых, типы обычно не являются прилагательными. Существительное Association используется, по крайней мере, в некоторых найденных мною документах по системе Mathematica.

Я думаю, что AbstractDict будет гораздо более согласованным с другими типами, такими как AbstractArray , AbstractRange , AbstractSet и AbstractString , каждый из которых имеет прототипы конкретных типов Dict , Array , Range , Set и String .

Наши типы исключений встречаются повсюду: одни называются FooError , другие называются BarException ; некоторые из них экспортируются, большинство - нет. Для единообразия можно использовать сквозной проход.

Итак, что было бы предпочтительнее, FooError или BarException ? Экспортируется или нет?

Для меня BarException подразумевает какую-то схему рейза / ловли.

Я предпочитаю многое, и некоторые другие в функциональном мире тоже используют шаблон Some / None (*), где поток управления более прямой и предсказуемый.

Итак, +1 за FooError

(*) Some / Void ex Optional в julia # 23642.

Эти вещи все еще находятся на столе после того, как функция зависла? Я особенно хотел бы заняться необязательными аргументами и аргументами ключевого слова, но список функций с несколькими необязательными аргументами (самый ясный случай использования вместо них аргументов ключевого слова) довольно длинный.

Пожалуйста, взгляните! У меня не было возможности систематически рассматривать эти вопросы.

Кстати, я заметил непоследовательность в именовании признаков: у нас есть iteratorsize , iteratoreltype , но IndexStyle , TypeRangeStep , TypeArithmetic и TypeOrder . Похоже, что варианты CamelCase более многочисленны и более свежие, так что, может быть, нам следует принять это соглашение повсюду?

Их обязательно нужно сделать последовательными. Хотите устроить пиар?

Я думаю, это следует исправить в рамках https://github.com/JuliaLang/julia/pull/25356.

РЕДАКТИРОВАТЬ: см. Также https://github.com/JuliaLang/julia/issues/25440

В основном это делается или может быть сделано в версиях 1.x. Я могу обновить флажки, но мы просто просмотрели их при вызове сортировки, и все, кроме # 25395 и аудита подчеркивания, выполнено.

Нижний аудит

Ниже приводится анализ всех символов, экспортированных из Base, которые содержат подчеркивания, не являются устаревшими и не являются строковыми макросами. Здесь главное отметить, что это только экспортированные имена; сюда не входят неэкспортированные имена, которые мы говорим людям называть квалифицированными.

Я разделил вещи по категориям. Надеюсь, это больше полезно, чем раздражает.

Отражение

У нас есть следующие макросы с соответствующими функциями:

  • [] @code_llvm , code_llvm
  • [] @code_lowered , code_lowered
  • [] @code_native , code_native
  • [] @code_typed , code_typed
  • [] @code_warntype , code_warntype

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

  • [x] module_name -> nameof (# 25622)
  • [x] module_parent -> parentmodule (# 25629, см. # 25436 для предыдущей попытки переименования)
  • [x] method_exists -> hasmethod (# 25615)
  • [x] object_id -> objectid (# 25615)
  • [] pointer_from_objref

pointer_from_objref возможно, можно было бы использовать с более описательным именем, может быть, что-то вроде address ?

Псевдонимы для взаимодействия с C

Псевдонимы типов, содержащие символы подчеркивания: C_NULL , Cintmax_t , Cptrdiff_t , Csize_t , Cssize_t , Cuintmax_t и Cwchar_t . Те, которые заканчиваются на _t должны остаться, так как они названы в соответствии с их соответствующими типами C.

C_NULL здесь необычный, поскольку это единственный псевдоним C, содержащий подчеркивание, которое не отражается в C (поскольку в C это всего лишь NULL ). Мы могли бы назвать это CNULL .

  • [] C_NULL

Подсчет бит

  • [] count_ones
  • [] count_zeros
  • [] trailing_ones
  • [] trailing_zeros
  • [] leading_ones
  • [] leading_zeros

Обсуждение их переименования см. # 23531. Я очень поддерживаю удаление подчеркиваний для них, а также некоторые из предлагаемых замен в этом PR. Я думаю, это нужно пересмотреть.

Небезопасные операции

  • [] unsafe_copyto!
  • [] unsafe_load
  • [] unsafe_pointer_to_objref
  • [] unsafe_read
  • [] unsafe_store!
  • [] unsafe_string
  • [] unsafe_trunc
  • [] unsafe_wrap
  • [] unsafe_write

Вероятно, это нормально - оставить все как есть; уродство подчеркивания еще больше подчеркивает их небезопасность.

Индексирование

  • [] broadcast_getindex
  • [] broadcast_setindex!
  • [] to_indices

Судя по всему, существуют broadcast_getindex и broadcast_setindex! . Я не понимаю, что они делают. Может быть, они могли бы использовать более описательное имя?

Интересно, что версия с одним индексом to_indices , Base.to_index не экспортируется.

Следы

  • [] catch_backtrace
  • [x] catch_stacktrace -> stacktrace(catch_backtrace()) (# 25615)

Предположительно, это catch блочные эквиваленты backtrace и stacktrace соответственно.

Задачи, процессы и сигналы

  • [] current_task
  • [] task_local_storage
  • [] disable_sigint
  • [] reenable_sigint
  • [] process_exited
  • [] process_running

Потоки

  • [] redirect_stderr
  • [] redirect_stdin
  • [] redirect_stdout
  • [x] nb_available -> bytesavailable (# 25634)

Было бы неплохо иметь более общую функцию перенаправления IO -> IO в которой все это можно было бы объединить, например, redirect(STDOUT, io) , тем самым удалив как подчеркивания, так и экспорт.

Продвижение

  • [] promote_rule
  • [] promote_shape
  • [] promote_type

См. # 23999 для соответствующего обсуждения promote_rule .

Печать

  • [x] print_with_color -> printstyled (см. # 25522)
  • [] print_shortest (см. № 25745)
  • [] escape_string (см. # 25620)
  • [] unescape_string

escape_string и unescape_string немного странны в том смысле, что они могут печатать в поток или возвращать строку. См. # 25620 для предложения переместить / переименовать их.

Загрузка кода

  • [] include_dependency
  • [] include_string

include_dependency . Используется ли это вообще вне базы? Я не могу представить себе ситуацию, когда вы бы хотели это вместо include в любом типичном сценарии.

include_string . Разве это не просто официально санкционированная версия eval(parse()) ?

То, что я не стал категоризировать

  • [x] gc_enable -> GC.enable (# 25616)
  • [] get_zero_subnormals
  • [] set_zero_subnormals
  • [] time_ns

get_zero_subnormals и set_zero_subnormals можно использовать с более информативными именами. Их нужно экспортировать?

+1 за method_exists => methodexists и object_id => objectid . Также глупо, что catch_stacktrace вообще существует. Его можно объявить устаревшим согласно его определению, stacktrace(catch_backtrace()) .

Как мы относимся к уменьшению подчеркивания C_NULL ? Я уже привык к этому, но я также согласен с аргументом, что ни одно из других имен C* не имеет подчеркивания.

Остальные имена C - это типы, а C_NULL - константа. Я думаю, что это хорошо, и следует соблюдать правила именования.

и следует правилам именования.

Как же так?

Константы часто заключаются в заглавные буквы с подчеркиванием - за ними следует C_NULL . Как сказал @ iamed2 , это значение, а не тип, поэтому соглашение об именах Cfoo не обязательно применяется.

Я ошибочно думал, что https://github.com/JuliaLang/julia/blob/master/doc/src/manual/variables.md#stylistic -conventions ссылается на константы, но это не так. Вероятно, так и должно быть.

Я предлагаю последовательный, математически обоснованный интерфейс для общих гильбертовых пространств, в которых векторы не являются массивами Жюлиа. Имена функций, такие как vecdot , vecnorm и т. Д., Вполне могут быть заменены общими концепциями inner и norm как описано в https: // github. com / JuliaLang / julia / issues / 25565.

Как я уже говорил несколько раз, это не общая проблема для вещей, которые нужно изменить.

Я считаю, что единственные элементы, оставшиеся под этим зонтиком для версии 1.0, - это № 25501 и № 25717.

Я бы хотел что-нибудь сделать с (get|set)_zero_subnormals но, возможно, лучшее краткосрочное решение - просто их не экспортировать.

Вероятно, следует рассмотреть то, как числа обрабатываются в контексте операций сбора, таких как map и collect . Было указано, что первый возвращает скаляр, а второй возвращает массив 0D.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги