2

以下方案顯示了一種抽象,我認爲這是不可能以聲明方式實現的。聲明式編程中的實習字符串

假設我想要創建一個符號對象,它允許您使用可以比較的字符串創建對象,如Symbol.for() in JavaScript。在JS一個簡單的實現可能是這樣的:

data MySymbol = MySymbol String 

makeSymbol :: String -> MySymbol 
makeSymbol s = MySymbol s 

compareSymbol :: MySymbol -> MySymbol -> Bool 
compareSymbol (MySymbol s1) (MySymbol s2) = s1 == s2 

但是,也許將來我想通過提高效率:

function MySymbol(text){//Comparable symbol object class 
    this.text = text; 
    this.equals = function(other){//Method to compare to other MySymbol 
    return this.text == other.text; 
    } 
} 

我可以很容易地在一個說明性語言比如Haskell寫全局註冊表,而不將接口更改爲MySymbol對象。 (我的類的用戶並不需要知道,我已經改變了它使用註冊表)

例如,這是很容易在Javascript完成:

function MySymbol(text){ 
    if (MySymbol.registry.has(text)){//check if symbol already in registry 
    this.id = MySymbol.registry.get(text);//get id 
    } else { 
    this.id = MySymbol.nextId++; 
    MySymbol.registry.set(text, this.id);//Add new symbol with nextId 
    } 
    this.equals = function(other){//To compare, simply compare ids 
    return this.id == other.id; 
    } 
} 
//Setup initial empty registry 
MySymbol.registry = new Map();//A map from strings to numbers 
MySymbol.nextId = 0; 

然而,這是不可能的在Haskell中創建一個可變的全局註冊表。 (我可以創建一個註冊表,但如果不改變接口到我的功能)。


具體來說,這三種可能的哈斯克爾的解決方案都有問題:

  1. 強制用戶通過註冊表參數或相當的,使接口實現依賴於
  2. 使用像Haskell的Control.Monad.Random一些花哨的Monad的東西,這將需要從一開始就預見優化或改變接口(並且基本上只是將狀態的概念添加到您的程序,因此bre AKS引用透明等)
  3. 有一個緩慢的實現可能不是在一個給定的應用是可行的

這些解決方案都允許我從我的Haskell的接口足夠抽象掉的實現。

所以,我的問題是:有沒有造成三大問題之一以上所列, 實現這種優化,以在Haskell一個符號對象(或任何聲明性語言)的方式以及是否有任何其他情況下一個命令式語言可以表達一種抽象語言(例如上面的一種優化):一種聲明式語言所不能的?

+3

噢,是的,如果你不允許將接口提升爲monadic(如你的第2號),那麼命令式語言可以表達聲明式語言不能的各種抽象。例如,一個可變變量的抽象。 – luqui

+5

注意,如果您沒有正確實施實習,那麼您將失去參考透明度。例如。如果您公開了實習編號,則取決於評估順序。比方說,如果你序列化你的符號,你的文件輸出可能會改變,因爲應該是正確的重構。所以,當我處於嚴謹的思維狀態時,我說*必須準確建模依賴關係* - 推廣到monad或其他捕獲這些效應的結構。當我在實踐中思考,避開透明度,謹慎行事,並使用'unsafePerformIO',希望我做對了。 – luqui

+9

看起來好像你想擁有你的蛋糕並且也吃它 - 你想要使用抽象,並不保證它是純粹的,就像它保證是純淨的一樣。 – luqui

回答

4

intern package顯示如何。正如@luqui所討論的,它在幾個關鍵時刻使用unsafePerformIO,並小心隱藏在實習期間產生的標識符。