5

我很好奇Kiselyov 和 Shan 在Function Pearl: Implicit Configurations文章 中讨论的对隐式参数的反对意见。

在存在隐式参数的情况下内联代码(β-reduce)是不合理的。

真的吗?我希望 GHC 应该内联到与传递的隐式参数相同的范围,不是吗?

我相信我理解他们的反对意见:

如果添加、删除或更改术语的签名,则术语的行为可能会发生变化。

GHC 的用户文档解释说,程序员必须注意多态递归单态限制。这是否是他们所说的内联问题的意思?

我认为这个多态递归示例也涵盖了“对隐式参数进行泛化”的含义?还要别的吗?

Data.Reflection中的ReifiesStorable类型类真的是解决这些困难的明智之选吗?它似乎在每次访问时都会反序列化整个隐式数据结构,这听起来对性能来说是灾难性的。例如,我们可能希望我们的隐含信息是 Cayley 表或字符表,它们占据了 ram,并且必须在数百万次代数运算期间访问。

是否有一些更好的解决方案使用隐式参数,或者编译器可以在幕后轻松优化的另一种技术,同时仍然通过使用状态线程或其他方式的类型系统保证更多?

4

1 回答 1

7

是的,GHC 手册中的示例显示了添加类型签名如何通过隐式参数改变代码的语义,我相信这就是打破内联的意思;内联应用程序len_acc1与应用程序len_acc2会产生相同的代码,尽管两者具有不同的语义。

就隐式参数的泛化而言,这意味着您不能编写可以对多个隐式参数进行操作的函数;没有对它们进行抽象的机制,因为函数使用的隐式参数由其类型固定。使用反射,您可以轻松编写类似 的函数doSomethingWith :: (Reifies s a, Num a) => Proxy s -> a,该函数可以对任何具体化数值的类型进行操作。

至于ReifiesStorable,您正在查看的是反射包的旧版本;最新版本有一个非常有效的实现,reify它只需要一个函数调用的成本。1请注意,即使使用旧实现,您通常也不会ReifiesStorable直接使用该类,而是Reifies使用ReifiesStorable用于具体化 a 的类StablePtr,因此最终只复制了几个字节,而不是整个对象。(这也是论文中的原始实现所做的。)这两种实现对于实际使用来说绝对足够快,旧的“慢”实现需要大约 100 毫秒来具体化和反映 100000 个值,而新的实现则在 10小姐。

(完全披露:我致力于新的实施。)

1快速实现取决于 Haskell 实现细节。较旧、较慢的实现会自动用于尚未测试快速实现的 Haskell 实现;到目前为止,GHC 和 Hugs 已被证明可以与快速实施一起工作。您可以使用 请求缓慢的实现-fslow,但除非 GHC 对其类型类的实现进行重大检查,否则它不太可能停止工作。(即使是这样,您也只需重新编译使用反射的包即可使其再次工作。)

于 2012-04-28T13:16:46.737 回答