未装箱类型的限制

Ale*_*nko 10 haskell type-families newtype

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

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

    newtype Vec = Vec (# Float#, Float# #)
    
    Run Code Online (Sandbox Code Playgroud)

    但您可以定义类型synonim:

    type Vec = (# Float#, Float# #)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 类型系列无法返回未装箱类型:

    type family Unbox (a :: *) :: # where
        Unbox Int    = Int#
        Unbox Word   = Word#
        Unbox Float  = Float#
        Unbox Double = Double#
        Unbox Char   = Char#
    
    Run Code Online (Sandbox Code Playgroud)

这背后有一些根本原因,还是因为没有人要求这个功能?

And*_*ács 10

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

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

data Id = Id (forall a. a -> a)
Run Code Online (Sandbox Code Playgroud)

因为这样的函数必须适用于任意大小的对象.它需要一些额外的基础结构来允许这个功能,例如我们可能要求运行时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)
Run Code Online (Sandbox Code Playgroud)

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

(不是说虽然不可能设计出可用的实现)

  • 类型Vec = ...只是同义词。newtype很好地定义了一个与包装值类型不同的新类型。碰巧,所有用于定义新类型的Haskell构造都以“ *”类型返回。用于未装箱类型的`newtype` -s将需要我在上面提到的基础结构中的很大一部分,以实现对大小不同的对象的多态性,否则它将只能单态使用,与仅使用包装类型相比,它们不会特别好。 (2认同)