以下场景显示了一个在我看来不可能以声明方式实现的抽象。
假设我想创建一个 Symbol 对象,它允许您使用可以比较的字符串创建对象,例如JavaScript 中的 Symbol.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 解决方案都有问题:
- 强制用户传递注册表参数或等效参数,使接口实现依赖
- 使用一些花哨的 Monad 东西,比如 Haskell 的Control.Monad.Random,这需要从一开始就预见到优化或更改接口(基本上只是将状态的概念添加到您的程序中,因此破坏了引用透明度等)
- 实现缓慢,在给定的应用程序中可能不实用
这些解决方案都不允许我从我的 Haskell 接口中充分抽象出实现。
所以,我的问题是:有没有办法在 Haskell(或任何声明性语言)中实现对 Symbol 对象的这种优化,而不会导致上面列出的三个问题之一,还有其他情况下命令式语言可以表达抽象吗? (例如上面的优化)声明性语言不能?