我想知道为什么Haskell中的未装箱类型有这些限制:
您无法为未装箱类型定义新类型:
/posts/:id
但您可以定义类型synonim:
:id
类型系列无法返回未装箱类型:
newtype Vec = Vec (# Float#, Float# #)
这背后有一些根本原因,还是因为没有人要求这个功能?
答案 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]
在编译时包装未装箱的值(因为它在运行时被删除)。我认为这可能不足以证明对类型推理引擎进行必要的更改是合理的。