Clojure的dosync是正则序?

简介:
  解释器求值的顺序可以分为应用序和正则序,应用序是先求值参数,再执行表达式;正则序则是先将表达式按照实际参数展开,然后再执行。具体可以看看过去写的 这篇文章

   Clojure的求值可以肯定是应用序的,如执行
(defn mytest [a b] 
      (
if  ( =  a  0 )
          a
          b))
(mytest 
0   1 / 0 )
        

尽管在(mytest 0 1/0)中a绑定为0,如果求值器是完全展开再求值,那应该正常执行并返回a,也就是1;但是因为clojure是应用序,因此参数b的1/0会先计算,这显然会报错。

   clojure的dosync用于将一些表达式包装成事务,Ref的更新操作没有包装在事务里,会抛出异常
;;定义mutable的Ref
 (def song (ref #{}))

;;添加一首歌
(alter song conj 
" dangerous " )

   alter用于向Ref查询并添加元素,用conj将"dangerous"这首歌加入集合,但是alter要求执行在一个事务里,因此上面的代码会报错
java.lang.IllegalStateException: No transaction running (NO_SOURCE_FILE: 0 )

   如果你用dosync包装就没有问题
user =>  (dosync (alter song conj  " dangerous " ))
#{
" dangerous " }

   返回更新后的结果集合。这个跟我们要谈的正则序和应用序有什么关系呢?可能你看出来了,如果说clojure是应用序,那么在表达式 (dosync (alter song conj  " dangerous " ))中,alter也应该先执行,应当照样报" No transaction running"的错误才对,为何却没有呢?难道dosync是按照正则序执行?

   查看dosync的文档
user =>  (doc dosync)
-------------------------
clojure.core
/ dosync
([
&  exprs])
Macro
  Runs the exprs (in an implicit 
do ) in a transaction that encompasses
  exprs and any nested calls.  Starts a transaction 
if  none is already
  running on 
this  thread. Any uncaught exception will abort the
  transaction and flow out of dosync. The exprs may be run more than
  once, but any effects on Refs will be atomic.

   这是一个宏,他的作用是将表达式包装在一个事务里,如果当前线程没有事务,那么就启动一个。
查看源码:

(defmacro dosync
  
" Runs the exprs (in an implicit do) in a transaction that encompasses
  exprs and any nested calls.  Starts a transaction  if  none is already
  running on 
this  thread. Any uncaught exception will abort the
  transaction and flow out of dosync. The exprs may be run more than
  once, but any effects on Refs will be atomic.
"
  [ &  exprs]
  `(sync nil 
~ @exprs))

   本质上dosync是调用了sync这个宏,sync干了些什么?
(defmacro sync
  
" transaction-flags => TBD, pass nil for now

  Runs the exprs (in an implicit 
do ) in a transaction that encompasses
  exprs and any nested calls.  Starts a transaction 
if  none is already
  running on 
this  thread. Any uncaught exception will abort the
  transaction and flow out of sync. The exprs may be run more than
  once, but any effects on Refs will be atomic.
"
  [flags - ignored - for - now  &  body]
  `(. clojure.lang.LockingTransaction
      (runInTransaction (fn [] 
~ @body))))

   找到了,原来是调用了 clojure.lang.LockingTransaction. runInTransaction这个静态方法,并且将exps包装成一个匿名函数

fn []  ~ @body

     因此,dosync并非正则序, dosync是个宏, (dosync (alter song conj  " dangerous " )) 展开之后,其实是
(sync nil (fun [] (alter song conj  " dangerous " )))
   
     这就解答了为什么 (dosync (alter song conj  " dangerous " )) 可以正常运行的疑问。宏的使用,首先是展开,然后才是按照应用序的顺序求值。

文章转自庄周梦蝶  ,原文发布时间2010-07-13 
  

目录
相关文章
|
Java PHP 开发工具
编程语言Clojure入门
在众多的编程语言中,不少开发人员熟悉Java、C#、PHP等。但是很早以前,也有一些小众的语言,比如Lisp语言,它是一种适用于符号处理和自动推理的编程语言,内部使用表结构来表达非数值计算。而Clojure语言是在JVM上实现的Lisp风格的语言,语法与Lisp类似,且可以和Java语言进行互操作
1139 0
编程语言Clojure入门
|
7月前
|
Java Shell API
Scala和Kotlin脚本编程
Scala和Kotlin作为运行在JVM上的编程语言,解决了Java的很多痛点。今天我们来聊聊如何将Scala和Kotlin作为脚本语言使用(Java不支持以脚本形式运行哦)。
58 0
|
Scala Kotlin
How is Kotlin an improvement over Scala, Clojure, or Groovy?
How is Kotlin an improvement over Scala, Clojure, or Groovy?
884 0
|
.NET Java C#
|
Java
《Clojure程序设计》——导读
本节书摘来自异步社区《Clojure程序设计》一书中的导读,作者 【美】Stuart Halloway , Aaron Bedra,更多章节内容可以访问云栖社区“异步社区”公众号查看
2006 0