图形设计网站,做网站怎么选服务器,政务公开和网站建设情况,网站开发软件排行榜本文讨论了编程语言的一种趋势#xff0c;即允许相同的语法表达
在两个不同阶段或环境#xff08;上下文#xff09;中执行的计算同时保持跨阶段#xff08;上下文#xff09;的一致行为。这些阶段通常在时间上#xff08;运行时间#xff09;或空间上#xff08;运行…本文讨论了编程语言的一种趋势即允许相同的语法表达
在两个不同阶段或环境上下文中执行的计算同时保持跨阶段上下文的一致行为。这些阶段通常在时间上运行时间或空间上运行地点有所不同。
作者提供了三种体现这种“双相编程biphasic programming”概念的语言示例
ZigZig 允许使用“comptime”关键字在编译时运行普通函数提供与基础语言相同的表达能力.这使得源代码中的构建时间和运行时执行之间能够无缝切换。WinglangWinglang 是一种用于编写云应用程序的编程语言其设计采用了双相概念。它提供预检代码在编译时运行以定义云基础设施和运行中代码在运行时运行以与基础设施交互。两个阶段具有相同的语法但具有不同的规则和功能。React 服务器组件 (RSC)RSC 允许 React 组件指定它们应该在服务器端还是客户端渲染从而实现服务器渲染和客户端渲染组件的灵活组合。此方法旨在通过最小化服务器和客户端之间传输的动态 HTML 和组件信息量来优化页面性能。
作者提出双相规划可用于解决各种问题探索这些解决方案规则之间的重叠和差异可能会产生有趣的见解文章还提到虽然编译时代码执行并不是一个新想法但 Zig 的方法似乎避免了其他元编程 系统的几个缺点。
双相编程问题 虽然双相编程可以在表达力、性能和灵活性方面带来好处但开发人员必须做好准备以应对在项目中采用这种模式所带来的日益增加的复杂性和潜在挑战
复杂性增加双相编程要求开发人员在同一代码库中管理两个不同的执行阶段例如编译时和运行时从而增加了一层复杂性。这会增加认知负担使代码更难理解和维护。参数化和数据需求双相模型通常需要更多的参数和数据来捕捉两个阶段的细微差别与更简单的单相模型相比这使得它们更难以拟合和验证。工具和生态系统支持现有的开发工具、库和框架可能不完全支持双相编程范式需要开发人员投入时间和精力来构建定制解决方案或调整他们的工作流程。性能权衡在编译时执行代码的能力可以提供性能优势但也可能引入新的性能考虑例如增加编译时间或缓存和记忆的潜在问题。采用和学习曲线双相编程代表了传统编程模型的转变开发人员在加入团队并将新方法集成到现有代码库和开发实践中时可能会面临阻力或挑战。调试和故障排除将代码执行分为两个不同的阶段可能会使调试和解决问题变得更加困难因为根本原因可能隐藏在编译时和运行时环境之间的交互中
1、案例Zig Zig一种系统编程语言可让您编写高性能代码并相对轻松地逐步采用到 C/C 代码库中。
它的主要创新之一是一种名为“comptime”的全新元编程方法可让您在编译时运行普通函数。
与 C、C 和 Rust 中的预处理系统和宏系统相比comptime 的独特之处在于它通过“comptime”关键字为您提供了与基础语言相同的[2](https://rybicki.io/blog/2024/06/30/biphasic-programming.htmlfn:2)表达能力而不是引入只有高级用户才可能想要学习的完全独立的领域特定语言。
const expect import(std).testing.expect;
fn fibonacci(index: u32) u32 { if (index 2) return index; return fibonacci(index - 1) fibonacci(index - 2); }
test fibonacci { // 运行时测试斐波那契 try expect(fibonacci(7) 13); //在编译时测试斐波那契 try comptime expect(fibonacci(7) 13); }
作为双相编程的一种情况comptime 允许 Zig 用户在源代码中无缝切换在构建时运行代码和在运行时运行代码而不会带来陡峭的学习曲线。
它改变了开发人员的思维模式不再将元编程视为高级魔法而是将其视为一种优化工具还可以利用它来实现泛型和其他代码生成用途。
不管怎样编译时代码执行并不是一个全新的想法。然而Zig 的方法似乎确实避免了一些缺点。例如与 Rust 及其const 函数不同Zig 不会对 comptime 函数强制使用函数着色。同样与 C 的模板系统不同Zig 不会引入任何用于表示泛型的新语法。与支持 hygenic 宏的 Scheme 和 Racket 等 Lisp 相比Zig 并不要求所有内容都是列表。
TL;DR Zig 支持一种双相编程形式其中相同的函数可以在两个不同的阶段运行这两个阶段在时间上构建时间与运行时间和空间上在构建系统上与在运行二进制文件的机器上有所不同。
2、案例React 服务器组件 我注意到的第二个双相编程示例是React Server Components (RSC)。React 本身并不是一门语言但作为一个 JavaScript Web 框架它作为编写和编写大型网站的 UI 组件及其相关 UI 逻辑的基础系统拥有相当大的知名度。
最近前端 JavaScript 生态系统一直在进行大量探索以找出如何最有效地在服务器或客户端上呈现 UI 组件以提高页面性能。已经提出了许多解决方案其中最雄心勃勃的解决方案之一就是 RSC。
RSC 背后的想法是允许 React 组件指定它应该在服务器端还是客户端呈现并允许这些组件自由组合在一起。
例如 组件Feed可能在服务器上呈现因为它需要从数据库获取 feed 项列表 而每个子组件FeedItem可以在客户端呈现因为它们是项状态的纯函数 而FeedItemPreview可能在服务器上呈现因为它需要从数据库获取项的内容。
开发人员可以选择在哪里计算哪些组件底层引擎通常是生成服务器端代码和客户端代码的 JavaScript 打包器会优化所有内容以便在需要时在服务器或客户端上呈现组件从而最大限度地减少来回传输的动态 HTML 和组件信息量。
让这一切正常运行并稳定下来仍是一项艰巨的工作。但我认为该范式是双相编程的一个有趣例子。
有很多方法可以减少需要在客户端浏览器上发送和执行的代码量并将更多工作转移到服务器上但当今大多数现有解决方案都要求开发人员将 React 组件视为纯客户端抽象或纯服务器端抽象。
例如要么在服务器上呈现整个页面要么在客户端呈现整个页面反之亦然。如果引擎可以得到足够的优化并且生成的代码可以足够调试那么采用 React 组件模型并让开发人员切换组件的呈现位置似乎是一种强大的抽象。
React Server Components 承诺一种双相编程形式其中可以使用相同的 JavaScript JSX 语法来表示在服务器或客户端上呈现的组件并且可以灵活组合。服务器端和客户端渲染同时进行但它们在空间上有所不同在服务器上与在浏览器上。
我还想特别提到Electric Clojure 这是我在[urlhttps://systemsdistributed.com/\]Systems Distributed[/url]的一次闪电演讲中发现的这个项目它采用了类似的想法在前端/后端边界上提供强大的组合但使用的是 Clojure 语言。
3、案例Winglang 我对“双相编程”理念如此好奇的很大一部分原因是在过去的两年里我一直在研究Winglang这是一种用于编写云应用程序的新编程语言它在设计中大量采用了这一概念。这个项目是我介绍的三个例子中最年轻的一个它只开发了两年但在本文中我将尝试尽可能简短地介绍它以便为其双相类型系统提供足够的背景信息。
Winglang 背后的要点是由于拥有大量计算资源AWS、Azure 和 GCP 等主要云提供商能够为开发人员提供各种可扩展的高级服务如队列、发布-订阅主题、工作流、流、存储桶等。通俗地说这些通常被称为资源。Terraform和CloudFormation等基础设施即代码工具使得使用 JSON 或[urlhttps://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-overview.html\]YAML\[/url\]管理这些资源成为可能。
原则上使用这些资源构建复杂的应用程序应该不难。但是如果您的应用程序足够大并且拥有许多资源那么将每个无服务器函数或容器服务与其所需资源的权限和配置明确连接起来就很容易出错。围绕这些资源设计自定义接口也很困难。
Winglang 旨在让您编写将基础架构资源和应用程序逻辑组合在一起的库和应用程序通过该语言所称的预检和飞行代码。下面是一个示例程序来演示
// Import some libraries. bring s3; bring lambda; bring redis; bring triggers;
// 定义我们的抽象。class Cache { _redis: redis.Redis; _bucket: s3.Bucket; new() { this._redis new redis.Redis(); this._bucket new s3.Bucket(); } pub inflight get(key: str): str { // Check Redis first, otherwise fall back to S3 let var value this._redis.get(key); if value nil { value this._bucket.getObject(key); this._redis.set(key, value!); } return value!; } pub inflight set(key: str, value: str) { // Update S3 and redis with the new entry this._bucket.putObject(key, value); this._redis.set(key, value); } pub inflight reset() { this._redis.flush(); this._bucket.empty(); } }
let cache new Cache();//每小时清空缓存一次。 let schedule new triggers.Schedule(rate: 1h); schedule.onTick(inflight () { cache.reset(); });
//创建一个 AWS Lambda 函数来执行一些虚假的业务逻辑。let fn new lambda.Function(inflight (key) { let value cache.get(key!); return Found value: value; });// 将功能发布到公共 URL。 fn.expose();
在程序的顶层范围内所有代码都是预检代码。除其他外我们可以定义类、实例化资源并调用预检函数如onTick()和expose()来扩充和创建基础架构。这些语句在编译时执行。
但无论inflight使用关键字在哪里我们都会引入一个代码范围该代码只能在应用程序部署到云后运行。
get()、set()和reset()都是预检函数。
可以将 Winglang 的预检/运行中区别与 Zig 的计算时间/运行时区别进行比较。但由于这两种语言是围绕不同的用例构建的因此它们的设计截然不同这可能并不奇怪。例如Zig 的计算时间旨在避免所有潜在的副作用而 Winglang 的预检鼓励副作用以便您可以改变基础设施图。
Wing 提供了一种双相编程形式其中可以执行代码来定义云基础设施或与云基础设施进行交互。这两个阶段称为预检和飞行在时间编译时与运行时和空间上有所不同预检在构建系统上运行而飞行代码可以在支持 JavaScript 运行时的任何计算系统上执行。
元编程总结 一个要点是这种双相编程可用于解决许多不同的问题。在 Zig 中它使人们能够轻松进行编译时元编程。在 React 中它使编写更专业和优化的前端应用程序成为可能。在 Wing 中它允许您对分布式程序的基础设施和应用程序问题进行建模。这太酷了
但这里可能还有更多值得探索的地方比如这些双相解决方案的规则如何重叠或不同。
在 Zig 中您可以在 comptime 运行的每个函数也可以在运行时安全运行——因此我们可以说哪些函数可以在 comptime 运行以及哪些函数可以在运行时运行之间存在子集关系。这同样适用于 React Server Components——您可以在客户端上呈现的任何组件也可以在服务器上呈现。但在 Wing 中预检和检修两个阶段是严格分开的因此要表示可以在任一阶段运行的代码您需要为这些函数添加单独的标签如“非阶段函数”。
另一个悬而未决的问题是了解双相编程在多大程度上代表了无法用普通语言表达的能力。
Zig 需要为这个 comptime 事物添加一个新的关键字
但是否有其他现有语言可以让你做到这一点也许在用户空间 将其作为专用语言功能提供是否会提供任何改进的安全性或错误处理 元编程系统与双相编程有关。例如C 预处理可以被认为是双相编程因为它允许您在预处理器中运行代码这是运行时之前的编译阶段。但它不满足我提供的定义因为预处理器只进行文本替换而 C 的预处理器宏是有限的——ifdef 与真正的 if 语句完全不同。另一方面Lisp 风格的卫生宏如 Scheme 和 Racket 中的宏是通过支持与基础语言相同的表达能力的函数来表达的所以我认为可以说 Lisp 提供了一些最古老的双相编程示例 根据[Zig 文档](https://ziglang.org/documentation/master/comptime)comptime 表达式在某些方面受到限制 - 例如它们不能调用外部函数、包含return或try表达式或执行副作用。但是该语言的很大一部分是可用的并且所包含的示例表明 comptime 函数不需要明确标记为这样这有助于使该功能感觉更普通
JavaScript 不是最快的语言但它可靠且拥有广泛的生态系统。我们有兴趣在未来支持其他语言
网友讨论 1、我喜欢 双相 这个词在 Javascript 网络开发中以前的术语是 同构 或 通用。我认为这些术语并没有真正流行起来。 近十年来我一直在服务器端和浏览器端渲染相同的 React 组件我发现了一些非常好的模式而这些模式在其他地方并不多见。
以下是我在个人项目中使用的架构模式。为了好玩我开始用 F# 编写并使用 Fable 编译成 JS https://fex-template.fly.dev
一个基本要素是将 express 移植到浏览器并恰如其分地命名为 browser express https://github.com/williamcotton/browser-express
有了它您不仅可以编写双相用户界面组件还可以编写路由处理程序。在我看来通过大量使用其他 React 框架的经验这种方法远远优于主流框架所采用的方法甚至优于 React 开发人员所期望的工具使用方式。一个很好的副作用是网站在启用 Javascript 后也能正常运行。这也意味着交互时间是即时的。
它始终关注请求本身通过浏览器中的点击和表单发布事件创建模拟 HTTP 请求。它围绕处理传入请求和传出响应的中间件进行了适当的架构并为浏览器或服务器运行时提供了并行的中间件。它使用链接和表单等网页和浏览器原生概念来处理用户输入而不是通过 React 中的受控表单来加倍处理浏览器的状态。我不禁注意到React 正在开始摒弃受控表单。他们终于意识到这种设计是错误的。
因为代码是以这种双相的方式编写的并且注入了运行时上下文所以避免了浏览器或服务器运行时的任何条件。在我看来将文件标记为 使用客户端 或 使用服务器 是一种漏洞百出的抽象。
总之我很喜欢这篇文章并打算在实践中使用这个术语
2、最终编译时和运行时之间的任何区别都会被消解。其他一些二分法的例子也可以通过类似的通用酸来部分消解 动态类型与静态类型这是一个连续体JIT 和编译可以从两端进行攻击--在某种意义上动态类型的程序也是静态类型的--所有函数类型都是依赖函数类型所有值类型都是和类型。毕竟从属和的一个项、一个从属对只是一个盒装值。 单态化与多态化--通过表/接口/协议大致以指令缓存密度换取数据缓存密度 RC vs GC vs 堆分配通过编译器辅助证明内存所有权关系说明这应该如何发生 将堆栈和指令指针特权化而不是让这种瞬态程序状态成为与其他数据结构一样的一流数据结构以实现你自己的共同程序和其他任何东西Zig 决定内存分配不应被赋予特权以至于成为一种 隐形设施让人以为它是全局性的。 我们可以使用指针函数当你恰好知道需要多少项目以及如何访问、拥有、分配和取消分配这些项目时这些函数就能以更有效的方式透明地进行单形态化。 取而代之的是在优化代码的过程中或多或少都要考虑到内存使用、执行效率、指令密度、表示语义的清晰度等等等等。
目前我们有一些奇怪的孤立方式可以在某些语言中实现特定的特权并对你能走多远设定了相当武断的界限。我希望有一天我们能有一种语言能将所有这些决策制定和工程设计溶解到通用的设施中在这种设施中语言可以是你需要的任何东西--它只是一个中立的基底用于表达计算以及你想如何生产出可以以各种方式运行的机器制品。
据推测未来这样的语言如果真的存在应该会从今天的证明助手中衍生出来。
3、编程语言和代码的其他 双相 特性
- 由内联代码注释生成的文档Knuth 的识字编程- 测试代码
我们可以扩展到
- 安全性超越 perl 污点- O(n) 运行时和内存分析- 并行或聚类- 延迟预算
对于那些有学术倾向的人来说形式语言语义如 https://en.wikipedia.org/wiki/Denotational\_semantics 与运算等比较。
4、“双相编程”也存在于 Apache Spark、Tensorflow 等框架、Gradle 等构建工具以及代码优先工作流引擎中。第一阶段的执行会生成一个稍后要执行的代码 DAG。在我看来对于新手来说最难的事情是第一阶段和第二阶段的代码交错在一起没有直接明确的界限第一阶段的代码类似于内部 DSL。
5、双相编程的另一个示例是使用 DSL 生成解析器的解析器生成器例如 Tree Sitter 或 Lezer。
6、作者是自鸣得意的反 Lisp 狂人Lisp 中并非所有东西都是列表。 事实上Lisp 和 Forth 是最强大的“双相”语言之一因为完整语言中的两种表达式都可以在编译时进行求值。 Pre-Scheme 是 Scheme 的一个无 GC、静态类型的“系统”子集它允许您使用完整的 Scheme 语言来处理任何可以在编译时进行可证明求值的表达式例如使用 DEFINE 在顶层引入变量。
更多元编程https://www.jdon.com/74451.html