函数式语言如何表示内存中的代数数据类型?

时间:2014-07-18 02:15:36

标签: haskell memory functional-programming sml algebraic-data-types

如果您在Haskell中编写生物信息学算法,您可能会使用代数数据类型来表示核苷酸:

data Nucleotide = A | T | C | G

你会在标准ML或OCaml中做同样的事情,我认为(我从未真正使用过)。

类型Nucleotide的值可以清楚地包含在两位中。但是,这样做会导致访问时间比每个Nucleotide值使用一个字节要慢,因为您需要使用二元运算符选择两个感兴趣的位。

因此,在决定如何表示代数数据类型时,编译器必须在内存效率和计算效率之间进行固有的权衡。此外,由于值可以是可变大小的事实,因此代数数据类型在内存中的表示变得更加复杂:

data Maybe a = Just a | Nothing

显然,Maybe a形式的Just a值在逻辑上大于Nothing形式的值。在这样一个极端的例子中:

data Hulk a b c d e = Big a b c d e | Little

您绝对不希望存储Little值空指针或Big值中包含的五个值的零值。我假设你只是使用堆分配的可变大小的内存,在开头有一个构造函数ID(例如0Big1为{{1} }})。但是,如果要在堆栈上存储Little值(更快的表示),则必须将空白内存与Hulk值一起存储,以便{{1}类型的所有值都存储是相同的大小。另一个权衡。

Simon Marlow在previous StackOverflow question回答了关于GHC的一般性问题。但是,我有三个相关的问题仍然没有答案:

  • 标准ML(SML / NJ和MLton)和OCaml使用相同的技术吗?
  • 如果是这样,这些语言(或他们的兄弟姐妹)的任何不太常见的编译器是否会尝试其他技术?
  • 在这些语言中是否有一种相当简单的方法(理想情况下是一个pragma或选项标志)来使用更高效的内存表示,比如Little的两位表示?这种记忆效率对于许多生物信息学应用是必要的;如果每个Hulk必须是一个字节,那么高性能的生物信息学算法就不得不求助于手工操作。

1 个答案:

答案 0 :(得分:2)

没有一个答案:数据类型是抽象结构,可以由实施者自行决定以各种方式实现。在实践中,诸如单独编译之类的考虑往往会在某种程度上限制事物。

对于将仅包含nullary构造函数的数据类型打包到尽可能少的特定情况,您可以继续将函数从数据类型定义为小整数并返回。由抽象类型(或在Haskell中,newtype)隐藏的整数类型也是一个合理的选择。将小整数打包并打包成您正在使用的任何聚合形式将是您的工作。

顺便说一句,Real World OCaml对OCaml值的表示有a very nice chapter(粗略总结:就此问题而言,与GHC没有太大差别)。