# set 与 def `set` 和 `def` 都会把一个值绑定到名字下,且二者的求值结果都是所绑定的值。它们的区别在于写入哪个作用域: | | `set` | `def` | |---|-------|-------| | 模块作用域下 | 写入模块命名空间 | 写入模块命名空间 | | 函数内部 | 创建或重新绑定局部变量 | 写入模块命名空间 | | 具名函数 | 像普通值一样绑定闭包 | `def name {\|params\| ...}` 声明一个函数 | | 惯用法 | 循环计数器、累加器、临时状态 | 模块常量、配置、共享状态、函数 | 只需记住一条规则:`def` 始终写入模块命名空间;当作用域内存在局部变量时,`set` 写入该局部变量。 在模块作用域下两者一致——都写入命名空间——这也是脚本在顶层可以随意使用其中任何一个的原因。二者的区别只在函数和闭包内部才会显现。 ## 模块作用域 —— `def` 模块级绑定对文件中的所有函数和闭包,以及模块的导入者都可见。声明请使用 `def`:
def base-url "https://api.example.com"
def retry-limit 3

def url-for {|path| [str $base-url $path]}
`def` 也是声明具名函数的语法:
def greet {|name| [str "hello " $name]}
声明不会被提升:`def` 必须在使用该名字之前执行。
[greet "Alice"]            # error — greet is not defined yet
def greet {|name| ...}
在顶层,`set` 写入的是同一个模块命名空间,因此这样也能工作——但模块声明的惯例是使用 `def`:
set max-runs 3             # works at top level — writes the namespace
def retry-limit 3          # preferred for module declarations
## 局部作用域 —— `set` 在函数内部,`set` 会重新绑定已有的局部变量,或在首次使用时创建一个新的局部变量。循环计数器、累加器和临时空间都应使用这种形式:
def total {|prices|
    set sum 0
    for price in $prices {
        set sum ($sum + $price)
    }
    $sum
}
局部变量按引用捕获,因此闭包中 `set` 外层局部变量会修改原变量:
def make-counter {||
    set n 0
    {|| set n ($n + 1) $n}
}

def bump [make-counter]
[bump]                     # => 1
[bump]                     # => 2
对于单位递增,优先使用 `incr` 而不是 `set n ($n + 1)`(见语法与风格)。 ## 函数内的 `def` 写入模块 函数内的值 `def` 不会创建局部变量。它会写入模块命名空间,与出现在顶层时完全一样:
def ready false

def init {||
    def ready true         # updates the module binding
}

[init]
$ready                     # => true
回调正是这样更新共享模块状态的——在模块作用域声明槽位,然后在回调内用 `def` 绑定它:
def last-event nil

def on-event {|ev|
    def last-event $ev
}

def start {||
    [pub/subscribe "events" "on-event"]
}

[start]
# ... when an event arrives, the module binding updates
在同一位置使用 `set` 会创建一个闭包局部变量,而模块绑定保持不变:
def status "idle"

def run {||
    set status "busy"      # new local — module $status stays "idle"
    $status
}
如果模块值是集合,`<<` 可以从任意闭包修改这个共享绑定(裸形式,无需 `set`):
def seen @{}

def watch {||
    @{ 1 2 3 } |> each { $seen << $it }
}
## 如何选择
def config %{ :host "localhost" :port 8080 }   # module state
def handler {|req res| ... }                   # named function

def process {|items|
    set count 0                                # local counter
    for item in $items { incr count }
    $count
}
- 模块常量、配置、共享状态、函数:`def`。 - 局部计数器、累加器、临时变量:`set`。 - 从函数或回调内部重新绑定模块状态:`def`。 - 让值仅对外层函数及其闭包可见:`set`。 ## 另见 - 语法与风格 —— 赋值、追加和捕获的局部变量。 - 函数 —— `def` 形式、闭包、多参数个数(multi-arity)和捕获的局部变量。 - 模块与导入 —— 模块绑定如何导出。 - 元编程 —— 特殊形式表中的 `set`/`def`。