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,并小心隐藏在实习期间产生的标识符。