Is everything in Haskell stored in thunks, even simple values?

前端 未结 5 1619
南旧
南旧 2021-02-02 06:17

What do the thunks for the following value/expression/function look like in the Haskell heap?

val = 5                -- is `val` a pointer to a box containing 5?         


        
5条回答
  •  被撕碎了的回忆
    2021-02-02 07:09

    In general, even primitive values in Haskell (e.g. of type Int and Float) are represented by thunks. This is indeed required by the non-strict semantics; consider the following fragment:

    bottom :: Int
    bottom = div 1 0
    

    This definition will generate a div-by-zero exception only if the value of bottom is inspected, but not if the value is never used.

    Consider now the add function:

    add :: Int -> Int -> Int
    add x y = x+y
    

    A naive implementation of add must force the thunk for x, force the thunk for y, add the values and create an (evaluated) thunk for the result. This is a huge overhead for arithmetic compared to strict functional languages (not to mention imperative ones).

    However, an optimizing compiler such as GHC can mostly avoid this overhead; this is a simplified view of how GHC translates the add function:

    add :: Int -> Int -> Int
    add (I# x) (I# y) = case# (x +# y) of z -> I# z 
    

    Internally, basic types like Int is seen as datatype with a single constructor. The type Int# is the "raw" machine type for integers and +# is the primitive addition on raw types. Operations on raw types are implemented directly on bit-patterns (e.g. registers) --- not thunks. Boxing and unboxing are then translated as constructor application and pattern matching.

    The advantage of this approach (not visible in this simple example) is that the compiler is often capable of inlining such definitions and removing intermediate boxing/unboxing operations, leaving only the outermost ones.

提交回复
热议问题