9.0 KiB
14. 交互式会话(REPL)
REPL 是持续接收输入、检查并求值、呈现结果的交互式会话。Endorphin 的 REPL 可作为平台对象和程序能力的交互入口:使用者可以准备定义、执行表达式、查看对象,并观察调用与同步的后续状态。
本章是设计提案。它复用已有对象、Preflight/inflight、权限和呈现模型;会话行为是候选设计,尚未确定启动命令、控制命令、完整输入文法或原生会话类型。完整未定范围见规范状态。
14.1 会话上下文
建议一次 REPL 会话保留当前输入环境,使后续输入可以使用前面准备的定义、名称绑定和结果对象。会话上下文包含:
| 信息 | 用途 |
|---|---|
| 操作身份与可用权限 | 判断本次查看、调用、编辑或同步是否允许 |
| workspace 与 namespace 上下文 | 解释资源归属及当前查找范围 |
| 已准备的定义与依赖 | 为后续输入提供确定的函数、类型和资源引用 |
| 会话名称绑定 | 在多次输入之间引用已有值 |
| 调用记录与结果引用 | 查看本次输入的等待、成功、失败或后续状态 |
| 本地副本与同步关联 | 使用已取得的数据,并接收后续变化 |
workspace 和 namespace 上下文由 REPL 外部选择,不增加源码中的 namespace 自声明,也不决定代码一定在客户端或服务器执行。会话范围的临时名称与 namespace 中的平台命名入口分别处理。
建议在会话中准备声明时,先建立会话可用定义;将定义登记到共享 namespace 是单独的编辑动作,遵守 w 权限。这样可以试验逻辑,再选择保存范围。临时定义怎样获得资源身份、保留多久以及如何登记,仍需正式规则。
“REPL 会话”描述交互上下文,不预先确定它必须是原生 Session 类型。Session 的候选设计见运行与状态。
14.2 提交输入与执行时机
建议用户提交一个完整输入单元后,REPL 先进行 Preflight 检查。表达式及需要取得实际值的绑定通过检查后进入 inflight;定义则准备为后续可用的对象,不因提交声明而执行其中的业务逻辑。
| 提交内容 | 检查与准备 | 可观察效果 |
|---|---|---|
| use 声明 | 解析 namespace 与可用声明 | 后续输入可按名称规则引用依赖,不执行整个 namespace |
| 函数、类型或 agent 定义 | 检查定义及依赖 | 提供会话可用的定义,不调用函数、读取业务数据或启动 agent |
| 数值或普通表达式 | 检查名称及类型 | 求值并呈现结果 |
| 包含调用的表达式 | 检查目标和参数 | 使用实际参数发起调用,呈现结果或等待状态 |
| 名称绑定 | 检查右侧表达式及绑定规则 | 求得值后建立会话绑定,供后续输入使用 |
| 已有对象的名称 | 解析当前绑定 | 查看对象;函数对象的查看不等于调用 |
一次提交可以是多行函数定义;逐个字符输入和编辑尚未提交的文本不触发业务执行。输入单元边界、顶层绑定文法及多个语句的处理仍待定义,本表不把函数体内的全部语句直接扩展到顶层。
定义、实际调用与结果对象的生命周期沿用Preflight 与 inflight。REPL 的反复接收输入属于宿主交互调度;它不要求无任务时持续执行某个业务函数,也不要求为交互环境引入字节码。
14.3 定价示例
下面是拟议交互过程,不是实际执行日志,也不是完整程序。会话外部上下文为 market:shop.checkout,依赖第 9 章的 discountedPrice。假定操作用户获准使用目标函数,其名称在导入后可见且没有冲突;amount 的跨输入保留属于本章提出的会话绑定行为。
输入:use atelier:shop.pricing
结果:依赖已准备,尚未调用定价函数
输入:discountedPrice
结果:查看函数对象及获准取得的接口信息
输入:discountedPrice(100, 10)
结果:90
输入:let amount = discountedPrice(100, 10)
结果:amount 绑定到本次成功取得的数值 90
输入:discountedPrice(amount, 5)
结果:85
输入函数名时,REPL 处理函数对象;提交调用表达式时,才使用实际参数执行。后一调用使用已有 amount,因此按定价例子先得到 90,再得到 85。若初始参数换为 80 与 7,amount 为 73,再扣减 5 的结果为 68。
新输入绑定的普通结果不自动变成持续计算。amount 在本例持有一次调用的结果;要持续跟随远程对象变化,需要另行建立同步关联。
14.4 结果对象与呈现
建议 REPL 将一次输入的执行记录与成功结果对象分别保留。执行记录回答“这次输入是否完成”,结果对象回答“取得了什么,可以怎样使用”。输出到屏幕的文本只是对象的一种呈现,不代替对象身份与类型。
数值和文本可以直接显示;函数显示可查看的定义或接口;其他类型按已有呈现契约处理。取得可渲染值时可以提供界面预览,媒体或复杂对象可以提供相应查看方式。具体类型与默认查看器仍遵守资源访问与呈现的待定范围。
建议允许使用者将结果关联到明确名称,继续传入函数或检查类型及身份;不预先引入隐式的“上一个结果”变量。显示资源 ID、类型身份、加载状态或同步状态属于查看能力,其源码接口与界面布局尚未确定。
查看结果不自动重新执行产生它的表达式。尤其对带业务写入的调用,展开对象、刷新界面或重开历史记录不能成为隐式重试的理由。
14.5 等待、失败与持续更新
远程调用未完成时,建议 REPL 显示该次输入的等待状态;已有结果仍可查看。后续输入如果依赖未就绪结果,不能把等待状态当成成功值。第一版可先采用按提交顺序处理求值输入的方式;是否允许独立输入在等待期间并发执行,仍需确定。
| 情况 | 应保留的区别 |
|---|---|
| Preflight 发现输入错误 | 报告诊断,不进入该输入的业务执行 |
| 调用尚未完成 | 标识本次调用,不伪造成功结果 |
| 执行或网络发生失败 | 区分已知失败与结果尚不能确认;业务效果不因此保证已回滚 |
| 一次读取成功 | 返回该次结果,不自动持续订阅 |
| 对象保持同步关联 | 可以继续更新本地副本,原获取调用的完成状态与副本同步状态分别处理 |
| 请求取消调用 | 表示请求停止,不能保证已完成的远程业务效果被撤销 |
建议同步更新关联到原对象或结果记录,不占用新的输入位置,也不重新求值所有历史表达式。一次获取完成后,副本仍可接收更新;连接中断后,本地已有数据与“同步是否有效”应分别可见。版本与新鲜度规则见跨端对象与同步的规范状态。
14.6 历史、定义更新与会话结束
建议把输入历史与可恢复的会话状态分开:历史记录便于查阅,不保证对象仍可取得,也不充当恢复全部状态的脚本。重新打开历史不自动重放调用;保存或恢复会话不应以重复执行业务写入来恢复结果。
修改函数定义遵守新的资源身份规则。会话中的名称如何选用新定义属于输入环境的绑定规则;已有调用和已经保存的确定引用仍保留原目标。REPL 不以“函数名相同”为由替换正在执行的定义。
结束会话需要分别处理输入环境、本地副本、同步关联、未完成调用和平台资源。建议提供查看未完成活动及解除关联的操作;结束会话是否取消某个任务、平台是否继续保留结果、离线后如何恢复,不由关闭输入窗口单独决定。
平台对象是否回收仍依据有效保留根及生命周期;会话历史是否作为保留根、解除关联如何影响保留关系尚未确定。结束会话不等于删除该会话曾经访问的全部平台对象。
14.7 首版设计范围
建议首版先覆盖输入单元、会话绑定、Preflight 诊断、普通表达式与函数调用、类型化结果查看,以及等待和失败记录。在此基础上,再加入平台对象获取、同步状态查看与解除关联。
命令名、快捷键、并发求值、会话持久化、定义登记接口和断线恢复不是已有源码规则。REPL 也不自动成为调试器、数据库管理器或任意远程执行入口;其操作继续受类型、调用契约和权限约束。各项候选与待定边界集中维护在REPL 规范状态。