在我的Clojure REPL中看似神奇的行为

时间:2014-08-04 19:47:59

标签: clojure leiningen

我正在修补Clojure,目前正在试验clojure.lang.PersistentQueue来模拟Dijkstra的睡觉理发师问题中的等候室。

barber.core=> (def q (ref clojure.lang.PersistentQueue))
#'barber.core/q
barber.core=> q
#<Ref@37c3a6f0: clojure.lang.PersistentQueue>
barber.core=> @q
clojure.lang.PersistentQueue
barber.core=> (dosync (alter q concat :customer))

IllegalArgumentException Don't know how to create ISeq from: java.lang.Class  clojure.lang.RT.seqFrom (RT.java:505)
barber.core=> (dosync (alter q conj :customer))

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword  clojure.lang.RT.seqFrom (RT.java:505)
barber.core=> (dosync (alter q conj :customer))
(:customer)

如您所见,我发送了两次相同的命令。它第一次抛出异常。然而,第二次似乎工作得很好。我现在可以conjpop我的q直到希基的奶牛回家。

在哪个世界这是可以接受的?幕后发生了什么,我没有看到?

Whatever is going on here, I don’t like it.

1 个答案:

答案 0 :(得分:9)

PersistentQueue是一个类,而不是该类的实例。请改用PersistentQueue/EMPTY

其次,concat返回一个惰性序列,无论您传入的是什么类型。您不能在队列上concat并获得队列。请使用多态的conj

很多都是未定义的行为。垃圾进,垃圾出。

我认为正在发生的是懒惰与错误的相互作用。当您在concat课程上尝试PersistentQueue时,成功会返回延迟序列。尝试打印值时,错误发生在事务外部。打印导致评估序列的第一个元素(PersistentQueue类),但concat是懒惰的:它还没有尝试制作第二个参数(:customer)。您的Ref现在包含一个惰性序列,其第二个元素尚未被评估。

第二次尝试修改Ref时,conj强制评估Ref中延迟序列的下一个可用元素,产生另一个错误。由于序列无法生成任何值,因此其值为延迟序列。

现在您的Ref包含一个空序列,其conj被定义为调用cons。现在你的Ref包含一个非懒惰的一个元素序列,实际上是一个列表。