[英]What does sharing refer to in the implementation of a functional programming language
共享意味着如果臨時數據將被多次使用,則將其存儲。 也就是說,函數只評估它的參數一次。 一個例子是:
let x = sin x in x * x
還有哪些其他功能有助於共享 ,以及它們如何與實際執行IO的需求相互作用?
共享是一種平等:指針平等。 在Haskell的價值土地(語義解釋)中,沒有共享這樣的東西。 如果它們具有Eq
實例,則值只能相等,然后將“相等”定義為二元關系(==)
。
因此,共享通過引用由於實現而不是語義而存在的底層指針相等來逃避語義解釋。 不過,這有時會產生副作用。 不幸的是,由於Haskell是由其語義解釋定義的,因此共享的使用是未定義的行為。 它與特定的實現有關。 一些圖書館使用共享,因此行為與GHC相關聯。
但是,有一種內置的共享機制。 這由StableName
接口公開。 我們使用makeStableName :: a -> IO (StableName a)
生成StableName a
對象,並且有一個instance Eq (StableName a)
StableName
為任何類型引入了某種相等性。
StableName
相等幾乎是指針相等。 引用Haddock文檔
如果
sn1 :: StableName
和sn2 :: StableName
和sn1 == sn2
那么sn1
和sn2
是通過對同一對象調用makeStableName
創建的。
請注意,這只是一個if語句,而不是if和only if 。 事實上兩個東西可以是“指針等價”,但仍然有不同的穩定名稱有時是(a)強迫Haskell留給GC的靈活性和(b)允許StableName
存在於任何Haskell實現中的漏洞,即使有在實現中沒有“指針等式”這樣的東西。
這些StableName
仍然沒有語義含義,但是因為它們在IO
monad中被引入了“OK”。 相反,它們可能被認為是在任何類型上實現可能的最小(最具體)等式關系的(具有諷刺意味的)不穩定子集。
函數式編程中最明顯的共享示例來自Clean,它基於圖形重寫。 在那里,計算是指DAG,因此我們可以將表達式(sin x) * (sin x)
視為
(*)
/ \
sin x sin x
圖重寫系統有一個明確的共享概念,所以我們可以將計算表達為
(*)
/ \
\ /
sin x
將乘法節點指向同一節點,從而共享sin x
的計算。 術語重寫系統沒有如此明確的共享概念,但優化仍然是相關的。 在GHC中,有時可以表達與局部變量的共享,例如重寫
f x = (sin x) * (sin x)
成
f x = sinx * sinx
where sinx = sin x
但由於這兩者在語義上是等價的,因此編譯器可以自由地以相同的方式實現,無論是否共享。 根據我的理解,GHC通常會保留與局部變量一起引入的共享,有時會引入它(向第一個添加共享),但是在術語重寫系統中沒有正式的共享表達,要么行為依賴於實現(參見tel的評論和回答)。
共享觸摸IO,因為無法共享副作用值。 如果我們考慮一種不純的語言,那么它們之間就存在差異
(string-append (read-line)
(read-line))
和
(let ((s (read-line)))
(string-append s s))
第一個執行IO動作兩次,從用戶請求兩行並附加它們; 第二個“共享”IO動作,執行一次並將其附加到自身。 通常,共享純計算可以在不改變程序結果的情況下減少執行時間,同時共享副作用值(可能隨時間變化或與用戶交互的值)會改變結果。 為了讓編譯器自動共享計算,它需要知道它們是純粹的,因此減少評估的數量並不重要。 Clean以獨特的類型做到這一點; IO操作具有類型屬性UNQ,它告訴編譯器不應該共享它。 Haskell對IO monad的做法有所不同。
您的示例不是共享的示例 - 它只是將值與自身相乘(然后將原始值拋出)。
共享是指數據結構的某些部分在較大的數據結構或不同的數據結構中出現兩次的情況。 例如:
p = (1, 2)
pp = (p, p) -- p is shared between the first and second component of pp
xs = [1, 2, 3]
ys = 0::1::xs
zs = 5::xs -- ys and zs share the same tail
在內存中,這種共享將導致DAG結構。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.