我一直致力于将我用Python编写的静态网站生成器转换为Racket。这主要是为了更好地了解Racket的学习练习。我现在已经使用了Racket版本,但有一件事我更喜欢Python版本:pathlib.Path
对象。生成器有很多路径处理,在Python代码中看起来像这样:
render_template(root / "templates" / "index.jinja")
而Racket代码看起来更像是这样:
(render-template (build-path (root) "templates" "index.jinja"))
我发现在Python代码中能够更清晰地读取路径,因此我想修改Racket阅读器以支持以下内容:
(render-template (root) / "templates" / "index.jinja")
与读者一起玩,我想出了如何让这种表达起作用:
(render-template / foo / templates / index.jinja /)
我对语法没问题,但我无法弄清楚如何将路径元素评估为Racket表达式而不仅仅是字符串。即使我确实想到了这一点,我仍然有一个问题,即天真的字符串处理会导致类似的问题:
(render-template / (root) / (string-join
(list "templates" "subdir") "/")
/ "index.jinja" /)
那么,关于我在这里可以做什么的任何建议/意见? :)
答案 0 :(得分:3)
如果我理解正确,原来你有这样的事情:
(define (root)
"/")
(define (render-template path)
(displayln path))
(render-template (build-path (root) "templates" "index.jinja"))
;; => /templates/index.jinja
我建议只是改变render-template
以便它可能
用多个路径调用"部件" - 它处理呼叫
build-path
为你:
(define (render-template . path-parts)
(define path (apply build-path path-parts))
(displayln path))
现在你可以这样称呼它:
(render-template (root) "templates" "index.jinja")
;; => /templates/index.jinja
顺便说一下,以原始方式调用它仍然有效,因为
在这种情况下,build-path
将充当身份:
(render-template (build-path (root) "templates" "index.jinja"))
;; => /templates/index.jinja
我认为这是最" Rackety"办法。一件好事
s表达式是你不必输入"分隔符"例如,
或/
。
空间就足够了。而且我认为您阅读和编写的球拍代码越多,
你越有这种感觉。
当然,也许最安全的东西就是制作能力 你自己的小(或大)语言。如果你想要一个" DSL"来写 主要由路径组成的文件,可能是一回事。但在 在这种情况下,我不确定我是否看到了大胜。
如果有的话,也许你只想要一个宏
这使/
在此上下文中充当空格。即到
make /
表示"没有"它最终需要在扩展代码中。
例如:
#lang racket/base
(require (for-syntax racket/base syntax/parse))
(define-syntax (render-template stx)
(define-splicing-syntax-class pp
(pattern (~seq part (~optional (~literal /)))))
(syntax-parse stx
[(_ p:pp ...) #'(do-render-template p.part ...)]))
(define (do-render-template . path-parts)
(define path (apply build-path path-parts))
(displayln path))
(render-template (root) / "templates" / "index.jinja")
;; => /templates/index.jinja
(render-template (root) "templates" "index.jinja")
;; => /templates/index.jinja
请注意,此处/
是完全可选的。他们被视为
空格。
另请注意,实际工作是在函数中完成的,现在重命名为do-render-template
。宏只是一个包装器。通常情况下,宏必须尽可能少地完成工作,并且可以成为函数的函数。
但是,再一次,我个人不会为这个宏而烦恼,我会采用上面建议的方法。
更新:作为p.s.,如果我理解正确,Python PathLib.Path
将/
定义为运算符?那么,Racket并没有真正拥有"运营商"。它有功能。像/
这样的数学函数接受任意数量的参数。因此,我们写10 / 5 / 2
代替(/ 10 5 2)
。实际上,我们将整圈带回build-path
:一个可以接收任意数量路径部分的函数。
我想您可以有效地将build-path
重命名为/
:
(require (rename-in (except-in racket /)
[build-path /]))
(/ (root) "templates" "index.jinja")
但这并不是真正的运算符重载,因为这些是普通函数而不是方法。并且......我不会这样做。 :)