Haskellsウィークヘッドノーマルフォーム


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を理解している限り、文字列は文字のリストに他なりません。これは、型"hi" :: [Char]とを確認することで確認できます['h','i'] :: [Char]

私の理解では、上記の3つの例はほぼ同じ(リストのリスト)であり、したがって同じWHNF、つまり_に削減する必要があるため、混乱しています。何が欠けていますか?

ありがとう


これは関連しているようです
ベルギ

@Bergiこれらの質問は確かに関連しますが、アドレスなぜに思わないでもない"bla"し、['b','l','a']違っ出てくるでしょう。
leftaroundabout

@leftaroundabout "bla"オーバーロードされる可能性があります['b','l','a']が、String/であることが知られています[Char]
ベルギ

1
@Bergi私もそれについて考えましたが、オーバーロードされる['b', 'l', 'a']可能性があるため、実際には妥当ではありません。同様に、オンになっている"bla"場合にのみオーバーロードさ-XOverloadedStringsれます。
leftaroundabout

2
GHCiに固有の、おそらくパーサー関連のようです?(GHCでコンパイルされたコードでWHNFをテストする方法はわかりません。)引用符自体がトリガーのようです。
chepner

回答:


5

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

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

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

実験で観察しているのは、ポリモーフィック数値型とモノモーフィック数値型の組み合わせ、文字列リテラルと文字の明示的なリストの異なる内部表現などです。基本的に、異なるリテラル式がバイトコードにコンパイルされる方法の技術的な違いを観察しています。したがって、これらの実装の詳細をWHNFと関係があると解釈すると、絶望的に混乱することになります。一般的には、:sprint WHNFと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 -> ...

これ$dNumNum制約の辞書です。これは、生成されたコードがまだ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>)
     [])

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.