jim*_*337 2 error-handling clojure
我正在尝试使用failjure/ok->>(https://github.com/adambard/failjure#ok--and-ok-)编写线程宏,线程中的最后一个函数需要一个条件来执行。代码如下所示:
(f/ok->> (function1 param)
(function2 param1 param 2)
...
({conditional function here}))
Run Code Online (Sandbox Code Playgroud)
如果条件未命中,线程宏返回倒数第二个函数调用的结果。我试图写一个cond是检查的必要条件,然后或者如果条件过去了,还是以前的功能而造成的返回的功能函数,但是线程宏似乎结果不传递给内部的功能cond,但只的cond本身。(不正确的)代码如下所示:
(f/ok->> (function1 param)
(function2 param1 param 2)
...
(cond (condition?)
(function_if_passes_condition)
#(%))
Run Code Online (Sandbox Code Playgroud)
我想知道是否有一种干净的方法可以正确地做到这一点。我想有可能编写一个具有这种功能的全新线程宏,但到目前为止我所有的尝试都没有奏效(我之前没有写过一个defmacro实现线程宏的程序,这非常困难)我是 clojure 的新手,有 3 个月的经验)。
你的问题陈述似乎有点模糊,所以我会解决这个问题的简化版本。
请记住,宏是一种代码转换机制。也就是说,它将您希望编写的代码转换为编译器可接受的代码。这样,最好将结果视为编译器扩展。编写宏很复杂,而且几乎总是不必要的。所以,除非你真的需要它,否则不要这样做。
让我们编写一个辅助谓词和单元测试:
(ns tst.demo.core
(:use tupelo.core tupelo.test) ; <= *** convenience functions! ***
(:require [clojure.pprint :as pprint]))
(defn century? [x] (zero? (mod x 100)))
(dotest
(isnt (century? 1399))
(is (century? 1300)))
Run Code Online (Sandbox Code Playgroud)
假设我们要翻译这段代码:
(check-> 10
(+ 3)
(* 100)
(century?) )
Run Code Online (Sandbox Code Playgroud)
进入这个:
(-> 10
(+ 3)
(* 100)
(if (century) ; <= arg goes here
:pass
:fail))
Run Code Online (Sandbox Code Playgroud)
稍微重写一下目标:
(let [x (-> 10 ; add a temp variable `x`
(+ 3)
(* 100))]
(if (century? x) ; <= use it here
:pass
:fail))
Run Code Online (Sandbox Code Playgroud)
现在开始-impl功能。写一点,加上一些打印语句。仔细注意要使用的模式:
(defn check->-impl
[args] ; no `&`
(spyx args) ; <= will print variable name and value to output
))
(defmacro check->
[& args] ; notice `&`
(check->-impl args)) ; DO NOT use syntax-quote here
Run Code Online (Sandbox Code Playgroud)
并通过单元测试驱动它。请务必遵循将 args 包装在带引号的 vector 中的模式。这模拟[& args]了defmacro表达式中的作用。
(dotest
(pprint/pprint
(check->-impl '[10
(+ 3)
(* 100)
(century?)])
))
Run Code Online (Sandbox Code Playgroud)
结果:
args => [10 (+ 3) (* 100) (century?)] ; 1 (from spyx)
[10 (+ 3) (* 100) (century?)] ; 2 (from pprint)
Run Code Online (Sandbox Code Playgroud)
所以我们看到(1)中打印的结果,那么 impl 函数返回(2)中的(未修改的)代码。这是关键。宏返回修改后的代码。然后编译器编译修改后的代码代替原始代码。
用更多的打印写一些更多的代码:
(defn check->-impl
[args] ; no `&`
(let [all-but-last (butlast args)
last-arg (last args) ]
(spyx all-but-last) ; (1)
(spyx last-arg) ; (2)
))
Run Code Online (Sandbox Code Playgroud)
结果
all-but-last => (10 (+ 3) (* 100)) ; from (1)
last-arg => (century?) ; from (2)
(century?) ; from pprint
Run Code Online (Sandbox Code Playgroud)
注意发生了什么。我们看到了修改后的变量,但输出也发生了变化。再写一些代码:
(defn check->-impl
[args] ; no `&`
(let [all-but-last (butlast args)
last-arg (last args)
cond-expr (append last-arg 'x)] ; from tupelo.core
(spyx cond-expr)
))
cond-expr => [century? x] ; oops! need a list, not a vector
Run Code Online (Sandbox Code Playgroud)
哎呀!该append函数始终返回一个向量。只需用于->list将其转换为列表。你也可以输入(apply list ...).
cond-expr => (century? x) ; better
Run Code Online (Sandbox Code Playgroud)
现在我们可以使用语法引用来创建我们的输出模板代码:
(defn check->-impl
[args] ; no `&`
(let [all-but-last (butlast args)
last-arg (last args)
cond-expr (->list (append last-arg 'x))]
; template for output code
`(let [x (-> ~@all-but-last)] ; Note using `~@` eval-splicing
(if ~cond-expr
:pass
:fail))))
Run Code Online (Sandbox Code Playgroud)
结果:
(clojure.core/let
[tst.demo.core/x (clojure.core/-> 10 (+ 3) (* 100))]
(if (century? x) :pass :fail))
Run Code Online (Sandbox Code Playgroud)
看到tst.demo.core/x部分了吗?这是一个问题。我们需要重写:
(defn check->-impl
[args] ; no `&`
(let [all-but-last (butlast args)
last-arg (last args)]
; template for output code. Note all 'let' variables need a `#` suffix for gensym
`(let [x# (-> ~@all-but-last) ; re-use pre-existing threading macro
pred-result# (-> x# ~last-arg)] ; simplest way of getting x# into `last-arg`
(if pred-result#
:pass
:fail))))
Run Code Online (Sandbox Code Playgroud)
注意:正确使用~(eval) 和~@(eval-splicing)很重要。容易出错。现在我们得到
(clojure.core/let
[x__20331__auto__ (clojure.core/-> 10 (+ 3) (* 100))
pred-result__20332__auto__ (clojure.core/-> x__20331__auto__ (century?))]
(if pred-expr__20333__auto__
:pass
:fail))
Run Code Online (Sandbox Code Playgroud)
试试看是真的。从引用的向量中解包 args,并调用宏而不是 impl 函数:
(spyx-pretty :final-result
(check-> 10
(+ 3)
(* 100)
(century?)))
Run Code Online (Sandbox Code Playgroud)
带输出:
:final-result
(check-> 10 (+ 3) (* 100) (century?)) =>
:pass
Run Code Online (Sandbox Code Playgroud)
并编写一些单元测试:
(dotest
(is= :pass (check-> 10
(+ 3)
(* 100)
(century?)))
(is= :fail (check-> 10
(+ 3)
(* 101)
(century?))))
Run Code Online (Sandbox Code Playgroud)
结果:
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
Ran 3 tests containing 4 assertions.
0 failures, 0 errors.
Run Code Online (Sandbox Code Playgroud)
您可能还对本书感兴趣: 掌握 Clojure 宏
| 归档时间: |
|
| 查看次数: |
244 次 |
| 最近记录: |