Julia: Пользовательские инфиксные операторы

Созданный на 17 июн. 2016  ·  65Комментарии  ·  Источник: JuliaLang/julia

На странице https://groups.google.com/forum/#!topic/julia -dev/FmvQ3Fj0hHs обсуждается создание синтаксиса для пользовательских инфиксных операторов.

...

Отредактировано, чтобы добавить примечание: @johnmyleswhite указал, что ветка комментариев ниже является приглашением к велопрогулке. Пожалуйста, воздержитесь от новых комментариев, если вы не хотите добавить что-то действительно новое. Ниже представлено несколько предложений, помеченных смайликами "ура" (взрывающийся конус). Вы можете использовать эти значки, чтобы пропустить обсуждение и просто прочитать предложения, или найти различные предложения, чтобы вы могли проголосовать "большой палец вверх" или "большой палец вниз".

Голоса за/против этой ошибки в целом касаются того, считаете ли вы, что у Джулии должна быть какая-то пользовательская инфиксная идиома. Проголосовать за конкретную идею ниже следует в первом комментарии @Glen-O. (Ошибка получила 3 ​​отрицательных голоса и 1 положительный, прежде чем это было выяснено.)

...

Первоначальное предложение (только исторический интерес):

Предложение, которое, кажется, победило:

    a |>op<| b #evaluates (in the short term) and parses (in the long term) to `op(a,b)`

Для того, чтобы эта работа работала, необходимы лишь незначительные изменения:

  • Поместите приоритет <| выше приоритета |> вместо того, чтобы быть одинаковым.
  • Создайте группу <| слева направо.
  • Сделайте функцию <|(a,b...)=(i...)->a(i...,b...) . (как указано в ветке обсуждения, это будет иметь автономное использование, а также его использование в приведенной выше идиоме)

По желанию:

  • создайте новые функции >|(a...,b)=(i...)->b(a...,i...) и |<(a,b...)=a(b...) с соответствующими приоритетами и группировкой.

    • Pipe first означает оценку, а pipe last поддерживает ее как функцию, а > и < указывают, какая из них является функцией.

  • создайте новые функции >>|(a...,b)=(i...)->b(i...,a...) и <<|(a,b...)=(i...)->a(b...,i...) с соответствующим приоритетом и группировкой.
  • создать синонимы » , и (или) pipe для |> ; « , и (или) rcurry за <| ; и(или) lcurry за <<| ; с односимвольными синонимами, работающими как инфиксные операторы.
  • создайте макрос @infix в базе, который выполняет первое исправление синтаксического анализатора ниже.

Долгосрочные:

  • научите синтаксический анализатор заменять a |>op<| b на op(a,b) , чтобы при выполнении кода не возникало дополнительных накладных расходов, и чтобы операторы действительно могли быть определены в инфиксной позиции. (Это похоже на то, как синтаксический анализатор в настоящее время по-разному обрабатывает двоичные a:b и троичные a:b:c . Для максимальной настраиваемости он должен делать это для совпадающих синонимов, но не для несовпадающих синонимов, так что, например, a |> b « c по-прежнему будет рассматриваться как два бинарных оператора.)
  • научите синтаксический анализатор понимать запятые и/или пробелы, чтобы многоточия в приведенных выше определениях работали должным образом без лишних скобок.

(относится к https://github.com/JuliaLang/julia/issues/6946)

parser speculative

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

Стефан не старше меня.

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

Повторяя ветку julia-dev, я думаю, было бы полезно процитировать основной комментарий Стефана по этому предложению:

Просто чтобы задать ожидания, я не думаю, что до Джулии 1.0 будет много «синтаксических инноваций». (Единственное исключение, о котором я могу думать, это новый синтаксис векторизованных вызовов f.(v) .) Хотя иметь какой-то способ заставить произвольные функции вести себя как инфиксные операторы может быть неплохо, это просто не насущная проблема в языке.

Как человек, который участвовал в значительной части истории развития Джулии, я думаю, что было бы лучше сосредоточить усилия на семантических изменениях, а не на синтаксических. Осталось решить множество чрезвычайно важных семантических проблем, прежде чем Джулия достигнет версии 1.0.

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

Я думаю, что комментарии johnmyleswhite очень уместны в отношении предлагаемых «долгосрочных» изменений парсера. Но группы «незначительные изменения» и «необязательные», насколько я понимаю, довольно автономны и малоэффективны.

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

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

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

На самом деле я бы подумал о том, чтобы вернуть | для этого и использовать a |op| b . Возможно, общий инфиксный синтаксис более важен, чем побитовое или. (Мы уже говорили об исправлении побитовых операций раньше; они и так кажутся пустой тратой синтаксиса.)

a f b доступен вне синтаксиса объединения массивов и вызова макросов.

a f b может работать, но кажется довольно хрупким. Представьте, что вы пытаетесь объяснить кому-то, почему a^2 f b^2 f c^2 является законным, а a f b c и a+2 f b+2 f c+2 — нет. (Я знаю, что последний предполагает, что приоритет является предварительным временем, но независимо от того, каков приоритет, этот общий вид вещей вызывает беспокойство).

Что касается a |op| b : изначально я поддерживал аналогичное предложение, a %op% b , как вы можете видеть в ветке групп Google. Но хорошая вещь в предложенных |> и <| заключается в том, что каждый из них по отдельности полезен как бинарные операторы, и они естественным образом комбинируются для работы по желанию (то есть при правильном приоритете и группировке). ) Это означает, что вы можете реализовать это в краткосрочной перспективе, используя существующие механизмы парсера, и, таким образом, избежать головной боли для разработчиков парсеров в будущем, как я сказал в своем ответе johnmyleswhite выше.

Так что, хотя мне нравится a |op| b и, конечно же, я бы не возражал против этого, я думаю, что мы должны искать способ использовать два разных оператора для упрощения необходимых изменений парсера. Если мы стремимся к максимальной типизации и не возражаем против того, чтобы | означало «трубу», а не «побитовое или», то как насчет a |op\\ b или a |op& b ?

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

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

Как разработчик парсера я с этим однозначно согласен.

|> и <| — вполне хорошие инфиксные операторы, но реализация общего синтаксиса оператора с использованием двух других операторов не дает никаких преимуществ. И еще многое нужно сказать о том, насколько многословен и непривлекателен этот синтаксис.

нет никакой пользы от реализации общего синтаксиса операторов с использованием двух других операторов.

Чтобы было ясно, долгосрочное видение здесь таково, что будут двоичные f <| y , двоичные x |> f и троичные x |> f <| z , где первый является просто функцией, а второй два реализованы как преобразования в синтаксическом анализаторе.

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

И еще многое нужно сказать о том, насколько многословен и непривлекателен этот синтаксис.

Это справедливое замечание. Как насчет замены |> и <| на | и & ? Они имеют смысл как в паре, так и по отдельности, хотя могут немного раздражать хоккеиста.

Кража | и & для этого не будет хорошим распределением ASCII, и я подозреваю, что многие предпочли бы, чтобы разделители были симметричными.

Если людям нужен тернарный оператор x |> f <| y по другим причинам, это нормально, но я думаю, что его следует рассматривать отдельно. Я не уверен, что синтаксический анализатор должен преобразовывать |> в перевернутый <| . Другие подобные операторы, такие как < , так не работают. Но это тоже отдельная тема.

Кража обоих | и & для этого не было бы хорошим распределением ASCII, и я подозреваю, что многие предпочли бы, чтобы разделители были симметричными.

В ПОРЯДКЕ.

Я понимаю, что > и < сложно напечатать. С точки зрения симметрии и возможности набора текста на стандартной клавиатуре, я думаю, самым простым может быть что-то вроде &% и %& , но это очень уродливо, независимо от того, параллельна R или нет. /| и |/ тоже стоит рассмотреть.

...

Я не уверен, что синтаксический анализатор должен преобразовывать |> в перевернутый <|

Я думаю, вы неправильно поняли. a |> b должен анализироваться до b(a) . (Версия без специального синтаксического анализа будет ((x,y)->y(x))(a,b) , которая оценивает то же самое, но с большими накладными расходами.)

a |> b должен анализироваться как b(a)

А, хорошо, понял.

Я думаю, что мы могли бы годами спорить о том, каких персонажей использовать. Я бы доверил @StefanKarpinski (как самому высокопоставленному лицу в этом разговоре) вынести решение, и меня бы это устроило. Даже если это то, против чего я выступал (например, a f b .)

Вот несколько вариантов, чтобы увидеть, что нравится:
a |>op<| b (оставив текущий |> без изменений)
a |{ op }| b (близкое и одинаковое состояние сдвига на многих обычных клавиатурах, не слишком уродливо. Немного странно, как автономные.)
a \| op |\ b или a /| op |/ b или их комбинации
a $% op %$ b (относительно типизируемый, R-вдохновленный. Но довольно уродливый.)
a |% op %| b
a |- op -| b
a |: op :| b
a | op \\ b
a | op ||| b
a op b

Стефан не старше меня.

Похоже, вы только что номинировали себя на полномочия BDFL по этому вопросу! ;)

a @op@ b ?

Я предполагаю, что мой голос состоит в том, чтобы использовать все 4 из \| , |\ , /| и |/ . Вниз для оценки, вверх для каррирования; бар в сторону функции. Так:
a \| f (или f |/ a ) -> f(a)
a /| f (или f |\\ a ) -> (b...)->f(a,b...)
f |\ b (или b //| f ) -> (a...)->f(a...,b)
и поэтому:
a \| f |\ b (или a /| f |/ b ) -> f(a,b)
a \| f |\ b |\ c (или a /| b /| f |/ c ) -> f(a,b,c)

Каждый из 4 основных операторов, за исключением, возможно, |/ , полезен сам по себе. Избыточность, безусловно, была бы не питонической, но я думаю, что логическая аккуратность — юлианская. И с практической точки зрения вы можете использовать любую версию инфиксной идиомы, которую вам легче набирать; они оба одинаково удобочитаемы, поскольку, изучив один, вы, естественно, понимаете оба.

Очевидно, было бы одинаково разумно поменять местами все косые черты, чтобы стрелки вверх были для оценки, а стрелки вниз — для каррирования.

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

Мы стараемся избегать BDFL, насколько это возможно :)

Я просто подумал, что хотел бы отметить несколько быстрых вещей.

Во-первых, другое преимущество («автономное использование») предлагаемой нотации заключается в том, что <| можно использовать в других контекстах, что улучшает читабельность. Например, если у вас есть массив строк, A , и вы хотите дополнить их все слева до 10, прямо сейчас вам нужно написать map(i->lpad(i,10),A) . Это относительно трудно читать. С этим обозначением оно становится map(lpad<|10,A) , что, я думаю, вы согласитесь, значительно чище.

Во-вторых, идея, стоящая за этим, состоит в том, чтобы сохранить согласованность обозначений. Уже существует оператор |> , который позволяет изменить «исправление» вызова функции с префикса на постфикс. Это просто расширяет нотацию.

В-третьих, возможность использования прямого инфикса как a f b имеет большую проблему. a + b и a * b должны иметь одинаковый приоритет, поскольку + и * являются именами функций, и система не может имеют переменный приоритет. Это, иначе ему пришлось бы по-другому относиться к существующим инфиксным операторам, что могло бы вызвать путаницу.

Например, если у вас есть массив строк, A , и вы хотите дополнить их все слева до 10, прямо сейчас вам нужно написать map(i->lpad(i,10),A) . Это относительно трудно читать. С этим обозначением оно становится map(lpad<|10,A) , что, я думаю, вы согласитесь, значительно чище.

Я категорически не согласен. Предлагаемый синтаксис - простите меня - ASCII-салат, граничащий с некоторыми из худших нарушений Perl и APL, не имеющий прецедентов в других языках, чтобы дать случайному читателю ключ к пониманию того, что происходит. Текущий синтаксис, хотя и на несколько символов длиннее (пять?), довольно понятен любому, кто знает, что i->expr — это лямбда-синтаксис, который присутствует в большом и растущем наборе языков.

a + b и a * b в конечном итоге должны иметь одинаковый приоритет, поскольку + и * являются именами функций, и для системы было бы невозможно иметь переменный приоритет. Это, иначе ему пришлось бы по-другому относиться к существующим инфиксным операторам, что могло бы вызвать путаницу.

Я не думаю, что это реальная проблема; мы можем просто сказать, каков приоритет инфикса a f b , а также сохранить все существующие уровни приоритета. Это работает, потому что приоритет определяется именем функции; любая функция с именем "+" будет иметь приоритет "+".

Да, мы уже сделали это для синтаксиса 1+2 in 1+2 , и это не было проблемой.

Я не думаю, что это реальная проблема; мы можем просто сказать, каков приоритет инфикса afb, а также сохранить все существующие уровни приоритета. Это работает, потому что приоритет определяется именем функции; любая функция с именем "+" будет иметь приоритет "+".

Я не имел в виду, что сложно написать синтаксический анализатор, чтобы он работал. Я имел в виду, что это приводит к проблемам согласованности, поэтому я сказал, что «или ему придется по-разному относиться к существующим инфиксным операторам, что может вызвать путаницу». Среди прочего, обратите внимание на то, что ¦ и не сильно различаются по концепции, но один из них является предопределенным инфиксным оператором, а другой — нет.

Я категорически не согласен. Предлагаемый синтаксис - простите меня - ASCII-салат, граничащий с некоторыми из худших нарушений Perl и APL, не имеющий прецедентов в других языках, чтобы дать случайному читателю ключ к пониманию того, что происходит. Текущий синтаксис, хотя и на несколько символов длиннее (пять?), довольно понятен любому, кто знает, что i->expr — это лямбда-синтаксис, который присутствует в большом и растущем наборе языков.

Возможно, я должен быть более ясным в том, что я говорю. Я говорю, что возможность описать операцию как "lpad на 10" намного понятнее, чем это делает i->lpad(i,10) . И, на мой взгляд, lpad<|10 ближе всего к этому можно добраться в не зависящей от контекста форме.

Может быть, это поможет, если я опишу, откуда я родом. Я в первую очередь математик и математический физик, и "лямбда-синтаксис", хотя и разумный с точки зрения программирования, не самый понятный для тех, кто менее опытен в программировании. Насколько я понимаю, Julia в первую очередь нацелена на то, чтобы быть языком научных вычислений, отсюда и сильное сходство с MATLAB.

Я должен спросить - как lpad<|10 больше "ASCII салат", чем, скажем, x|>sin|>exp ? Тем не менее, была добавлена ​​нотация |> . Сравните, скажем, со сценарием bash, где | используется для передачи аргумента слева команде справа — если вы знаете, что это называется «конвейер», это имеет _немного_ больше смысла, но если вы Вы не умеете программировать, смысла нет. В этом отношении |> на самом деле имеет больше смысла, так как это смутно похоже на стрелку. И тогда <| является естественным расширением нотации.

Сравните с некоторыми другими предложениями, такими как %func% , у которого _есть_ есть прецедент в другом языке, но который совершенно непрозрачен для людей, не обладающих обширными знаниями в области программирования на этом языке.

Имейте в виду, я немного оглянулся на одно из старых обсуждений и вижу, что в другом языке использовалась нотация, которая теоретически была бы довольно хорошей. Очевидно, Haskell использует a |> b c d для представления b(a,c,d) . Если бы пробелы, следующие за именем функции, позволяли вам указывать «параметры» таким образом, это работало бы прекрасно — map(lpad 10,A) . Единственная проблема возникает с унарными операторами — например, map(+ 10,A) вызовет ошибку, так как интерпретируется как «+10» вместо i->+(i,10) .

На a f b : проблемы с приоритетом могут быть не такими серьезными, как предположил Глен-О, но если пользовательские инфиксные функции не имеют самого низкого приоритета, они существуют. Скажем, ради спора мы даем им прек-разы. В этом случае,
a^2 f b^2 => f(a^2,b^2)
a+2 f b+2 => a+f(2,b)+2
a^2 f^2 b^2 => (f^2)(a^2,b^2)
a f+2 b => синтаксическая ошибка?

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

О пользе идиомы карри
Я согласен с Глен-О, что (i)->lpad(i,10) просто хуже, чем lpad<|10 (или, если мы так выберем, lpad |\ 10 или что-то в этом роде). i — это совершенно лишнее когнитивное бремя и потенциальный источник ошибок; на самом деле, я клянусь, что когда я печатал это только что, я непреднамеренно сначала набрал (i)->lpad(x,10) . Итак, мне кажется хорошей идеей иметь операцию карри с инфиксом.
Однако, если это намерение, то на какой бы инфиксной идиоме мы ни остановились, мы можем создать свою собственную операцию карри. Если это a f b , то что-то вроде lpad rcurry 10 будет в порядке. Дело в удобочитаемости, а не в нажатиях клавиш. Поэтому я думаю, что это всего лишь слабый аргумент в пользу <| .

На a |> b c d
Мне очень нравится это предложение. Я думаю, мы могли бы сделать так, чтобы |> принимало пробелы с обеих сторон, поэтому a b |> f c d => f(a,b,c,d) .

(Примечание: если и мое предложение a b |> f c d , и предложение Glen-O map(lpad 10,A) , это действительно создает крайний случай: (a b) |> f c d => f((x)->a(x,b),c,d) . Но я думаю это терпимо)

Это все еще имеет те же проблемы с точки зрения приоритета оператора, что и a f b . Но почему-то я думаю, что они более терпимы, если вы можете хотя бы говорить о них с точки зрения приоритета оператора |> , а не приоритета тернарного оператора с .

Попробуйте lpad.(["foo", "bar"], 10) на 0.5. Существующий |> нравится далеко не всем.

@tkelman : я вижу проблему, но что ты хочешь сказать? Вы думаете, что мы должны исправить существующий |> , прежде чем мы добавим для него дополнительные применения? Если да, то как?

Я лично считаю, что мы должны избавиться от существующих |> .

Попробуйте lpad.(["foo", "bar"], 10) на 0.5. Существующий |> нравится далеко не всем.

Я думаю, вы упустили суть. Да, нотация func.() удобна и в некоторых ситуациях обходит проблему. Но я использую функцию map в качестве простой демонстрации. Любая функция, которая принимает функцию в качестве аргумента, выиграет от этой настройки. В качестве примера, исключительно для демонстрации моей точки зрения, вы можете захотеть отсортировать некоторые числа на основе их наименьшего общего кратного с некоторым ссылочным номером. Что выглядит аккуратнее и легче читается: sort(A,by=i->lcm(i,10)) или sort(A,by=lcm 10) ?

Я хотел бы еще раз отметить, что любой способ определения инфиксных операторов позволит создать оператор, который делает то, что Глен-О хочет, чтобы <| делал, так что в худшем случае он сможет написать что-то вроде sort(A,by=lcm |> currywith 10) . Цель этой страницы - обсудить, как сделать несколько a ... f ... b => f(a,b) . Я понимаю, что существующие операторы |> или предложенные <| имеют какое-то отношение к этому вопросу, но давайте постараемся не слишком отвлекаться.

Лично я считаю предложение a |> b c лучшим на данный момент. Это следует существующему соглашению Haskell; он логически связан с существующим оператором |> ; он достаточно удобочитаем и достаточно прост для ввода. Тот факт, что я чувствую, что это естественным образом распространяется и на другие виды использования, является второстепенным. Если вы не согласны, пожалуйста, хотя бы упомяните о своих чувствах к основной идиоме, а не только к предлагаемым вторичным употреблениям.

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

Я согласен, что сложно определить приоритет для a f b . Например, in явно выигрывает от приоритета сравнения, но вполне вероятно, что многим функциям, используемым в качестве инфикса, не нужен приоритет сравнения. Однако я не вижу никаких проблем с согласованностью. Разные операторы имеют разный приоритет. Добавление a f b не заставляет нашу руку давать + и * с одинаковым приоритетом.

Обратите внимание, что |> уже имеет приоритет рядом со сравнением. Для любого другого приоритета, честно говоря, я думаю, что круглые скобки подходят.

Если вы со мной не согласны, и если бы мы использовали a |> f b , то могли бы быть аналогичные операторы |+> , |*> и |^> , которые работал так же, как |> , но имел приоритет своего центрального оператора. Я думаю, что это излишество, но это возможно.

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

Связанные обсуждения: https://github.com/JuliaLang/julia/issues/554 , https://github.com/JuliaLang/julia/issues/5571 , https://github.com/JuliaLang/julia/pull/14476 , https://github.com/JuliaLang/julia/issues/11608 и https://github.com/JuliaLang/julia/issues/15612.

Я должен спросить - чем lpad<|10 больше "ASCII салат", чем, скажем, x|>sin|>exp? Тем не менее, было добавлено обозначение |>.

Я представляю, что @tkelman спорит

мы должны избавиться от существующего |>.

отчасти потому, что _both_ lpad<|10 и x|>sin|>exp отправляются на территорию ASCII-салатов :).

Я думаю, что @toivoh (a f b) с обязательными скобками — лучшее предложение на данный момент.

Относится к https://github.com/JuliaLang/julia/issues/11608 (и, следовательно, также https://github.com/JuliaLang/julia/issues/4882 и https://github.com/JuliaLang/julia/pull /14653): Если (a f b) => f(a,b) , то имеет смысл, если (a <strong i="8">@m</strong> b) => (<strong i="10">@m</strong> a b) . Это позволило бы заменить существующую макрологику специального случая для y ~ a*x+b на обычную (и, следовательно, гораздо более прозрачную) (y @~ a*x+b) .

Кроме того, «требуемые скобки» могут быть предпочтительной идиомой для кратких определений инфиксов. Вместо того, чтобы говорить (используя глупый пример) a + b = string(a) * string(b) , вам будет предложено (инструментами lint или предупреждениями компилятора) сказать (a + b) = string(a) * string(b) . Я понимаю, что на самом деле это не является прямым следствием выбора опции «требуются скобки» для инфикса, но это удобная идиома, которая позволит нам предупредить людей, ошибочно использующих инфикс на LHS, но уволить людей, которые делают это на цель.

В настоящее время я чувствую, что если вы применяете инфикс функции (а не префикс),
тогда это оператор, и он должен выглядеть и действовать как оператор.

И у нас есть поддержка инфиксных операторов, определенных с использованием юникода.
с https://github.com/JuliaLang/julia/issues/552

Я думаю, было бы неплохо, если бы это было расширено, чтобы вы могли добавить ключевые слова, как в первоначальном предложении.
Таким образом, мы могли бы иметь, например, 1 ⊕₂ 1 == 0
Возможность иметь произвольные имена для вашего инфикса кажется немного чрезмерной.

должен выглядеть и действовать как оператор.

Я согласен с тем, что должны быть строгие соглашения об именах для инфиксных операторов. Например: один символ юникода или заканчивается предлогом. Но это должны быть соглашения, которые развиваются органично, а не требования, навязываемые компилятором. Конечно, я не думаю, что #552 — это конец истории; если есть десятки жестко закодированных операторов, должен быть способ добавить больше программно, хотя бы для прототипирования новых функций.

...

Для меня предложение (a f b)(a <strong i="10">@m</strong> b) ) на голову выше остальных предложений в этой ошибке. Я почти готов сделать патч.

(a f b) => f(a,b)
(a f b c d) => f(a,b,c,d)
(a f) => синтаксическая ошибка
(a+2 f+2 b+2) => (f+2)(a+2,b+2)
(t1=a t2=f t3=b) => (t1=f)((t2=a),(t3=b)) (пробел имеет самый низкий приоритет, как в макросах)

...

Было бы неуместно для меня представить патч?

Я не понял последние два случая:

(a+2 f+2 b+2)=>(f+2)(a+2,b+2)
(t1=a t2=f t3=b)=>(t1=f)((t2=a),(t3=b))

Я нахожу синтаксис (a f b c d) очень странным. Поскольку 1 + 2 + 3 можно записать как +(1,2,3) , тогда f(a,b,c) не следует записывать как (a f b f c) ?

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

Я вижу две проблемы с (a f b c d) .

Во-первых, будет трудно читать, когда у вас есть более сложное выражение - одна из причин, по которой скобки могут вызывать разочарование, заключается в том, что часто бывает трудно с первого взгляда определить, какие скобки сочетаются с другими скобками. Вот почему инфиксные и постфиксные ( |> ) операторы желательны в первую очередь. В частности, нравится постфиксация, потому что она позволяет красиво и аккуратно читать слева направо без необходимости каждый раз иметь дело со скобками.

Во-вторых, это не оставляет возможности сделать что-то красивое, например сделать его поэлементным. Насколько я понимаю, f.(a,b) будет нотацией в 0.5, чтобы заставить f действовать поэлементно на своих аргументах с широковещательной передачей. Не будет аккуратного способа сделать то же самое с инфиксной нотацией, если это (a f b) . В лучшем случае это должно быть (a .f b) , что, на мой взгляд, теряет привлекательность симметрии, которую дает .( с .+ и .* .

Сравните, например, случай с желанием использовать пример из Haskell. shashi в #6946 указал на то, что здесь есть эквивалент. В Haskell вы бы написали circle 10 |> move 0 0 |> animate "scale" "ease" . Используя эту нотацию, это становится ((circle(10) move 0 0) animate "scale" "ease") , что не яснее, чем animate(move(circle(10),0,0),"scale","ease") . И если вы хотите скопировать свой круг в несколько мест, используя нотацию |> , у вас может быть circle 10 .|> copy [1 15 50] [3 14 25] . На мой взгляд, это самый аккуратный способ реализовать идею, а затем скобки выполняют свою обычную роль решения вопросов порядка работы.

И, как я уже отмечал, a|>f b c имеет то преимущество, что также имеет естественное расширение, позволяющее использовать ту же нотацию более широко: f b c будет анализироваться как «функция f с установлены параметры b и c ) и, таким образом, будет эквивалентно i->f(i,b,c) . Это позволяет ему работать не только для инфиксации, но и для других ситуаций, когда вы можете захотеть передать функция (особенно встроенная функция) с параметрами (отмечая, что стандарт должен сначала иметь объект функции).

|> также дает понять, какая из них является функцией. Если бы у вас было, скажем, (tissue wash fire dirty metal) , было бы довольно сложно с первого взгляда распознать wash как функцию. С другой стороны, tissue|>wash fire dirty metal имеет большой индикатор, говорящий: « wash — это функция».

Некоторые из последних возражений звучат для меня как фраза «но вы можете злоупотреблять этой функцией!» Мой ответ: конечно можно. Вы уже могли бы написать совершенно нечитаемый код, используя макросы, если бы захотели. Работа синтаксического анализатора состоит в том, чтобы разрешить законное использование; чтобы остановить злоупотребления, у нас есть условности/идиомы, а в некоторых случаях делинтерсы. Конкретно:

Я не понял последние два случая:

Они никоим образом не предназначены для того, чтобы быть примером для подражания; они просто показывают естественные последствия правил приоритета. Я думаю, что оба последних двух примера можно квалифицировать как злоупотребление синтаксисом, хотя (a^2 ಠ_ಠ b^2) => ಠ_ಠ(a^2,b^2) достаточно ясно.

не следует ли f(a,b,c) записывать как (afbfc)

Мое предложение (a f b c d) было, честно говоря, задним числом. Я думаю, что это имеет смысл, и я мог бы привести примеры, где это полезно, но я не хочу откладывать это предложение по этому вопросу, если оно вызывает споры.

[Например:

  • f — «объектный метод» объекта a, вероятно, сложный, использующий b, c и d, возможно, более простой.
  • f - это метод "естественной трансляции", такой как push! ]

Хотя (a f b f c) имело бы смысл, если бы f было похоже + , я думаю, что большинство операторов на самом деле не похожи + , поэтому это не должно быть нашей моделью.

будет трудно читать, когда у вас есть более сложное выражение

Опять же, мой ответ будет: «так что не злоупотребляйте этим».

Скажем, нам нужно каким-то образом написать сложное выражение, например, a / (b + f(c,d^e)) с инфиксом f . В предложении @toivoh это будет a / (b + (c f d^e)) . В использовании, подобном Haskell, это будет a / (b + (c |> f d^e)) или в лучшем случае, если приоритет |> был изменен, чтобы исправить этот конкретный пример, a / (b + c |> f d^e) . Я думаю, что @toivoh здесь так же хорош.

(tissue wash fire dirty metal)

Я думаю, что решение этой проблемы — строгие соглашения об именах для инфиксных операторов. Например, если бы существовало соглашение о том, что инфиксные операторы должны содержать один символ юникода или заканчиваться предлогом или символом подчеркивания, то приведенное выше выражение было бы чем-то вроде (tissue wash_ fire dirty metal) , что настолько ясно, насколько это выражение могло когда-либо надеяться быть .

...

поэлементно

Это обоснованная озабоченность. (a .f b) — плохая идея, потому что ее можно прочитать как ((a.f) b) . Мое первое предложение — (a ..f b) , но оно меня не очень радует.

circle 10 |> move 0 0 |> animate "scale" "ease"

Я использовал jquery, поэтому я определенно вижу преимущество такой цепочки функций. Но я думаю, что это не та же проблема, что и инфиксные операторы. Используя предложение (a f b) , вы могли бы написать вышеприведенное как:

circle 10 |> (move <| 0 0) |> (animate <| "scale" "ease")

... который не так лаконичен, как версия на Haskell, но все же довольно читабелен.

Возможно, это может быть ограничено только тремя вещами внутри () :
(a f (b,c))
.(a f (b,c)) с помощью оператора .(

Наконец, ответ на общий вопрос:

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

Очевидно, что мы все имеем право на свое мнение. (Мне не ясно, относился ли палец вверх к этой части комментария, но если да, то это тройное.)

Но мои контраргументы:

  • У Джулии уже есть десятки инфиксных операторов, многие из которых очень нишевые. Неизбежно, что будет предложено больше. Когда кто-то говорит: «Как вы можете иметь , но не § ?», я скорее отвечу «сделай сам», а не «жди, пока следующая версия не станет широко распространенной».
  • Что-то вроде (a § b) в высшей степени читабельно, а синтаксис достаточно легкий, чтобы учиться на одном или двух примерах.
  • Я не первый, кто поднимает этот вопрос, и не буду последним. Я понимаю, что разработчики языков должны очень скептически относиться к ползучим (неправильным) функциям, потому что, как только вы добавите уродливую функцию, ее практически невозможно исправить позже. Но, как я уже сказал выше, я думаю, что (a f b) достаточно чистый, чтобы вы не пожалели об этом.

Я действительно не уверен в ясности (a f b)

Вот возможный вариант использования:
select((((:emp_id, :last_name) from employee_tbl) where (:city, == ,'indianapolis')) orderby :emp_id));

Это, безусловно, жизнеспособное использование инфиксных функций.
Функция select либо является функцией идентификации, либо отправляет построенный запрос в базу данных.

Это чистый код?
Я просто не знаю.

.(a f b)

Да, это имеет смысл. Но это не очень читабельно.

(a @. f b) более удобочитаем? Потому что макрос @. для включения этого был бы простым однострочником.

[[[Если подумать, если бы мы разрешили инфиксные макросы без скобок, @Glen-O мог бы использовать их, чтобы делать то, что он хочет: circle(10) @> move 0 0 @> animate "scale" "ease" => @> (@> circle(10) move 0 0) animate "scale" "ease" =macro> animate(move(circle(10),0,0),"scale","ease") . Я думаю, что это решение более уродливо, чем (a f b) , но, по крайней мере, оно устранило бы эту общую ошибку, на мой взгляд.]]]

...

select((((:emp_id, :last_name) from employee_tbl) where (:city, = ,'indianapolis')) orderby :emp_id);

Я бы определенно предпочел использовать макрос для «где», чтобы условное выражение не приходилось странно заключать в кавычки. Так:

select((((:emp_id, :last_name) from employee_tbl) <strong i="22">@where</strong> city == 'indianapolis') orderby :emp_id);

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

select((((:emp_id, :last_name) from employee_tbl) <strong i="6">@where</strong> city == 'indianapolis') orderby :emp_id);

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

Если подумать, приоритет в этом выражении — справа налево. Итак, используя инфиксные макросы, это может быть так же:

select((:emp_id, :last_name) <strong i="11">@from</strong> employee_tbl <strong i="12">@where</strong> city == 'NYC' <strong i="13">@orderby</strong> :emp_id)

или даже:

<strong i="17">@select</strong> (:emp_id, :last_name) <strong i="18">@from</strong> employee_tbl <strong i="19">@where</strong> city == 'NYC' <strong i="20">@orderby</strong> :emp_id

Поэтому, хотя мне все еще нравится (a f b) , я начинаю понимать, что инфиксные макросы также являются хорошим ответом.

Вот полное предложение с примерами, за которыми следуют преимущества и недостатки:

основные виды использования:

  • a <strong i="28">@m</strong> b => <strong i="30">@m</strong> a b
  • a <strong i="33">@m</strong> b c => <strong i="35">@m</strong> a b c
  • a <strong i="38">@m</strong> b <strong i="39">@m2</strong> c => <strong i="41">@m2</strong> (<strong i="42">@m</strong> a b) c
  • <strong i="45">@defineinfix</strong> f; a <strong i="46">@f</strong> b => macro f(a,b...) :(f($a,$b...)) end; <strong i="48">@f</strong> a b => f(a,b)

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

  • t1=a <strong i="54">@m</strong> t2=b t3=c => <strong i="56">@m</strong> t1=a t2=b t3=c (хотя это не очень хороший стиль программирования)
  • t1 + a <strong i="59">@m</strong> t2 + b => <strong i="61">@m</strong> t1+a t2+b (хотя это не очень хороший стиль программирования)
  • a b <strong i="64">@m</strong> c => синтаксическая ошибка (??)
  • a <strong i="67">@m</strong> b [c,d] => пожалуйста , не делайте этого, но <strong i="70">@m</strong> a b[c,d] (ETA: нет, с патчем получается <strong i="72">@m</strong> a b ([c,d]) , что, вероятно, лучше.)
  • a <strong i="75">@m</strong> b ([c,d]) => <strong i="77">@m</strong> a b ([c,d])
  • [a <strong i="80">@m</strong> b] => плохой стиль, используйте круглые скобки для уточнения, но [a (<strong i="83">@m</strong> b)] (??)
  • a @> f b => @> a f b => f(a,b)
  • <strong i="90">@outermacro</strong> a b <strong i="91">@m</strong> c d => <strong i="93">@outermacro</strong> a (<strong i="94">@m</strong> b c d)

Преимущества:

  • определять инфиксные макросы, получать инфиксные функции бесплатно (с одноразовыми накладными расходами на оценку макроса. Это не так мало накладно, как магия синтаксического анализатора, но намного лучше, чем дополнительные вызовы функций при каждой оценке)
  • может привести к мощным DSL, как показано в приведенном выше примере, похожем на SQL.
  • Устраняет необходимость в отдельном операторе |> , поскольку это однострочный макрос. Аналогично для <| и остальных предложений @Glen-O.
  • явный, поэтому очень низкий риск случайного использования, в отличие от (a f b)
  • Как показано, макрос @defineinfix позволяет использовать сокращения для функций, а не для макросов.

(Незначительные) Недостатки:

  • приоритет и группировка, кажется, хорошо работают с RtoL в большинстве случаев, но могут быть исключения, для которых потребуются круглые скобки.
  • Я думаю, что a @> f b или даже a <strong i="112">@f</strong> b не так читаемы, как (a f b) (хотя они тоже не так ужасны).

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

эта активность обычно непропорциональна

Мне жаль. Я, наверное, больше всего виноват в том, что попал туда-сюда.

С другой стороны, я думаю, что эта тема явно добилась «полезного» прогресса. Любое из последних предложений (a f b) или [ a @> f b , с a <strong i="10">@f</strong> b определяемым как сокращение] явно превосходит более ранние предложения, такие как a %f% b или a |> f <| b .

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

...

ETA: Я думаю, что эта дискуссия уже достаточно зрелая, чтобы получить тег decision .

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

select((((:emp_id, :last_name) from employee_tbl) where (:city, == ,"indianapolis")) orderby :emp_id));

Лучше написано

sql"SELECT emp_id, last_name FROM employee_tbl WHERE city == 'indianapolis' ORDER BY emp_id"

Нестандартные строковые литералы — очень мощный синтаксис.
Я не могу найти хороших примеров их использования для встраивания DSL.
Но они могут это сделать.

И в этом случае я думаю, что результат намного чище, чем любая инфиксная операция, которую можно определить.
Хотя у него есть накладные расходы, связанные с необходимостью написания собственного микропарсера/токенизатора.


Я действительно не вижу необходимости в теге решения.
Это не имеет ни реализации в виде PR, ни пригодного для использования прототипа.
что позволяет людям проверить это.
В отличие от https://github.com/JuliaLang/julia/issues/5571#issuecomment -205754539 с его 8 пригодными для использования прототипами.

Мое отношение к этому поднимается и опускается каждый раз, когда я читаю ветку. Не думаю, что узнаю, пока не попробую. И сейчас я даже не знаю, для чего бы я его использовал. (В отличие от некоторых определений для |> и <| , которые я использовал в F#)

SQL-подобный синтаксис, правильный инструмент для работы — нестандартные строковые литералы.

Независимо от того, лучше ли SQL делать с NSL, я думаю, что есть уровень DSL, достаточно сложный, чтобы встроенные макросы были очень полезны, но не настолько сложный, чтобы стоило написать свой собственный микропарсер/токенизатор.

сейчас я даже не знаю, для чего бы я его использовал. (В отличие от некоторых определений |> и <|, которые я использовал в F#)

Предложение встроенных макросов позволит людям, среди прочего, создавать свои собственные макросы типа |> или <| , чтобы вы могли использовать его для всего, что вы сделали в F#.

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

Я действительно не вижу необходимости в теге решения.

Раньше я спрашивал, уместно ли мне создавать патч парсера, но никто не ответил. Пока единственное слово по этому поводу:

Я не думаю, что до Джулии 1.0 будет много «синтаксических инноваций».

Что, казалось бы, возражает против того, чтобы сделать патч сейчас, так как он может просто сидеть без дела и немного сгнить. Однако теперь вы говорите, что не стоит принимать решение по этому поводу (включая решение не принимать решение прямо сейчас?), если у нас нет «реализации в виде PR [или] пригодного для использования прототипа».

Что это обозначает? (Что такое PR?) Будет ли работать макрос, использующий символ '@' вместо токена @ , чтобы <strong i="22">@testinline</strong> a '@'f b => @f(a, b) ? Или я должен отправить патч на julia-parser.scm? (На самом деле я сначала начал думать о написании такого патча, и он выглядит так, как будто он должен быть простым, но моя схема очень ржавая.) Нужно ли мне создавать тестовые примеры?

Сейчас в этой ошибке 13 участников. Всего 5 человек проголосовали за одно или несколько предложений и/или проголосовали против самой ошибки, и только один из них (я) сделал это после того, как предложение встроенного макроса было вынесено на обсуждение. Это не вселяет в меня уверенность, что пришло время для прототипирования. Когда число людей, проголосовавших с момента последнего серьезного предложения, составит примерно половину числа участников, я надеюсь, станет ясным какой-то грубый консенсус, и тогда наступит время прототипирования, тестирования и принятия решения (или, как случай может быть, отказ от идеи).

Путем «реализации в качестве PR [или] пригодного для использования прототипа».
Я имею в виду то, с чем можно играть.
Так что можно увидеть, как это ощущается на практике.

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

Если вы сделали PR, его можно было скачать и протестировать.
Проще, если вы реализовали его с помощью макросов
или нестандартные строковые литералы,
его можно было протестировать без сборки julia.

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

Также +1 к тому, чтобы не ходить туда-сюда.

... или, может быть, пакет Infix.jl с макросами и нестандартными строковыми литералами.

Мы определенно достигли точки «рабочий код или GTFO» в этом разговоре.

Хорошо, тогда вот рабочий код: https://github.com/jamesonquinn/JuliaParser.jl

ETA: Должен ли я ссылаться на конкретную фиксацию или приведенная выше ссылка на последний мастер в порядке?

...

(У этого нет никаких удобных макросов, которые, как я ожидаю, вам понадобятся, таких как эквиваленты для |> , <| , ~ и @defineinfix из моего примера выше. Он также не удаляет _deprecate_ ныне бесполезную логику специального случая для ~ или оператора |> . Это просто изменения синтаксического анализатора, чтобы заставить его работать. протестирован базовый функционал, но не все крайние случаи.

...

Я думаю, что нынешний уродливый взлом с ~ показывает, что для такого рода вещей есть очевидный вариант использования. Используя этот патч, вы бы сказали @~ , когда вам нужно поведение макроса; намного чище, без особого случая. Или кто-то серьезно верит, что ~ совершенно уникальна и никто никогда не захочет делать это снова?

Обратите внимание, что патч (это еще не PR, потому что он нацелен на родной загрузочный парсер, но на данный момент схема, которая должна стоять на первом месте с точки зрения PR) более полезна, чем название проблемы здесь. Название проблемы — «пользовательские инфиксные операторы»; патч дает инфиксные макросы, а инфиксные операторы появляются только как побочный эффект.

Патч в его нынешнем виде не является критическим изменением, но я ожидаю, что если это станет планом, следующим шагом будет отказ от существующих в настоящее время ~ и |> , что в конечном итоге приведет к ломающие изменения.

...

Добавлено несколько простых тестов.

11608 был закрыт с довольно четким консенсусом в отношении того, что многие из нас не хотят использовать инфиксные макросы, а единственный текущий случай синтаксического анализа ~ был ошибкой (сделанной на раннем этапе для совместимости с R и без какой-либо другой особо веской причины). Мы намерены объявить его устаревшим и в конечном итоге избавиться от него, просто еще не сделали этого (вместе с работой по модификации API для интерфейса формул в пакетах JuliaStats).

Макросы теперь технически универсальны, но их входные аргументы всегда Expr , Symbol или литералы. Таким образом, они не могут быть расширены до новых типов, определенных в пакетах, как функции (инфиксные или иные). Возможные варианты использования инфиксных макросов лучше обслуживаются макросами DSL с префиксными аннотациями или строковыми литералами.

(Извините, что написал преждевременно, сейчас исправлено.)

В #11608 я вижу несколько отрицательных аргументов:

===

Во что превратится следующее?
...
y = 0.0 @in@ x == 1.0 ? 1 @in@ 2 : 3 @in@ 4

Это обсуждалось в теме:

Именно в таких случаях я всегда использую скобки...

и

тот же прецедент... применить, не будучи макросом: 0.0 in 1 == 1.0 ? 2 in 2 : 3 in 4

===

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

на который (частично) отвечает (и поддерживает) здесь:

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

===

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

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

Тем не менее, это такая же проблема для любого макроса. Если два пакета экспортируют макрос, вы просто не можете иметь оба с «использованием».

Вероятно, это больше проблема с инфиксными макросами? Ну, это зависит от того, для чего люди в конечном итоге используют их:

  • Просто способ иметь пользовательские инфиксные функции. В этом случае они ничем не хуже любой другой функции; рассылка работает нормально.
  • Как способ использовать другие стили программирования, используя такие операторы, как |> и <| , которые @Glen-O обсуждает выше. В этом случае, я думаю, быстро разовьются общие соглашения о том, что означает макрос, с небольшой вероятностью столкновения.
  • Как способ создания DSL специального назначения, как в приведенном выше примере SQL. Я думаю, что они будут использоваться в определенных контекстах, и вероятность столкновения не так уж велика.
  • Для таких вещей, как R ~ . На первый взгляд это выглядит наиболее проблематично; в R ~ используется для нескольких разных целей. Тем не менее, я думаю, что даже там это решаемо, с чем-то вроде:

macro ~(a,b) :(~(:$a, quote($b))) end

Тогда функция ~ может выполнять диспетчеризацию в зависимости от типа LHS, но RHS всегда будет Expr. Такого рода вещи позволили бы сосуществовать основным видам использования, которые он имеет в R (регрессия и построение графиков), то есть правильно выполнять диспетчеризацию, несмотря на то, что они исходят из разных пакетов.

(примечание: приведенное выше было отредактировано. Первоначально я думал, что выражение R, такое как a ~ b + c , использует привязку b и c через ленивую оценку R. Но это не так. t; b и c — это имена столбцов во фрейме данных, которые передаются явно, а не имена переменных в локальной области видимости, которые, таким образом, передаются неявно.)

===

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

Что я и сделал.

===

Макросы теперь технически универсальны, но их входными аргументами всегда являются Expr, Symbol или литералы. Таким образом, они не могут быть расширены до новых типов, определенных в пакетах, как функции (инфиксные или иные).

Это относится к пункту выше. Поскольку инфиксный макрос вызывает определенную функцию, эта функция по-прежнему может быть расширена через диспетчеризацию обычным способом. Поскольку он не вызывает конкретную функцию, он делает что-то структурное/синтаксическое (например, то, что сейчас делает |> ), которое не следует расширять или переопределять. Обратите внимание, что даже если он вызывает функцию, тот факт, что это макрос, все равно может быть полезен; например, он может заключать в кавычки некоторые из своих аргументов или обрабатывать их в обратные вызовы или даже одновременно взаимодействовать с именем и привязкой переменной, чего не может сделать прямой вызов функции.

===

Возможные варианты использования инфиксных макросов лучше обслуживаются макросами DSL с префиксными аннотациями или строковыми литералами.

Как было указано в упомянутой ветке:

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

Например, что более читаемо (и доступно для записи):

select((:emp_id, :last_name) <strong i="8">@from</strong> employee_tbl <strong i="9">@where</strong> city == 'NYC' <strong i="10">@orderby</strong> :emp_id)

или

send(orderby((<strong i="14">@where</strong> selectfrom((:emp_id, :last_name), employee_tbl) city == 'NYC'), :emp_id))

?

===

Ну наконец то:

11608 был закрыт с довольно четким консенсусом

Мне кажется, что голоса разделились довольно поровну, и решающий голос принадлежит тому, кто будет выполнять работу. Что теперь, по крайней мере, частично спорно; Я выполнил работу в JuliaParser, и я был бы готов сделать это в Scheme, если людям понравится эта идея.

Это мой последний пост в этой ветке, если только не будет положительной реакции на мой взломанный juliaparser. Я не намерен навязывать свою волю; просто изложить свою точку зрения.

Я выступаю за инфиксные макросы ( a <strong i="6">@m</strong> b => <strong i="8">@m</strong> a b ). Это не значит, что я не знаю аргументов против. Вот как я бы резюмировал лучший аргумент против:

Особенности языка начинаются с -100. Что предлагают инфиксные макросы, которые могли бы преодолеть это? По самой своей природе с помощью инфиксных макросов невозможно выполнить ничего, чего нельзя было бы сделать с помощью префиксных макросов.

Мой ответ: Julia — это в первую очередь язык для STEM-программистов. Математики, инженеры, статистики, физики, биологи, специалисты по машинному обучению, химики, эконометристы... И я думаю, что большинство из этих людей осознают полезность хороших обозначений. Возьмем пример, с которым я знаком в статистике: добавление независимых случайных величин эквивалентно свертыванию PDF-файлов или даже свертыванию производных CDF, но часто выражение чего-либо с использованием первого может быть на порядок более кратким и понятным, чем второе. .

Инфикс, префикс или постфикс — это в какой-то степени дело вкуса. Но есть и объективные причины для предпочтения инфикса во многих случаях. В то время как префикс и постфикс приводят к неудобоваримым осадкам встречных операторов, подобных тем, которые заставляют программистов на Форте звучать как немецкие политики, или тем, которые заставляют программистов на Лиспе звучать как карикатура Хомского, инфикс помещает операторы в то, что часто является наиболее когнитивным. естественное место, как можно ближе ко всем их операндам. Есть причина, по которой никто не пишет математические статьи на Forth, и почему даже немецкие математики используют инфиксные операторы при написании уравнений.

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

  • (a+b <strong i="18">@choose</strong> b) превосходит binomial(a+b,b) ;
  • score ~ age + treatment превосходит linearDependency(:score, :(age + treatment)) ;
  • domSelect("#logo") @| css "color" "red" @| fadeIn "slow" <strong i="25">@thenApply</strong> addClass "dummy" лучше, чем addOneTimeEventListener(fadeIn(css(domSelect("#logo"),"color","red"),"slow"),"done",(obj,evt)->addClass(obj,"dummy")) .

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

Можно ли сделать это с нестандартными строковыми литералами? Что ж, второй и третий примеры будут работать как NSL. Но проблема с NSL в том, что они дают вам слишком много свободы: если вы не знакомы с конкретной грамматикой, нет никакого способа быть уверенным даже в том, что представляют собой токены NSL, не говоря уже о порядке ее операций. С инфиксными макросами у вас достаточно свободы для выполнения всех приведенных выше примеров, но не настолько, чтобы при чтении «хорошего» кода не было ясно, что такое токены и куда идут подразумеваемые скобки.

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

Теперь, когда <| является правоассоциативным (#24153), работает ли первоначальное предложение a |>op<| b ?

Я сделал пакет для взлома, упомянутого Стивеном в https://github.com/JuliaLang/julia/pull/24404#issuecomment -341570934:

Я не знаю, сколько потенциальных инфиксных операторов это затронет, но я бы очень хотел использовать <~ . Синтаксический анализатор не будет сотрудничать - даже если я тщательно расставлю вещи, он хочет, чтобы a <~ b означало a < (~b) .

У <- аналогичная проблема.

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

Нам потенциально могут потребоваться пробелы в a < ~b ; мы уже добавляли подобные правила. Затем мы могли бы добавить <- и <~ в качестве инфиксных операторов.

Спасибо @JeffBezanson , это было бы здорово! Будет ли это частным случаем или более общим правилом? Я уверен, что есть некоторые детали того, каким должно быть правило, чтобы разрешить больше инфиксных операторов, дать четкий и предсказуемый код и как можно меньше ломать существующий код. В любом случае, я ценю помощь и быстрый ответ. С новым годом!

В случае, если a <~ b будет отличаться от a < ~b , я хотел бы видеть a =+ 1 как ошибку (или хотя бы предупреждение)

Я знаю, что это довольно старая дискуссия, и заданный вопрос был задан довольно давно, но я подумал, что стоит ответить:

Теперь, когда <| является правоассоциативным (#24153), работает ли первоначальное предложение a |>op<| b ?

Нет, к сожалению, |> по-прежнему имеет приоритет. Сделанное обновление делает так, что если вы определяете <|(a,b)=a(b) , то вы можете успешно выполнить a<|b<|c для получения a(b(c)) ... но это другая концепция.

Заморозка на 2 года, комментарий и коммит 2 и 5 дней назад!

См. Документирование настраиваемых бинарных операторов f45b6be

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

Смежные вопросы

Keno picture Keno  ·  3Комментарии

arshpreetsingh picture arshpreetsingh  ·  3Комментарии

i-apellaniz picture i-apellaniz  ·  3Комментарии

ararslan picture ararslan  ·  3Комментарии

wilburtownsend picture wilburtownsend  ·  3Комментарии