Files
tsl-devkit/docs/tsl/syntax/06_functions_and_calls.md
T
csh 3d83740f88 Squashed 'docs/standards/playbook/' changes from c3f8137..25d895d
25d895d 🐛 fix(gitea_workflow): clean up temp repos after job steps
2bc3b11 🐛 fix(gitea_workflow): clean up temporary repo dirs in workflows
98c3f30 📝 docs(agent_rules): allow plan execution on current branch
16c7230 📝 docs(prompts): define custom verify layering
8efc4dd 🐛 fix(skills): quote commit-message description
bc8498f 🐛 fix(ci): install tomli for gitea tests
55cda3b 🐛 fix(tests): report missing toml parser clearly
c0729c7 🐛 fix(playbook): import Optional for cli compatibility
63e24bf 📦 deps(skills): sync thirdparty skills
d2f9356 🐛 fix(ci): isolate gitea workflow repos
588b81d 🐛 fix(ci): inline gitea workflow bootstrap
e0b1c3a ♻️ refactor(skills): standardize first-party skill contracts
2c5050d ♻️ refactor(skills): rename repo skills source dir
f049dfb 📦 deps(skills): drop duplicate first-party superpowers skills
234b335  feat(workflow): add superpowers planning and execution state tracking
c1702a6 📝 docs(markdown): format tracked markdown and drop stale templates
2325409 📝 docs(markdown): clarify optional markdownlint usage
214c44e 🔧 chore(markdown): add markdownlint baseline and lint fixes
a22b324 📝 docs(templates): add execution and memory-bank prompt templates
223a797 📝 docs(templates): update README for Claude Code and current features
4ac8672 📝 docs: simplify README + platform-agnostic tools + auto-create local rules
2431c9d 📝 docs: add claude_md config and use cross-platform paths
d64b248 📝 docs: fix README.md inaccuracies and add Claude Code info
c8d6bf2 🐛 fix(playbook): use relative paths in CLAUDE.md when not at project root
6518f0f  feat(playbook): auto-create CLAUDE.md with path discovery
6ec9a45  feat(skills): add skill_link symlink support + platform-agnostic prompt
9f8b6b5 📝 docs: update README and config example for Claude Code support
79cff6c 📝 docs(skills): add Claude Code platform support
452c6f5  feat(playbook): auto-inject AGENTS.md into CLAUDE.md
e1dbf3c 🐛 fix(skills): remove dual-path from commit-message skill
f3a7259 🔧 chore(ci): use prepare_repo.sh in both workflows
da08212 🔧 chore(ci): extract prepare_repo.sh and clean up workflows
7ade85e 🗑️ remove(tsl): drop syntax_book/, data/ source and build script
f94dba0 ♻️ refactor(skills): update playbook.py and tests for thirdparty/ layout
b3df412 ♻️ refactor(skills): separate thirdparty skills into thirdparty/ subdirectory
64950e7 📦 deps(skills): sync thirdparty skills
a2e3cb0  feat(playbook): add no_backup deploy controls
8609d59 🐛 fix(docs): repair reference catalog source links
956da11 🐛 fix(playbook): publish hidden ci test fixes
3f67754 📦 deps(skills): sync thirdparty skills
08ca87b 📦 deps(skills): add karpathy thirdparty sync
96b705b 📝 docs(tsl): rebuild canonical syntax and routing manual
3ed5052 📦 deps(skills): sync thirdparty skills
60108dd 📦 deps(skills): sync thirdparty skills
da85d4e 🐛 fix(thirdparty): prune nested project snapshots
a2a697e 📦 deps(skills): sync thirdparty skills
9df610a 🐛 fix(thirdparty): exclude duplicated superpowers skills
33dd5bb 🐛 fix(thirdparty): preserve optional manifest fields
91b0ea7 🐛 fix(thirdparty): preserve manifest during snapshot update
2e26f98 🔧 chore(thirdparty): generalize skills sync pipeline
5b9c1e3 📦 deps(skills): sync superpowers
2f2d34a 📝 docs(readme): normalize subtree command spacing
62db7db 🐛 fix(ci): serialize superpowers update and sync
3463223 🐛 fix(ci): use literal superpowers sync paths
48f6de8 📦 deps(skills): sync superpowers
4b23529 🔧 chore(ci): merge superpowers update and sync workflow
a56d75b 📦 deps(skills): sync superpowers
84bcefa 🔧 chore(ci): use ci[bot] commit author name
00a07e5 📦 deps(skills): sync superpowers
7b84daf 🐛 fix(templates): enforce main loop progress tracking
51373d7 🔧 chore(ci): automate superpowers sync workflow
eaaa39c 🐛 fix(ci): prevent stale superpowers sync from restoring skills block
79755c6 📦 deps(skills): sync superpowers
836d878 📦 deps(skills): sync superpowers
8216c9f 📦 deps(skills): sync superpowers
9439505 🐛 fix(playbook): address reported repo issues

git-subtree-dir: docs/standards/playbook
git-subtree-split: 25d895d8b3f56624ccfe99ad7289e9eb49e0f316
2026-05-24 13:04:14 +08:00

21 KiB
Raw Blame History

Functions And Calls

文档类型:语法主线 是否可直接用于生成代码:是 是否含已验证可执行示例:是 是否含已验证反例:是 遇到不确定时跳转到:03_core_model.md(优先)、10_units_and_scope.md21_external_calls_and_threads.md

手册位置:第 6 篇,共 32 篇。上一篇:05_variables_and_constants.md。下一篇:07_expressions_and_operators.md

这一篇只负责 function / procedure 的定义、调用、参数传递和值返回,不延伸到业务函数库。

这一篇解决什么问题

回答“如何正确声明 functionprocedure、如何组织主函数和子函数、怎样使用参数修饰、默认参数与可变参数,以及哪些混写方式会直接编译失败”。

必须记住的规则

  • 最稳妥的函数骨架仍然是 function Name(...); begin ... end;
  • 不需要返回值时,可以改用 procedure Name(...); begin ... end;
  • 在文件模型层,functionprocedure 归同一类顶层外形;见 03_core_model.md
  • 当前解释器接受省略函数头后的分号,但文档默认仍保留这个分号。
  • 一个函数定义体里可以同时出现主函数和子函数。
  • 函数支持参数类型注解和返回值类型注解。
  • 不带类型注解时,多个参数用逗号分隔。
  • 带类型注解时,多个参数用分号分隔。
  • 当前解释器接受 constvar 形参修饰。
  • 当前解释器的运行时默认行为是:未修饰参数可以写回调用方;这是运行时默认,不是语言规范保证。
  • {$VarByRef-} 会把未修饰参数切换成按值传递;var 形参仍保持引用语义。
  • {$VarByRef-} 下,调用时可以用 in / out 前缀逐个参数覆盖默认传递方式。
  • 如果不确定当前任务是否需要写回语义,优先显式用 const 形参,或先切到 {$VarByRef-};不要依赖未修饰参数的运行时默认行为。
  • return expr; 会直接返回当前函数结果。
  • exit; 会立即结束当前函数;在当前最小样例里,如果此前没有写入返回结果,调用方观察到的是默认值 0
  • 调用时支持命名参数,写法是 name: value
  • 命名参数当前也支持 call(...) 这类按函数名或函数指针转调的模型。
  • 一旦某次调用里开始使用命名参数,后面的参数就不能再退回位置参数。
  • 对二进制函数 / 系统函数直接使用命名参数,当前会报 named parameter mode can't support here;这类函数要先用 TSL 再封一层。
  • 函数参数支持默认值。
  • 普通函数的默认值规则不要直接等同到 unit interface 声明;跨 unit 的默认参数边界见这一篇后面的对照例子,以及 10_units_and_scope.md
  • 当前解释器支持尾部 ... 形式的可变参数。
  • 在可变参数函数体里,ParamsParamCountRealParamCount 都已验证可用。
  • 可变参数组可以通过 ... 转发给另一个函数调用。
  • 可变参数组也可以通过 call(fc, ...)##fc(...)invoke(obj, name, 0, ...) 转发。
  • 当前解释器接受 a := function(...) begin ... end; 这种匿名函数写法。
  • 匿名函数可以直接作为参数传入另一个函数。
  • 当前解释器接受 ThisFunction(FuncName) 把已知函数绑定成函数值。
  • 当前解释器里,匿名函数和函数指针的稳定调用方式仍是 call(f, ...)##f(...)
  • 当前解释器没有通过 f(...) 这种“函数变量直接调用”写法;无论 f 是匿名函数、FindFunction(...) 还是 ThisFunction(...) 返回的函数指针,都不要默认写成直调。
  • 当前解释器接受 ::FuncName(...) 指向全局/系统函数,用来绕过当前作用域里的同名局部函数。
  • externalMakeInstance 和线程调用统一移到 21_external_calls_and_threads.md
  • 不要把顶层函数定义和松散语句混在同一个文件模型里。

已验证语法

基础函数 / 过程骨架

最短函数骨架:

代码块身份:已验证可执行示例

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

主函数加子函数:

代码块身份:已验证可执行示例

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

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

函数头省略分号在当前解释器里也可编译:

代码块身份:已验证可执行示例

function MissingSemi()
begin
    return 1;
end;

最短 procedure 骨架:

代码块身份:已验证可执行示例

procedure LogDone();
begin
end;

procedure 写回参数的最小运行样例:

这是验证样例外壳,不作为正式顶层模型归类依据。

代码块身份:已验证可执行示例

program test;
procedure Bump(var x);
begin
    x := x + 1;
end;
begin
    a := 1;
    Bump(a);
    WriteLn(a);
end.

已验证运行结果:

  • Bump(a) 后输出 2

签名增强

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

代码块身份:已验证可执行示例

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

带类型时的多参数分隔:

代码块身份:已验证可执行示例

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

类型名可以写成更偏说明性的名字:

代码块身份:已验证可执行示例

function Demo(a: input_value; b: handler): result_type;
begin
    return a;
end;

参数修饰:

代码块身份:已验证可执行示例

program test;
function ReadConst(const x);
begin
    return x + 1;
end;
procedure SetVar(var x);
begin
    x := x + 5;
end;
begin
    a := 10;
    WriteLn(ReadConst(a));
    SetVar(a);
    WriteLn(a);
end.

已验证运行结果:

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

参数传递方式

默认参数写回调用方:

代码块身份:已验证可执行示例

program test;
function TouchDefault(a);
begin
    a := 9;
end;
begin
    x := 1;
    TouchDefault(x);
    WriteLn(x);
end.

已验证运行结果:

  • TouchDefault(x) 之后,x 输出 9

{$VarByRef-}var 形参:

代码块身份:已验证可执行示例

program test;
function TouchDefault(a);
begin
    a := 9;
end;
{$VarByRef-}
function TouchValue(a);
begin
    a := 8;
end;
function TouchForcedVar(var a);
begin
    a := 7;
end;
{$VarByRef+}
begin
    x := 1;
    TouchDefault(x);
    WriteLn(x);
    y := 1;
    TouchValue(y);
    WriteLn(y);
    z := 1;
    TouchForcedVar(z);
    WriteLn(z);
end.

已验证运行结果:

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

in / out 调用前缀:

代码块身份:已验证可执行示例

program test;
{$VarByRef-}
procedure Touch3(a, b, c);
begin
    a := 1;
    b := 2;
    c := 3;
end;
begin
    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);
end.

已验证运行结果:

  • {$VarByRef-} 下,直接调用 Touch3(a, b, c) 后依次输出 000
  • 同样在 {$VarByRef-} 下,Touch3(in a, out b, c) 后依次输出 020
  • 说明 in / out 可以在调用点逐个参数覆盖当前默认传递方式

returnexit

代码块身份:已验证可执行示例

program test;
function Demo(x);
begin
    if x > 0 then
        exit;
    return 99;
end;
begin
    WriteLn(Demo(1));
    WriteLn(Demo(0));
end.

已验证运行结果:

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

调用增强

命名参数调用:

代码块身份:已验证可执行示例

function NamedArgsDemo();
begin
    return Pack(a: 1, b: 2);
end;

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

已验证运行结果:

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

跳过中间参数时,未命中的参数当前保持 nil

代码块身份:已验证可执行示例

program test;
function TestFunc(a, b, c);
begin
    WriteLn(IfNil(b));
    return 0;
end;
begin
    TestFunc(1, c: 3);
end.

已验证运行结果:

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

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

代码块身份:已验证可执行示例

program test;
function TestFunc(a, b, c);
begin
    return a * 100 + b * 10 + c;
end;
begin
    WriteLn(call("TestFunc", a: 1, c: 2, b: 3));
end.

已验证运行结果:

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

命名参数可以调换顺序:

代码块身份:已验证可执行示例

function NamedArgsDemo();
begin
    return Pack(b: 2, a: 1);
end;

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

命名参数也可以和位置参数混用:

代码块身份:已验证可执行示例

function NamedArgsDemo();
begin
    return Pack(1, b: 2);
end;

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

默认参数

默认值参数:

代码块身份:已验证可执行示例

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

带类型时也支持默认值:

代码块身份:已验证可执行示例

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

多个参数时,后面的参数可以带默认值:

代码块身份:已验证可执行示例

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

代码块身份:已验证可执行示例

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

默认值也可以写成表达式:

代码块身份:已验证可执行示例

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

unit interface 声明下的默认参数要单独看:

以下是不可照写的对比:普通函数默认参数能跑通,不等于跨 unit 声明边界也同样可靠。

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

// P.tsf
function P(a = 5, b = 6);
begin
    return a + b;
end;

// UnitLiteral.tsf
unit UnitLiteral;
interface
function H(a = 5);
implementation
function H(a);
begin
    return a;
end;
end.

// UnitConst.tsf
unit UnitConst;
interface
const CS = 888;
function F(a, b = 100, c = CS);
implementation
function F(a, b, c);
begin
    return a + b + c;
end;
end.

// main.tsl
program test;
uses UnitLiteral, UnitConst;
begin
    WriteLn(P());
    WriteLn(H());
    WriteLn(F(1));
    WriteLn(F(1, 2));
end.

// command
tsl .\main.tsl -LIBPATH "D:\path\to\dir\"

已验证运行结果:

  • 普通函数 P() 输出 11
  • unit interface 里声明的字面量默认值 H() 输出 5
  • F(1) 输出 101F(1, 2) 输出 3
  • 同一组文件下,UnitConst.CS 当前仍可读到 888,而 F(1, 2, 3) 输出 6
  • 因此当前解释器下,不要把“普通函数默认参数可用”直接泛化成“unit interface 里引用 unit const 的默认参数也同样可靠”
  • 这组多文件结果依赖真实查找路径;当前验证命令是把对应目录放进 -LIBPATH
  • 这类跨 unit 的声明边界,统一回看 10_units_and_scope.md

可变参数 ...

尾部可变参数:

代码块身份:已验证可执行示例

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

可变参数转发:

代码块身份:已验证可执行示例

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

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

ParamCountRealParamCount

代码块身份:已验证可执行示例

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## 转发可变参数:

代码块身份:已验证可执行示例

program test;
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;
begin
    WriteLn(DoFunc("Sum3", 1, 2, 3));
    WriteLn(DoFunc2(thisfunction(Sum3), 1, 2, 3));
end.

已验证运行结果:

  • DoFunc("Sum3", 1, 2, 3) 输出 6
  • DoFunc2(thisfunction(Sum3), 1, 2, 3) 输出 6

通过 invoke 转发可变参数:

代码块身份:已验证可执行示例

program test;
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
            return FuncA(...)
        else
            return FuncB(...);
    end;
end;
function DoInvoke(fc_name, ...);
begin
    obj := new TestC();
    return invoke(obj, fc_name, 0, ...);
end;
begin
    WriteLn(DoInvoke("Dispatch", "Int", 2, 20, 200));
    WriteLn(DoInvoke("Dispatch", "Other", 1, 2, 3));
end.

已验证运行结果:

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

匿名函数与函数指针

匿名函数变量:

代码块身份:已验证可执行示例

program test;
begin
    a := function(x, y)
    begin
        return x + y;
    end;
    WriteLn(call(a, 1, 2));
    WriteLn(##a(5, 6));
end.

已验证运行结果:

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

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

代码块身份:已验证可执行示例

program test;
function Apply(fun);
begin
    return call(fun, 2, 3);
end;
begin
    WriteLn(Apply(function(x, y)
    begin
        return x * y;
    end));
end.

已验证运行结果:

  • 输出 6

函数指针变量也可以通过 FindFunction(...) 拿到:

代码块身份:已验证可执行示例

program test;
function Add(a, b);
begin
    return a + b;
end;
begin
    f := FindFunction("Add");
    WriteLn(##f(1, 2));
end.

已验证运行结果:

  • 输出 3

也可以通过 ThisFunction(...) 从已知函数名直接拿到:

代码块身份:已验证可执行示例

program test;
function Add(a, b);
begin
    return a + b;
end;
begin
    f := ThisFunction(Add);
    WriteLn(Call(f, 3, 4));
end.

已验证运行结果:

  • 输出 7
  • 说明 ThisFunction(Add) 当前可以得到稳定可调用的函数值

直接 f(...) 在当前解释器里不要当成可靠写法:

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

// 匿名函数变量直接调用
program test;
begin
    a := function(x, y)
    begin
        return x + y;
    end;
    WriteLn(a(7, 8));
end.

// FindFunction 返回值直接调用
program test;
function Add(a, b);
begin
    return a + b;
end;
begin
    f := FindFunction("Add");
    WriteLn(f(3, 4));
end.

// ThisFunction 返回值直接调用
program test;
function Add(a, b);
begin
    return a + b;
end;
begin
    f := ThisFunction(Add);
    WriteLn(f(3, 4));
end.

已验证结果:

  • 上面三种 f(...) 直调写法都会报 function:<name> compile error or not found
  • 因此当前手册只把 call(f, ...)##f(...) 写成稳定规则

:: 指向全局函数

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

代码块身份:已验证可执行示例

program test;
function Demo();
begin
    return array(StrToInt("123"), ::StrToInt("123"));
end;
function StrToInt(s);
begin
    return 888;
end;
begin
    r := Demo();
    WriteLn(r[0]);
    WriteLn(r[1]);
end.

已验证运行结果:

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

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

系统交互专题

external、动态库函数指针、MakeInstance 和线程调用,统一见 21_external_calls_and_threads.md。这一篇只保留“普通函数怎样定义和调用”的主线。

最小可编译示例

如果你只是要写一个能被 session 稳定续写的函数 / 过程,从下面任一骨架起步:

代码块身份:已验证可执行示例

function Hello();
begin
    return 1;
end;

代码块身份:已验证可执行示例

procedure HelloProc();
begin
end;

常见误写

  • 把顶层函数定义和松散语句混写。
  • 以为函数头后的分号是当前解释器的硬性要求。
  • 以为 procedure 只是 function 的别名,不涉及参数传递语义。
  • 带类型注解时仍然用逗号分隔参数。
  • 以为默认未修饰参数天然就是按值传递。
  • a = 1 这种比较表达式误当成命名参数调用。
  • 以为默认值只能用于无类型参数。
  • const 形参上直接赋值。
  • 把普通函数的默认值规则原样套到 unit interface 里的 const 默认参数上。
  • 把匿名函数或 FindFunction(...) 返回值默认写成 f(...) 直调。
  • 把命名参数直接套到二进制函数或系统函数上。
  • 在一次调用里先进入命名参数模式,后面又退回位置参数。

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

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

value := Add(1, 2);

上面这种混写方式会编译失败,问题不在 Add 本身,而在于文件模型混了“函数定义体”和“松散语句”两种写法。

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

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

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

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

Pack(a = 1, b = 2)

这类写法不要当成命名参数。它虽然可能编译通过,但在我于 2026-04-09 的实测里返回结果不对,不能当成可靠的命名参数语法。

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

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

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

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

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

不要把上面这种未修饰参数自动理解成“按值传递”。在当前解释器里,它会把调用方实参改掉;如果当前任务要求按值语义,先看 {$VarByRef-}in / out 的例子。

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

procedure Bad(const x);
begin
    x := 2;
end;

上面这种写法会编译失败;const 形参在当前解释器里不能被重新赋值。

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

unit UnitConst;
interface
const CS = 888;
function F(a, b = 100, c = CS);

不要把上面这种 unit interface 声明直接当成已经等价于普通函数默认参数规则。在当前解释器里,对应的 F(1) 实测输出是 101,不是按 CS = 888 补成的结果;具体边界见 10_units_and_scope.md

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

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)

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

WriteLn(IntToStr(value: 200));

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

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

function Pack(a, b, c);
begin
    return a * 100 + b * 10 + c;
end;
begin
    WriteLn(Pack(a: 1, 2, c: 3));
end.

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

跳转指引