Warum verwenden einige funktionale Programmiersprachen einen Platz für Funktionsanwendungen?

34

Nachdem ich mir einige Sprachen für die funktionale Programmierung angesehen habe, habe ich mich immer gefragt, warum einige fp-Sprachen ein oder mehrere Leerzeichen für die Funktionsanwendung (und -definition) verwenden, während die meisten (alle?) Imperativen / objektorientierten Sprachen Klammern verwenden, wie es scheint sei der mathematischere Weg. Ich denke auch, dass der letztere Stil viel klarer und lesbarer ist als ohne die Parens.

Wenn wir also eine Funktion f (x) = x² haben, gibt es zwei Alternativen, um sie aufzurufen:

  • FP: f x

    Beispiele:

    • ML, Ocaml, F #
    • Haskell
    • LISP, Schema (irgendwie)
  • Nicht-FP: f(x)

    Beispiele:

    • Fast alle imperativen Sprachen (ich kenne die Kommentare / Antworten)
    • Erlang
    • Scala (erlaubt auch "Operator Notation" für einzelne Argumente)

Was sind die Gründe, die Klammern "wegzulassen"?

pvorb
quelle
9
Um es noch verwirrender zu machen, ähm, ich meinte kurz und bündig .
Den
11
@ Wenn Sie Haskell lernen, wird die Syntax eines der am wenigsten verwirrenden Dinge sein: p
Simon Bergot
5
Ist Ihnen die Ironie bewusst, zu sagen, dass die Verwendung von Klammern mathematischer und dann die Potenzierungsfunktion als Beispiel wäre?
Jörg W Mittag
12
@Wenn Sie sich selbst Schaden zufügen, lehnen Sie eine Sprache aufgrund ihrer Syntax ab. Sicherlich hatten Sie keine Probleme beim Erlernen von XML oder SQL (dies sind keine Mehrzwecksprachen, aber sie definieren ihre eigene Syntax).
Simon Bergot
7
Ich möchte darauf hinweisen, dass Shell-Skripte (z. B. Bash) im Allgemeinen keine Parameter zum Aufrufen von Befehlen verwenden. PowerShell hat die schlechteste der beiden Welten darin, dass Funktionen mit runden Klammern deklariert und ohne diese aufgerufen werden.
Kris Harper

Antworten:

59

Das scheint der mathematischere Weg zu sein

funktionale sprachen sind von lambda-kalkül inspiriert . In diesem Feld werden für die Funktionsanwendung keine Klammern verwendet.

Ich denke auch, dass der letztere Stil viel klarer und lesbarer ist als ohne die Parens.

Die Lesbarkeit liegt im Auge des Betrachters. Sie sind es nicht gewohnt, es zu lesen. Es ist ein bisschen wie mit mathematischen Operatoren. Wenn Sie die Assoziativität verstehen, benötigen Sie nur wenige Parens, um die Struktur Ihres Ausdrucks zu verdeutlichen. Oft braucht man sie nicht.

Currying ist auch ein guter Grund, diese Konvention zu verwenden. In haskell können Sie Folgendes definieren:

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

Mit parens können Sie zwei Konventionen verwenden: f(5, 6)(nicht Curry) oder f(5)(6)(Curry). Die haskell-Syntax hilft dabei, sich an das Currying-Konzept zu gewöhnen. Sie können immer noch eine nicht-Curry-Version verwenden, es ist jedoch schmerzhafter, sie mit Kombinatoren zu verwenden

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

Beachten Sie, wie Sie in der zweiten Version dazu gezwungen werden, x als Variable zu registrieren, und dass der Unterausdruck eine Funktion ist, die eine Ganzzahl annimmt und 5 hinzufügt? Die Curry-Version ist viel leichter, wird aber auch von vielen als lesbarer angesehen.

Haskell-Programme verwenden in großem Umfang Teilanwendungen und Kombinatoren, um Abstraktionen zu definieren und zu komponieren. Dies ist also kein Spielzeugbeispiel. Eine gute Funktionsschnittstelle ist eine, bei der die Reihenfolge der Parameter eine benutzerfreundliche Verwendung ermöglicht.

Ein weiterer Punkt: Eine Funktion ohne Parameter sollte mit aufgerufen werden f(). In haskell schreiben Sie einfach, da Sie nur unveränderliche, verzögert ausgewertete Werte manipulieren f, und betrachten dies als einen Wert, der bei Bedarf einige Berechnungen durchführen muss. Da die Auswertung keine Nebenwirkungen hat, ist es nicht sinnvoll, für die parameterlose Funktion und den zurückgegebenen Wert eine andere Notation zu verwenden.

Es gibt auch andere Konventionen für die Funktionsanwendung:

  • Lisp: (fx) - Präfix mit externen Klammern
  • Viertens: xf - postfix
Simon Bergot
quelle
7
Kleiner Nitpick: Die Begriffe sind "currying" und "curried", nicht "currification" und "currified".
Doval
"nicht curried" was ist eine nicht curried Funktion in haskell?
Ven
3
@ user1737909 Eine Funktion, die ein Tupel als Argument verwendet.
Doval
1
@Doval Eigentlich sollte es "schönfinkeling" und "schönfinkeled" sein. Aber na ja, die Geschichte bestimmt den Sieger immer im Nachhinein.
Profpatsch
Wenn man an eine in Klammern gesetzte Currysyntax denkt, scheint etwas wie f(a, ,b)oder add(a, )oder add(, b)standardmäßig flexibler zu sein.
Phresnel
25

Die Grundidee ist, die wichtigste Operation (Funktionsanwendung) am einfachsten zu lesen und am einfachsten zu schreiben. Ein Leerzeichen ist sehr unauffällig zu lesen und sehr einfach zu tippen.

Beachten Sie, dass dies nicht spezifisch für funktionale Sprachen ist, z. B. in Smalltalk , einer der ersten OO-Sprachen und davon inspirierten Sprachen ( Self , Newspeak , Objective-C), sondern auch in Io und von ihm inspirierten Sprachen (Ioke, Seph). Leerzeichen werden für Methodenaufrufe verwendet.

Smalltalk-Stil:

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

(Im letzteren Fall lautet der Name der Methode replace:with:.)

Io-Style:

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

Scala erlaubt auch Leerzeichen für Methodenaufrufe:

foo bar(baz)

Und lassen Sie die Klammern weg, wenn es nur ein Argument gibt:

foo bar baz

Mit Ruby können Sie auch die Klammern weglassen:

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

Verwenden von Klammern, was der mathematischere Weg zu sein scheint

Nicht wirklich:

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

Die mathematische Notation hat sich über eine lange Zeit auf schrecklich inkonsistente Weise entwickelt.

Wenn überhaupt, lassen sich funktionale Sprachen von der λ-Rechnung inspirieren, bei der die Funktionsanwendung mit Leerzeichen geschrieben wird.

Jörg W. Mittag
quelle
3
Es gibt auch ein Argument für die Wirtschaftlichkeit der Bearbeitung. Insbesondere unterstützen funktionale Sprachen typischerweise die Komposition direkt. Das Setzen von Klammern um die Funktion anstelle des Arguments ist sinnvoll. Sie müssen es nur "einmal" tun: (e. F. G) x im Gegensatz zu e (f (g (x))). Zumindest würde Haskell nicht verhindern, dass man f (x) anstelle von f x macht. Es ist einfach nicht idiomatisch.
Nomen
18

Klammern für die Funktionsanwendung sind nur einer von vielen Sätteln, die Euler uns hinterlassen hat. Wie alles andere braucht auch die Mathematik Konventionen, wenn es verschiedene Möglichkeiten gibt, etwas zu tun. Wenn Ihre mathematische Ausbildung nur bis zu einem nicht-mathematischen Fach an der Universität reicht, sind Sie wahrscheinlich nicht mit den vielen Bereichen vertraut, in denen die Funktionsanwendung ohne diese unsinnigen Klammern (z. B. Differentialgeometrie, Abstrakte Algebra) problemlos abläuft. Und Sie erwähnen nicht einmal Funktionen, die infix angewendet werden (fast alle Sprachen), "outfix" wie das Aufnehmen einer Norm oder diagrammartig (Befunge, Algebraic Topology).

Als Antwort würde ich sagen, dass ein viel höherer Anteil von funktionalen Programmierern und Sprachdesignern über eine umfassende mathematische Ausbildung verfügt. Von Neumann sagt: "Junger Mann, in der Mathematik versteht man Dinge nicht. Man gewöhnt sich nur daran."

Sean D
quelle
7
Noch nicht einmal mich auf loszulegen f(x)vs. sin xvs. vs. |x|vs. x!vs. x + yvs. xyvs. ½vs. eine Summe gegen einen integralen gegen ...
Jörg W Mittag
2
+1 für das von Neuman-Zitat. +1 für den Rest der Antwort, aber ich kann nicht +2 geben. :(
pvorb
2
Ich verstehe hier einen Hauch von Elitismus. Ich bestreite die Neutralität von "funktionalen Programmiersprachen, die besser ausgebildet sind" .
CaptainCodeman
7
@CaptainCodeman Er sagt mehr in Mathematik ausgebildet, eine Aussage, der ich zustimme. Zumindest wenn es um Haskell geht, merkt man, dass beide Konzepte mathematischer Natur sind und die Community auch mathematischer ausgerichtet ist. Sie sind nicht intelligenter, nur besser in Mathematik ausgebildet.
Paul
5
@CaptainCodeman Nein, da er nie angedeutet hat, dass sie generell besser ausgebildet sind, sondern nur besser in Mathematik. Genau das sagte er: "Umfangreiche mathematische Ausbildung". :)
Paul
8

Obwohl Simons Antwort viel Wahres enthält, gibt es meiner Meinung nach auch einen viel praktischeren Grund. Die Art der funktionalen Programmierung erzeugt aufgrund der Funktionsverkettung und -zusammensetzung tendenziell viel mehr Klammern als die imperative Programmierung. Auch diese Verkettungs- und Kompositionsmuster lassen sich in der Regel ohne Klammern eindeutig darstellen.

Die Quintessenz ist, dass das Lesen und Verfolgen all dieser Klammern ärgerlich wird. Dies ist wahrscheinlich der Hauptgrund, warum LISP nicht beliebter ist. Wenn Sie also die Möglichkeiten der funktionalen Programmierung nutzen können, ohne übermäßige Klammern zu verwenden, werden Sprachdesigner wahrscheinlich dazu tendieren, diesen Weg zu gehen. Schließlich sind Sprachdesigner auch Sprachnutzer.

Sogar Scala ermöglicht es dem Programmierer, unter bestimmten Umständen Klammern wegzulassen, die beim Programmieren in einem funktionalen Stil häufig vorkommen, sodass Sie sozusagen das Beste aus beiden Welten herausholen. Beispielsweise:

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

Die Parens am Ende sind für die Assoziativität erforderlich, und die anderen sind (ebenso wie die Punkte) weggelassen. Durch diese Schreibweise wird die Verkettung der von Ihnen ausgeführten Vorgänge betont. Es mag einem imperativen Programmierer nicht vertraut sein, aber einem funktionalen Programmierer erscheint es sehr natürlich und fließend, auf diese Weise zu schreiben, ohne dass Syntax eingefügt werden muss, die dem Programm keine Bedeutung hinzufügt, sondern nur dazu da ist, den Compiler glücklich zu machen .

Karl Bielefeldt
quelle
2
"Die Quintessenz ist, dass das Lesen und Verfolgen all dieser Klammern ärgerlich wird. Dies ist wahrscheinlich der Hauptgrund, warum LISP nicht beliebter ist." Sie sind besonders schwer zu lesen, und ich finde, dass andere Sprachen eine viel umständlichere Syntax haben.
Giorgio
2
Die LISP-Syntax macht die lästigen Klammern mit ihrer überaus hohen Orthogonalität weitgehend wett. Das bedeutet nur, dass es andere Einlösungsfunktionen hat, die es noch benutzbar machen, nicht, dass die Parens nicht ärgerlich sind. Ich verstehe, dass nicht jeder so denkt, aber die große Mehrheit der Programmierer, die ich getroffen habe, tut es.
Karl Bielefeldt
"Lost In Stupid Parentheses" ist mein Lieblings-LISP-Backronym. Computer können auch auf humane Weise Codeblöcke angeben und die Redundanz aufheben, wie dies in Sprachen wie Wisp der Fall ist .
Cees Timmerman
4

Beide Prämissen sind falsch.

  • Diese funktionalen Sprachen belegen keinen Platz für Funktionsanwendungen. Sie analysieren einfach alle Ausdrücke, die nach einer Funktion als Argument stehen.

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

    Natürlich funktioniert es normalerweise nicht, wenn Sie weder ein Leerzeichen noch Parens verwenden, nur weil der Ausdruck nicht richtig mit einem Token versehen ist

    GHCi> f3

    <‌interactive>: 7: 1:
        Nicht im Geltungsbereich: 'f3'
        Vielleicht hast du 'f' gemeint (Zeile 2)

    Wenn diese Sprachen jedoch auf 1-stellige Variablennamen beschränkt wären, würde dies auch funktionieren.

  • Auch mathematische Konventionen erfordern normalerweise keine Klammern für die Funktionsanwendung. Mathematiker schreiben nicht nur oft sin x - für lineare Funktionen (normalerweise werden sie dann lineare Operatoren genannt) ist es auch sehr üblich, das Argument ohne Parens zu schreiben, vielleicht, weil (ähnlich wie bei jeder Funktion in der funktionalen Programmierung) lineare Funktionen oft ohne direkte Versorgung behandelt werden ein Argument.

Ihre Frage lautet also wirklich: Warum sind für manche Sprachen Klammern für die Funktionsanwendung erforderlich? Nun, anscheinend halten einige Leute, wie Sie, dies für lesbarer. Ich stimme dem überhaupt nicht zu: Ein Paar Parens ist nett, aber sobald Sie mehr als zwei von ihnen ineinander verschachtelt haben, wird es verwirrend. Lisp-Code demonstriert dies am besten, und so gut wie alle funktionalen Sprachen würden ähnlich überladen aussehen, wenn Sie überall Parens benötigen würden. Dies geschieht nicht so häufig in imperativen Sprachen, aber hauptsächlich, weil diese Sprachen nicht ausdrucksstark genug sind, um prägnante, klare Einzeiler zu schreiben.

links herum
quelle
Verstanden, die Frage ist nicht perfekt. :) Die Schlussfolgerung zum Zeichnen lautet also: "Klammern skalieren nicht."
Pvorb
2
Es gibt auch keine nicht funktionalen Sprachen , die einmütig Klammern verlangen. In Smalltalk object method: argsund Objective C [object method: args]sowie in Ruby und Perl können Klammern in Funktions- und Methodenaufrufen weggelassen werden, sodass nur Mehrdeutigkeiten behoben werden müssen.
Hobbs
1

Eine kleine historische Klarstellung: Die ursprüngliche Notation in der Mathematik war ohne Klammern !

Die Notation fx für eine beliebige Funktion von x wurde von Johan Bernoulli um 1700 eingeführt, kurz nachdem Leibniz angefangen hatte, über Funktionen zu sprechen (tatsächlich schrieb Bernoulli ϕ x ). Zuvor verwendeten viele Leute jedoch bereits Notationen wie sin x oder lx (den Logarithmus von x ) für bestimmte Funktionen von x ohne Klammern. Ich vermute, das ist der Grund, warum viele Menschen heute noch sin x anstelle von sin (x) schreiben .

Euler, der ein Schüler von Bernoulli war, adoptierte Bernoullis ϕ x und wandelte das Griechische in einen lateinischen Buchstaben um, indem er fx schrieb . Später bemerkte er, dass es leicht sein könnte, f für eine "Menge" zu verwechseln und zu glauben, dass fx ein Produkt sei, und begann, f: x zu schreiben . Daher ist die Notation f (x) nicht auf Euler zurückzuführen. Natürlich benötigt Euler Klammer aus dem gleichen Grund noch wir Klammer in funktionalen Programmiersprachen müssen: zum Beispiel zu unterscheiden (fx) + y von f (x + y) . Ich vermute, dass die Leute nach Euler die Klammer als Standard übernommen haben, weil sie hauptsächlich gesehen haben, wie Euler zusammengesetzte Ausdrücke wie f (ax + b) geschrieben hat.. Er schrieb selten nur FX .

Mehr dazu unter: Hat Euler jemals f (x) in Klammern geschrieben? .

Ich weiß nicht, ob Designer funktionaler Programmiersprachen oder die Erfinder der Lambda-Rechnung die historischen Wurzeln ihrer Notation kannten. Persönlich finde ich die Notation ohne Klammern natürlicher.

Michael Bächtold
quelle