Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In what sense is (# a #), the one-element unboxed tuple, different from 'a'?

Tags:

haskell

In Data.Primitive.SmallArray, there's the function:

indexSmallArray## :: SmallArray a -> Int -> (#a#)

It returns its result in a one-element unboxed tuple.

My question is: what do we gain from returning the unboxed tuple? Why not return simply a?

I do get the usefulness of unboxed tuples of more than one element. As the Levity Polymorphism paper says:

Originally conceived to support returning multiple values from a function, an unboxed tuple is merely Haskell syntax for tying multiple values together. Unboxed tuples do not exist at runtime, at all [...] during compilation, the unboxed tuple is erased completely

like image 372
danidiaz Avatar asked Aug 16 '20 20:08

danidiaz


1 Answers

It seems that the aim is to decouple the work of indexing into the array from "forcing" a itself to WHNF.

If we simply returned a, then evaluating a call to indexSmallArray## to WHNF would both perform the indexing and evaluate the result to WHNF.

Pattern-matching on (# a #) will perform the indexing (thus freeing the reference to the array hiding behind the thunk, potentially allowing the array to be garbage-collected) but does not force the single component a to WHNF (it could be undefined for all we know).

In the docs of a primop that uses a similar technique, we find:

primop IndexArrayOp "indexArray#" GenPrimOp
Array# a -> Int# -> (#> a #)
{Read from specified index of immutable array. Result is packaged into an unboxed unary tuple; the result itself is not yet evaluated. Pattern matching on the tuple forces the indexing of the array to happen but does not evaluate the element itself. Doing this pattern match immidiately can be useful for (1) preventing additional thunks from building up on the heap and (2) eliminating references to the argument array, allowing it to be garbage collected more promptly.}

I guess we could accomplish the same using a wrapper type like data Wrap a = Wrap a, but this would cause unnecessary heap allocations, which don't happen with the unboxed tuple.

like image 135
danidiaz Avatar answered Nov 16 '22 00:11

danidiaz