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中尤其有问题,因为他们在许多情况下使用惰性评估。这真的让我大开眼界。