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

16

Идея вдохновлена ​​тем фактом, что операторы фактов, такие как +, -,% и т. Д., Могут рассматриваться как функции с одним или двумя переданными аргументами, без побочных эффектов. Предполагая, что я или кто-то еще пишет язык, который останавливает передачу более двух аргументов, а также работает только через возвращаемое значение:

а) приведет ли такой язык к более легкому пониманию кода?

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

c) ограничения сделали бы язык чрезмерно громоздким для более сложных программ.

d) (бонус) любые другие комментарии о плюсах / минусах

Замечания:

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


источник
4
Здравствуй. Списки за и против имеют тенденцию давать плохие ответы. Есть ли способ перефразировать ваш вопрос, чтобы получить необходимую информацию, но в другом формате?
MetaFight
22
Твои рассуждения даже не имеют смысла для меня. У некоторых функций мало аргументов, поэтому давайте ограничим все функции? Обычно, когда кто-то предлагает произвольные ограничения, есть причина, которую можно получить. Я не вижу, что это могло бы принести вам.
2
Не то чтобы в вопросах «что если» что-то не так (хотя на них иногда трудно ответить, как сказал @MetaFight), но даже если вы, кто задумался над этим и достаточно заботился, чтобы задать вопрос, на самом деле не можете назвать Я уверен, что моя первоначальная реакция «что? нет! это глупо, зачем ты это делаешь» точна.
6
Существует довольно много языков, которые допускают только один аргумент для каждой функции: все, что основано на лямбда-исчислении. В результате , как правило , представляет собой функцию , принимающую один аргумент - список, или функция , возвращающая функцию , которая принимает следующий аргумент , пока все аргументы не будут обработаны: result = f(a)(b)…(z). Это имеет место в семействе языков ML, таких как Haskell, но также концептуально в других языках, таких как Lisp, JavaScript или Perl.
amon
3
@Orangesandlemons: Хорошо, тогда я могу кодировать произвольное число целых чисел в одном целом числе, используя только умножение и сложение (для кодирования) и деление и вычитание (для декодирования). Итак, вам также нужно запретить целые числа или хотя бы умножение, сложение, деление и вычитание. (Одним из следствий возможности программирования является то, что вы можете кодировать практически все, используя практически все, и, таким образом, ограничивать вещи действительно очень сложно. В общем, ограничения на самом деле ничего не «ограничивают», они просто раздражают программистов.)
Йорг W Mittag

Ответы:

40

Роберт К. Мартин в своей книге «Чистый код» настоятельно рекомендует использовать функции с максимальным значением 0, 1 или 2 параметра, поэтому, по крайней мере, есть один опытный автор книги, который считает, что с помощью этого стиля код становится чище (однако он конечно, не абсолютный авторитет здесь, и его мнения спорны).

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

Однако я не думаю, что было бы хорошей идеей изобрести новый язык для этого:

  • если вы действительно хотите применять такое правило в своем коде, вам просто нужен инструмент анализа кода для существующего языка, для этого не нужно изобретать совершенно новый язык (например, для C # может быть использовано что-то вроде 'fxcop') ).

  • иногда объединение параметров с новым типом просто не стоит хлопот, иначе это станет чисто искусственной комбинацией. См., Например, этот File.Openметод из .Net Framework. Он принимает четыре параметра, и я уверен, что разработчики этого API сделали это намеренно, потому что они думали, что это будет наиболее практичным способом предоставления различных параметров функции.

  • иногда существуют сценарии реального мира, в которых более 2 параметров упрощают работу по техническим причинам (например, когда вам требуется сопоставление 1: 1 с существующим API, где вы привязаны к использованию простых типов данных и не можете комбинировать разные параметры в один пользовательский объект)

Док Браун
источник
16
Запах с несколькими параметрами часто разные параметры на самом деле принадлежат друг другу. Возьмем, к примеру, расчет индекса массы тела, ИМТ. Это функция длины и веса человека. f (длина, вес), но эти два параметра действительно связаны друг с другом, потому что вы когда-нибудь делали бы этот расчет с ростом одного человека и весом другого? Таким образом, чтобы представить, что лучше, вы получите f (человек), где человек может иметь вес, длину интерфейса.
Питер Б
@PieterB: конечно, посмотрите мои изменения.
Док Браун
5
Крошечный, крошечный язычок: «может указывать на запах кода» Разве запах кода по определению не является просто указанием на то, что вам следует что-то пересмотреть, даже если вы в конечном итоге не измените код? Таким образом, запах кода не будет "указан". Если конкретный аспект указывает на возможность возникновения проблемы, она является код запах. Нет?
jpmc26
6
@ jpmc26: С другой точки зрения, пахнет кодом - возможная проблема, а не ее указание ;-) Все зависит от точного определения запаха кода (и как только он пахнет, он испортился, не так ли? ?)
hoffmale
3
@PieterB Кто-нибудь на самом деле это делает? Теперь вам нужно создавать новый экземпляр Person каждый раз, когда вы просто хотите рассчитать BMI с двумя произвольными значениями. Конечно, если ваше приложение уже использует Persons для начала, и вы часто делаете что-то вроде f (person.length, person.height), вы можете немного его очистить, но создание новых объектов специально для параметров группы кажется излишним.
Юрист
47

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

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

Это называется Фреге-Шенфинкелинг, Шёнфинкелинг, Шенфинкель-Карри или Карри, после Хаскелла Карри, который исследовал его в 1950-х годах, Моисея Шенфинкеля, который описал его в 1924 году, и Готтлоба Фреге, который предвидел его в 1893 году.

Другими словами, ограничение количества аргументов оказывает абсолютно нулевое влияние.

Йорг Миттаг
источник
2
Конечно, если вы разрешаете возвращать функции. Даже если нет, все, что вам нужно сделать, это вызвать следующую функцию в основной программе. Т.е. у вас может быть 1 + 1 + 1, где первое добавление - это функция, которая возвращает функцию для дополнительного сложения, или ее можно просто вызвать дважды. Последний стиль будет, по идее, чище или я ошибаюсь?
5
«оказывает абсолютно нулевое влияние» - вопрос ОП заключался в том, будет ли читаемость кода увеличиваться или уменьшаться, и я полагаю, что вы не утверждаете, что такое конструктивное решение для языка не влияет на это, не так ли?
Док Браун
3
@DocBrown С приличной мощностью и перегрузкой операторов я могу взять язык карри и сделать его похожим на неиспользуемый язык. Пример: f *(call_with: a,b,c,d,e) перегрузка call_with :для начала цепочки, ,для удлинения цепочки и *для запуска LHS f, передавая каждое содержимое цепочки по одному. Достаточно слабая система перегрузки операторов просто делает синтаксис обременительным, но это вина системной перегрузки операторов больше всего на свете.
Якк
На самом деле, карри Хаскелла сводит функцию с n аргументами к функции с одним аргументом, возвращая другую функцию с n - 1 аргументом.
Райан Райх
2
@RyanReich Вы правы, что видите «призрак» из «количества аргументов» функции Haskell в сигнатуре ее типа. Это скорее призрак, чем настоящая подпись, потому что в общем случае у вас нет возможности узнать, является ли переменная последнего типа в подписи также типом функции. В любом случае, присутствие этого призрака не делает недействительным тот факт, что в Haskell все функции принимают 1 аргумент и возвращают либо нефункциональное значение, либо другую функцию, которая также принимает 1 аргумент. Это встроено в ассоциативность ->: a-> b-> c is a -> (b-> c). Так что вы в основном ошибаетесь здесь.
Ян
7

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

J может быть очень кратким, но, как и его предшественник APL, он также может быть очень загадочным - но это в основном результат цели создателя подражать математической лаконичности. Можно сделать J-программу более читабельной, используя имена, а не символы для создания операторов.

Алфей
источник
ах, так что это позволяет массивам взаимодействовать с собой.
5

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

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

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

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

Корт Аммон - Восстановить Монику
источник
Это лучший ответ.
Джаред Смит
1

Вам понадобятся две вещи:

  • закрытие
  • Составной тип данных

Я добавлю математический пример, чтобы объяснить ответ, написанный Йоргом Миттагом .

Рассмотрим гауссову функцию .

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

На первом шаге мы разработаем гауссову функцию, которая принимает все три параметра, а именно среднее значение, дисперсию и свободную переменную.

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

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

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

Структура поэтому:

  • Оценивать (расчет)
    • ParameterizedGaussian (закрытие: формула плюс несколько связанных переменных)
      • GaussianParameters (составной тип данных)
        • Среднее значение
        • Дисперсия (значение)
    • X (значение свободной переменной)
rwong
источник
1
  1. Практически на любом языке программирования вы можете передать некоторый тип списка, массива, кортежа, записи или объекта в качестве единственного аргумента. Его единственная цель - хранить другие предметы, а не передавать их функции по отдельности. Некоторые Java IDE даже имеют функцию « Извлечь объект параметров », чтобы сделать это. Внутренне Java реализует переменное число аргументов, создавая и передавая массив.

  2. Если вы действительно хотите сделать то, о чем вы говорите, в чистом виде, вам нужно взглянуть на лямбда-исчисление. Это именно то, что вы описываете. Вы можете искать его в Интернете, но описание, которое имело смысл для меня, было в Типах и Языках программирования .

  3. Посмотрите на языки программирования Haskell и ML (ML проще). Они оба основаны на лямбда-исчислении и концептуально имеют только один параметр для каждой функции (если вы немного щурились).

  4. Пункт 2 Джоша Блоха: «Рассмотрим строителя, когда сталкиваемся со многими параметрами конструктора». Вы можете видеть, насколько многословно это получается , но работать с API, написанным таким образом, очень приятно.

  5. Некоторые языки имеют именованные параметры, что является еще одним подходом, позволяющим значительно облегчить навигацию по сигнатурам больших методов. Например, Котлин назвал аргументы .

GlenPeterson
источник