9

私はいくつかの刺激的なことにつまずきました。haskell が弱い頭部正規形 (WHNF) で機能することを知っており、これが何であるかを知っています。次のコードを ghci に入力します (私の知る限りでは、式を WHNF に縮小するコマンド :sprint を使用しています)。

let intlist = [[1,2],[2,3]]
:sprint intlist

これはintlist = _私にとって完全に理にかなっています。

let stringlist = ["hi","there"]
:sprint stringlist 

これはstringlist = [_,_] すでに私を混乱させます。しかしその後:

let charlist = [['h','i'], ['t','h','e','r','e']]
:sprint charlist

驚くほど与えるcharlist = ["hi","there"]

私が Haskell を理解している限り、文字列は char のリストに他なりません。これは、型"hi" :: [Char]['h','i'] :: [Char].

私の理解では、上記の 3 つの例はすべてほぼ同じ (リストのリスト) であり、したがって同じ WHNF、つまり _. 私は何が欠けていますか?

ありがとう

4

1 に答える 1

5

式を WHNF に還元:sprintしないことに注意してください。もしそうなら、以下はむしろ与えるでしょう:4_

Prelude> let four = 2 + 2 :: Int
Prelude> :sprint four
four = _

むしろ、:sprintバインディングの名前を取り、バインディングの値の内部表現をトラバースし、_未評価のサンク (つまり、中断された遅延関数) のプレースホルダーとして使用しながら、既に「評価された部分」 (つまり、コンストラクターである部分) を表示します。呼び出します)。値が完全に未評価の場合、評価は行われず、WHNF に対しても行われません。(値が完全に評価されている場合は、WHNF だけでなく、それが得られます。)

実験で観察しているのは、多形対単形数値型の組み合わせ、文字列リテラルと明示的な文字リストの異なる内部表現などです。基本的に、さまざまなリテラル式がバイトコードにコンパイルされる方法の技術的な違いを観察しています。したがって、これらの実装の詳細を WHNF と関係があると解釈すると、どうしようもなく混乱することになります。一般に、:sprintWHNF と Haskell 評価のセマンティクスについて学習する方法としてではなく、デバッグ ツールとしてのみ使用する必要があります。

何が行われているかを本当に理解したい場合:sprintは、GHCi でいくつかのフラグをオンにして、式が実際にどのように処理され、最終的にバイトコードにコンパイルされるかを確認できます。

> :set -ddump-simpl -dsuppress-all -dsuppress-uniques

intlistこの後、あなたが与えた理由を見ることができます_

> let intlist = [[1,2],[2,3]]
==================== Simplified expression ====================
returnIO
  (: ((\ @ a $dNum ->
         : (: (fromInteger $dNum 1) (: (fromInteger $dNum 2) []))
           (: (: (fromInteger $dNum 2) (: (fromInteger $dNum 3) [])) []))
      `cast` <Co:10>)
     [])

returnIOと外側の呼び出しを無視して:、で始まる部分に集中できます。((\ @ a $dNum -> ...

制約$dNumの辞書は次のとおりです。Numこれは、生成されたコードがまだ type の実際の型aを解決していないことを意味しますNum a => [[a]]。したがって、式全体は、適切な型 (の辞書) を取る関数呼び出しとして表されますNum。つまり、これは評価されていないサンクであり、次のようになります。

> :sprint intlist
_

一方、タイプを として指定するIntと、コードはまったく異なります。

> let intlist = [[1::Int,2],[2,3]]
==================== Simplified expression ====================
returnIO
  (: ((: (: (I# 1#) (: (I# 2#) []))
         (: (: (I# 2#) (: (I# 3#) [])) []))
      `cast` <Co:6>)
     [])

:sprint出力も同様です。

> :sprint intlist
intlist = [[1,2],[2,3]]

同様に、リテラル文字列と明示的な文字リストの表現はまったく異なります。

> let stringlist = ["hi", "there"]
==================== Simplified expression ====================
returnIO
  (: ((: (unpackCString# "hi"#) (: (unpackCString# "there"#) []))
      `cast` <Co:6>)
     [])

> let charlist = [['h','i'], ['t','h','e','r','e']]
==================== Simplified expression ====================
returnIO
  (: ((: (: (C# 'h'#) (: (C# 'i'#) []))
         (: (: (C# 't'#)
               (: (C# 'h'#) (: (C# 'e'#) (: (C# 'r'#) (: (C# 'e'#) [])))))
            []))
      `cast` <Co:6>)
     [])

出力の違いは、:sprintGHCi が評価された部分 (明示的な:コンストラクター) と評価されていない部分 (サンク) を考慮するアーティファクトを表しますunpackCString#

于 2020-02-04T17:29:43.963 に答える