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

Jez*_*mas 1 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)
Run Code Online (Sandbox Code Playgroud)

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

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

无论发生什么,我都不喜欢它.

Stu*_*rra 9

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

其次,concat无论您传入的类型如何,都会返回一个延迟序列.您无法concat在队列中获取队列.使用conj,这是多态的,而不是.

很多这是未定义的行为.垃圾进垃圾出.

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

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

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

  • 这种语言是伏都教. (5认同)
  • @JezenThomas你真的不需要了解斯图尔特在这里所说的细节:这听起来像伏都教,因为他正在解释当你做违法行为时实际发生的事情的实施细节,这一切都相当深奥.但是,如果你只是按照他的第一句话的建议,你将避免陷入奇怪的错误情景; 你的Clojure程序不必关心伏都教. (4认同)