# TSL 函数与调用

这一篇只负责 `function` / `procedure` 的定义、调用、参数传递和值返回。跨 `unit` 声明边界、外部系统交互和业务函数库只在本页保留最小边界。

## 本篇职责

<!-- section-id: syntax-05-001 -->

回答“如何正确声明 `function` 和 `procedure`、`.tsl` 脚本语句区如何调用后置函数声明、怎样使用参数修饰、普通函数默认参数与可变参数，以及哪些函数写法会直接编译失败”。

## 核心规则

<!-- section-id: syntax-05-002 -->

- 最稳妥的函数骨架仍然是 `function Name(...); begin ... end;`。

<!-- quickstart-rule: function-default -->

- 用户提示词里的“函数”默认对应 `function`，不要自动改写成 `procedure`。

<!-- quickstart-rule: procedure-explicit -->

- `procedure Name(...); begin ... end;` 只在用户明确要求 `procedure` / 过程时生成；不要因为没有返回值就自动改用 `procedure`。
- `procedure` 头后不允许写返回类型；返回类型注解只用于 `function`。
- 在 `.tsl` 文件模型层，脚本语句后可以接函数声明；语句区在前顺序执行，声明区在后提供函数/过程定义。见 [02_core_model.md](02_core_model.md)。
- 在 `.tsf` 文件模型层，顶层 `function` / `procedure` 是模块/函数扩展声明；部署到解释器 `funcext` 后可被脚本直接调用。
- 函数头后默认保留分号；不要为了简写主动省略。
- 函数体内部的普通语句照常用分号结尾；顶层函数/过程声明的 `end` 后必须加分号（写成 `end;`）。
- 函数体内的控制流块（`if`/`while`/`for` 等）的 `end` 后加不加分号都合法；唯一例外是 `if` 的 `then` 块 `end` 与 `else` 之间不能加分号。控制流分号规则见 [07_control_flow.md](07_control_flow.md)。
- 一个函数定义体里可以同时出现主函数和子函数。
- 函数支持参数类型注解和返回值类型注解。
- 不带类型注解时，多个参数用逗号分隔。
- 带类型注解时，多个参数用分号分隔。
- 没有明确类型名证据时，不要为了“看起来更完整”而发明参数类型或返回类型。
- `const` 与 `var` 形参修饰属于文档明确写法。
- 按文档运行时边界，未修饰参数可以写回调用方；这是运行时默认行为，不是语言规范保证。
- `{$varByRef-}` 会把未修饰参数切换成按值传递；`var` 形参仍保持引用语义。
- 在 `{$varByRef-}` 下，调用时可以用 `in` / `out` 前缀逐个参数覆盖默认传递方式。
- 如果不确定任务是否需要写回语义，优先显式用 `const` 形参，或先切到 `{$varByRef-}`；不要依赖未修饰参数的运行时默认行为。
- `return expr;` 会直接返回当前函数结果。
- `exit;` 会立即结束当前函数；在本页最小样例里，如果此前没有写入返回结果，调用方观察到的是默认值 `0`。

<!-- quickstart-rule: named-arguments -->

- 调用时支持命名参数，写法是 `name: value`。
- 命名参数也支持 `call(...)` 这类按函数名或函数指针转调的模型。

<!-- quickstart-rule: named-argument-order -->

- 一旦某次调用里开始使用命名参数，后面的参数就不能再退回位置参数。
- 对二进制函数 / 系统函数直接使用命名参数，会报 `named parameter mode can't support here`；这类函数要先用 TSL 再封一层。
- 函数参数支持默认值。
- `unit interface` 中的默认参数可以引用该接口中可访问的 `const`；实现函数头不要重复声明默认值。
- 默认参数表达式属于新一代 TSL 能力；表达式里引用变量时，该变量按 `0` 求值。面向旧运行时或版本不明时，默认只生成字面量 / 可访问常量默认值。
- 尾部 `...` 形式的可变参数属于文档明确写法。
- 在可变参数函数体里，`Params`、`ParamCount`、`RealParamCount` 都可用。
- 可变参数组可以通过 `...` 转发给另一个函数调用。
- 可变参数组也可以通过 `call(fc, ...)`、`##fc(...)`、`invoke(obj, name, 0, ...)` 转发；这些属于调用转发边界，只在有对应文档明确样例时生成。
- `a := function(...) begin ... end;` 这种匿名函数写法属于文档明确写法。
- 匿名函数可以直接作为参数传入另一个函数。
- `thisFunction(FuncName)` 可以把已知函数绑定成函数值。
- 匿名函数和 TSL 函数值的稳定调用方式仍是 `call(f, ...)` 或 `##f(...)`。
- `f(...)` 这种“函数变量直接调用”写法不作为可写事实；无论 `f` 是匿名函数、`findFunction(...)` 还是 `thisFunction(...)` 返回的函数指针，都不要默认写成直调。
- `::FuncName(...)` 可以指向全局/系统函数，用来绕过当前作用域里的同名局部函数。
- `system.FuncName(...)` 专门强制指定系统函数；它与 `::FuncName(...)` 的全局限定语义不要互相替代。
- `external`、原生函数指针包装、`makeInstance` / C 回调和线程调用的事实见 [17_external_calls_and_threads.md](17_external_calls_and_threads.md)。
- 不要在 `.tsl` 的函数声明区之后继续追加脚本语句。

## 可直接照写示例

<!-- section-id: syntax-05-003 -->

使用这些示例时遵守：

- 普通运行示例默认按 `.tsl` 脚本语句区书写；入口语句放前面，函数 / 过程 / 类型声明放在后置声明区。
- `.tsf` 函数 / 过程示例只按可复用顶层声明理解，不要在 `.tsf` 里追加顺序执行入口语句。
- `procedure` 示例只在用户明确要求 `procedure` / 过程时复制；普通“写函数”任务不要用。
- `external`、原生函数指针包装、C 回调、线程、系统交互和二进制函数边界不在本页硬推断；相关事实见 [17_external_calls_and_threads.md](17_external_calls_and_threads.md) 或函数库文档。

### 基础函数 / 过程骨架

<!-- section-id: syntax-05-004 -->

<!-- tags: 定义函数, 怎么写函数, 函数骨架, 最少几行, procedure 怎么写, 函数放哪里, 先调用后声明 -->

`.tsl` 语句区调用后置函数声明：

代码块身份：可直接照写示例

```tsl
a := 1;
Test();

function Test();
begin
    echo "test";
end;
```

代码块身份：输出片段

```text
test
```

最短函数骨架：

代码块身份：可直接照写示例

```tsl
function Add(a, b);
begin
    return a + b;
end;
```

主函数加子函数：

代码块身份：可直接照写示例

```tsl
function MultiFunc();
begin
    return Twice(3);
end;

function Twice(x);
begin
    return x * 2;
end;
```

显式要求 `procedure` 时的最短骨架：

代码块身份：可直接照写示例

```tsl
procedure LogDone();
begin
end;
```

显式要求 `procedure` 且需要写回参数时的最小运行样例：

代码块身份：可直接照写示例

```tsl
a := 1;
Bump(a);
writeLn(a);

procedure Bump(var x);
begin
    x := x + 1;
end;
```

代码块身份：输出片段

```text
2
```

### 签名增强：参数类型与返回值类型注解

<!-- section-id: syntax-05-005 -->

<!-- tags: 返回值类型, 声明返回类型, 参数标类型, 类型注解, 函数签名 -->

带参数类型和返回值类型：

代码块身份：可直接照写示例

```tsl
function Demo(a: integer): integer;
begin
    return a;
end;
```

带类型时的多参数分隔：

代码块身份：可直接照写示例

```tsl
function Demo(a: integer; b: integer);
begin
    return a + b;
end;
```

参数修饰：

代码块身份：可直接照写示例

```tsl
a := 10;
writeLn(ReadConst(a));
SetVar(a);
writeLn(a);

function ReadConst(const x);
begin
    return x + 1;
end;
function SetVar(var x);
begin
    x := x + 5;
end;
```

结果说明：

- `ReadConst(a)` 输出 `11`
- `SetVar(a)` 之后，`a` 输出 `15`
- 直接给 `const` 形参赋值会编译失败

代码块身份：输出片段

```text
11
15
```

### 参数传递方式

<!-- section-id: syntax-05-006 -->

<!-- tags: 传引用, 按引用传, 引用传递, 传值, 按值传递, 改形参影响外面, 写回调用方, 出参, 入参, 只读参数, 不让函数改 -->

未修饰参数默认写回调用方：

代码块身份：可直接照写示例

```tsl
x := 1;
TouchDefault(x);
writeLn(x);

function TouchDefault(a);
begin
    a := 9;
end;
```

结果说明：

- `TouchDefault(x)` 之后，`x` 输出 `9`

代码块身份：输出片段

```text
9
```

`{$varByRef-}` 与 `var` 形参：

代码块身份：可直接照写示例

```tsl
x := 1;
TouchDefault(x);
writeLn(x);
y := 1;
TouchValue(y);
writeLn(y);
z := 1;
TouchForcedVar(z);
writeLn(z);

function TouchDefault(a);
begin
    a := 9;
end;
{$varByRef-}
function TouchValue(a);
begin
    a := 8;
end;
function TouchForcedVar(var a);
begin
    a := 7;
end;
{$varByRef+}
```

结果说明：

- 默认模式下，`TouchDefault(x)` 后 `x` 输出 `9`
- `{$varByRef-}` 下，未修饰参数版本 `TouchValue(y)` 之后，`y` 仍输出 `1`
- `{$varByRef-}` 下，`var` 形参版本 `TouchForcedVar(z)` 之后，`z` 输出 `7`

代码块身份：输出片段

```text
9
1
7
```

`in` / `out` 调用前缀：

代码块身份：可直接照写示例

```tsl
a := 0;
b := 0;
c := 0;
Touch3(a, b, c);
writeLn(a);
writeLn(b);
writeLn(c);
Touch3(in a, out b, c);
writeLn(a);
writeLn(b);
writeLn(c);

{$varByRef-}
function Touch3(a, b, c);
begin
    a := 1;
    b := 2;
    c := 3;
end;
```

结果说明：

- 在 `{$varByRef-}` 下，直接调用 `Touch3(a, b, c)` 后依次输出 `0`、`0`、`0`
- 同样在 `{$varByRef-}` 下，`Touch3(in a, out b, c)` 后依次输出 `0`、`2`、`0`
- 说明 `in` / `out` 可以在调用点逐个参数覆盖默认传递方式

代码块身份：输出片段

```text
0
0
0
0
2
0
```

### `return` 与 `exit`

<!-- section-id: syntax-05-007 -->

<!-- tags: 返回值, 怎么返回结果, 结果返回, 提前结束, 提前返回, 中途退出函数, 无返回值退出 -->

代码块身份：可直接照写示例

```tsl
writeLn(Demo(1));
writeLn(Demo(0));

function Demo(x);
begin
    if x > 0 then
        exit;
    return 99;
end;
```

结果说明：

- `Demo(1)` 输出 `0`
- `Demo(0)` 输出 `99`
- 说明 `exit;` 会立即结束当前函数体
- 在这个最小例子里，因为 `exit;` 之前没有写入返回结果，调用方观察到的是默认值 `0`

代码块身份：输出片段

```text
0
99
```

### 调用增强：命名参数与混用规则

<!-- section-id: syntax-05-008 -->

<!-- tags: 按参数名传, 命名实参, 关键字参数, 指定参数名, 位置参数混用 -->

命名参数调用：

代码块身份：可直接照写示例

```tsl
writeLn(Pack(a: 1, b: 2));

function Pack(a, b);
begin
    return a * 10 + b;
end;
```

代码块身份：输出片段

```text
12
```

结果说明：

- `Pack(a: 1, b: 2)` 返回 `12`
- `Pack(b: 2, a: 1)` 返回 `12`
- `Pack(1, b: 2)` 返回 `12`

跳过中间参数时，未命中的参数保持 `nil`：

代码块身份：可直接照写示例

```tsl
TestFunc(1, c: 3);

function TestFunc(a, b, c);
begin
    writeLn(ifNil(b));
    return 0;
end;
```

结果说明：

- 输出 `1`
- 说明 `TestFunc(1, c: 3)` 这种调用里，中间参数 `b` 会保持 `nil`

代码块身份：输出片段

```text
1
```

通过 `call(...)` 也支持命名参数：

代码块身份：可直接照写示例

```tsl
writeLn(call("TestFunc", a: 1, c: 2, b: 3));

function TestFunc(a, b, c);
begin
    return a * 100 + b * 10 + c;
end;
```

结果说明：

- 输出 `132`
- 说明 `call(...)` 也支持按参数名传值
- 说明命名参数传入后不再按位置解释，而是按名字绑定到形参

代码块身份：输出片段

```text
132
```

### 默认参数

<!-- section-id: syntax-05-009 -->

<!-- tags: 默认值, 可选参数, 参数省略, 不传就用默认 -->

默认值参数：

代码块身份：可直接照写示例

```tsl
function AddOne(a = 1);
begin
    return a + 1;
end;
```

带类型时也支持默认值：

代码块身份：可直接照写示例

```tsl
function TypedAdd(a: integer = 1): integer;
begin
    return a + 1;
end;
```

多个参数时，后面的参数可以带默认值：

代码块身份：可直接照写示例

```tsl
function Pack(a, b = 2);
begin
    return a * 10 + b;
end;
```

代码块身份：可直接照写示例

```tsl
function Pack(a: integer; b: integer = 2): integer;
begin
    return a * 10 + b;
end;
```

新一代 TSL 的默认值也可以写成表达式：

代码块身份：可直接照写示例

```tsl
function ExprDefault(a = 1 + 2);
begin
    return a;
end;
```

结果说明：

- `AddOne()` 返回 `2`
- `AddOne(5)` 返回 `6`
- `TypedAdd()` 返回 `2`
- `TypedAdd(5)` 返回 `6`
- `Pack(1)` 返回 `12`
- `Pack(a: 1)` 返回 `12`
- `ExprDefault()` 返回 `3`

表达式默认值的版本边界：

代码块身份：可直接照写示例

```tsl
function RefDefault(a, b = a + 1);
begin
    return b;
end;
```

边界说明：

- 在支持默认参数表达式的新一代 TSL 中，`RefDefault(5)` 返回 `1`，因为默认表达式里的变量 `a` 按 `0` 求值
- 这项能力自 2025-08-27 后的 NG 客户端 / 新一代 TSL 服务端提供；目标版本不明时不要生成变量参与的默认表达式

`unit interface` 可以使用接口中可访问的常量作为默认值：

代码块身份：配置片段 / 概念骨架

```text
// UnitDefaults.tsf
unit UnitDefaults;

interface

const default_value = 888;
function F(a, b = 100, c = default_value);

implementation

function F(a, b, c);
begin
    return a + b + c;
end;

end.

// main.tsl
uses UnitDefaults;
echo F(1), ",", F(1, 2);
```

代码块身份：输出片段

```text
989,891
```

说明：默认值只在 `interface` 声明处写一次；实现函数头使用同一组形参，但不重复 `= ...`。

### 可变参数 `...`

<!-- section-id: syntax-05-010 -->

<!-- tags: 任意多个参数, 不定参数, 变长参数, 参数个数不固定, 参数转发 -->

尾部可变参数：

代码块身份：可直接照写示例

```tsl
function SumAll(...);
begin
    s := 0;
    for i, v in Params do
        s := s + v;
    return s;
end;
```

可变参数转发：

代码块身份：可直接照写示例

```tsl
function Forward(...);
begin
    return SumAll(...);
end;

function SumAll(...);
begin
    s := 0;
    for i, v in Params do
        s := s + v;
    return s;
end;
```

`ParamCount` 与 `RealParamCount`：

代码块身份：可直接照写示例

```tsl
function CountArgs(a, b, ...);
begin
    return ParamCount * 10 + RealParamCount;
end;
```

结果说明：

- `SumAll(1, 2, 3, 4)` 返回 `10`
- `Forward(1, 2, 3, 4)` 返回 `10`
- `CountArgs(1, 2, 3, 4)` 返回 `44`
- `CountArgs(1, 2)` 返回 `22`

通过 `call` 与 `##` 转发可变参数：

代码块身份：可直接照写示例

```tsl
writeLn(DoFunc("Sum3", 1, 2, 3));
writeLn(DoFunc2(thisFunction(Sum3), 1, 2, 3));

function Sum3(a, b, c);
begin
    return a + b + c;
end;
function DoFunc(fc, ...);
begin
    return call(fc, ...);
end;
function DoFunc2(fc, ...);
begin
    return ##fc(...);
end;
```

结果说明：

- `DoFunc("Sum3", 1, 2, 3)` 输出 `6`
- `DoFunc2(thisFunction(Sum3), 1, 2, 3)` 输出 `6`

代码块身份：输出片段

```text
6
6
```

通过 `invoke` 转发可变参数：

代码块身份：可直接照写示例

```tsl
writeLn(DoInvoke("Dispatch", "Int", 2, 20, 200));
writeLn(DoInvoke("Dispatch", "Other", 1, 2, 3));

type TestC = class
public
    function FuncA(a, b, c);
    begin
        return a + b + c;
    end;
    function FuncB(a, b, c);
    begin
        return a * 100 + b * 10 + c;
    end;
    function Dispatch(kind, ...);
    begin
        if kind = "Int" then
        begin
            return FuncA(...);
        end
        else
        begin
            return FuncB(...);
        end
    end;
end;
function DoInvoke(fc_name, ...);
begin
    obj := new TestC();
    return invoke(obj, fc_name, 0, ...);
end;
```

结果说明：

- `DoInvoke("Dispatch", "Int", 2, 20, 200)` 输出 `222`
- `DoInvoke("Dispatch", "Other", 1, 2, 3)` 输出 `123`

代码块身份：输出片段

```text
222
123
```

### 匿名函数与函数指针

<!-- section-id: syntax-05-011 -->

<!-- tags: lambda, 闭包, 函数当变量, 函数当参数, 回调, 函数指针, 把函数存起来, 匿名函数作用域, 对象成员, 宿主函数, 临时变量, 捕获外层变量 -->

匿名函数作用域边界：

- 匿名函数定义在类的方法中时，可以访问当前对象的成员和方法。
- 匿名函数不能访问宿主函数的临时变量；不要把它当成会捕获外层临时变量的词法闭包。

匿名函数变量：

代码块身份：可直接照写示例

```tsl
a := function(x, y)
begin
    return x + y;
end;
writeLn(call(a, 1, 2));
writeLn(##a(5, 6));
```

结果说明：

- `call(a, 1, 2)` 输出 `3`
- `##a(5, 6)` 输出 `11`

代码块身份：输出片段

```text
3
11
```

匿名函数也可以直接作为参数传入：

代码块身份：可直接照写示例

```tsl
writeLn(Apply(function(x, y)
begin
    return x * y;
end));

function Apply(fun);
begin
    return call(fun, 2, 3);
end;
```

结果说明：

- 输出 `6`

代码块身份：输出片段

```text
6
```

函数指针变量也可以通过 `findFunction(...)` 拿到：

代码块身份：可直接照写示例

```tsl
f := findFunction("Add");
writeLn(##f(1, 2));

function Add(a, b);
begin
    return a + b;
end;
```

结果说明：

- 输出 `3`

代码块身份：输出片段

```text
3
```

也可以通过 `thisFunction(...)` 从已知函数名直接拿到：

代码块身份：可直接照写示例

```tsl
f := thisFunction(Add);
writeLn(call(f, 3, 4));

function Add(a, b);
begin
    return a + b;
end;
```

结果说明：

- 输出 `7`
- 说明 `thisFunction(Add)` 可以得到稳定可调用的函数值

代码块身份：输出片段

```text
7
```

直接 `f(...)` 不要当成可靠写法：

代码块身份：反例 / 不可照写

```text
// 匿名函数变量直接调用
a := function(x, y)
begin
    return x + y;
end;
writeLn(a(7, 8));
```

结果说明：

- 上面这种匿名函数变量直调会报 `function:<name> compile error or not found`
- `findFunction(...)` 和 `thisFunction(...)` 返回的函数值也不要写成 `f(...)` 直调
- 因此本页只把 `call(f, ...)` 和 `##f(...)` 写成稳定规则

### `::` 指向全局函数

<!-- section-id: syntax-05-012 -->

<!-- tags: 重名, 同名函数, 调全局的那个, 作用域冲突, 指定全局函数 -->

当当前作用域里有同名局部函数时，可以用 `::FuncName(...)` 指定去调全局/系统函数：

代码块身份：可直接照写示例

```tsl
r := Demo();
writeLn(r[0]);
writeLn(r[1]);

function Demo();
begin
    return array(strToInt("123"), ::strToInt("123"));
end;
function strToInt(s);
begin
    return 888;
end;
```

结果说明：

- 第一项 `strToInt("123")` 命中局部函数，输出 `888`
- 第二项 `::strToInt("123")` 命中全局系统函数，输出 `123`
- 说明 `::` 可以绕过当前作用域的同名局部函数，直接指向全局/系统函数

代码块身份：输出片段

```text
888
123
```

如果 `::FuncName(...)` 指向的全局函数本身不存在，会在运行时报 `function:<name> compile error or not found`

### 系统交互专题

<!-- section-id: syntax-05-013 -->

<!-- tags: system 前缀, 指定系统函数, 绕过同名用户函数, 系统函数限定 -->

系统函数可能与用户定义函数同名。需要明确指定系统实现时，使用 `system.FuncName(...)`：

代码块身份：可直接照写示例

```tsl
value := system.strToInt("123");
writeLn(value);
```

代码块身份：输出片段

```text
123
```

本节只拥有 `system.` 这一调用限定语法。示例中的真实函数名、签名和目标 scope 必须由 `tsl-api-reference` skill 核对；不要从本节推断任意系统 API。

`external`、原生函数指针包装、`makeInstance` / C 回调和线程调用，统一见 [17_external_calls_and_threads.md](17_external_calls_and_threads.md)。

## 默认生成模板

<!-- section-id: syntax-05-014 -->

如果你只是要写一个能被智能体稳定续写的 `.tsl` 脚本，从语句区起步，需要函数时把声明区放在后面：

代码块身份：可直接照写示例

```tsl
Hello();

function Hello();
begin
    echo "hello";
end;
```

如果你要写 `.tsf` 模块/函数扩展，默认从 `function` 骨架起步：

代码块身份：可直接照写示例

```tsl
function HelloValue();
begin
    return 1;
end;
```

只有用户明确要求 `procedure` / 过程时，才用下面这个 `.tsf` 骨架：

代码块身份：可直接照写示例

```tsl
procedure HelloProc();
begin
end;
```

## 禁止项

<!-- section-id: syntax-05-015 -->

- 在 `.tsl` 的声明区后面继续写脚本语句。
- 用户只说“写一个函数”时，默认改成 `procedure`。
- 因为任务没有返回值，就自动改成 `procedure`。
- 为了简写主动省略函数头后的分号；默认保留分号。
- 以为 `procedure` 只是 `function` 的别名，不涉及参数传递语义。
- 带类型注解时仍然用逗号分隔参数。
- 没有类型名证据时，发明说明性参数类型或返回类型。
- 以为默认未修饰参数天然就是按值传递。
- 把 `a = 1` 这种比较表达式误当成命名参数调用。
- 以为默认值只能用于无类型参数。
- 在 `const` 形参上直接赋值。
- 在旧运行时或版本不明时生成默认参数表达式。
- 以为默认参数表达式里的形参会取本次调用实参；其中变量按 `0` 求值。
- 在 `unit interface` 和 `implementation` 的函数头上重复写默认值。
- 用 `::FuncName(...)` 和 `system.FuncName(...)` 互相替代，而不区分全局限定与系统函数限定。
- 把匿名函数或 `findFunction(...)` 返回值默认写成 `f(...)` 直调。
- 把命名参数直接套到二进制函数或系统函数上。
- 在一次调用里先进入命名参数模式，后面又退回位置参数。

代码块身份：反例 / 不可照写

```text
a := 1;
Add(1, 2);

function Add(a, b);
begin
    return a + b;
end;

echo "after function";
```

上面的问题不在 `Add` 本身，而在于 `.tsl` 的函数声明区后面又继续出现脚本语句。正确做法是把会执行的语句全部放在声明区之前。

代码块身份：输出片段

```text
invalid statement
```

代码块身份：反例 / 不可照写

```text
function Demo(a: integer, b: integer);
begin
    return a + b;
end;
```

上面这种写法会编译失败；参数一旦带类型，分隔符应改成分号。

代码块身份：反例 / 不可照写

```text
Pack(a = 1, b = 2);
```

这类写法不要当成命名参数。它虽然可能编译通过，但文档结果不符合命名参数语义，不能当成可靠的命名参数语法。

代码块身份：反例 / 不可照写

```text
function Demo(a: integer, b: integer = 2): integer;
begin
    return a + b;
end;
```

上面这种写法也不对；参数一旦带类型，分隔符仍然应保持分号。

代码块身份：反例 / 不可照写

```text
function TouchDefault(a);
begin
    a := 9;
end;
```

不要把上面这种未修饰参数自动理解成“按值传递”。按文档边界，它会把调用方实参改掉；如果任务要求按值语义，先看 `{$varByRef-}` 和 `in` / `out` 的例子。

代码块身份：反例 / 不可照写

```text
function Bad(const x);
begin
    x := 2;
end;
```

上面这种写法会编译失败；`const` 形参不能被重新赋值。

代码块身份：反例 / 不可照写

```text
unit UnitConst;
interface

const default_value = 888;
function F(a, b = 100, c = default_value);
```

上面的片段缺少 `implementation` 和 `end.`，因此不能作为完整 `unit` 文件直接照写；它不是“接口常量不能作为默认值”的反例。完整可写结构见本页默认参数章节。

代码块身份：反例 / 不可照写

```text
a := function(x, y)
begin
    return x + y;
end;
writeLn(a(7, 8));
```

不要把上面这种匿名函数变量的直调写法直接当成可靠规则。它会报 `function:a compile error or not found`；稳定写法仍然是 `call(a, 7, 8)` 或 `##a(7, 8)`。

代码块身份：反例 / 不可照写

```text
writeLn(intToStr(value: 200));
```

不要把上面这种写法直接套到二进制函数或系统函数上。它会报 `named parameter mode can't support here`；如果确实要命名传参，先用 TSL 函数再封一层。

代码块身份：反例 / 不可照写

```text
writeLn(Pack(a: 1, 2, c: 3));

function Pack(a, b, c);
begin
    return a * 100 + b * 10 + c;
end;
```

不要在同一次调用里“先进入命名参数模式，再退回位置参数”。这类写法会直接编译失败，错误信息包含 `paramname: not found`。
