【问题标题】:Are implicit parameters a difficulty for inlining in GHC?隐式参数是 GHC 内联的困难吗?
【发布时间】:2012-04-28 12:23:10
【问题描述】:

我很好奇 Kiselyov 和 Shan 在 Functional Pearl: Implicit Configurations 文章中讨论的对 implicit parameters 的反对意见。

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

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

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

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

GHC 的用户文档解释说,程序员必须小心polymorphic recursionmonomorphism restriction。这就是他们所说的内联问题的意思吗?

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

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

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

【问题讨论】:

    标签: haskell ghc lambda-calculus implicit-parameters


    【解决方案1】:

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

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

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

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

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

    【讨论】:

    • 啊,甜蜜,所以Data.Reflection 现在全是黑魔法。 :) 有你推荐阅读的任何代码或文章吗? Data.Reflection 已经变得更有意义了,因为我注意到了示例目录。不过,我应该在 Data.TaggedData.Proxy 上阅读另一篇文章。
    猜你喜欢
    • 2013-12-27
    • 2014-12-27
    • 2017-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-03
    • 1970-01-01
    相关资源
    最近更新 更多