Skip to main content
函数 用于封装可复用的计算逻辑。运行时还提供两类“调度语义”:
  • async def:在运行时线程池执行函数体,并返回 Future/CompletableFuture
  • sync def:在宿主提供的主线程执行器上执行函数体,并返回 CompletableFuture
本章介绍函数的定义、调用方式,以及 async/sync/await 相关语法与边界。

函数基础

定义函数

Fluxon 使用 def 关键字定义函数,形如:
def add(a, b) = &a + &b
要点:
  • 参数列表括在 () 中,多个参数使用逗号分隔。
  • = 右侧是一个表达式,作为函数体的返回值
  • 在函数体内部需要通过 &a&b 读取参数的值,与读取普通变量的规则完全一致。
当函数逻辑较复杂时,可以使用大括号包裹多条语句:
def max(a, b) = {
	if &a >= &b then &a else &b
}
在块体形式中:
  • 可以声明中间变量、使用循环与分支。
  • 最后一条 return 或表达式的值会作为函数的返回结果(见下文)。

Lambda 表达式

Lambda 是可作为值传递的匿名函数,参数写在竖线之间:
inc = |x| &x + 1
pairSum = |a, b| { sum = &a + &b; &sum }
要点:
  • 单参数可使用 || expr 简写,隐式参数名为 it(在体内用 &it 读取)。
  • 需要显式参数名或多参数时,使用 |x||a, b| 等形式。
  • 右侧可为单表达式,或 { ... } 包裹的多语句块。
  • 参数与外部变量在体内一律用 & 读取;外部局部变量会被闭包捕获,调用时读取的是当前值。

调用与组合

  • 直接传给期待函数值的内置函数/扩展,如集合操作:
list = [1, 2, 3]
doubled = &list :: map(|x| &x * 2)
doubled2 = &list :: map(|| &it * 2)
  • 存储后显式调用,可用运行时提供的 call
adder = |x| &x + 1
result = call(&adder, [5])   # => 6
Lambda 可以嵌套、返回或放入集合,常用于 map/each 等链式处理。

调用函数

定义函数后,可以像其他语言一样通过 name(arg1, arg2) 语法调用:
result = add(1, 2)
best = max(&x, &y)
调用时注意:
  • 调用点传入的参数是普通表达式,可以是字面量、变量引用、函数调用等。
  • 传参时不需要在函数名或参数名上写 &,只有在函数体内部访问参数与外部变量时才需要 &
函数同样可以作为值进行传递或存储在映射、列表中(取决于运行时能力与宿主集成),具体能力以函数目录与运行时文档为准。

提前返回

在块体函数中,return 可用于提前返回:
def abs(n) = {
	if &n >= 0 {
		return &n
	}
	return -&n
}
与控制流章节中的示例一致:
  • return 会立即结束当前函数执行,后续语句不会再运行。
  • 如果函数体中没有显式的 return,则最后一个表达式的值会作为返回结果;若函数体为空,则返回 null(具体行为以实现为准)。

异步基础

异步函数

Fluxon 提供 async def 语法定义异步函数;调用时会返回一个异步句柄(底层通常是 FutureCompletableFuture):
async def fetchUser(id) = {
	response = http-get("/users/" + &id)
	return &response
}
示例中的 http-get 作为虚构函数,仅用于说明异步调用形态。
如果异步函数仅包含单个表达式,也可以写成简写形式:
async def delay(ms) = sleep(&ms)

主线程函数(sync def)

如果你需要把一段逻辑固定在宿主的“主线程”执行(例如游戏/GUI 主线程),可以用 sync def 定义主线程函数。 调用主线程函数会返回 CompletableFuture,通常需要配合 await 获取结果:
sync def onMainThread(x) = &x + 1

result = await onMainThread(41)
主线程执行器由宿主通过 FluxonRuntime#setPrimaryThreadExecutor 提供;详见 运行时配置与调试

等待与结果获取

await expr 会在 expr 返回 Future/CompletableFuture 时等待其完成,否则直接返回 expr 的值。 它是一个“可等待也可直通”的表达式,因此可以自然嵌入控制流与函数返回。 例如:
async def loadAndLog(id) = {
	user = await fetchUser(&id)
	log("loaded user: " + &user.name)
	return &user
}
示例中的 &user.name 使用成员访问 .(反射)。嵌入式集成默认关闭反射访问,如需使用请参阅 成员访问(.)
示例中的 fetchUserlog 作为虚构函数,仅用于说明异步调用形态。

从宿主注册 async 函数(示例)

如果你希望文档示例能落到“可运行”的最小闭环,推荐由宿主侧注册一个 async 函数,再在脚本侧 await
import org.tabooproject.fluxon.Fluxon;
import org.tabooproject.fluxon.runtime.FluxonRuntime;

public class Demo {
    public static void main(String[] args) {
        FluxonRuntime runtime = FluxonRuntime.getInstance();
        runtime.registerAsyncFunction("doubleAsync", 1, ctx -> ctx.getNumber(0).intValue() * 2);

        Object result = Fluxon.eval("await doubleAsync(21)");
        System.out.println(result); // 42
    }
}

异步与控制流

由于 Fluxon 的控制结构本身就是表达式,因此可以自然地与异步函数组合使用:
async def loadAll(ids) = {
	results = []
	for id in &ids {
		user = await fetchUser(&id)
		results += [&user]
	}
	return &results
}
可以在 whenif 或循环内部使用 await
async def loadWithFallback(id) = {
	user = await fetchUser(&id)
	when &user.status {
		"ok" -> return &user
		"not-found" -> return await fetchGuestUser()
		else -> return null
	}
}
示例中的 fetchUserfetchGuestUser 作为虚构函数,仅用于说明异步调用形态。

上下文调用

除了普通的函数调用外,Fluxon 还提供 上下文调用 它用于把某个值临时设为“当前目标”,并在其基础上调用扩展函数。 例如,给玩家发送一个信息,可以写作 &player :: sendMessage("Hello"),而不是 sendMessage(&player, "Hello")

基本语法

target :: func(args...)
  • target:任意表达式的结果,会被设置到运行时环境的 target 槽位中。
  • func(args...):在该 target 作为目标的上下文中执行的表达式,通常是扩展函数调用。
示例:
import 'fs:time'

time() :: formatTimestamp(1755611940830L)
如果 fs:time 库提供了 formatTimestamp 扩展函数,它会在 time() 这个上下文中被调用,并返回格式化后的字符串。

链式上下文调用

:: 可以多次连续使用,前一个上下文的结果会作为下一个上下文调用的 target
import 'fs:time'

time() :: formatTimestamp(1755611940830L) :: split("-")
含义近似于:
parts = split(formatTimestamp(time(), 1755611940830L), "-")
这个例子仅用于说明链式调用的效果,不代表可以等价于这种写法
常见的链式模式:
  • 对字符串做一系列转换:
    text = 'hello world'
    &text :: uppercase() :: split(' ')
    
  • 对集合进行多步操作:
    list = [1, 2, 3]
    &list :: size() :: toString()
    

与引用 & 的组合

上下文调用经常与引用操作符 & 一起使用,把某个变量的当前值作为上下文目标:
list = [1, 2, 3]
&list :: contains(1)
该语法的解析顺序是 先取引用,再做上下文调用,即 (&list) :: contains(1)
同样地,可选引用也能与上下文调用搭配:
(&?list ?: []) :: size()
list 未定义或为 null&?list 会产生 null,再通过 ?: 提供默认空列表,最终对 [] 调用 size()

与 Elvis / 条件等操作符的配合

上下文调用本质上仍是一个普通表达式结果,因此可以和 Elvis、条件表达式等组合:
list = [1, 2, 3]

has = &list :: contains(1) ?: false

if &list :: size() > 2 then 'large' else 'small'
也可以先通过 Elvis 构造安全的默认值,再做上下文调用:
list = null
(&list ?: [1]) :: contains(1)

代码块形式的上下文调用

:: 右侧除了可以是单个调用表达式,还可以是一个代码块 { ... }。在代码块内,多条语句都会在同一个 target 上下文中执行:
import 'fs:time'

time() :: {
	formatted = formatTimestamp(1755611940830L)
	parts = &formatted :: split(' ')
	&parts
}
在上例中:
  • 进入 { ... } 时,运行时会把 time 设置为当前上下文目标。
  • 块内可直接调用与 time 相关的扩展函数,例如 formatTimestamp
  • 块执行完毕后,原来的上下文目标会自动恢复(不会泄漏到块外)。
实现上,解释器会在进入上下文调用时保存旧的 target,把 target 设为 :: 左侧表达式的结果, 执行完右侧表达式或代码块后再恢复旧值。字节码生成器也遵循同样的逻辑。

相关链接