在编程语言解释器运行时

时间:2016-06-25 09:48:10

标签: haskell io referential-transparency

要将IO函数添加到用Haskell编写的编程语言解释器中,我基本上有两个选项:

  • 修改整个解释器以在IO monad中运行
  • 让解释程序可以调用的运行时函数使用unsafePerformIO

前者对我来说是一个坏主意 - 这有效地否定了任何纯度的好处,让IO几乎到达程序的任何地方。我目前也大量使用ST,并且必须修改大量的程序才能实现这一点,因为我无法同时使用STIO时间(?)。

后者让我感到紧张 - 正如函数名称所述, 不安全,但我认为在这种情况下它可能是合理的。特别:

  • 此更改所涉及的代码量非常小。
  • 在评估解释表达式期间,通过在控制点使用seq,可以明确对可以执行IO的点进行排序。
  • 也许更重要的是,IO操作返回的值只能在代码的解释部分中使用,我可以通过使用相同参数多次调用解释器来保证引用透明性,因为操作计数器将是作为同一更改的一部分穿过整个系统,始终以唯一值传递给使用unsafePerformIO的每个函数。

在这种情况下,是否有充分理由不使用unsafePerformIO

更新

有人问我为什么要在翻译中保持纯洁。有很多原因,但也许最紧迫的是我打算稍后为这种语言构建一个编译器,该语言将包含各种元编程技术,这些技术需要编译器包含解释器,但我想成为能够保证编译结果的纯度。为此目的,语言将有一个纯子集,我希望解释器在执行该子集时是纯粹的。

1 个答案:

答案 0 :(得分:2)

如果我理解正确,你想将IO动作添加到解释语言(不纯的初学者),而解释器本身是纯粹的。

第一个选项是来自解释器的抽象primops。例如,解释器可以在一些未指定的monad中运行,而priops被注入:

data Primops m = Primops
  { putChar :: Char -> m ()
  , getChar :: m Char
  , ...
  }

interpret :: Monad m => Primops m -> Program -> m ()

现在解释器不能执行任何IO操作,除了封闭的primops列表。 (您可以使用自定义monad实现类似的结果,而不是将primops作为参数传递。)

但是,在你确切地说出为什么需要纯粹的翻译之前,我认为它过度工程化了。可能你不是吗?如果您只是想让解释器的纯部分易于测试,那么最好将这些部分提取到单独的纯函数中。这样,顶级入口点将是不纯的,但很小,但所有解释器的逻辑都是可测试的。