Clojure 谓词的副作用是什么?为什么它们不好?

Clojure 谓词的副作用是什么?为什么它们不好?,clojure,lazy-evaluation,Clojure,Lazy Evaluation,我想知道fns的谓词(如remove或filter)的副作用是什么。似乎有一系列的可能性。显然,如果谓词写入文件,这是一个副作用。但是考虑一下这样的情况: (def *big-var-that-might-be-garbage-collected* ...) (let [my-ref *big-var-that-might-be-garbage-collected*] (defn my-pred [x] (some-operation-on my-ref x))) 即使对

我想知道
fn
s的谓词(如
remove
filter
)的副作用是什么。似乎有一系列的可能性。显然,如果谓词写入文件,这是一个副作用。但是考虑一下这样的情况:

(def *big-var-that-might-be-garbage-collected* ...)

(let [my-ref *big-var-that-might-be-garbage-collected*]
  (defn my-pred
    [x]
    (some-operation-on my-ref x)))
即使
的某些操作仅仅是一个不改变状态的查询,
my pred
保留了对
*big…
的引用,这一事实也改变了系统的状态,因为无法对big var进行垃圾收集。这也被认为是副作用吗

在我的例子中,我想在谓词中写入日志系统。这是副作用吗


为什么谓词中的副作用不被鼓励?这是因为
filter
remove
以及它们的朋友们工作懒散,以至于您无法确定何时调用谓词(以及-因此-何时发生副作用)?

在评估函数是否纯时,通常不考虑GC,尽管许多使函数不纯的操作可能会产生GC效果

日志记录是一种副作用,它会改变程序或世界中的任何状态。纯函数获取数据并返回数据,而不修改任何其他内容

介绍了在函数式语言中避免副作用的原因

问题在于确定对函数的任何给定调用何时甚至是否会发生副作用

如果您只关心相同的输入返回相同的答案,那么您很好。副作用取决于功能的执行方式。例如

(first (filter odd? (range 20)))
; 1
但是如果我们安排
odd?
按原样打印它的参数:

(first (filter #(do (print %) (odd? %)) (range 20)))
它将在返回
1
之前打印
012345678910111213141516171819

原因是,
filter
(如果可以的话)以32个元素的块处理其序列参数

如果我们将限制从
范围中去掉

(first (filter #(do (print %) (odd? %)) (range)))
。。。我们得到一个完整大小的块打印:
012345678910111213141516171819012345678910112131415161718192021222325262728293031


仅仅打印论点是令人困惑的。如果副作用严重,事情可能会严重出错

my ref
是否会成为与
*big…
相同对象的另一个引用,这样只要
my pred
存在,它就不会被垃圾收集?这正是我的观点。我在寻找一个“微妙”副作用的例子。下面的答案提供了更多信息的链接。关于Clojure或Haskell的任何书籍或网络介绍也将讨论副作用的危险。副作用在Clojure和Haskell中尤其有问题,因为他们在许多情况下使用惰性评估。这真的让我大开眼界。