类型类和 levity 多态性

0xd*_*00d 3 haskell

假设我有一个可以取消装箱/重新装箱的值的类:

class Unboxable a (rep :: RuntimeRep) (unboxedTy :: TYPE rep) | a -> rep, a -> unboxedTy where
  unbox :: a -> unboxedTy
  rebox :: unboxedTy -> a
Run Code Online (Sandbox Code Playgroud)

像这样的例子非常好:

instance Unboxable Int 'IntRep Int# where
  unbox (I# w) = w
  rebox = I#
Run Code Online (Sandbox Code Playgroud)

但是如果我尝试支持不可装箱值的元组,例如

instance (Unboxable a repa unboxedTyA, Unboxable b repb unboxedTyB)
       => Unboxable (a, b) ('TupleRep '[ repa, repb ]) (# unboxedTyA, unboxedTyB #) where
  unbox (a, b) = (# unbox a, unbox b #)
  rebox (# a# , b# #) = ( rebox a# , rebox b# )
Run Code Online (Sandbox Code Playgroud)

我得到一个超出我理解的错误:

    A levity-polymorphic type is not allowed here:
      Type: unboxedTyA
      Kind: TYPE repa
    In the type of binder ‘a#’
   |
32 |   rebox (# a# , b# #) = ( rebox a# , rebox b# )
   |            ^^
Run Code Online (Sandbox Code Playgroud)

unboxifrebox被注释掉会出现类似的错误。如果那很重要,我在 ghc 8.8.2 上。

这到底是什么意思,有没有办法表达最后一个实例?

HTN*_*TNW 6

你不能有一个带有 unknown 的变量RuntimeRep。这种情况根本不可能。例如,for 实例(Float, Float)必须存储/加载 32 位值,但 for 实例(Double, Double)将处理 64 位值。但是,这样的实例必须是这个实例的特化,因此共享代码,使得这种区分变得不可能。为了抓住这一点,当一个变量被引入时,它的类型与 匹配TYPE rep,如果rep不完全知道(本质上,值需要传递的“调用约定”),你看到的错误被提出。短语“levity polymorphism”是一个遗留物,“levity”指的是提升类型之间的差异(通过包含一个底部值来定义并且它们都被装箱(例如Int[String])) 与未提升的类型(没有底值,其中一些被装箱 ( ByteArray#) 和一些被拆箱 ( Int#)) 如果你真的想,你可以使用 Template Haskell 来生成这些装箱和拆箱函数,但是你无法获得所有这些(因为有无限多个并且您不能使用多态性)。

  • 因为这就是 Haskell 的工作原理。多态函数不是“模板”;它们仅编译一次,并且轻率多态性不够特殊,不足以保证添加这样的模板系统。如果你真的想要这个模板化,你绝对可以编写一个编译器插件来按需生成这些类型类实例,但这样做通常会使语言变得不规则,需要一个非常有力的论据来解释为什么它是必要的。通常,您不需要多态未装箱代码。 (2认同)