代码以及 slides 都在仓库里 https://github.com/bhuztez/razor
Regular Expression Matching in the Wild https://swtch.com/~rsc/regexp/regexp3.html
Efficient submatch addressing for regular expressions https://laurikari.net/ville/regex-submatch.pdf
How Pony ORM translates Python generators to SQL queries https://av.tib.eu/media/19938
Programming Is Hard, Let's Go Shopping! https://blog.codinghorror.com/programming-is-hard-lets-go-shopping/
因为时间有限删掉,或者一开始就没想到要加进去的一些东西,演讲结束后,在后面的交流中提到的,补充在这里
大部分框架都是用 inotify 之类的 API 来发现文件改动,有改动就自动重启 server。可是有时候你文件改了一半,连语法都有错误,此时重启就会出现一堆莫名其妙的错误。造框架的时候,要把这问题搞好也不容易。
而用 Emacs 编辑 Erlang 代码,编辑好了,存一下,C-c C-k,会先编译成 beam 文件,假如编译失败,那就停了。假如生成了 beam 文件,就会通过热更新替换进去,不需要重启 server 就立即生效了,且能替换进去的至少是编译通过了的。而造框架并不需要去搞什么监听文件改动的事,省去了很多麻烦。
所以,后面框架的设计,主要的一条就是在编译的时候,做更多检查,生成代码。而不是在运行期根据不同的配置有不同的行为。尽管做这些是为了开发的时候,热更新代码开发体验更好,同时因为代码都在编译期生成好了,运行期没有不必要的额外的开销。
有些网站 Apache 或者 nginx 为了一些特殊的活动页,配置了超级多的正则表达式规则,因为他们用的语言本身不支持 pattern matching,所以用正则表达式规则在弥补这个缺陷。但是这不是没有代价的。
而在 razor_url_dispatch 里,你这么写直接会告诉你 rule conflict,不让你通过
-dispatch({page,"0", {endpoint, special_page}}).
-dispatch({page,"{id:integer}", {endpoint, page}}).
推荐的做法是像下面这样
-dispatch({page,"{id:integer}", {endpoint, page}}).
handle_request({'GET', page, #{id := 0}}) ->
ok;
handle_request({'GET', page, #{id := ID}}) ->
ok;
而在 Erlang 里,下面这样的代码同样会提示你 Warning: this clause cannot match because ...
handle_request({'GET', page, #{id := 0}}) ->
ok;
handle_request({'GET', page, #{id := 0}}) ->
ok;
handle_request({'GET', page, #{id := ID}}) ->
ok;
因为更合理的划分,我们在编译的时候就可以得到更多 warning。同时也简化了正则表达式的实现,可以使用 tagged DFA 来一遍扫描。
失败人士的口号是,performance comes mostly from correctness。所以失败人士拒绝跑分。
为什么要支持子查询
子查询和 aggregation 一起的时候,需要正确判断到底是 where 还是 having。我们不能只看表达式里有没有出现 aggregation。举个例子
test(DB, subquery_having) ->
razor_db:select(
DB,
[ Name
|| #{id := ID, name := Name} <- from(i1),
group_by(Name),
Count <- [count(ID)],
exists(
select(
[ #{count => C}
|| #{count := C} <- from(i2),
C == Count
]))
]);
生成的代码是这样的
test(DB, subquery_having) ->
[Name
|| {Name}
<- razor_db:query(DB,
"SELECT T1.name FROM i1 AS T1 GROUP BY "
"T1.name HAVING (EXISTS((SELECT T2.count "
"AS count FROM i2 AS T2 WHERE (T2.count "
"= count(T1.id)))))",
[])];
为什么一定要支持 Common Table Expression
因为根据 SQL 标准,Common Table Expression 必须 fencing
也就是我们把过滤条件写在最后的 SELECT 里,SQL 标准不允许 planner 把这个过滤条件挪到 Common Table Expression 里。所以必须把过滤条件写在 Common Table Expression 里。只要这个过滤条件里有参数,那么就意味着不可以手工写一段 SQL,拼在最前面了。
<br />
{br, []}
<span><a href="index.html">Index</a></span>
{span, [], [{a, [{href, <<"index.html">>}], [<<"Index">>]}]}
要注意的是,最后的 razor_api_example,就已经是接近完整的代码了。
包括前面各种例子,都是用 pattern matching 来展示结果的,并没有太大必要增加 Live Coding。因为代码已经太多了。