未装箱类型的限制

时间:2015-11-17 09:33:31

标签: haskell type-families newtype

我想知道为什么Haskell中的未装箱类型有这些限制:

  1. 您无法为未装箱类型定义新类型:

    /posts/:id

    但您可以定义类型synonim:

    :id
  2. 类型系列无法返回未装箱类型:

    newtype Vec = Vec (# Float#, Float# #)
    
  3. 这背后有一些根本原因,还是因为没有人要求这个功能?

2 个答案:

答案 0 :(得分:9)

Haskell中的参数多态性依赖于t :: *类型的所有值均匀表示为指向运行时对象的指针的事实。因此,相同的机器代码适用于多态值的所有实例化。

对比Rust或C ++中的多态函数。例如,身份函数仍然具有类似于forall a. a -> a的类型,但由于不同a类型的值可能具有不同的大小,因此编译器必须为每个实例生成不同的代码。这也意味着我们无法在运行时框中传递多态函数:

data Id = Id (forall a. a -> a)

因为这样的函数必须对任意大小的对象正常工作。它需要一些额外的基础结构才能允许此功能,例如,我们可能要求运行时forall a. a -> a函数采用额外的隐式参数,这些参数包含有关a值的大小和构造函数/析构函数的信息。

现在,newtype Vec = Vec (# Float#, Float# #)的问题在于,即使Vec具有种类*,期望某些t :: *值的运行时代码也无法处理它。它是一组堆栈分配的浮点数,而不是指向Haskell对象的指针,并将其传递给期望Haskell对象的代码会导致段错误或错误。

一般来说,(# a, b #)不一定是指针大小的,因此我们无法将其复制到指针大小的数据字段中。

由于相关原因,不允许退回#类型的类型系列。请考虑以下事项:

type family Foo (a :: *) :: # where
  Foo Int = Int#
  Foo a   = (# Int#, Int# #)

data Box = forall (a :: *). Box (Foo a)

我们的Box不是可表示的运行时,因为Foo a对于不同的a - s具有不同的大小。通常,#上的多态性需要为不同的实例化生成不同的代码,例如在Rust中,但这与常规参数多态性交互很严重,并且使得多态值的运行时表示很难,因此GHC不会烦恼任何这个。

(不是说虽然不可能设计出可用的实施方案)

答案 1 :(得分:4)

newtype允许用户定义类实例

instance C Vec where ...

无法为未装箱的元组定义。输入同义词而不提供此类功能。

此外,Vec不是盒装类型。这意味着您不能再使用Vec来实例化类型变量,除非它们的类型允许它。例如,{_ 1}}应该被禁止。编译器应该跟踪"常规" newtypes和" unboxed" newtypes在某种程度上。我认为,这将允许数据构造函数[Vec]在编译时包装未装箱的值(因为它在运行时被删除)。我认为这可能不足以证明对类型推理引擎进行必要的更改是合理的。