Używanie gramatyki języka naturalnego w płynnym API

14

Majstruję przy abstrakcji zapytań dotyczących interfejsu API bazy danych WebSQL / Phonegap, i jestem przekonany, że mam wątpliwości co do zdefiniowania płynnego interfejsu API, który naśladuje użycie naturalnej gramatyki języka angielskiego.

Najłatwiej to wyjaśnić za pomocą przykładów. Poniżej znajdują się wszystkie poprawne zapytania w mojej gramatyce, a komentarze wyjaśniają zamierzony semantyczny:

//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);

Edycja na podstawie opinii Quentin Pradet : Ponadto wydaje się, że interfejs API musiałby obsługiwać zarówno formy liczby mnogiej, jak i liczby pojedynczej, więc:

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

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

Dla celów pytania załóżmy, że nie wyczerpałem tutaj wszystkich możliwych konstrukcji. Załóżmy również, że potrafię objąć najbardziej poprawne zdania angielskie - w końcu sama gramatyka jest ograniczona do czasowników i koniugatów zdefiniowanych przez SQL.


Edycja dotycząca grupowania : jedno „zdanie” to jedna grupa, a pierwszeństwo jest zdefiniowane w SQL: od lewej do prawej. Wiele grupowań można wyrazić za pomocą wielu whereinstrukcji:

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

Jak widać, definicja każdej metody zależy od kontekstu gramatycznego, w którym się znajduje. Na przykład argument „metody koniunkcji” or()i and()można go pominąć lub odwołać się do nazwy pola lub oczekiwanej wartości.

Moim zdaniem wydaje się to bardzo intuicyjne, ale chciałbym usłyszeć Twoją opinię: czy jest to dobry, przydatny interfejs API, czy powinienem cofnąć się do prostszej implementacji?

Dla przypomnienia: ta biblioteka zapewni również bardziej konwencjonalny, nie płynny interfejs API oparty na obiektach konfiguracyjnych.

szermierz
źródło
1
Łańcuch jest również powodem, dla którego jQuery jest bardzo sławny. Całkiem proste, sekwencyjne i zrozumiałe.
Joseph
3
To jest interesujące! Prawdopodobnie powinieneś o to zapytać programistów.
Benjamin Gruenbaum
2
Jak poradziłbyś sobie z grupowaniem? Równowartość: ... where foo = 1 or (bar = 2 and qux = 3)?
7
IMO tego rodzaju płynne API jest okropne. Na przykład brak pierwszeństwa operatora jest denerwujący. Parsowałbym where("name").equals("foo").or("bar")jak (name=="foo")or bar. Wtedy nie jest jasne, kiedy ciąg reprezentuje literał, a kiedy przedstawia nazwę kolumny, ...
CodesInChaos
4
btw. jeśli chcesz użyć DSL do przeszukiwania bazy danych, możesz użyć już istniejącego DSL o nazwie SQL.
CodesInChaos

Odpowiedzi:

23

Myślę, że to bardzo źle. Studiuję język naturalny i jest on pełen dwuznaczności, którą można rozwiązać tylko za pomocą kontekstu i dużej wiedzy ludzkiej. Fakt, że języki programowania nie są dwuznaczne, jest bardzo dobrą rzeczą! Nie sądzę, że chcesz, aby znaczenie metod zmieniało się zgodnie z kontekstem:

  • To dodaje więcej niespodzianek, ponieważ wprowadzasz dwuznaczność
  • Twoi użytkownicy będą chcieli korzystać z konstrukcji, których nie obejmiesz, np. find("user").where("name").and("email").equals("foo");
  • Trudno jest zgłaszać błędy: co możesz zrobić find("user").where("name").not().is().null();?

Załóżmy również, że potrafię objąć najbardziej poprawne zdania angielskie - w końcu sama gramatyka jest ograniczona do czasowników i koniugatów zdefiniowanych przez SQL.

Nie, nie możesz objąć najbardziej poprawnych angielskich zdań. Inni próbowali już wcześniej i bardzo szybko się komplikuje. Nazywa się to zrozumieniem języka naturalnego, ale tak naprawdę nikt tego nie próbuje: najpierw staramy się rozwiązywać mniejsze problemy. W przypadku biblioteki masz zasadniczo dwie opcje:

  • albo ograniczysz się do części angielskiej: to daje ci SQL,
  • lub próbujesz objąć „angielski” i dowiadujesz się, że nie jest to możliwe z powodu niejasności, złożoności i różnorodności języka.
Quentin Pradet
źródło
Dzięki za wkład. Zredagowałem moje pytanie, aby uwzględnić pierwszy wymieniony przypadek, i dodałem pewne zastrzeżenia. Zgadzam się z twoim stanowiskiem w sprawie dwuznaczności - oto sedno mojego pytania. Czy dopuszczalne jest, aby języki programowania były ambicje w dobrze określonych kontekstach?
fencliff
jeśli jest niejednoznaczny, nie jest dobrze zdefiniowany. Jeśli jest niejednoznaczny, istnieje więcej niż jeden potencjalny wynik i coś musi wybrać jeden z nich. Albo wybór będzie dobrze zdefiniowany, albo parser języka wybierze losowo. Tak więc dwuznaczność w języku programowania jest dobra, jeśli chcesz języka stochastycznego (1 + 1 może równać się 2, a czasami może równać się 1 lub 3) w przeciwieństwie do deterministycznego (1 + 1 zawsze równa się 2).
Michael Paulukonis
Mówisz o jego propozycji czy po angielsku? Przez większość czasu dwuznaczność można rozwiązać za pomocą kontekstu i / lub wiedzy (której częścią jest zdrowy rozsądek), czegoś, co nie jest dostępne dla komputera.
Quentin Pradet
+1 Dobry chwyt na wrażliwość kontekstu jego konkretnej implementacji płynnego API. Podobał mi się pomysł jego płynnego API na pierwszy rzut oka, ale nie przyjrzałem się tak dokładnie, aby to zobaczyć. Zdecydowanie ogromny problem.
Jimmy Hoffa
jeśli uczyni gramatykę jednoznaczną i pozbawioną kontekstu, to o co chodzi? pewni, że mogą chcieć usunąć liczbę mnogą pojedynczych form czasowników i tym podobne, ale nie zmienia to sedna tego, co próbowali zrobić. Nie miałbyś is()lub equal()tylko equals(). Po tym nie widzę problemu z raportowaniem błędów. null()stałby się również dosłowny do porównania, a nie funkcją składni. find("user").where("name").is().not().null();staje sięfind("user").where("name").not().equals(null);
kiedy
3

Zwykle zgadzam się z postami innych osób, że nie jest to świetny projekt. Uważam jednak, że mam inne powody.

Przedstawiasz to, co uważam za konkretną składnię zapytań SQL. Mocno wierzę, że konkretna składnia nigdy nie może pomóc językowi, tylko boli, jeśli jest zły.

Jednak abstrakcyjna składnia to inna historia. Streszczenie składni określa strukturę twojego języka i sposób łączenia fraz w celu tworzenia większych fraz. Uważam, że sukces języka zależy w dużej mierze od jakości jego abstrakcyjnej definicji składni.

Mój problem z płynnym interfejsem API nie polega na tym, że jest on dwuznaczny, niejasny lub nieekspresyjny - chodzi o to, że ukrywa prawdziwy język i jego strukturę, a przez to sprawia, że ​​sprawy stają się znacznie bardziej skomplikowane niż muszą być ( wprowadzając dwuznaczności, nieoczywiste błędy składniowe itp.).

Ponieważ wspomniałeś, że zapewnisz również „bardziej konwencjonalny interfejs API”, wygląda na to, że już to wszystko wiesz. Do tego mówię „dobrze!” Ale to nie znaczy, że nie możesz równolegle rozwijać płynnego API! Pojedyncza abstrakcyjna definicja składni może obsługiwać wiele konkretnych składni. Chociaż należy pamiętać, że abstrakcyjna składnia to prawdziwa okazja, konkretna składnia może być również bardzo pomocna.


źródło
2

Oprócz bardzo dobrych argumentów Quentina Pradeta wątpię w domniemane zalety tego języka.

Przypuszczalnie celem gramatyki zbliżonej do języka naturalnego jest uczynienie go dostępnym. Ale SQL jest już dość zbliżony do języka naturalnego. Czy jeden z nich jest naprawdę bliższy językowi angielskiemu niż drugi?

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

select user from table where name = 'foo'

Naprawdę nie widzę korzyści z twojej gramatyki, z punktu widzenia intuicyjności lub czytelności. W rzeczywistości wersja SQL wygląda na bardziej czytelną (i łatwiejszą do wpisania) ze względu na białe znaki.


źródło
2

Istnieje wiele złych, mniej niż idealnych decyzji projektowych, które wydają się być podejmowane przy rozważaniu tego API.

Pierwszym z nich jest pytanie o użyteczność - do czego służy? Wydaje się, że tworzy to strukturę danych, która skompiluje się w dialekt SQL. Nawiasem mówiąc, gramatyka wydaje się być ograniczonym zestawem SQL. Pytanie „na czym polega ta przewaga nad używaniem SQL?” staje się kluczem. Jeśli pisanie przy użyciu płynnego interfejsu jest bardziej kłopotliwe niż pisanie łańcucha z odpowiednią interpolacją, nie można pisać przy użyciu tego interfejsu API.

Angielski jest dwuznaczny. Próba modelowania płynnego interfejsu w języku angielskim jest złym wyborem (lepiej jest używać łaciny ). Gdy istnieje wiele możliwych poprawnych analiz tego samego zestawu połączeń, prowadzi to do zamieszania i zaskoczenia . Żadna z tych rzeczy nie jest dobra w API.

SQL zawiera więcej części niż ten API ujawnia. Połączenia (w dowolnej z niezliczonych form) są wyraźnie nieobecne w zestawie przykładów. Podzapytania ( foo in (select id from bar)), związki i grupowanie według to tylko niektóre z często używanych rzeczy. Złożone grupy logiczne nie wydają się być obecne w żaden intuicyjny sposób.

Jeśli ktoś pisał przy użyciu tego interfejsu API, a następnie stwierdził, że interfejs API nie jest w stanie wyrazić żądanego zapytania, znaczna część czasu zostanie utracona. Nie jest dobrym wyborem stosowanie stylów mieszanych do wykonywania zapytań w aplikacji (proste zapytania w tym interfejsie, złożone w surowym sql) - i ostatecznie zostanie użyty ten, który jest bardziej wyrazisty.

Podczas gdy programowanie jest powszechne, biegła znajomość języka angielskiego nie jest. Nawet z ograniczeniem języka do „SQL like” istnieją niuanse tego, jak native speaker czytałby coś i ktoś, kto zna angielski jako drugi lub trzeci język.

W języku angielskim niepotrzebna jest nadmiarowość w interfejsie API. W szczególności equal()vs equals()robienie tego samego. Chociaż nie jestem tego pewien, uważam, że is()jest to opcja dodana w celu dopasowania bliższego angielskiego. Z zadowoleniem przyjmuję wysłuchanie moich rantów na temat nadmiarowości metod w ruby ​​na czacie - nie popełniaj tego samego błędu.

Usiądź i napisz wyczerpujący przykładowy zestaw zapytań, których chcesz użyć. Określ, z kim będziesz sobie radzić w tych wszystkich przykładach, w sposób niejednoznaczny, który jest mniej kłopotliwy niż same zapytania. Jeśli nie możesz, zastanów się, czy warto pójść ścieżką pisania API. SQL jest tam, gdzie jest dzisiaj (nie jest idealny, ale nie znalazłem nic lepszego) przez dekady udoskonalania.

RFC 1925 - Dwanaście prawd sieciowych

(12) W projektowaniu protokołu osiągnięto doskonałość nie wtedy, gdy nie ma już nic do dodania, ale kiedy nie ma już nic do zabrania.


źródło