尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Unison 代码共享与远程命名空间:从 Causal 数据结构到 `gh:` 远程引用协议

Unison 代码共享与远程命名空间:从 Causal 数据结构到 `gh:` 远程引用协议 编程语言编译器语言运行时开发工具【免费下载链接】unisonA friendly programming language from the future项目地址https://gitcode.com/gh_mirrors/un/unison点击查看免费下载本指南围绕 Unison 代码共享机制展开聚焦分享代码时所需的底层数据模型——Causal因果链、命名空间Namespace与NameTree表示、以及远程引用gh:user/repo[/path][?reftreeish]的解析方案。读完本文你将理解 Unison 如何判断某次变更是否发生在另一次之后、如何在本地与远程代码树之间建立链接以及如何在真实仓库源码中定位这些设计的实现。说明本文对应仓库中的 docs/sharing-code.markdown 设计笔记并以当前仓库的源码实现为辅证。该文档是早期设计草稿部分数据结构如RemoteStatus、GithubRef在后续实现中有所演进文中会同步给出对应源码位置。代码共享的核心Causal数据结构共享代码首先要回答一个基础问题两个代码快照之间谁在谁之后Unison 用Causal结构来表达这种因果先后关系。设计文档中给出的抽象定义如下data Causal m e One { currentHash :: Hash, head :: e } | Cons { currentHash :: Hash, head :: e, tail :: m (Causal e) } -- The merge operation flattens and normalizes for order | Merge { currentHash :: Hash, head :: e, tails :: Map Hash (m (Causal e)) }Causal有三种形态One单个节点代表一条因果链的起点只包含当前哈希currentHash与头部值headCons线性后继head是最新值tail指向历史Merge合并节点tails :: Map Hash (m (Causal e))指向多个父节点。文档特别强调合并操作会展平并规范化顺序flattens and normalizes for order保证合并结果的确定性。在e上要求一个交换半群CommutativeSemigroup约束从而可以确定性地把多个分支的头部内容合并成一个新head。源码中的Causal实现设计文档中的Causal在仓库中有两个可对照的实现抽象代数化定义 parser-typechecker/src/Unison/Codebase/Causal/Type.hs 中的Causal m e同样由UnsafeOne、UnsafeCons、UnsafeMerge三种构造器组成。其模块注释用代数公理完整刻画了Causal上的五个操作见同文件 L24-L42before : Causal a - Causal a - m Bool定义Causal上的偏序head返回因果链中的最新值one构造单节点cons在线性链上追加新头部merge交换但不结合的合并操作满足before c1 (merge c1 c2)与before c2 (merge c1 c2)sequence c1 c2 cons (head c2) (merge c1 c2)是合并后再取另一侧头部的复合操作。同文件 L88-L89 用before a b ( Just a) $ lca a b实现了先后判断a在b之前当且仅当a是b的最低公共祖先LCA。lca的搜索采用双向广度优先见 lca交替扩展左右两侧的predecessors直到某一侧命中另一侧已访问的currentHash。惰性版实现 codebase2/codebase/U/Codebase/Causal.hs 中的Causal m hc he pe e进一步泛化parents :: Map hc (m (Causal m hc he pe pe))以父哈希为键、以m (...)包一层惰性求值只有需要时才加载父节点value :: m e同样惰性。Eq实例按causalHash比较L22-L23并提供emap对值与父值做映射、hoist做单子变换。这两个实现与设计文档一脉相承currentHash对应索引哈希parents/tails对应可枚举的先驱节点。Unison 的命名空间Namespace正是包在Causal里才使得共享一个分支、并知道它相对于其他分支的位置成为可能。命名空间与NameSegment/Path共享的是命名空间因此文档紧接着定义了一级名称与路径的表示-- just one level of name, like Foo or Bar, but not Foo.Bar newtype NameSegment NameSegment { toText :: Text } newtype Path Path { toList :: [NameSegment] } data Namespace m Namespace { terms :: Relation NameSegment Referent , types :: Relation NameSegment Reference , children :: Relation NameSegment (Codetree m) } data Codetree m Codetree (Causal m Namespace)要点NameSegment是单个层级的名称如Foo、Bar不含点号或斜杠Path是若干NameSegment组成的序列即Foo.Bar这类多级路径Namespace三个字段分别记录该层级下的项termsNameSegment→Referent、类型typesNameSegment→Reference以及子命名空间childrenNameSegment→Codetree m。三者都是Relation即多对多关系的集合式表示Codetree m Codetree (Causal m Namespace)明确点出每一个子命名空间自身也是一棵由Causal驱动的代码树这使任意子路径都可以独立共享、独立判断因果先后。当前仓库中NameSegment的实现位于 codebase2/core/Unison/NameSegment.hs内部定义在Unison.NameSegment.Internal引用处。该文件还定义了一批哨兵sentinel名称段docSegmentdoc、libSegmentlib、baseSegmentbase、builtinSegmentbuiltin等L31-L80。这些是 Unison 保留的固定命名层级如lib下存放外部库、builtin下存放内建定义共享命名空间时需特别注意避开。新版代码库中Namespace的演进版出现在 codebase2/codebase/U/Codebase/Branch/Type.hsBranch m用Map NameSegment (Map Referent (m MdValues))表示 terms、Map NameSegment (Map Reference (m MdValues))表示 typesCausalBranch m Causal m CausalHash BranchHash (Branch m) (Branch m)L34则是对Causal包裹命名空间这一设计的直接落地——相比文档草稿它把关系集合改成了映射并为每个名称下的引用挂上惰性的元数据集合。命名空间与编辑操作的关系文档提出了三个重要观点是理解整篇设计的钥匙命名空间只是你偏好的解析以及一定程度上的渲染代码的方式。命名空间结构本身是可变的、随偏好而异的不应与底层的、按哈希寻址的定义存储混为一谈编辑Edits只是编辑辅助命令如todo、propagate的状态。换句话说Edits不是代码共享的必需品而是编辑器辅助功能的内部状态应该考虑让代码库对这一数据的表示保持模块化因为二者确实可以分离——即使未来出现文档/编辑器支持其他功能所需的意外状态与偏好这些数据依然有意义。对应地文档给出了编辑状态的数据结构newtype EditMap EditMap { toMap :: Map GUID (Causal Edits) } data Edits Edits { terms :: Relation Reference TermEdit , types :: Relation Reference TypeEdit } -- maps local paths to remote paths data RemoteStatus Map Path RemoteSpec值得注意EditMap中的每个编辑集由GUID标识本身也是一个Causal——编辑历史同样需要判断先后、同样可以被合并。Edits记录了引用Reference被如何编辑terms映射到TermEdittypes映射到TypeEdit。当前仓库中这两类编辑的定义为codebase2/codebase/U/Codebase/TermEdit.hsdata TermEdit Replace Referent Typing | Deprecatecodebase2/codebase/U/Codebase/TypeEdit.hsdata TypeEdit Replace Reference | Deprecate。也就是说一个编辑要么把某引用替换为另一个引用替换项需要携带新的类型信息要么将其弃用Deprecate。EditMap的现代形态见 codebase2/codebase/U/Codebase/Branch/Type.hs 的Patch记录termEdits :: Map Referent (Set TermEdit)与typeEdits :: Map Reference (Set TypeEdit)。而RemoteStatus把本地路径映射到远程规格RemoteSpec在设计文档中只是占位定义后续设计在 docs/branchless.md 中演进为更完整的RemotePath与Link机制详见下文远程引用一节。名称的分隔符问题/还是.文档提出一个待决问题是否要在名字中区分/路径分隔符和.分隔符讨论围绕类型A及其构造子A.A应处于什么层级展开一方面你通常不需要把类型A与其构造子A.A分离——你无法在不导出上一级命名空间中的类型的情况下单独导出构造子另一方面类型A也许应该自然地组织成A/A其构造子同样位于A/A。这让人想起 Haskell 中每种类型一个独立模块的做法但在 Unison 中重新组织会更加容易文档给出了命令行示例/mycode mv ClassA* ClassA/ /mycode mv ClassB* ClassB/ /mycode cd ClassA /mycode/ClassA ls .这段会话演示了在/mycode下把ClassA*类型、构造子等所有相关名称整体移动进ClassA/子目录ClassB同理然后进入ClassA查看。mv命令在后续的 docs/branchless.md 中有进一步说明它可以同时针对 Terms、Types、Directories 或三者全部并用哈希限定名来区分歧义。NameTree表示为承载命名空间树这一概念文档给出了两种NameTree表示方案empty /A (type) /A (term) /A/A (ctor)data NameTree a Causal (Relation Name (NameTree a))或带叶子与共享点的变体data NameTree a Leaf a | Branch (Relation Name (NameTree a)) | SharePoint (Causal (NameTree a))第一种方案把整棵树包进一个Causal树的每个分支节点都是Relation Name (NameTree a)第二种方案则显式区分三种节点Leaf a叶子承载值Branch (Relation Name (NameTree a))普通分支挂载子树SharePoint (Causal (NameTree a))共享点——此处是一棵独立的Causal子树意味着该子命名空间可以被单独共享/克隆。SharePoint正是按目录共享的表示基础只有挂载了SharePoint的子路径才具备独立的因果历史才适合作为共享的边界。仓库中SharePoint思想的后续体现在docs/branchless.md的Link类型data Link m LocalLink (Branch m) | RemoteLink RemotePathdocs/branchless.md本地链接指向本地分支远程链接指向远程RemotePath避免在本地重复分发外部库。远程引用gh:user/repo[/path][?reftreeish]共享的关键在于如何在本地命名空间中引用远程命名空间。文档定义了远程路径与远程引用的数据结构data RemotePath RemotePath RemoteRef Path data RemoteRef GithubRef { username :: Text, repo :: Text, treeish :: Text } -- | ... -- gh:user/repo[/path][?reftreeish] -- treeish defaults to repos default_branch -- gh:aryairani/unison/libs?reftopic/370 becomes -- RemotePath (GithubRef aryairani unison topic/370) (Path [libs])解析规则URI 语法gh:user/repo[/path][?reftreeish]treeish分支名、tag 或 commit缺省时使用仓库的default_branch例gh:aryairani/unison/libs?reftopic/370解析为RemotePath (GithubRef aryairani unison topic/370) (Path [libs])——即aryairani/unison 仓库中topic/370分支下的libs目录。这一设计的后续版本在 docs/branchless.md 中展开为data BranchPath BranchPath RepoRef Path data RepoRef Local | GithubRef { username :: Text, repo :: Text, treeish :: Text }其中Local分支路径形如BranchPath Local (Path [libs,community,DL])对应本地路径/libs/community/DL远程则形如BranchPath (GithubRef aryairani unison topic/370) (Path [libs])对应gh:aryairani/unison/libs?reftopic/370。treeish可能包含斜杠如topic/370这使解析变得棘手。文档给出的应对策略依赖 Git 的一个性质如果存在分支a/b就不可能再创建分支a或a/b/c。因此可以先从 GitHub API 拉取分支列表https://api.github.com/repos/user/repo/branches用每个分支名加/去匹配treeish-prefixed-path的前缀命中分支前缀后剩余后缀即命名空间内的路径。不过文档随即自我修正GitHub 的网页 UI 根本不会展示 Unison 的路径结构所以这个从 GitHub URL 反推路径的思路意义有限——最终建议是坚持使用自定义的gh:username/repo[:treeish][/path]URI 方案并让 Unison 的 JavaScript 查看器生成带查询参数/片段的 URL如?branchhashpathpath用于分享。附GitHub API 资源形态笔记文档末尾记录了 GitHub 目录与文件在 API 中的资源形态可作为实现远程引用的参考以下 URL 原属unisonweb/unison仓库的示例仅用于说明 API 返回的字段结构目录如unison-src/demorefmasterurl: https://api.github.com/repos/unisonweb/unison/contents/unison-src/demo?refmaster html_url: https://github.com/unisonweb/unison/tree/master/unison-src/demo git_url: https://api.github.com/repos/unisonweb/unison/git/trees/f8d91c6cc2ee1bc8f2bfc759e328a851d0df3b95文件如unison-src/Base.urefmasterurl: https://api.github.com/repos/unisonweb/unison/contents/unison-src/Base.u?refmaster html_url: https://github.com/unisonweb/unison/blob/master/unison-src/Base.u git_url: https://api.github.com/repos/unisonweb/unison/git/blobs/e617fbad4e32d25380f536179f558f9213cd4bad download_url: https://raw.githubusercontent.com/unisonweb/unison/master/unison-src/Base.u要点GitHub 把网页上可见的 URL 称为html_url可用任意 treeish分支、tag、commit参数化引用文件或目录但 GitHub 自身并不知道 Unison 命名空间的内部路径因此远程共享的主协议仍是gh:URI。远程引用方案的整体小结综合设计文档与后续 docs/branchless.md 的演进Unison 代码共享的完整链路可以归纳为命名空间是Causal包裹的数据每个命名空间含子命名空间都是一条可判断先后的因果链One/Cons/Merge三种形态覆盖单点、线性和合并三种演化方式编辑与命名空间解耦EditMap/Edits只是todo、propagate等编辑辅助命令的状态TermEdit/TypeEdit仅表达替换与弃用两种动作未来编辑器的新偏好也不会污染共享数据模型本地/远程引用分离本地子路径用Local或直接内联外部依赖用RemoteLink RemotePathGithubRef引用避免重复分发外部库发布时通过传递性发布算法把本地依赖镜像进./_Libs若冲突则_Libs1依此类推远程依赖则携带指向远程仓库的链接docs/branchless.mdURI 语法收敛以gh:user/repo[/path][?reftreeish]为主要远程引用语法treeish缺省为default_branch借助Git 分支名不互为前缀的性质消除解析歧义。相关源码与文档导航设计文档本体docs/sharing-code.markdown分支/命名空间设计的姊妹篇docs/branchless.md含Causal、Namespace、Edits、远程同步与 GitHub 解析的展开讨论Causal代数化实现parser-typechecker/src/Unison/Codebase/Causal/Type.hsOne/Cons/Merge模式、before、lcaCausal惰性泛化实现codebase2/codebase/U/Codebase/Causal.hs命名空间/分支的现代表示codebase2/codebase/U/Codebase/Branch/Type.hsNameSegment及哨兵名称段codebase2/core/Unison/NameSegment.hs编辑动作定义codebase2/codebase/U/Codebase/TermEdit.hs、codebase2/codebase/U/Codebase/TypeEdit.hs赞分享编程语言编译器语言运行时开发工具【免费下载链接】unisonA friendly programming language from the future项目地址https://gitcode.com/gh_mirrors/un/unison点击查看免费下载相关推荐Unison 库代码复用与发布设计从分支合并到 gh: 远程引用的演进Unison 库代码复用与发布设计从分支合并到 gh: 远程引用的演进 本文以仓库内 docs/publishing library1.md https://编程语言编译器语言运行时开发工具Unison 命令行 Tab 补全从本地命名空间到远程 Share 的完整机制解析Unison 命令行 Tab 补全从本地命名空间到远程 Share 的完整机制解析 导读 本篇指南聚焦 Unison 代码管理工具UCM中 debug.t编程语言编译器语言运行时开发工具如何 10 分钟上手 Tailwind CSS零基础快速入门完整教程如何 10 分钟上手 Tailwind CSS零基础快速入门完整教程 本文将带你认识 Tailwind CSS —— 一个 Utility First工具类前端前端构建上一篇Sunshine 新手实操指南从安装到客厅电视串游戏只走一遍下一篇Edu-Mail-Generator3 条命令免费拿到可用教育邮箱的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表