【问题标题】:Interning strings in declarative programming声明式编程中的实习字符串
【发布时间】:2017-06-07 21:11:31
【问题描述】:

以下场景显示了一个在我看来无法以声明方式实现的抽象。

假设我想创建一个 Symbol 对象,它允许您使用可以比较的字符串创建对象,例如 Symbol.for() in JavaScript。 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 解决方案都有问题:

  1. 强制用户传递注册表参数或等效参数,使接口实现依赖
  2. 使用一些花哨的 Monad 东西,比如 Haskell 的 Control.Monad.Random,这需要从一开始就预见到优化或更改接口(基本上只是将状态的概念添加到您的程序中,因此破坏了引用透明度等)
  3. 实施缓慢,在给定的应用程序中可能不实用

这些解决方案都不允许我从我的 Haskell 接口中充分抽象出实现。

所以,我的问题是: 有没有办法在 Haskell(或任何声明性语言)中对 Symbol 对象实现这种优化,而不会导致上面列出的三个问题之一, 还有其他情况,命令式语言可以表达声明性语言不能表达的抽象(例如上面的优化)吗?

【问题讨论】:

  • 哦,是的,如果您不允许将接口提升为 monadic(如您的第 2 项),那么命令式语言可以表达声明性语言无法表达的各种抽象。例如,可变变量的抽象。
  • 请注意,如果您没有正确实施实习,您将失去参照透明度。例如。如果您暴露了实习ID,它将取决于评估顺序。比如说,如果你序列化了你的符号,你的文件输出可能会因为应该是正确的重构而改变。所以当我有严格的想法时,我会说必须准确地建模依赖关系——提升为捕捉这些效果的单子或其他结构。当我在实际的头脑中,回避透明度,抛开谨慎,并使用unsafePerformIO,希望我做对了。
  • 好像你也想吃蛋糕——你想使用一个不能保证是纯的抽象,就好像它被保证是纯的一样。
  • 请参阅this package,了解将不安全代码打包到安全接口中的示例。归根结底,强类型系统应该限制编译器接受的程序数量(甚至是正确的程序)。有趣的后续问题确实是:“Haskell 的类型系统是否存在问题,使我无法在运行时像 X 语言那样高效地解决方案”。

标签: haskell symbols declarative imperative-programming declarative-programming


【解决方案1】:

intern package 显示了方法。正如@luqui 所讨论的,它在几个关键时刻使用unsafePerformIO,并小心隐藏在实习期间产生的标识符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-11
    • 1970-01-01
    • 1970-01-01
    • 2014-03-12
    • 2023-03-18
    • 1970-01-01
    相关资源
    最近更新 更多