- 编程语言
- 编译器
- 语言运行时
- 开发工具
【免费下载链接】unison
A friendly programming language from the future
alias.many(别名copy)是 Unison 命令行交互环境(UCM)中用于批量复制定义的核心命令:它接受一个或多个源定义名称,以及一个目标命名空间,将每个源定义按你指定的相对路径复制(建立别名)到目标命名空间下。本文以仓库中的 transcript 实录 alias-many.output.md 为主体,结合命令的解析与执行源码,讲解它的语法、行为、实际输出格式以及底层实现原理,帮助你在日常编码中高效地把lib.builtins等命名空间中的定义整理进自己的代码库。
命令语法与帮助信息
alias.many的正式语法与别名在 unison-cli/src/Unison/CommandLine/InputPatterns.hs 中定义:
aliasMany :: InputPattern aliasMany = InputPattern "alias.many" ["copy"] I.Visible (Parameters [("definition to alias", definitionQueryArg)] $ OnePlus ("alias names", exactDefinitionArg)) ...即:命令主名为alias.many,同时接受简写copy;参数要求为“一个或多个源名称 + 一个目标命名空间”(OnePlus)。通过 UCM 内执行help alias.many可以查看官方帮助,原文实录如下:
> help alias.many alias.many (or copy) `alias.many <relative1> [relative2...] <namespace>` creates aliases `relative1`, `relative2`, ... in the namespace `namespace`. `alias.many foo.foo bar.bar .quux` creates aliases `.quux.foo.foo` and `.quux.bar.bar`.关键语义有两点:
- 目标命名空间中的名称 = 你指定的源名称(保持相对路径)。例如
alias.many foo.foo bar.bar .quux会在.quux下分别创建.quux.foo.foo与.quux.bar.bar,源名称的“相对路径”原样平移到目标命名空间。 - 这是一次复制(建立别名)而非移动:原命名空间中的定义仍然保留,目标命名空间中获得的是指向同一份定义的新名称。这正是“alias(别名)”的含义。
实战演示:把 List 的 15 个函数复制进mylib
transcript 实录中给出了一个完整的、可复现的实战用例。前置步骤是在一个空代码库中合并内置库并把当前文件加入(这两步在实录中标记为:hide,即不在输出中展示):
> builtins.merge lib.builtins> add随后,一条alias.many命令一次性把lib.builtins中List的 15 个函数复制到mylib命名空间:
> alias.many List.adjacentPairs List.all List.any List.chunk List.chunksOf List.dropWhile List.first List.init List.intersperse List.isEmpty List.last List.replicate List.splitAt List.tail List.takeWhile mylib Here's what changed in mylib : Added definitions: 1. List.adjacentPairs : [a] -> [(a, a)] 2. List.all : (a ->{g} Boolean) -> [a] ->{g} Boolean 3. List.any : (a ->{g} Boolean) -> [a] ->{g} Boolean 4. List.chunk : Nat -> [a] -> [[a]] 5. List.chunksOf : Nat -> [a] -> [[a]] 6. List.dropWhile : (a ->{g} Boolean) -> [a] ->{g} [a] 7. List.first : [a] -> Optional a 8. List.init : [a] -> Optional [a] 9. List.intersperse : a -> [a] -> [a] 10. List.isEmpty : [a] -> Boolean 11. List.last : [a] -> Optional a 12. List.replicate : Nat -> a -> [a] 13. List.splitAt : Nat -> [a] -> ([a], [a]) 14. List.tail : [a] -> Optional [a] 15. List.takeWhile : (a ->{𝕖} Boolean) -> [a] ->{𝕖} [a] Tip: You can use `undo` or use a hash from `reflog` to undo this change.可以看到命令执行的输出是一份标准“diff”摘要:
- 变更对象:显示
Here's what changed in mylib,即目标命名空间被修改; - Added definitions:列出新增的 15 个定义,每一项都附带完整的类型签名(例如
List.replicate : Nat -> a -> [a]); - 撤销提示:输出末尾提示“你可以用
undo或从reflog中取一个 hash 来撤销这次变更”。
这 15 个被复制的函数及其实现定义在 alias-many.md(transcript 源文件)的:hide-all代码块中,例如List.adjacentPairs、List.all、List.dropWhile、List.replicate、List.takeWhile等,其中List.takeWhile的签名带有能力(ability)参数{𝕖},表示其谓词可以具有能力效果:
List.takeWhile : (a ->{𝕖} Boolean) -> [a] ->{𝕖} [a] List.takeWhile p xs = go xs acc = match xs with x +: xs -> if p x then go xs (acc :+ x) else acc _ -> acc go xs []这也解释了为什么输出中部分签名的效果变量显示为{g}或{𝕖}——alias.many复制的只是定义本身(名称绑定),类型签名中的效果变量在复制后由类型检查器保留其原始信息。
验证复制结果:find-in mylib
复制完成后,实录紧接着执行find-in mylib来验证mylib命名空间中确实存在这 15 个定义:
> find-in mylib 1. List.adjacentPairs : [a] -> [(a, a)] 2. List.all : (a ->{g} Boolean) -> [a] ->{g} Boolean 3. List.any : (a ->{g} Boolean) -> [a] ->{g} Boolean 4. List.chunk : Nat -> [a] -> [[a]] 5. List.chunksOf : Nat -> [a] -> [[a]] 6. List.dropWhile : (a ->{g} Boolean) -> [a] ->{g} [a] 7. List.first : [a] -> Optional a 8. List.init : [a] -> Optional [a] 9. List.intersperse : a -> [a] -> [a] 10. List.isEmpty : [a] -> Boolean 11. List.last : [a] -> Optional a 12. List.replicate : Nat -> a -> [a] 13. List.splitAt : Nat -> [a] -> ([a], [a]) 14. List.tail : [a] -> Optional [a] 15. List.takeWhile : (a ->{𝕖} Boolean) -> [a] ->{𝕖} [a]find-in的输出与复制摘要中列出的 15 个定义完全一致,证明复制是完整且无损的——不仅名称一致,类型签名也原样保留。注意find-in的打印采用紧凑的单行排版,而alias.many的变更摘要采用了多行换行排版,二者只是展示格式不同。
源码视角:alias.many在 UCM 中的完整执行链路
结合源码可以更深入地理解这条命令的工作方式。整条链路涉及三个文件:
1. 参数解析(InputPatterns.hs)
在 unison-cli/src/Unison/CommandLine/InputPatterns.hs 中,参数被解析为内部指令类型:
\case srcs@(_ : _) Cons.:> dest -> Input.AliasManyI <$> traverse handleHashQualifiedSplitArg srcs <*> handlePath'Arg dest args -> wrongArgsLength "at least two arguments" args- 每个源名称通过
handleHashQualifiedSplitArg解析为带 hash 限定符的路径(HQ'.HashQualified (Path.Split Path)),因此源名称既可以是普通名称,也可以是#hash形式的 hash 限定名称; - 目标命名空间通过
handlePath'Arg解析为Path',支持.absolute前缀; - 若参数不足两个,会返回
wrongArgsLength "at least two arguments"错误。
2. 指令类型(Input.hs)
解析结果对应 unison-cli/src/Unison/Codebase/Editor/Input.hs 中定义的数据构造子:
= AliasManyI [HQ'.HashQualified (Path.Split Path)] Path'第一个字段是源定义列表(每个都是可带 hash 限定符的拆分路径),第二个字段是目标命名空间。inputDescription(见 HandleInput.hs)会把该指令重新渲染回可读的命令文本,用于记录变更历史:
AliasManyI srcs0 dest0 -> do srcs <- traverse hqs srcs0 dest <- p' dest0 pure ("alias.many " <> Text.intercalate " " srcs <> " " <> dest)3. 执行与 diff 生成(HandleInput.hs)
真正的执行逻辑位于 unison-cli/src/Unison/Codebase/Editor/HandleInput.hs:
-- this implementation will happily produce name conflicts, -- but will surface them in a normal diff at the end of the operation. AliasManyI srcs dest' -> do root0 <- Cli.getCurrentProjectRoot0 currentBranch0 <- Cli.getCurrentBranch0 destPP <- Cli.resolvePath' dest' old <- Cli.getBranch0FromProjectPath destPP description <- inputDescription input let (unknown, actions) = foldl' (go root0 currentBranch0 (PP.absPath destPP)) mempty srcs Cli.stepManyAt destPP.branch description actions new <- Cli.getBranch0FromProjectPath destPP (ppe, diff) <- diffHelper old new执行步骤可以概括为:
- 解析并定位目标命名空间:通过
Cli.resolvePath'解析用户输入的目标路径,再用Cli.getBranch0FromProjectPath取到目标分支(old); - 逐个折叠计算复制动作:对每个源名称执行
go,累计得到一批actions;注释明确指出该实现“乐于产生名称冲突,但会在操作结束时的常规 diff 中暴露它们”,也就是说如果目标命名空间已有同名定义,命令不会中途报错,而是由最终 diff 呈现冲突; - 原子化提交变更:
Cli.stepManyAt destPP.branch description actions把这一批动作作为一个步骤写入目标分支,并用inputDescription生成的命令文本作为该步骤的描述,这正是它可被undo/reflog撤销的原因; - 输出 diff:重新读取目标分支(
new),通过diffHelper old new计算前后差异并打印给用户——也就是我们在实录输出中看到的Added definitions列表。
使用建议与注意事项
- 一次性批量复制:相比逐条执行
alias.term,alias.many可以在一条命令里复制任意多个定义,适合把lib.builtins等基础命名空间中的一组函数整批纳入自己的代码库; - 名称冲突的处理方式:从源码注释可以确认,
alias.many不会在复制前检查目标命名空间是否已有同名定义;冲突会在命令结束的 diff 中呈现,因此复制后请留意输出中的Added definitions是否与预期一致; - 撤销与历史:每次
alias.many都是一个可撤销的变更步骤,出错时可用undo,或从reflog中提取对应的 hash 回滚; - hash 限定源:源名称支持 hash 限定形式(如
List.map#abc123),这在精确复制某个特定 hash 的定义时很有用; - 关于效果变量
{g}/{𝕖}:实录中部分签名(如List.takeWhile)带有效果变量,复制后这些信息原样保留,表明别名绑定与定义本身的效果信息无关,只是新增了名称指向。
本文依据 alias-many.output.md 的完整实录,并交叉印证了 InputPatterns.hs、Input.hs 与 HandleInput.hs 中的命令实现。如果你希望亲手复现,可以运行仓库中的 transcript 测试框架,或直接在 UCM 中按上述命令逐步操作。
- 编程语言
- 编译器
- 语言运行时
- 开发工具
【免费下载链接】unison
A friendly programming language from the future
相关推荐
Unison 的 `alias.many`(copy)命令:批量创建别名与整理命名空间实战指南
Unison 的 alias.many (copy)命令:批量创建别名与整理命名空间实战指南 alias.many 是 Unison 命令行交互工具(UCM)中
编程语言编译器语言运行时开发工具Velero 命名空间 Glob 模式:用通配符批量备份与恢复 Kubernetes 命名空间
Velero 命名空间 Glob 模式:用通配符批量备份与恢复 Kubernetes 命名空间 本指南深入讲解 Velero 在 backup 、 restor
云原生灾备存储后端3步一键完成 Office 下载安装激活:LKY_OfficeTools 免费完整指南
3步一键完成 Office 下载安装激活:LKY_OfficeTools 免费完整指南 刚重装完系统,Word、Excel 的快捷方式又全没了。要自己上官网找对
桌面应用CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考