Usando gramática de linguagem natural na API fluente

14

Eu estou mexendo com uma abstração de consulta sobre WebSQL / Phonegap Database API, e eu me vejo atraído e duvidoso, definindo uma API fluente que imita o uso da gramática natural da língua inglesa.

Pode ser mais fácil explicar isso por meio de exemplos. A seguir, todas as consultas válidas em minha gramática e os comentários explicam a semântica pretendida:

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

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

Edite com base no feedback de Quentin Pradet : Além disso, parece que a API teria que suportar formas verbais no plural e no singular, então:

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

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

Por questão de questão, vamos supor que eu não tenha esgotado todas as construções possíveis aqui. Vamos supor também que eu possa cobrir a maioria das frases inglesas corretas - afinal, a própria gramática é limitada aos verbos e conjunções definidos pelo SQL.

Editar em relação ao agrupamento : Uma "sentença" é um grupo, e a precedência é como definida no SQL: da esquerda para a direita. Vários agrupamentos podem ser expressos com várias declarações where :

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

Como você pode ver, a definição de cada método depende do contexto gramatical em que se encontra. Por exemplo, o argumento para "métodos de conjunção" or() e and() pode ser omitido, ou refere-se a um nome de campo ou valor esperado.

Para mim, isso parece muito intuitivo, mas eu gostaria que você ouvisse sua opinião: esta é uma API boa e útil, ou devo recuar para uma implementação mais direta?

Para o registro: esta biblioteca também fornecerá uma API mais convencional, não fluente, baseada em objetos de configuração.

    
por fencliff 01.03.2013 / 15:52
fonte

4 respostas

23

Eu acho muito errado. Eu estudo o idioma natural e é cheio de ambigüidade que só pode ser resolvido com contexto e muito conhecimento humano. O fato de as linguagens de programação não serem ambíguas é uma coisa muito boa! Eu não acho que você queira que o significado dos métodos mude de acordo com o contexto:

  • Isso adiciona mais surpresas, já que você traz ambiguidade
  • Seus usuários desejarão usar construções que você não terá coberto, por exemplo. %código%
  • É difícil relatar erros: o que você pode fazer com find("user").where("name").and("email").equals("foo"); ?

Let's also presume that I can cover most correct English sentences - after all, the grammar itself is limited to the verbs and conjuctions defined by SQL.

Não, você não pode cobrir as frases inglesas mais corretas. Outros já tentaram e fica muito complicado muito rapidamente. É chamado de entendimento da linguagem natural mas ninguém realmente tenta isso: estamos tentando resolver problemas menores primeiro. Para sua biblioteca, você basicamente tem duas opções:

  • ou você se restringe a um subconjunto de inglês: isso dá a você SQL,
  • ou você tenta cobrir "inglês" e descobre que isso não é possível devido à ambiguidade, complexidade e diversidade do idioma.
por 01.03.2013 / 16:00
fonte
3

Eu costumo um pouco de acordo com os posts dos outros que isso não é um ótimo design. No entanto, acredito que tenho motivos diferentes.

Você está apresentando o que eu vejo como uma sintaxe concreta para consultas SQL. Acredito firmemente que a sintaxe concreta nunca pode ajudar uma língua, apenas prejudicada se for ruim.

No entanto, a sintaxe abstrata é uma história diferente. A sintaxe abstrata define a estrutura da sua linguagem e como as frases podem ser combinadas para construir frases maiores. Eu sinto que o sucesso de uma linguagem depende strongmente da qualidade de sua definição de sintaxe abstrata.

Meu problema com a API fluente não é que seja ambíguo, ou pouco claro, ou não expressivo - é que ele oculta a linguagem real e sua estrutura e, ao fazê-lo, acaba tornando as coisas muito mais complicadas do que ser (introduzindo ambiguidades, erros de sintaxe não óbvios, etc.).

Desde que você mencionou que você também fornecerá uma "API mais convencional", parece que você já sabe de tudo isso. Para isso eu digo "bom!" Mas isso não significa que você também não possa desenvolver sua API fluente em paralelo! Uma única definição de sintaxe abstrata pode suportar várias sintaxes concretas. Embora você deva ter em mente que a sintaxe abstrata é a verdadeira, uma sintaxe concreta também pode ser muito útil.

    
por 01.03.2013 / 18:59
fonte
2

Além dos pontos muito positivos de Quentin Pradet, tenho dúvidas sobre os supostos benefícios dessa linguagem.

Presumivelmente, o objetivo de uma gramática próxima da linguagem natural é torná-la acessível. Mas o SQL já está bem próximo da linguagem natural. Um deles é realmente mais próximo do inglês do que do outro?

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

select user from table where name = 'foo'

Eu realmente não vejo o benefício de sua gramática, do ponto de vista da intuição ou legibilidade. Na verdade, a versão do SQL parece mais legível (e é mais fácil de digitar) devido ao espaço em branco.

    
por 01.03.2013 / 17:12
fonte
2

Há um número de bad menor do que as decisões de design ideais que parecem ter sido tomadas ao considerar essa API.

A primeira é a questão da utilidade - a que finalidade ela serve? Isso parece estar criando uma estrutura de dados que será compilada em um dialeto de SQL. Aliás, a gramática parece ser um conjunto limitado de SQL. A questão de "que vantagem isso serve apenas usando SQL?" torna-se chave. Se é mais difícil escrever usando a interface fluente do que apenas escrever uma string com a interpolação apropriada, então não se escreve usando esta API.

O inglês é ambíguo. Tentar modelar uma interface fluente em inglês é uma má escolha (é melhor você usar Latim ). Quando há vários parsings válidos do mesmo conjunto de chamadas, isso causa confusão e surpresa . Nenhum deles é bom para ter em uma API.

Existem mais partes no SQL do que esta API está expondo. As junções (em qualquer uma das suas inúmeras formas) são notavelmente ausentes do conjunto de exemplos. As subconsultas ( foo in (select id from bar) ), uniões e group by são algumas das coisas que são frequentemente usadas. Agrupamentos complexos de lógica não parecem estar presentes de maneira intuitiva.

Se alguém estava escrevendo usando esta API e depois descobriu que a API não é capaz de expressar a consulta desejada, um tempo significativo será perdido. É uma má escolha estar usando estilos mistos para fazer uma consulta em uma aplicação (consultas simples nesta API, complexas em SQL bruto) - e, finalmente, a mais expressiva será usada.

Embora a programação seja difundida, a fluência do inglês não é. Mesmo com uma limitação da linguagem para "SQL like", há nuances de como um falante nativo leria algo e alguém que tivesse inglês como segunda ou terceira língua.

Há redundância desnecessária na API por causa do inglês. Em particular equal() vs equals() fazendo a mesma coisa. Embora eu não tenha certeza, acredito que is() seja um não-adicionado para o inglês mais próximo. Congratulo-me com qualquer um para ouvir minha reclamação sobre a redundância de métodos em Ruby no bate-papo - não cometa o mesmo erro.

Sente-se e escreva um conjunto abrangente de exemplos das consultas que você deseja usar. Determine com quem você lidará com todos esses exemplos de uma maneira não ambígua que seja menos trabalhosa do que as próprias consultas. Se você não puder, considere se vale a pena percorrer o caminho da escrita da API. O SQL é onde está hoje (não é perfeito, mas não encontrei nada melhor) ao longo de décadas de refinamento.

RFC 1925 - As Doze Verdades da Rede

(12) In protocol design, perfection has been reached not when there is nothing left to add, but when there is nothing left to take away.

    
por 01.03.2013 / 17:15
fonte