在流利的API中使用自然语言语法


14

我正在修改基于WebSQL / Phonegap数据库API的查询抽象,我发现自己既对定义一种模仿自然英语语法使用的流畅API感兴趣,又对此表示怀疑。

通过示例解释这可能是最简单的。以下是我语法中所有有效的查询,并且注释说明了预期的语义:

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

根据Quentin Pradet的反馈进行编辑:此外,API似乎必须同时支持复数形式和单数形式,因此:

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

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

出于疑问,让我们假设我在这里没有穷尽所有可能的构造。我们还假设我可以覆盖正确的英语句子-毕竟,语法本身仅限于SQL定义的动词和连接词。


编辑分组:一个“句子”是一组,其优先级如SQL中所定义:从左到右。多个分组可以用多个where语句表示:

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

正如可以看到的,每个方法的定义是依赖于语法语境它是在例如将参数“结合的方法” or()and()可以被排除在外,指的是字段名称预期值。

对我来说,这感觉非常直观,但是我希望您能听到您的反馈:这是一个很好的,有用的API,还是我应该退缩以更简单地实现?

作为记录:该库还将基于配置对象提供更常规的,非流利的API。


1
链接也是jQuery非常著名的原因。非常简单,连续且易于理解。
约瑟夫

3
这是有趣的!您可能应该问程序员。
本杰明·格林鲍姆

2
您将如何处理分组?相当于:... where foo = 1 or (bar = 2 and qux = 3)

7
IMO这种流利的API太可怕了。例如,缺少运算符优先级是令人讨厌的。我解析where("name").equals("foo").or("bar")(name=="foo")or bar。然后不清楚何时字符串代表文字,何时呈现列名,...
CodesInChaos

4
顺便说一句 如果要使用DSL查询数据库,则可以使用称为SQL的现有DSL。
CodesInChaos 2013年

Answers:


23

我认为这是非常错误的。我研究自然语言,并且充满歧义,只能通过上下文和许多人类知识来解决。编程语言不是模棱两可的事实是一件好事!我认为您不希望根据上下文更改方法的含义:

  • 由于您带来歧义,这会增加更多的惊喜
  • 您的用户将要使用您没有涵盖的结构,例如。 find("user").where("name").and("email").equals("foo");
  • 很难报告错误:您该怎么办find("user").where("name").not().is().null();

我们还假设我可以覆盖最正确的英语句子-毕竟,语法本身仅限于SQL定义的动词和连接词。

不,您无法涵盖最正确的英语句子。其他人曾经尝试过,并且很快变得非常复杂。这被称为自然语言理解,但没有人真正尝试:我们正在尝试首先解决较小的问题。对于您的图书馆,基本上有两个选择:

  • 要么将自己限制为英语的一部分:这会给您SQL,
  • 或者您尝试掩盖“英语”,但发现由于语言的歧义性,复杂性和多样性,这是不可能的。

感谢您的输入。编辑了我的问题,以涵盖您列出的第一种情况,并添加了一些警告。我同意你在歧义上的立场-这是我的问题的关键。在定义良好的上下文中,编程语言是否是模棱两可的?
fencliff 2013年

如果不明确,则定义不明确。如果模棱两可,则可能有不止一个潜在的结果,因此必须从其中选择一个。选择将是明确定义的,或者语言解析器将随机选择。因此,如果要使用随机语言(1 + 1可能等于2,有时可能等于1或3)而不是确定性语言(1 + 1始终等于2),那么编程语言中的歧义是可以的。
Michael Paulukonis 2013年

您是在谈论他的建议还是英语?在大多数情况下,可以使用上下文和/或知识(其中一部分是常识)来解决歧义,这对于计算机而言是不可用的。
昆汀·普拉德

+1很好地了解了他对流利的API的特定实现的上下文敏感性。乍一看,我很喜欢他的流利API的想法,但看上去却没那么仔细。绝对是个大问题。
Jimmy Hoffa

如果他使语法明确且上下文无关,那么问题是什么?确保他们可能想要删除动词的复数形式和单数形式以及类似的内容,但这并不会改变他们尝试做的事情的核心。您将没有is()equal()只有equals()。此后,看不到报告错误的问题。null()也将成为要比较的文字,而不是语法函数。find("user").where("name").is().not().null();成为find("user").where("name").not().equals(null);
WHN

3

我倾向于在某种程度上同意其他人的帖子,这不是一个很好的设计。但是,我相信我有不同的原因。

您正在介绍我认为是SQL查询的具体语法的内容。我坚信,具体语法永远无法帮助一种语言,只有在语言不好的情况下才会受到伤害。

但是,抽象语法是另一回事。抽象语法定义了语言的结构以及如何组合短语以构建更大的短语。我认为一种语言的成功在很大程度上取决于其抽象语法定义的质量。

我使用流畅的API的问题不是因为它模棱两可,不清楚,或者不具有表达力-而是它隐藏了真实的语言及其结构,从而使事情变得比原本要复杂得多(通过引入歧义,非显而易见的语法错误等)。

既然您提到您还将提供“更传统的API”,那么听起来您已经了解了这一切。为此,我说“好!” 但这并不意味着您也不能并行开发流利的API!单个抽象语法定义可以支持多种具体语法。虽然您应该记住抽象语法是真正的交易,但具体语法也可能非常有帮助。


2

除了Quentin Pradet的优点之外,我还怀疑这种语言的好处。

语法接近自然语言的目的大概是使其易于使用。但是SQL已经非常接近自然语言。其中一个真的比另一个更接近英语吗?

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

select user from table where name = 'foo'

从直观性或可读性的角度来看,我真的看不到语法的好处。实际上,由于其空白,SQL版本看起来更具可读性(并且更容易键入)。


2

有许多的比似乎已经在考虑这个API已取得理想的设计决策少。

首先是效用问题-效用是什么?这似乎正在创建一个数据结构,该数据结构将编译为SQL的方言。顺便说一句,语法似乎是一组有限的SQL。问题“与仅使用SQL相比,这有什么优势?” 成为关键。如果使用流利的接口编写代码比只编写带有适当插值的字符串要麻烦的多,那么就不会使用此API编写代码。

英语是模棱两可的。尝试用英语为流利的界面建模是一个糟糕的选择(最好使用Latin)。当同一组调用链有多个可能有效的解析时,这会引起混乱和意外。这些都不是API中的好东西。

SQL包含的部分比此API公开的要多。示例集中显然不存在任何形式的联接。子查询(foo in (select id from bar)),联合和分组依据是经常使用的一些东西。似乎没有以任何直观的方式显示复杂的逻辑分组。

如果使用此API进行编写,然后发现该API无法表达所需的查询,则会浪费大量时间。使用混合样式在应用程序中进行查询(此api中的简单查询,原始sql中的复杂查询)是一个糟糕的选择-最终将使用更具表现力的查询。

尽管编程很普遍,但英语流利程度却不高。即使将语言限制为“类似于SQL”,也有一些细微差别,以英语为母语的人和以英语为第二或第三语言的人会如何阅读。

为了英语起见,API中没有多余的冗余。特别是equal()vs equals()做同样的事情。虽然我不确定,但我相信is()为了配合更接近的英语,这是一项无人操作。我欢迎任何人听听我关于聊天中红宝石方法冗余的抱怨-不要犯同样的错误。

坐下来,写下您想使用的查询的综合示例集。确定谁将以一种毫不含糊的方式处理所有这些示例,这种方式比查询本身要麻烦得多。如果不能,请考虑是否值得编写API。经过数十年的改进,SQL才是今天的地方(虽然不是完美的,但我还没有发现更好的东西)。

RFC 1925-十二个网络真相

(12)在协议设计中,达到完美不是在没有什么可添加的东西的时候,而是在没有什么可以补充的时候。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.