Haskells弱头范式


9

我偶然发现了一些令人讨厌的东西。我知道haskell可用于弱头正常形式(WHNF),而且我知道这是什么。将以下代码键入ghci(据我所知,我正在使用命令:sprint,将表达式简化为WHNF):

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]

我感到困惑,因为根据我的理解,以上所有三个示例大致相同(列表列表),因此应简化为相同的WHNF,即_。我想念什么?

谢谢


似乎有关
Bergi

@Bergi这些问题当然是相关的,但似乎都没有解决原因,"bla"而且结果['b','l','a']也有所不同。
leftaroundabout

@leftaroundabout因为"bla"可能会重载,但['b','l','a']已知为String/ [Char]
Bergi

1
@Bergi我也考虑过这一点,但是它并不是很合理,因为['b', 'l', 'a']可能被重载,同样"bla",只有在-XOverloadedStrings打开时才被重载。
大约

2
似乎与解析器相关,可能特定于GHCi?(我不知道如何用GHC编译的代码测试WHNF。)引号本身似乎是触发因素。
chepner

Answers:


5

请注意,:sprint不会将表达式简化为WHNF。如果确实如此,那么以下将给出4而不是_

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

而是,:sprint使用绑定的名称,遍历该绑定的值的内部表示,并在_用作未评估的thunk的占位符(即,悬挂的惰性函数)时显示已经“求值的部分”(即,构造函数的部分)。电话)。如果该值未完全评估,则不会进行评估,甚至不会对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约束的字典。这意味着生成的代码尚未解析类型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#

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.