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

34

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

Поэтому, если у нас есть функция f (x) = x², есть две альтернативы ее вызову:

  • FP: f x

    Примеры:

    • ML, Ocaml, F #
    • Haskell
    • ЛИСП, Схема (как-то)
  • Non-FP: f(x)

    Примеры:

    • Почти все императивные языки (я знаю, см. Комментарии / ответы)
    • Erlang
    • Scala (также допускает «нотацию оператора» для отдельных аргументов)

Каковы причины "опущения" скобок?

pvorb
источник
9
Чтобы сделать это более запутанным, я имею в виду краткий .
Ден
11
@ Если вы изучите haskell, синтаксис будет одним из самых запутанных: p
Саймон Бергот,
5
Вы понимаете иронию, говоря, что использование скобок было бы более математическим, а затем использование функции возведения в степень в качестве примера?
Jörg W Mittag
12
@ В противном случае вы наносите себе вред, отказываясь от языка из-за его синтаксиса. Конечно, у вас не было проблем с изучением xml или sql (это не языки общего назначения, но они определяют свой собственный синтаксис).
Саймон Берго
7
Я хотел бы отметить, что сценарии оболочки (например, bash) обычно не используют параметры для вызова команд. В PowerShell есть два худших варианта: функции объявляются с круглыми скобками и вызываются без них.
Крис Харпер

Ответы:

59

который кажется более математическим способом

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

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

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

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

add :: Int -> Int -> Int
add x y = x + y

x = add 5 6 -- x == 11
f = add 5
y = f 6 -- y == 11
z = ((add 5) 6) -- explicit parentheses; z == 11

С паренсом вы можете использовать два соглашения: f(5, 6)(не карри) или f(5)(6)(карри). Синтаксис haskell помогает привыкнуть к концепции карри. Вы все еще можете использовать версию без карри, но более болезненно использовать ее с комбинаторами

add' :: (Int, Int) -> Int
add' (x, y) = x + y
u = add'(5, 6) -- just like other languages
l = [1, 2, 3]
l1 = map (add 5) l -- [6, 7, 8]
l2 = map (\x -> add'(5, x)) l -- like other languages

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

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

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

Есть и другие соглашения для применения функции:

  • Lisp: (fx) - префикс с внешними скобками
  • Далее: xf - postfix
Саймон Бергот
источник
7
Незначительный придирка: термины «карри» и «карри», а не «каррификация» и «карри».
Довал
«без карри» Что такое функция без карри в haskell?
Ven
3
@ user1737909 Функция, которая принимает кортеж в качестве аргумента.
Довал
1
@Doval На самом деле это должны быть «schönfinkeling» и «schönfinkeled». Ну да ладно, история всегда определяет победителя задним числом.
Profpatsch
Думая о синтаксисе скобки карирования что - то вроде f(a, ,b)или add(a, )или add(, b)будет казаться более гибкими по умолчанию.
Phresnel
25

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

Обратите внимание, что это относится не только к функциональным языкам, например к Smalltalk , одному из первых ОО-языков и языков, вдохновленных им ( Self , Newspeak , Objective-C), но также к Io и языкам, вдохновленным им (Ioke, Seph), пробел используется для вызова методов.

Smalltalk-стиль:

anArray sort
anArray add: 2
aString replace: "a" with: "b"

(В последнем случае название метода replace:with:.)

Io-стиль:

anArray sort
anArray add(2)
aString replace("a", "b")

Scala также позволяет использовать пробелы для вызовов методов:

foo bar(baz)

И опуская скобки, если есть только один аргумент:

foo bar baz

Ruby также позволяет убрать скобки:

an_array.sort
an_array.add 2
a_string.replace "a", "b"

используя скобки, что представляется более математическим способом

На самом деле, нет:

f(x)
sin x
x²
|x|
x!
x + y
xy
½

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

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

Йорг Миттаг
источник
3
Существует также аргумент в пользу экономии редактирования. В частности, функциональные языки обычно поддерживают композицию напрямую. Размещать скобки вокруг функции вместо аргумента имеет смысл. Вы должны сделать это только «один раз»: (например, f. G) x, а не e (f (g (x))). Кроме того, Haskell, по крайней мере, не помешает кому-то делать f (x) вместо f x. Это просто не идиоматично.
номен
18

Скобки для приложения функций - лишь одно из многих седел, с которыми нам пришлось столкнуться Эйлеру. Как и все остальное, математика нуждается в соглашениях, когда есть несколько способов что-то сделать. Если ваше математическое образование распространяется только на нематематический предмет в университете, то вы, вероятно, не слишком знакомы со многими областями, в которых применение функций происходит без каких-либо из этих бессмысленных скобок (например, дифференциальная геометрия, абстрактная алгебра). И вы даже не упомянули функции, которые применяются в инфиксном (почти на всех языках), «нарезаемом», например, принимают норму, или схематично (Befunge, алгебраическая топология).

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

Шон Д
источник
7
Даже не заводите меня f(x)против: sin xпротив, против, |x|против, x!против, x + yпротив, xyпротив ½суммирования, против интеграла, против ...
Йорг Миттаг,
2
+1 за цитату фон Неймана. +1 для остальной части ответа, но я не могу дать +2. :(
Пворб
2
Я уловил намек на элитарность здесь; Я оспариваю нейтральность «дизайнеры функционального языка программирования лучше образованы» .
CaptainCodeman
7
@CaptainCodeman Он говорит более образованным в математике, заявление, с которым я согласен. По крайней мере, когда дело доходит до Хаскелла, вы можете заметить, что обе концепции более математичны по своей природе, а сообщество более математически склонно. Они не умнее, просто лучше математики.
Пол
5
@CaptainCodeman Нет, поскольку он никогда не подразумевал, что они более образованные в целом, просто более образованные в математике. Это именно то, что он сказал: «обширное математическое образование». :)
Пол
8

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

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

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

val message = line split "," map (_.toByte)

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

Карл Билефельдт
источник
2
«Суть в том, что все эти скобки раздражают, когда их читают и отслеживают. Вероятно, это причина номер один, почему LISP не так популярен». их особенно трудно читать, и я считаю, что в других языках синтаксис гораздо более неловкий.
Джорджио
2
Синтаксис LISP в значительной степени компенсирует раздражающие скобки с его сверхвысокой степенью ортогональности. Это просто означает, что у него есть другие полезные функции, которые делают его еще пригодным для использования, а не то, что парни не раздражают. Я понимаю, что не все так считают, но подавляющее большинство программистов, с которыми я встречался, так и делают.
Карл Билефельдт
"Lost In Stupid Parentheses" - мой любимый backronym LISP. Компьютеры также могут использовать гуманный способ задания блоков кода и сбрасывать избыточность, как в таких языках, как Wisp .
Сис Тиммерман
4

Оба предположения неверны.

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

    GHCi> f 3
    4
    GHCi> f (3)
    4
    GHCi> (f) 3
    4

    Конечно, если вы не используете ни пробел, ни паренсы, то обычно это не сработает, просто потому, что выражение неправильно маркировано

    GHCi> f3

    <‌interactive>: 7: 1:
        Недопустимо: «f3»
        Возможно, вы имели в виду «f» (строка 2)

    Но если бы эти языки были ограничены именами переменных из 1 символа, это тоже сработало бы.

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

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

leftaroundabout
источник
Понял меня, вопрос не идеален. :) Таким образом, вывод, который следует сделать: «Круглые скобки не масштабируются».
Пворб
2
Не являются нефункциональные языки, которые единодушны в требовании скобок; для нескольких примеров, Smalltalk имеет object method: args, Objective C имеет [object method: args], и Ruby и Perl оба позволяют опускать круглые скобки в вызовах функций и методов, требуя только разрешения неоднозначности.
Хоббс
1

Небольшое историческое уточнение: оригинальная запись в математике была без скобок !

Обозначение fx для произвольной функции x было введено Йоханом Бернулли около 1700 года, вскоре после того, как Лейбниц начал говорить о функциях (фактически Бернулли написал ϕ x ). Но до этого многие люди уже использовали такие обозначения, как sin x или lx (логарифм x ) для определенных функций x без скобок. Я подозреваю, что это причина, по которой многие люди до сих пор пишут грех х вместо греха (х) .

Эйлер, который был студентом Бернулли, принял Бернулли ϕ x и изменил греческое на латинское письмо, написав fx . Позже он заметил, что было бы легко спутать f с «количеством» и поверить, что fx был продуктом, поэтому он начал писать f: x . Следовательно, обозначение f (x) не принадлежит Эйлеру. Конечно, Эйлер нуждался в круглых скобках по той же причине, по которой нам все еще нужны круглые скобки в функциональных языках программирования: например, чтобы отличить (fx) + y от f (x + y) . Я подозреваю, что люди после Эйлера приняли скобки по умолчанию, потому что они в основном видели, как Эйлер писал составные выражения типа f (ax + b), Он редко писал только FX .

Более подробно об этом см .: Эйлер когда-нибудь писал f (x) с круглыми скобками? ,

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

Майкл Бехтольд
источник