Verwenden der Grammatik natürlicher Sprache in der fließenden API

14

Ich arbeite an einer Abfrageabstraktion über die WebSQL / Phonegap-Datenbank-API, und ich bin sowohl davon überzeugt als auch bezweifelt, eine fließende API zu definieren, die die Verwendung der Grammatik in natürlicher englischer Sprache imitiert.

Dies lässt sich am einfachsten anhand von Beispielen erklären. Das Folgende sind alle gültigen Abfragen in meiner Grammatik und Kommentare erklären die beabsichtigte Semantik:

//find user where name equals "foo" or email starts with "foo@"
find("user").where("name").equals("foo").and("email").startsWith("foo@")

//find user where name equals "foo" or "bar"
find("user").where("name").equals("foo").or("bar");

//find user where name equals "foo" or ends with "bar"
find("user").where("name").equals("foo").or().endsWith("bar");

//find user where name equals or ends with "foo"
find("user").where("name").equals().or().endsWith("foo");

//find user where name equals "foo" and email is not like "%contoso.com"
find("user").where("name").equals("foo").and("email").is().not().like("%contoso.com");

//where name is not null
find("user").where("name").is().not().null();

//find post where author is "foo" and id is in (1,2,3)
find("post").where("author").is("foo").and("id").is().in(1, 2, 3);

//find post where id is between 1 and 100
find("post").where("id").is().between(1).and(100);

Edit basierend auf dem Feedback von Quentin Pradet : Außerdem müsste die API anscheinend sowohl Plural- als auch Singular-Verbformen unterstützen.

//a equals b
find("post").where("foo").equals(1);

//a and b (both) equal c
find("post").where("foo").and("bar").equal(2);

Um der Frage willen nehmen wir an, dass ich hier nicht alle möglichen Konstrukte ausgeschöpft habe. Nehmen wir auch an, dass ich die meisten korrekten englischen Sätze abdecken kann - schließlich ist die Grammatik selbst auf die von SQL definierten Verben und Konjunktionen beschränkt.


Bearbeiten hinsichtlich der Gruppierung : Ein "Satz" ist eine Gruppe, und die Rangfolge ist wie in SQL definiert: von links nach rechts. Mehrere Gruppierungen können mit mehreren whereAnweisungen ausgedrückt werden :

//the conjunctive "and()" between where statements is optional
find("post")
  .where("foo").and("bar").equal(2).and()
  .where("baz").isLessThan(5);

Wie Sie sehen, hängt die Definition jeder Methode vom grammatikalischen Kontext ab, in dem sie sich befindet. Zum Beispiel das Argument für "Konjunktionsmethoden" or()und and()kann entweder weggelassen werden oder auf einen Feldnamen oder einen erwarteten Wert verweisen .

Für mich fühlt sich das sehr intuitiv an, aber ich möchte, dass Sie Ihr Feedback hören: Ist dies eine gute, nützliche API, oder sollte ich zu einer geradlinigeren Implementierung zurückkehren?

Um es festzuhalten: Diese Bibliothek bietet auch eine konventionellere, nicht fließende API, die auf Konfigurationsobjekten basiert.

Fencliff
quelle
1
Verkettung ist auch der Grund, warum jQuery sehr berühmt ist. Ziemlich direkt, sequentiell und verständlich.
Joseph
3
Das ist interessant! Sie sollten dies wahrscheinlich bei Programmierern erfragen.
Benjamin Gruenbaum
2
Wie würden Sie mit Gruppierungen umgehen? Das Äquivalent von ... where foo = 1 or (bar = 2 and qux = 3):?
7
IMO diese Art der fließenden API ist schrecklich. Zum Beispiel ist die mangelnde Priorität des Bedieners ärgerlich. Ich würde analysieren where("name").equals("foo").or("bar")als (name=="foo")or bar. Dann ist es nicht klar, wann ein String ein Literal repräsentiert und wann er einen Spaltennamen präsentiert, ...
CodesInChaos
4
btw. Wenn Sie ein DSL zum Abfragen einer Datenbank verwenden möchten, können Sie das bereits vorhandene DSL mit dem Namen SQL verwenden.
CodesInChaos

Antworten:

23

Ich denke es ist sehr falsch. Ich lerne natürliche Sprache und sie ist voller Mehrdeutigkeiten, die nur mit Kontext und viel menschlichem Wissen gelöst werden können. Die Tatsache, dass Programmiersprachen nicht mehrdeutig sind, ist eine sehr gute Sache! Ich glaube nicht, dass Sie die Bedeutung von Methoden je nach Kontext ändern möchten:

  • Dies führt zu weiteren Überraschungen, da Sie Unklarheiten mit sich bringen
  • Ihre Benutzer möchten Konstruktionen verwenden, die Sie nicht behandelt haben, z. find("user").where("name").and("email").equals("foo");
  • Es ist schwer, Fehler zu melden: Womit können Sie umgehen find("user").where("name").not().is().null();?

Nehmen wir auch an, dass ich die meisten korrekten englischen Sätze abdecken kann - schließlich ist die Grammatik selbst auf die von SQL definierten Verben und Konjunktionen beschränkt.

Nein, die meisten korrekten englischen Sätze können nicht abgedeckt werden. Andere haben es bereits versucht, und es wird sehr schnell sehr kompliziert. Es heißt natürliches Sprachverständnis, aber das versucht niemand wirklich: Wir versuchen zuerst, kleinere Probleme zu lösen. Für Ihre Bibliothek haben Sie grundsätzlich zwei Möglichkeiten:

  • entweder Sie beschränken sich auf eine Untergruppe von Englisch: das gibt Ihnen SQL,
  • oder Sie versuchen, "Englisch" zu behandeln und stellen fest, dass dies aufgrund der Mehrdeutigkeit, Komplexität und Vielfalt der Sprache nicht möglich ist.
Quentin Pradet
quelle
Danke für deinen Beitrag. Meine Frage wurde bearbeitet, um den ersten von Ihnen aufgelisteten Fall abzudecken, und einige Einschränkungen wurden hinzugefügt. Ich stimme Ihrem Standpunkt zur Mehrdeutigkeit zu - das ist der Kern meiner Frage. Ist es akzeptabel, dass Programmiersprachen in genau definierten Kontexten ehrgeizig sind?
Fencliff
Wenn es mehrdeutig ist, ist es nicht genau definiert. Wenn es mehrdeutig ist, gibt es mehr als ein mögliches Ergebnis, und es muss sich etwas dafür entscheiden. Entweder ist die Auswahl genau definiert, oder der Sprachparser wählt sie nach dem Zufallsprinzip aus. Zweideutigkeit in einer Programmiersprache ist also in Ordnung, wenn Sie eine stochastische Sprache (1 + 1 entspricht 2 und manchmal 1 oder 3) im Gegensatz zu deterministisch (1 + 1 entspricht immer 2) wünschen.
Michael Paulukonis
Sprechen Sie über seinen Vorschlag oder Englisch? In den meisten Fällen können Mehrdeutigkeiten mithilfe von Kontexten und / oder Kenntnissen (von denen ein Teil ein gesunder Menschenverstand ist) behoben werden, die einem Computer nicht zur Verfügung stehen.
Quentin Pradet
+1 Guter Fang an der Kontextsensitivität seiner speziellen Implementierung einer fließenden API. Ich mochte die Idee seiner fließenden API auf den ersten Blick, schaute aber nicht so genau hin, um dies zu sehen. Auf jeden Fall ein großes Problem.
Jimmy Hoffa
wenn er die Grammatik eindeutig und kontextfrei macht, worum geht es dann? Sicher möchten sie vielleicht mehrere Verse einzelner Formen von Verben und ähnlichen Dingen entfernen, aber es ändert nichts am Kern dessen, was sie zu tun versuchten. Das hättest du nicht is()oder equal()nur equals(). Ihr Problem mit dem Melden von Fehlern wird danach nicht mehr angezeigt. null()würde auch ein Literal zum Vergleichen werden, anstatt eine Syntaxfunktion. find("user").where("name").is().not().null();wirdfind("user").where("name").not().equals(null);
WHN
3

Ich stimme den Beiträgen anderer eher zu, dass dies kein großartiges Design ist. Ich glaube jedoch, dass ich verschiedene Gründe habe.

Sie präsentieren, was ich als konkrete Syntax für SQL-Abfragen betrachte. Ich bin der festen Überzeugung, dass eine konkrete Syntax einer Sprache niemals helfen kann, sondern nur schadet, wenn sie schlecht ist.

Die abstrakte Syntax ist jedoch eine andere Geschichte. Abstrakte Syntax definiert die Struktur Ihrer Sprache und wie Phrasen kombiniert werden können, um größere Phrasen zu bilden. Ich bin der Meinung, dass der Erfolg einer Sprache stark von der Qualität ihrer abstrakten Syntaxdefinition abhängt.

Mein Problem mit der fließenden API ist nicht, dass sie mehrdeutig oder unklar oder nicht aussagekräftig ist. Sie verbirgt die eigentliche Sprache und ihre Struktur und macht die Dinge dadurch viel komplizierter, als sie sein müssen. durch Einführung von Mehrdeutigkeiten, nicht offensichtlichen Syntaxfehlern usw.).

Da Sie erwähnt haben, dass Sie auch eine "konventionellere API" bereitstellen, scheint Ihnen das alles bereits bekannt zu sein. Dazu sage ich "Gut!" Das heißt aber nicht, dass Sie nicht auch Ihre flüssige API parallel entwickeln können! Eine einzelne abstrakte Syntaxdefinition kann mehrere konkrete Syntaxen unterstützen. Während Sie bedenken sollten, dass die abstrakte Syntax die Realität ist, kann eine konkrete Syntax auch sehr hilfreich sein.


quelle
2

Neben den sehr guten Punkten von Quentin Pradet bezweifle ich die angeblichen Vorteile dieser Sprache.

Vermutlich besteht der Sinn einer der natürlichen Sprache nahe stehenden Grammatik darin, sie zugänglich zu machen. Aber SQL kommt der natürlichen Sprache schon sehr nahe. Ist einer von ihnen dem Englischen wirklich näher als der andere?

find("user").where("name").equals("foo")

select user from table where name = 'foo'

Ich sehe den Nutzen Ihrer Grammatik vom Standpunkt der Intuitivität oder Lesbarkeit nicht wirklich. Tatsächlich sieht die SQL-Version aufgrund ihres Leerzeichens lesbarer aus (und ist einfacher zu tippen).


quelle
2

Es gibt eine Reihe von schlecht weniger als idealen Design - Entscheidungen , die bei der Prüfung dieser API gemacht zu haben scheinen worden.

Die erste ist die Frage des Nutzens - welchem ​​Zweck dient sie? Dies scheint eine Datenstruktur zu schaffen, die in einen SQL-Dialekt kompiliert wird. Übrigens scheint die Grammatik eine begrenzte Menge von SQL zu sein. Die Frage: "Welchen Vorteil hat dies gegenüber der Verwendung von SQL?" wird zum Schlüssel. Wenn es umständlicher ist, über die flüssige Schnittstelle zu schreiben, als nur einen String mit der entsprechenden Interpolation zu schreiben, wird über diese API nicht geschrieben.

Englisch ist mehrdeutig. Der Versuch, eine fließende Benutzeroberfläche auf Englisch zu modellieren, ist eine schlechte Wahl (Sie sollten lieber Latein verwenden ). Wenn es mehrere möglicherweise gültige Parsings derselben Aufrufkette gibt, führt dies zu Verwirrung und Überraschung . Beides ist nicht gut in einer API.

SQL enthält mehr Teile, als diese API bereitstellt. Verknüpfungen (in einer ihrer unzähligen Formen) fehlen im Beispielsatz besonders. Unterabfragen ( foo in (select id from bar)), Gewerkschaften und Gruppieren nach sind einige der Dinge, die häufig verwendet werden. Komplexe Gruppierungen von Logik scheinen auf keine intuitive Weise vorhanden zu sein.

Wenn Sie mit dieser API geschrieben haben und dann festgestellt haben, dass die API die gewünschte Abfrage nicht ausdrücken kann, geht viel Zeit verloren. Es ist keine gute Wahl, gemischte Stile für die Ausführung von Abfragen in einer Anwendung zu verwenden (einfache Abfragen in dieser API, komplex in Raw-SQL) - und letztendlich wird diejenige verwendet, die aussagekräftiger ist.

Während das Programmieren weit verbreitet ist, ist Englisch nicht fließend. Sogar mit einer Beschränkung der Sprache auf "SQL-like" gibt es Nuancen, wie ein Muttersprachler etwas lesen würde und jemanden, der Englisch als zweite oder dritte Sprache hat.

Es gibt unnötige Redundanz in der API für Englisch. Insbesondere equal()vs equals()das gleiche zu tun. Ich bin mir zwar nicht sicher, aber ich glaube, dass dies is()ein No-Op ist, der hinzugefügt wurde, um ein passendes Englisch näher zu bringen. Ich freue mich über jeden, der sich im Chat über die Redundanz der Ruby-Methoden beschwert - machen Sie nicht den gleichen Fehler.

Setzen Sie sich und schreiben Sie ein umfassendes Beispielset der Abfragen auf, die Sie verwenden möchten. Stellen Sie fest, mit wem Sie all diese Beispiele auf eine nicht mehrdeutige Art und Weise behandeln, die weniger umständlich als die Abfragen selbst ist. Wenn nicht, überlegen Sie, ob es sich lohnt, die API zu schreiben. SQL ist dort, wo es heute ist (es ist nicht perfekt, aber ich habe nichts Besseres gefunden), und das über Jahrzehnte hinweg.

RFC 1925 - Die zwölf Wahrheiten der Vernetzung

(12) Im Protokolldesign wurde die Perfektion nicht erreicht, wenn nichts mehr hinzuzufügen ist, sondern wenn nichts mehr wegzunehmen ist.


quelle