Restrictions for Unpacked Types

I wonder why Unboxed types in Haskell have the following limitations:

  • You cannot define the newtype type for unboxed:

    newtype Vec = Vec (# Float#, Float# #) 

    but you can determine the type of synonim:

     type Vec = (# Float#, Float# #) 
  • Family types cannot return an unpacked type:

     type family Unbox (a :: *) :: # where Unbox Int = Int# Unbox Word = Word# Unbox Float = Float# Unbox Double = Double# Unbox Char = Char# 

Are there any fundamental reasons for this, or simply because no one asked for it?

+7
haskell type-families newtype
source share
2 answers

Haskell's parametric polymorphism is based on the fact that all values ​​of types t :: * uniformly represented as a pointer to a run-time object. Thus, the same machine code works for all instances of polymorphic values.

Contrast polymorphic functions in Rust or C ++. For example, the authentication function still has a type similar to forall a. a -> a forall a. a -> a , but since values ​​of different types a can have different sizes, compilers must generate different codes for each stat. It also means that we cannot pass polymorphic functions at runtime:

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

since such a function should work correctly for objects of arbitrary size. This function requires some additional infrastructure, for example, we might require the forall a. a -> a runtime function forall a. a -> a forall a. a -> a took additional implicit arguments that carry information about the size and constructors / destructors of the values ​​of a .

Now the problem with newtype Vec = Vec (# Float#, Float# #) is that even if Vec has a good * , the runtime code that expects a value of some t :: * cannot process it. This is a pair of floats allocated by the stacks, not a pointer to a Haskell object, and passing it to code that expects Haskell objects will result in segfaults or errors.

In the general case (# a, b #) , the size of the pointer is not necessary, so we cannot copy it to the data fields of the size of the pointer.

Type types returning # types are not allowed for appropriate reasons. Consider the following:

 type family Foo (a :: *) :: # where Foo Int = Int# Foo a = (# Int#, Int# #) data Box = forall (a :: *). Box (Foo a) 

Our Box does not represent runtime since Foo a has different sizes for different a -s. As a rule, polymorphism over # requires the generation of different code for different instances, for example, in Rust, but this is poorly related to regular parametric polymorphism and makes it difficult to represent polymorphic values ​​at run time, so the GHC does not worry about any of this.

(Not to mention that it is impossible to use a useful implementation)

+7
source share

A newtype will allow class instances to be defined

 instance C Vec where ... 

which cannot be defined for unpacked tuples. Typical synonyms instead do not offer such functionality.

Also, Vec will not be a nested type. This means that you can no longer create generic variables with Vec in the general case, unless their type allows. For example, [Vec] should be disabled. The compiler should keep track of the “regular” new types and the “unpacked” new types in some way. This seems to me to be the only advantage that allows the Vec data constructor to wrap unboxed values ​​at compile time (since it is deleted at runtime). This would probably not be enough to justify the necessary changes to the type inference mechanism. I guess.

+4
source share

All Articles