Error handling 如何编写Clojure线程宏?
我正在尝试使用Error handling 如何编写Clojure线程宏?,error-handling,clojure,Error Handling,Clojure,我正在尝试使用failjure/ok->()编写一个线程宏,线程中的最后一个函数需要执行一个条件。代码如下所示: (f/ok->> (function1 param) (function2 param1 param 2) ... ({conditional function here})) (f/ok->> (function1 param) (function2 param1 param 2)
failjure/ok->
()编写一个线程宏,线程中的最后一个函数需要执行一个条件。代码如下所示:
(f/ok->> (function1 param)
(function2 param1 param 2)
...
({conditional function here}))
(f/ok->> (function1 param)
(function2 param1 param 2)
...
(cond (condition?)
(function_if_passes_condition)
#(%))
其中,如果未命中条件,线程宏将返回倒数第二个函数调用的结果。
我试图编写一个检查必要条件的cond
函数,然后如果条件通过,则返回该函数,或者仅返回上一个函数的结果,但是线程宏似乎不会将结果传递给cond
中的函数,而只传递给cond
本身。(不正确)代码如下所示:
(f/ok->> (function1 param)
(function2 param1 param 2)
...
({conditional function here}))
(f/ok->> (function1 param)
(function2 param1 param 2)
...
(cond (condition?)
(function_if_passes_condition)
#(%))
我想知道是否有一个干净的方法来正确地做到这一点。我认为有可能编写一个具有这种功能的全新线程宏,但到目前为止,我所有的尝试都没有成功(我以前没有编写一个实现线程宏的
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)))
为此:
(-> 10
(+ 3)
(* 100)
(if (century) ; <= arg goes here
:pass
:fail))
并进行单元测试。确保遵循将args包装在带引号的向量中的模式。这模拟了[&args]
在defmacro
表达式中所做的操作
(dotest
(pprint/pprint
(check->-impl '[10
(+ 3)
(* 100)
(century?)])
))
结果:
args => [10 (+ 3) (* 100) (century?)] ; 1 (from spyx)
[10 (+ 3) (* 100) (century?)] ; 2 (from pprint)
(clojure.core/let
[tst.demo.core/x (clojure.core/-> 10 (+ 3) (* 100))]
(if (century? x) :pass :fail))
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
Ran 3 tests containing 4 assertions.
0 failures, 0 errors.
因此,我们看到(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)
))
结果
all-but-last => (10 (+ 3) (* 100)) ; from (1)
last-arg => (century?) ; from (2)
(century?) ; from pprint
注意发生了什么。我们看到了修改后的变量,但输出也发生了变化。编写更多代码:
(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
哎呀!append
函数始终返回向量。只需使用->list
将其转换为列表即可。您还可以键入(应用列表…
)
现在,我们可以使用语法引号创建输出模板代码:
(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))))
结果:
args => [10 (+ 3) (* 100) (century?)] ; 1 (from spyx)
[10 (+ 3) (* 100) (century?)] ; 2 (from pprint)
(clojure.core/let
[tst.demo.core/x (clojure.core/-> 10 (+ 3) (* 100))]
(if (century? x) :pass :fail))
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
Ran 3 tests containing 4 assertions.
0 failures, 0 errors.
参见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))))
注意:正确使用~
(eval)和~@
(eval拼接)非常重要。容易出错。现在我们得到
(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))
真的试试看。从带引号的向量中展开参数,并调用宏而不是impl函数:
(spyx-pretty :final-result
(check-> 10
(+ 3)
(* 100)
(century?)))
输出:
:final-result
(check-> 10 (+ 3) (* 100) (century?)) =>
:pass
并编写一些单元测试:
(dotest
(is= :pass (check-> 10
(+ 3)
(* 100)
(century?)))
(is= :fail (check-> 10
(+ 3)
(* 101)
(century?))))
结果:
args => [10 (+ 3) (* 100) (century?)] ; 1 (from spyx)
[10 (+ 3) (* 100) (century?)] ; 2 (from pprint)
(clojure.core/let
[tst.demo.core/x (clojure.core/-> 10 (+ 3) (* 100))]
(if (century? x) :pass :fail))
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
Ran 3 tests containing 4 assertions.
0 failures, 0 errors.
您也可能对本书感兴趣:
您的问题陈述似乎有点模糊,因此我将解决问题的简化版本 请记住,宏是一种代码转换机制。也就是说,它将您希望编写的代码转换为编译器可以接受的代码。这样,最好将结果视为编译器扩展。编写宏非常复杂,几乎总是不必要的。所以,除非你真的需要,否则不要这样做 让我们编写一个助手谓词和单元测试:
(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)))
为此:
(-> 10
(+ 3)
(* 100)
(if (century) ; <= arg goes here
:pass
:fail))
并进行单元测试。确保遵循将args包装在带引号的向量中的模式。这模拟了[&args]
在defmacro
表达式中所做的操作
(dotest
(pprint/pprint
(check->-impl '[10
(+ 3)
(* 100)
(century?)])
))
结果:
args => [10 (+ 3) (* 100) (century?)] ; 1 (from spyx)
[10 (+ 3) (* 100) (century?)] ; 2 (from pprint)
(clojure.core/let
[tst.demo.core/x (clojure.core/-> 10 (+ 3) (* 100))]
(if (century? x) :pass :fail))
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
Ran 3 tests containing 4 assertions.
0 failures, 0 errors.
因此,我们看到(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)
))
结果
all-but-last => (10 (+ 3) (* 100)) ; from (1)
last-arg => (century?) ; from (2)
(century?) ; from pprint
注意发生了什么。我们看到了修改后的变量,但输出也发生了变化。编写更多代码:
(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
哎呀!append
函数始终返回向量。只需使用->list
将其转换为列表即可。您还可以键入(应用列表…
)
现在,我们可以使用语法引号创建输出模板代码:
(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))))
结果:
args => [10 (+ 3) (* 100) (century?)] ; 1 (from spyx)
[10 (+ 3) (* 100) (century?)] ; 2 (from pprint)
(clojure.core/let
[tst.demo.core/x (clojure.core/-> 10 (+ 3) (* 100))]
(if (century? x) :pass :fail))
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
Ran 3 tests containing 4 assertions.
0 failures, 0 errors.
参见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))))
注意:正确使用~
(eval)和~@
(eval拼接)非常重要。容易出错。现在我们得到
(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))
真的试试看。从带引号的向量中展开参数,并调用宏而不是impl函数:
(spyx-pretty :final-result
(check-> 10
(+ 3)
(* 100)
(century?)))
输出:
:final-result
(check-> 10 (+ 3) (* 100) (century?)) =>
:pass
并编写一些单元测试:
(dotest
(is= :pass (check-> 10
(+ 3)
(* 100)
(century?)))
(is= :fail (check-> 10
(+ 3)
(* 101)
(century?))))
结果:
args => [10 (+ 3) (* 100) (century?)] ; 1 (from spyx)
[10 (+ 3) (* 100) (century?)] ; 2 (from pprint)
(clojure.core/let
[tst.demo.core/x (clojure.core/-> 10 (+ 3) (* 100))]
(if (century? x) :pass :fail))
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
Ran 3 tests containing 4 assertions.
0 failures, 0 errors.
您也可能对本书感兴趣:
您熟悉
cond->
吗?例如,(>“tuba”(string/replace#a“e”)(str“r”)(cond->(偶数12)(str“s”)
产生“tubers”,因为12是偶数。您熟悉cond->
?例如,(>“tuba”(string/replace#a“e”)(str“r”)(cond->(偶数12)(str“s”))
产生“tubers”,因为12是偶数。