2

我正在尝试“净化”我的一些 Clojure 函数。我想达到一个点,即我所有的副作用代码都在一个函数中显式声明。在开始时获取一些数据并在结束时将其写入数据库很容易,并且在两者之间只有一个纯函数转换。但是,通常的情况是转换函数需要在逻辑中间某处读取另一个 DB:

(defn transform-users
      [users]
      (let [ids (map :id users)
            profiles (db/read :profiles ids)]
       (profiles->something profiles)))

(->> (db/read :users)
     (transform-users)
     (db/write :something)

显然这是一个非常简单的例子,但重点是,我如何从那里获得副作用db/read功能,我怎样才能使transform-users纯(并且作为一个好处,易于测试)?

4

2 回答 2

1

您可以在这里做的一件事是一种类似依赖注入的方法,将(潜在的)副作用函数作为可选参数提供,例如:

(defn transform-users
  [users & {:keys [ids->profiles]
            :or {ids->profiles #(db/read :profiles %)}]
  (let [ids (map :id users)
        profiles (ids->profiles ids)]
    (profiles->something profiles)))

这应该很容易测试,因为您可以毫不费力地模拟注入的函数。作为奖励,通过提供默认值,您可以记录您的期望并让函数方便调用。

于 2014-11-19T17:20:13.593 回答
1

为什么要将配置文件的读取与转换配置文件结合起来?

  (->> (db/read :users)
       (map :id)
       (db/read :profiles)
       (profile->something)
       (db/write :something)

(这也暴露了您正在对数据库进行两次往返的事实。在哪里 db/read :user-profiles ?)

  (->> (db/read :user-profiles)
       (profile->something)
       (db/write :something)

也许:

  (->> (read-profiles-from-users)
       (profile->something)
       (db/write :something)
于 2014-11-19T22:57:55.647 回答