首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >cli-工具配置文件的验证过程:避免嵌套if

cli-工具配置文件的验证过程:避免嵌套if
EN

Stack Overflow用户
提问于 2016-08-29 12:34:45
回答 3查看 110关注 0票数 3

考虑这段完全嵌套的clojure代码,它描述了向命令行工具传递的edn-config文件的验证过程。

代码语言:javascript
复制
(defn -main [& [config-path]]
  (if config-path
    (if-let [content (read-file config-path)]
      (if-let [raw-data (read-edn content)]
        (if-let [parsed (parse raw-data)]
          (start-processing parsed)
          (error "parsing-error"))
        (error "invalid-edn-format"))
      (error "file-not-found"))
    (error "no argument"))

注意:被调用的函数是虚拟函数。

这就需要更少的嵌套和更少强制的方法来做到这一点。你对这里的改进有什么建议吗?

上面的函数可以归结为实际的4个验证函数,它们在一个链中调用:(1)参数-检查,(2)文件读取,(3)分析-EDN,(4)分析-数据。它们对待“错误”的方式不同:对于1和4,我使用的是clojure.spec,因此:cljure.spec/ fail在失败时返回。其他的(2和3)将抛出一个异常,当有问题时。这使得这里的抽象变得特别困难。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-08-29 14:52:43

使用Either monad。

首先,您需要修改函数以返回Either

代码语言:javascript
复制
(require '[cats.monad.either :as either])

(defn assert-config-path-e [config-path]
  (if config-path
    (either/right config-path)
    (either/left "no argument")))

(defn read-file-e [config-path]
  ;; interpret return value as error
  (if-let [content (read-file config-path)]
    (either/right content)
    (either/left "file-not-found")))

(defn read-edn-e [content]
  (try
    (read-edn content)
    (catch ...
      ;; interpret thrown exception as error
      (either/left "invalid-edn-format"))))

(defn parse-e [raw-data]
  (if-let [parsed (parse raw-data)]
    (either/right parsed)
    (either/left "parsing-error")))

Right值表示成功的计算,Left -错误已经发生.

之后,使用do-表示法/mlet组合所有内容。

代码语言:javascript
复制
(require '[cats.core :as cats])

(defn -main [& [config-path]]
  (cats/mlet [_ (assert-config-path-e config-path)
              content (read-file-e config-path)
              raw-data (read-edn-e content)
              parsed (parse-e raw-data)]
    (cats/return (start-processing parsed)))
票数 1
EN

Stack Overflow用户

发布于 2016-08-29 13:58:35

使用delay延迟计算,但允许将值绑定到本地let变量。接下来使用and来计算表达式,但是会在nil上短路,这就是你想要的.然后,cond将给出特定的错误消息,并在没有错误的情况下评估最后一步。

代码语言:javascript
复制
(defn -main [& [config-path]]
  (let [content  (delay (read-file config-path))
        raw-data (delay (read-edn @content))
        parsed   (delay (parse @raw-data))]
    (and config-path
         @content
         @raw-data
         @parsed)
    (cond
      (nil? config-path) (error "no argument")
      (nil? @content)    (error "file-not-found")
      (nil? @raw-data)   (error "invalid-edn-format")
      (nil? @parsed)     (error "parsing-error")
      :else (start-processing @parsed))))

它具有以下优点:

  • 这更贴切
  • 容易理解(也许,一旦你通过delay?)
  • 只剩下所谓的函数,而不是强迫他们去做不属于他们定义范围内的事情,保持这些功能的纯洁性。
  • 不涉及宏(不是坏事,但使用核心函数更好)
票数 1
EN

Stack Overflow用户

发布于 2016-08-29 13:36:48

我将考虑为逻辑的每个部分编写单独的函数,以便它们能够处理验证,并使用一致的方法报告错误(在本例中为例外):

代码语言:javascript
复制
(defn validate-config-path [config-path]
  (if config-path
    config-path
    (throw ...)))

(defn read-raw-config [config-path]
  (try
    (read-file config-path)
    (catch ...
      (throw ...))))

(defn read-edn-config [raw-config]
  (try
    ...
    (catch ...
      (throw ...))))

(defn read-parsed-config [edn-config]
  (try
    ...
    (catch ...
      (throw ...))))

(defn -main [& [config-path]]
  (try
    (-> config-path
        validate-config-path
        read-edn-config
        read-parsed-config)
    (catch Exception e
      (error (.getMessage e)))))

使用这种方法,您可以轻松地分别测试每个部分(因为它封装在一个单独的函数中),并在其他地方重用它。您还可以集中处理在一个地方向用户显示错误的方式,这样就可以更改向用户显示编程错误的方式。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39206491

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档