h5網(wǎng)站怎么做api對接關鍵詞seo深圳
本文討論了編程語言的一種趨勢,即允許相同的語法表達
- 在兩個不同階段或環(huán)境(上下文)中執(zhí)行的計算
- 同時保持跨階段(上下文)的一致行為。
- 這些階段通常在時間上(運行時間)或空間上(運行地點)有所不同。
作者提供了三種體現(xiàn)這種“雙相編程(biphasic programming)”概念的語言示例:
- Zig:Zig 允許使用“comptime”關鍵字在編譯時運行普通函數(shù),提供與基礎語言相同的表達能力.這使得源代碼中的構建時間和運行時執(zhí)行之間能夠無縫切換。
- Winglang:Winglang 是一種用于編寫云應用程序的編程語言,其設計采用了雙相概念。它提供預檢代碼(在編譯時運行以定義云基礎設施)和運行中代碼(在運行時運行以與基礎設施交互)。兩個階段具有相同的語法,但具有不同的規(guī)則和功能。
- React 服務器組件 (RSC):RSC 允許 React 組件指定它們應該在服務器端還是客戶端渲染,從而實現(xiàn)服務器渲染和客戶端渲染組件的靈活組合。此方法旨在通過最小化服務器和客戶端之間傳輸?shù)膭討B(tài) HTML 和組件信息量來優(yōu)化頁面性能。
作者提出,雙相規(guī)劃可用于解決各種問題,探索這些解決方案規(guī)則之間的重疊和差異可能會產(chǎn)生有趣的見解文章還提到,雖然編譯時代碼執(zhí)行并不是一個新想法,但 Zig 的方法似乎避免了其他元編程 系統(tǒng)的幾個缺點。
雙相編程問題
雖然雙相編程可以在表達力、性能和靈活性方面帶來好處,但開發(fā)人員必須做好準備,以應對在項目中采用這種模式所帶來的日益增加的復雜性和潛在挑戰(zhàn)
- 復雜性增加:雙相編程要求開發(fā)人員在同一代碼庫中管理兩個不同的執(zhí)行階段(例如編譯時和運行時),從而增加了一層復雜性。這會增加認知負擔,使代碼更難理解和維護。
- 參數(shù)化和數(shù)據(jù)需求:雙相模型通常需要更多的參數(shù)和數(shù)據(jù)來捕捉兩個階段的細微差別,與更簡單的單相模型相比,這使得它們更難以擬合和驗證。
- 工具和生態(tài)系統(tǒng)支持:現(xiàn)有的開發(fā)工具、庫和框架可能不完全支持雙相編程范式,需要開發(fā)人員投入時間和精力來構建定制解決方案或調(diào)整他們的工作流程。
- 性能權衡:在編譯時執(zhí)行代碼的能力可以提供性能優(yōu)勢,但也可能引入新的性能考慮,例如增加編譯時間或緩存和記憶的潛在問題。
- 采用和學習曲線:雙相編程代表了傳統(tǒng)編程模型的轉變,開發(fā)人員在加入團隊并將新方法集成到現(xiàn)有代碼庫和開發(fā)實踐中時可能會面臨阻力或挑戰(zhàn)。
- 調(diào)試和故障排除:將代碼執(zhí)行分為兩個不同的階段可能會使調(diào)試和解決問題變得更加困難,因為根本原因可能隱藏在編譯時和運行時環(huán)境之間的交互中
1、案例:Zig
Zig一種系統(tǒng)編程語言,可讓您編寫高性能代碼,并相對輕松地逐步采用到 C/C++ 代碼庫中。
它的主要創(chuàng)新之一是一種名為“comptime”的全新元編程方法,可讓您在編譯時運行普通函數(shù)。
與 C、C++ 和 Rust 中的預處理系統(tǒng)和宏系統(tǒng)相比,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 用戶在源代碼中無縫切換在構建時運行代碼和在運行時運行代碼,而不會帶來陡峭的學習曲線。
它改變了開發(fā)人員的思維模式,不再將元編程視為高級魔法,而是將其視為一種優(yōu)化工具,還可以利用它來實現(xiàn)泛型和其他代碼生成用途。
不管怎樣,編譯時代碼執(zhí)行并不是一個全新的想法。然而,Zig 的方法似乎確實避免了一些缺點。例如,與 Rust 及其const 函數(shù)不同,Zig 不會對 comptime 函數(shù)強制使用函數(shù)著色。同樣,與 C++ 的模板系統(tǒng)不同,Zig 不會引入任何用于表示泛型的新語法。與支持 hygenic 宏的 Scheme 和 Racket 等 Lisp 相比,Zig 并不要求所有內(nèi)容都是列表。
TL;DR:?Zig 支持一種雙相編程形式,其中相同的函數(shù)可以在兩個不同的階段運行,這兩個階段在時間上(構建時間與運行時間)和空間上(在構建系統(tǒng)上與在運行二進制文件的機器上)有所不同。
2、案例:React 服務器組件
我注意到的第二個雙相編程示例是React Server Components?(RSC)。React 本身并不是一門語言,但作為一個 JavaScript Web 框架,它作為編寫和編寫大型網(wǎng)站的 UI 組件及其相關 UI 邏輯的基礎系統(tǒng),擁有相當大的知名度。
最近,前端 JavaScript 生態(tài)系統(tǒng)一直在進行大量探索,以找出如何最有效地在服務器或客戶端上呈現(xiàn) UI 組件以提高頁面性能。已經(jīng)提出了許多解決方案,其中最雄心勃勃的解決方案之一就是 RSC。
RSC 背后的想法是允許 React 組件指定它應該在服務器端還是客戶端呈現(xiàn),并允許這些組件自由組合在一起。
例如,
-
組件Feed可能在服務器上呈現(xiàn)(因為它需要從數(shù)據(jù)庫獲取 feed 項列表),
-
而每個子組件FeedItem可以在客戶端呈現(xiàn)(因為它們是項狀態(tài)的純函數(shù)),
-
而FeedItemPreview可能在服務器上呈現(xiàn)(因為它需要從數(shù)據(jù)庫獲取項的內(nèi)容)。
開發(fā)人員可以選擇在哪里計算哪些組件,底層引擎(通常是生成服務器端代碼和客戶端代碼的 JavaScript 打包器)會優(yōu)化所有內(nèi)容,以便在需要時在服務器或客戶端上呈現(xiàn)組件,從而最大限度地減少來回傳輸?shù)膭討B(tài) HTML 和組件信息量。
讓這一切正常運行并穩(wěn)定下來仍是一項艱巨的工作。但我認為該范式是雙相編程的一個有趣例子。
有很多方法可以減少需要在客戶端瀏覽器上發(fā)送和執(zhí)行的代碼量,并將更多工作轉移到服務器上,但當今大多數(shù)現(xiàn)有解決方案都要求開發(fā)人員將 React 組件視為純客戶端抽象,或純服務器端抽象。
例如,要么在服務器上呈現(xiàn)整個頁面,要么在客戶端呈現(xiàn)整個頁面,反之亦然。如果引擎可以得到足夠的優(yōu)化并且生成的代碼可以足夠調(diào)試,那么采用 React 組件模型并讓開發(fā)人員切換組件的呈現(xiàn)位置似乎是一種強大的抽象。
React Server Components 承諾一種雙相編程形式,其中可以使用相同的 JavaScript + JSX 語法來表示在服務器或客戶端上呈現(xiàn)的組件,并且可以靈活組合。服務器端和客戶端渲染同時進行,但它們在空間上有所不同(在服務器上與在瀏覽器上)。
我還想特別提到Electric Clojure ,這是我在[url=https://systemsdistributed.com/\]Systems Distributed[/url]的一次閃電演講中發(fā)現(xiàn)的這個項目,它采用了類似的想法,在前端/后端邊界上提供強大的組合,但使用的是 Clojure 語言。
3、案例:Winglang
我對“雙相編程”理念如此好奇的很大一部分原因是,在過去的兩年里,我一直在研究Winglang,這是一種用于編寫云應用程序的新編程語言,它在設計中大量采用了這一概念。這個項目是我介紹的三個例子中最年輕的一個(它只開發(fā)了兩年),但在本文中,我將嘗試盡可能簡短地介紹它,以便為其雙相類型系統(tǒng)提供足夠的背景信息。
Winglang 背后的要點是,由于擁有大量計算資源,AWS、Azure 和 GCP 等主要云提供商能夠為開發(fā)人員提供各種可擴展的高級服務,如隊列、發(fā)布-訂閱主題、工作流、流、存儲桶等。通俗地說,這些通常被稱為資源。Terraform和CloudFormation等基礎設施即代碼工具使得使用 JSON 或[url=https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-overview.html\]YAML\[/url\]管理這些資源成為可能。
原則上,使用這些資源構建復雜的應用程序應該不難。但是,如果您的應用程序足夠大并且擁有許多資源,那么將每個無服務器函數(shù)或容器服務與其所需資源的權限和配置明確連接起來就很容易出錯。圍繞這些資源設計自定義接口也很困難。
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();
});
//創(chuàng)建一個 AWS Lambda 函數(shù)來執(zhí)行一些虛假的業(yè)務邏輯。let fn = new lambda.Function(inflight (key) => {
let value = cache.get(key!);
return "Found value: " + value;
});// 將功能發(fā)布到公共 URL。
fn.expose();
在程序的頂層范圍內(nèi),所有代碼都是預檢代碼。除其他外,我們可以定義類、實例化資源并調(diào)用預檢函數(shù)(如onTick()和expose())來擴充和創(chuàng)建基礎架構。這些語句在編譯時執(zhí)行。
但無論inflight使用關鍵字在哪里,我們都會引入一個代碼范圍,該代碼只能在應用程序部署到云后運行。
get()、set()和reset()都是預檢函數(shù)。
可以將 Winglang 的預檢/運行中區(qū)別與 Zig 的計算時間/運行時區(qū)別進行比較。但由于這兩種語言是圍繞不同的用例構建的,因此它們的設計截然不同,這可能并不奇怪。例如,Zig 的計算時間旨在避免所有潛在的副作用,而 Winglang 的預檢鼓勵副作用,以便您可以改變基礎設施圖。
Wing 提供了一種雙相編程形式,其中可以執(zhí)行代碼來定義云基礎設施,或與云基礎設施進行交互。這兩個階段稱為預檢和飛行,在時間(編譯時與運行時)和空間上有所不同(預檢在構建系統(tǒng)上運行,而飛行代碼可以在支持 JavaScript 運行時的任何計算系統(tǒng)上執(zhí)行)。
元編程總結
一個要點是,這種雙相編程可用于解決許多不同的問題。在 Zig 中,它使人們能夠輕松進行編譯時元編程。在 React 中,它使編寫更專業(yè)和優(yōu)化的前端應用程序成為可能。在 Wing 中,它允許您對分布式程序的基礎設施和應用程序問題進行建模。這太酷了!
但這里可能還有更多值得探索的地方:比如這些雙相解決方案的規(guī)則如何重疊或不同。
- 在 Zig 中,您可以在 comptime 運行的每個函數(shù)也可以在運行時安全運行——因此我們可以說,哪些函數(shù)可以在 comptime 運行以及哪些函數(shù)可以在運行時運行之間存在子集關系。
- 這同樣適用于 React Server Components——您可以在客戶端上呈現(xiàn)的任何組件也可以在服務器上呈現(xiàn)。
- 但在 Wing 中,預檢和檢修兩個階段是嚴格分開的,因此要表示可以在任一階段運行的代碼,您需要為這些函數(shù)添加單獨的標簽(如“非階段函數(shù)”)。
另一個懸而未決的問題是了解雙相編程在多大程度上代表了無法用普通語言表達的能力。?
- Zig 需要為這個 comptime 事物添加一個新的關鍵字?
但是否有其他現(xiàn)有語言可以讓你做到這一點,也許在用戶空間?
將其作為專用語言功能提供是否會提供任何改進的安全性或錯誤處理?
-
元編程系統(tǒng)與雙相編程有關。例如,C 預處理可以被認為是雙相編程,因為它允許您在預處理器中運行代碼,這是運行時之前的編譯階段。但它不滿足我提供的定義,因為預處理器只進行文本替換,而 C 的預處理器宏是有限的——ifdef 與真正的 if 語句完全不同。另一方面,Lisp 風格的衛(wèi)生宏(如 Scheme 和 Racket 中的宏)是通過支持與基礎語言相同的表達能力的函數(shù)來表達的,所以我認為可以說 Lisp 提供了一些最古老的雙相編程示例?
-
根據(jù)[Zig 文檔](https://ziglang.org/documentation/master/comptime),comptime 表達式在某些方面受到限制 - 例如,它們不能調(diào)用外部函數(shù)、包含return或try表達式或執(zhí)行副作用。但是,該語言的很大一部分是可用的,并且所包含的示例表明 comptime 函數(shù)不需要明確標記為這樣,這有助于使該功能感覺更普通?
JavaScript 不是最快的語言,但它可靠且擁有廣泛的生態(tài)系統(tǒng)。我們有興趣在未來支持其他語言?
網(wǎng)友討論:
1、我喜歡 "雙相 "這個詞!在 Javascript 網(wǎng)絡開發(fā)中,以前的術語是 "同構 "或 "通用"。我認為這些術語并沒有真正流行起來。
近十年來,我一直在服務器端和瀏覽器端渲染相同的 React 組件,我發(fā)現(xiàn)了一些非常好的模式,而這些模式在其他地方并不多見。
以下是我在個人項目中使用的架構模式。為了好玩,我開始用 F# 編寫,并使用 Fable 編譯成 JS:
https://fex-template.fly.dev
一個基本要素是將 express 移植到瀏覽器,并恰如其分地命名為 browser express:
https://github.com/williamcotton/browser-express
有了它,您不僅可以編寫雙相用戶界面組件,還可以編寫路由處理程序。在我看來,通過大量使用其他 React 框架的經(jīng)驗,這種方法遠遠優(yōu)于主流框架所采用的方法,甚至優(yōu)于 React 開發(fā)人員所期望的工具使用方式。一個很好的副作用是,網(wǎng)站在啟用 Javascript 后也能正常運行。這也意味著交互時間是即時的。
它始終關注請求本身,通過瀏覽器中的點擊和表單發(fā)布事件創(chuàng)建模擬 HTTP 請求。它圍繞處理傳入請求和傳出響應的中間件進行了適當?shù)募軜?#xff0c;并為瀏覽器或服務器運行時提供了并行的中間件。它使用鏈接和表單等網(wǎng)頁和瀏覽器原生概念來處理用戶輸入,而不是通過 React 中的受控表單來加倍處理瀏覽器的狀態(tài)。我不禁注意到,React 正在開始摒棄受控表單。他們終于意識到這種設計是錯誤的。
因為代碼是以這種雙相的方式編寫的,并且注入了運行時上下文,所以避免了瀏覽器或服務器運行時的任何條件。在我看來,將文件標記為 "使用客戶端 "或 "使用服務器 "是一種漏洞百出的抽象。
總之,我很喜歡這篇文章,并打算在實踐中使用這個術語!
2、最終,編譯時和運行時之間的任何區(qū)別都會被消解。其他一些二分法的例子也可以通過類似的通用酸來部分消解:
-
動態(tài)類型與靜態(tài)類型,這是一個連續(xù)體,JIT 和編譯可以從兩端進行攻擊--在某種意義上,動態(tài)類型的程序也是靜態(tài)類型的--所有函數(shù)類型都是依賴函數(shù)類型,所有值類型都是和類型。畢竟,從屬和的一個項、一個從屬對只是一個盒裝值。
-
單態(tài)化與多態(tài)化--通過表/接口/協(xié)議,大致以指令緩存密度換取數(shù)據(jù)緩存密度
-
RC vs GC vs 堆分配,通過編譯器輔助證明內(nèi)存所有權關系,說明這應該如何發(fā)生
-
將堆棧和指令指針特權化,而不是讓這種瞬態(tài)程序狀態(tài)成為與其他數(shù)據(jù)結構一樣的一流數(shù)據(jù)結構,以實現(xiàn)你自己的共同程序和其他任何東西:Zig 決定,內(nèi)存分配不應被賦予特權,以至于成為一種 "隱形設施",讓人以為它是全局性的。
-
我們可以使用指針函數(shù),當你恰好知道需要多少項目,以及如何訪問、擁有、分配和取消分配這些項目時,這些函數(shù)就能以更有效的方式透明地進行單形態(tài)化。
-
取而代之的是,在優(yōu)化代碼的過程中,或多或少都要考慮到內(nèi)存使用、執(zhí)行效率、指令密度、表示語義的清晰度等等等等。
目前,我們有一些奇怪的孤立方式,可以在某些語言中實現(xiàn)特定的特權,并對你能走多遠設定了相當武斷的界限。我希望有一天,我們能有一種語言,能將所有這些決策制定和工程設計溶解到通用的設施中,在這種設施中,語言可以是你需要的任何東西--它只是一個中立的基底,用于表達計算,以及你想如何生產(chǎn)出可以以各種方式運行的機器制品。
據(jù)推測,未來這樣的語言,如果真的存在,應該會從今天的證明助手中衍生出來。
3、編程語言和代碼的其他 "雙相 "特性:
- - 由內(nèi)聯(lián)代碼注釋生成的文檔(Knuth 的識字編程)
- - 測試代碼
我們可以擴展到
- - 安全性(超越 perl 污點)
- - O(n) 運行時和內(nèi)存分析
- - 并行或聚類
- - 延遲預算
對于那些有學術傾向的人來說,形式語言語義,如 https://en.wikipedia.org/wiki/Denotational\_semantics 與運算等比較。
4、“雙相編程”也存在于 Apache Spark、Tensorflow 等框架、Gradle 等構建工具以及代碼優(yōu)先工作流引擎中。第一階段的執(zhí)行會生成一個稍后要執(zhí)行的代碼 DAG。在我看來,對于新手來說,最難的事情是第一階段和第二階段的代碼交錯在一起,沒有直接明確的界限(第一階段的代碼類似于內(nèi)部 DSL)。
5、雙相編程的另一個示例是使用 DSL 生成解析器的解析器生成器,例如 Tree Sitter 或 Lezer。
6、作者是自鳴得意的反 Lisp 狂人:Lisp 中并非所有東西都是列表。
事實上,Lisp 和 Forth 是最強大的“雙相”語言之一,因為完整語言中的兩種表達式都可以在編譯時進行求值。
Pre-Scheme 是 Scheme 的一個無 GC、靜態(tài)類型的“系統(tǒng)”子集,它允許您使用完整的 Scheme 語言來處理任何可以在編譯時進行可證明求值的表達式(例如,使用 DEFINE 在頂層引入變量)。
更多元編程:https://www.jdon.com/74451.html