以下场景显示了一个抽象,在我看来不可能声明地实现。
假设我想创建一个符号对象,该对象允许您使用可以比较的字符串创建对象,例如JavaScript中的符号。for((。JS中的一个简单实现可能是这样的:
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这样的声明语言写这句话:
data MySymbol = MySymbol String
makeSymbol :: String -> MySymbol
makeSymbol s = MySymbol s
compareSymbol :: MySymbol -> MySymbol -> Bool
compareSymbol (MySymbol s1) (MySymbol s2) = s1 == s2
但是,也许将来我想通过使用全局注册表提高效率,而无需更改接口到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中创建一个可变的全球注册表。(我可以创建一个注册表,但不是不将接口更改为我的功能。(
具体来说,这三种可能的Haskell解决方案都有问题:
- 迫使用户通过注册表参数或同等学历,使接口实现依赖
- 使用一些花哨的单调材料,例如Haskell's Control.Monad.Random,它需要从开始或更改接口的优化预测(并且基本上只是将状态概念添加到您的程序中,从而破坏了参考透明度等。(
- 实施缓慢,在给定的应用程序中可能不实用
这些解决方案都没有使我能够从Haskell接口中充分抽象实现。
因此,我的问题是:是否有一种方法可以将此优化实现到Haskell(或任何声明性语言(中的符号对象,而不会引起上面列出的三个问题之一,还有其他情况,命令式语言可以表达出摘要(例如上述优化(,声明语言不能?
实习包显示了如何。正如@luqui所讨论的那样,它在几个关键时刻使用了unsafePerformIO
,并且要小心隐藏在实习期间产生的标识符。