AI News HubLIVE
站內改寫4 分鐘閱讀

公開的魔法:生成的程式碼還是原始碼嗎?

本文追溯了原始碼從組合語言到編譯語言再到解釋語言的演變歷史,並探討了AI生成的程式碼如何挑戰傳統原始碼的概念,暗示提示詞可能成為新的原始碼,並引用了Knuth的文學程式設計作為可能的方向。

來源Hacker News AI作者: chotchkies

在20世紀70年代,一群學者開始抱怨軟體只以編譯後的製品形式交付。儘管那些試圖透過軟體盈利的公司對此視而不見,但一小群支援自由軟體的人還是接受了這一理念。到了80年代,他們將自己的要求正式化。

隨後在90年代,家用電腦的普及加上廉價網際網路的分發渠道,使得“自由軟體”進入更廣泛的日常用語,但“自由”一詞的多義性讓大多數人關注點落在金錢上。

鑑於開發平庸到合格水平軟體的成本已降至每月20美元的訂閱費,我們有必要重新審視“自由”的另一面。

要求隨軟體分發原始碼的原則相當合理。如果使用者想要修復缺陷,就需要原始碼(以及執行步驟)。如果使用者想改變原作者有限意圖之外的目的,原始碼也很有幫助。最後,它有助於新手透過示例學習。

如果你在這裡,你可能已經知道什麼是原始碼,但讓我們看看它是如何隨時間演變的。

遠古時代

計算機的核心是一個是/否機器。這種是/否、真/假、開/關(腳註:技術上是正確的)的特性適合編碼為基數為2的數字系統。CPU載入指令,然後執行。

問題在於記住“139”是加法指令,還是“2736”?等等,兩者都是,而且略有不同。

這就是組合語言發揮作用的地方。將難以記憶的數字替換為略微容易記憶的字元。這些字元是助記符,它們代表的數字是操作碼。

因此,你不再寫“139”,而是寫ADD;不再寫“2736”,而是寫ADDS。在兩種情況下,你都需要開啟手冊來記住第二個指令的副作用。

將助記符及其引數(你要加的內容)翻譯成操作碼/二進位制的程式稱為組譯器,這就是組合語言。

70年代的程式碼

20世紀70年代初,C語言被髮明,到70年代末已得到廣泛使用。還有其他語言,但我們的敘述中不關心它們。

C語言(以及其他編譯語言)所做的是提供一種更緊湊的方式來表達程式。

// 我醒來時有零個青檸
int limeCounter = 0;

// 我去商店
limeCounter += 5;

printf("我有 %d 個青檸\n", limeCounter);

這在工作量上是一個巨大的減少,因為僅僅“我去商店”這部分在彙編中是這樣的:

ldr  w1, [x0]       // 載入 limeCounter
add  w1, w1, #5     // +5
str  w1, [x0]       // 存回

[點選此處檢視完整彙編程式碼...]

將C版本翻譯成彙編版本的過程稱為編譯。你可以將編譯和彙編步驟不可見地結合,但它們是兩個獨立的階段。

一致性不保證

利用編譯器引入的一個微妙變化是,它為翻譯者留下了一些自由發揮的空間。

在我們的例子中,編譯器選擇了ADD助記符,但ADDS也可以工作。

細心的讀者還會注意到,對於這個簡單的程式,變數從未被使用,因此可以移除加法操作,直接將青檸計數器從5開始。

編譯器可以並且確實會利用這些自由。這些就是編譯器最佳化。這也意味著不同版本或品牌的編譯器會產生不同的結果輸出。

當人們期望原始碼時,他們指的是C版本。那最接近作者的意圖。

可解釋性

編譯語言的一個缺點是它們生成的二進位制檔案與所針對的特定CPU繫結。你可以編寫跨平臺的編譯程式碼,但這需要更多的細心和專注。

90年代末,當辣妹組合與朋友們狂歡時,Netscape團隊正在努力工作。他們的網路瀏覽器(Netscape的,不是辣妹的)內部有一個引擎,可以讀取隱藏在網頁中的未編譯程式碼,這些程式碼可以修改頁面本身。

由於瀏覽器將在多種不同的CPU上執行,它也採用了一種不同的正規化:直譯器。直譯器不是將語言轉換為彙編,然後再彙編,而是將語言轉換為其自己的指令集,然後透過自己的引擎將其傳遞給CPU。(或者足夠接近)

JavaScript不是第一種解釋型語言,但它可能是最普遍的。從那時起,它被迭代、從瀏覽器中提取出來,併成為LLM首選的語言之一,這可能是因為它使用得非常普遍。

隨著直譯器的發展,有些建立了自己的獨立位元組碼,但沒有常見的格式。

由於沒有可分發的製品,這意味著解釋型語言會提供原始碼。人們曾努力混淆原始碼以控制智慧財產權,但這樣做會使診斷和修復問題變得更加困難。本質上,混淆後的程式碼仍然是JavaScript,只是可讀性較差的變體。這一點後面會很重要。

因為原始碼就在網頁中,它極大地降低了人們學習入門門檻。人們確實學習了,這可能是歷史上最好的學習時機之一。

我們的孩子也會嘲笑我們

正規化在不斷轉移和演變。正規化轉換的奇怪之處在於,它們很難被定性,直到它們落地並穩定下來。

讓我們回到開源運動的原則。其精神歸結為希望普通從業者能夠:

  • 修復交付軟體中的缺陷
  • 向原作者學習
  • 無需依賴原作者即可擴充套件功能

但我們越來越多地看到的是介面的改變。開發者編寫一組指令,計算機按原樣執行。

提示驅動開發要麼生成可重複執行的持久化製品,要麼將代理框架本身視為直譯器。如果持久化製品從未被檢查,那麼它在實質上與機器碼或臨時彙編沒有區別。

因此,原始碼變成了那個提示詞或一系列提示詞。大多數人在輸出足夠好時就停止最佳化提示詞,因為他們從未打算分享過程。

提示詞的輸出變成了JavaScript(或Rust、Kotlin),但所有意圖都被剝離了。沒有東西可以學習,更接近於是交付混淆後的JavaScript或組合語言版本,並假裝那是原始碼。作者自己可能都不理解中間表示。但“交付提示詞”這個術語幾乎被嘲笑地使用,因為它非常不可靠。

程式語言不支援的一個特性是歧義性。如果英語是一種壓縮形式,那麼它是有損的。英語在歧義中茁壯成長,“應該”這個詞是所有需求文件的禍害。所以單獨的提示詞遠遠不夠好。

因此,我不是在主張孤立地“交付提示詞”,但六個月前的原始碼正在失去價值。我們是否需要發明新的東西?

Knuth知道

要記住的一點是,對於每一次啟示,它們都不是尖端技術,而是超前於時代的東西,耐心等待更廣泛的行業趕上來。

我們的編譯器例子發生在1975年,但概念自1957年就已存在。直譯器僅在一年後(1958年)被構想,但主流直到1995年才趕上。

如果我們看看我所描述問題的形狀,它與Donald Knuth在1984年引入的文學程式設計有著奇怪的相似之處。程式設計師寫一篇論文,其中嵌入程式碼,然後利用一對工具。一個工具(tangle)最佳化編譯器的輸出,另一個工具(weave)最佳化讀者的輸出。計算機消費指令,我們消費意圖。不是在程式碼庫中散佈註釋,而是交付一篇論文,用程式碼來解決歧義。在文學程式設計中,散文就是程式。在今天的世界中,提示詞或其某種衍生形式似乎需要保留。特別是對於開源。如果意圖丟失,“原始碼”並不比交付彙編或混淆後的JavaScript好多少。

它仍然是某些學術圈的好奇之物。Jupyter筆記本在某種程度上接近了,但在實踐中它們往往只是一串連續的單元格。Knuth的解決方案可能沒有被廣泛採用,但他肯定看到了問題的到來,等待了40多年讓我們其他人趕上來。

我不確定這裡的正確做法是什麼,但這首歌聽起來非常熟悉。我不知道所有的歌詞,但我想我至少能哼出曲調。