haskell-jp / beginners #18 at 2021-09-21 23:02:51 +0900

@mod_poppo <@UL1HFJATE> もしくは他の方でも構いません
「Haskellで戦う競技プログラミング」でプリミティブ型についての説明をされている部分があったのですがいまいちピンとこなかったので質問させてください。
以下第4章 P.33より抜粋
 ここでの「プリミティブ型」という言い方は便宜的なものです。GHC 的にはプリミティブ型と言った
ら GHC.Prim で定義されている unboxed 型のことでしょうが、ここでは「GHC の unboxed 型を
ラップした型」程度の意味です。
「GHC的にはプリミティブ型といったらGHC.Primで定義されているunboxed型」と「GHC の unboxed 型をラップした型」これらの文が理解できませんでした。
GHC.Primが何のモジュールなのかもHackage見ただけではイマイチ理解できないレベルです。ざっくりとでも構いません、以上の文を噛み砕いていただけると助かります
GHC の基本ライブラリって ghc-prim の上に base が乗っかってる、みたいな構造をしてて、 ghc-prim には「GHC内部で定義された、実際には Haskell で実装が書かれてない型や関数」があり、 base でそれらを簡単に呼び出すためのラッパーが定義されています。

-- in ghc-prim
data Int#
-- コード上では Void 型(発散値(エラーとか無限ループとか)以外を持たない型)と同等に定義されるが、GHCがGHC内部の定義に読み替える

-- in base
data Int = I# Int#

みたいなことになっています。`GHC.Prim` は ghc-prim のモジュールですね。この Int# をプリミティブ型と呼ぶのが良いのだろうが、この場合 Int をそう呼んでいる、と言うことです。「unboxed」という言葉を除けばこれで説明し切れてるんではないだろうか。
unboxedを説明する前に # について注意しておくと、 # は演算子を表す記号ではなく、 Int# で一個の名前です。

{-# LANGUAGE MagicHash #-}

で、英数字名の識別子の後ろに # をくっつけても識別子とみなされるようになります。
unboxed は、ちゃんと説明しようとすると「ヒープ」、「スタック」、「ポインタ」、「サンク」くらいの用語を使うのですが、聞いたことあります?
そこらへんの用語は問題なく使用していただいて大丈夫です
boxedは大まかにいって「いつ何時でも実体がヒープにあることが強制されており、その値は必ず実体へのポインタとして表現される」型のことです。このboxedは型がliftedである(発散値を値として持つ≒値の実体がサンクでありうる)ための必要条件ですね、多分
unboxedはこの否定をとって得られます。典型的には(多分全てかな?)値が格納されているところにそのまま実体があります。だから Int# はヒープに居てもスタックに居てもよくて、まんま32bitsか64bitsの符号付き整数を表すビットパターンです。
data Int = I# Int# がスタックに居るとしてもヒープに居るとしても、`Int` の値が居るところには整数そのものを表すビットパターンはいなくて、ただヒープへのポインタだけがあります。そのポインタはサンクを指しているかもしれないし、2wordの構造体を指しているかもしれないです。後者の場合、`I#` を表すタグであるワードと、整数そのものを表すビットパターン(`Int#`)であるワードからなる構造体です。
一方で、`Int#` は、スタックに居ようとヒープに居ようと、1word分のビットパターンがそこにあります。
「値」という用語に混乱が見られるので、どうか真似しないでください
む、難しいですね……
なんとか理解したいと思います
ご丁寧にありがとうございました
無関係な話題も多いですが https://haskell.jp/blog/posts/2017/13-about-kind-system-part2.html の「型の分類」の節が参考になるかと思います。
@igrep ありがとうございます!読んでみます!
今更ですが、boxed typeとはC--に直した時に void * になる型のことで、そのうちlifted typeとはその void * がthunkを指しうる型のこと、とか説明したらいい気がしてきました