简体   繁体   English

Haskell 弱头范式

[英]Haskells Weak Head Normal Form

I've stumbled over some irritating things.我被一些烦人的事情绊倒了。 I know that haskell works with weak head normal form (WHNF) and I know what this is.我知道 haskell 与弱头部范式 (WHNF) 一起工作,我知道这是什么。 Typing the following code into ghci (I am using the command :sprint which reduces the expression to WHNF to my knowledge.):将以下代码输入 ghci(我正在使用命令 :sprint 将表达式简化为 WHNF,据我所知。):

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

gives intlist = _ this makes totally sense to me.给出intlist = _这对我来说完全有意义。

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

gives stringlist = [_,_] This already confuses me.给出stringlist = [_,_]这已经让我感到困惑。 But then:但是之后:

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

surprisingly gives charlist = ["hi","there"]令人惊讶的是charlist = ["hi","there"]

As far as I understood Haskell, strings are nothing else than lists of chars, which seems to be confirmed by checking the types "hi" :: [Char] and ['h','i'] :: [Char] .据我了解 Haskell,字符串只不过是字符列表,这似乎可以通过检查类型"hi" :: [Char]['h','i'] :: [Char]

I am confused, because in my understanding all three examples above are more or less the same (a list of lists) and should therefore reduce to the same WHNF, namely _.我很困惑,因为根据我的理解,上面的所有三个示例或多或少都相同(列表列表),因此应该减少到相同的 WHNF,即 _。 What am I missing?我错过了什么?

Thanks谢谢

Note that :sprint does not reduce an expression to WHNF.需要注意的是:sprint不会减少WHNF的表达式。 If it did, then the following would give 4 rather than _ :如果是这样,那么以下将给出4而不是_

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

Rather, :sprint takes the name of a binding, traverses the internal representation of the binding's value, and shows the already "evaluated parts" (ie, the parts that are constructors) while using _ as a placeholder for unevaluated thunks (ie, the suspended lazy function calls).相反, :sprint使用绑定的名称,遍历绑定值的内部表示,并显示已经“评估的部分”(即构造函数的部分),同时使用_作为未评估的 thunk 的占位符(即,暂停延迟函数调用)。 If the value is completely unevaluated, no evaluation will be done, not even to WHNF.如果该值完全未评估,则不会进行评估,甚至不会对 WHNF 进行评估。 (And if the value is completely evaluated, you'll get that, not just WHNF.) (如果该值被完全评估,你会得到它,而不仅仅是 WHNF。)

What you are observing in your experiments is a combination of polymorphic versus monomorphic numeric types, different internal representations for string literals versus explicit lists of characters, etc. Basically, you're observing technical differences in how different literal expressions are compiled to byte code.您在实验中观察到的是多态与单态数字类型的组合、字符串文字与显式字符列表的不同内部表示等。基本上,您正在观察不同文字表达式如何编译为字节码的技术差异。 So, interpreting these implementation details as having something to do with WHNF is going to hopelessly confuse you.因此,将这些实现细节解释为与 WHNF 有关系会让您感到非常困惑。 Generally, you should use :sprint as a debugging tool only, not as a way to learn about WHNF and the semantics of Haskell evaluation.通常,您应该将:sprint仅用作调试工具,而不是了解 WHNF 和 Haskell 评估语义的一种方式。

If you really want to understand what :sprint is doing, you can turn on a few flags in GHCi to see how expressions are actually being handled and, so, eventually compiled to bytecode:如果你真的想了解:sprint正在做什么,你可以在 GHCi 中打开一些标志来查看表达式实际上是如何处理的,因此最终编译为字节码:

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

After this, we can see the reason your intlist gives _ :在此之后,我们可以看到您的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>)
     [])

You can ignore the returnIO and the outer : call, and concentrate on the part that starts with ((\\ @ a $dNum -> ...您可以忽略returnIO和外部:调用,并专注于以((\\ @ a $dNum -> ...

Here $dNum is the dictionary for the Num constraint.这里$dNumNum约束的字典。 This means that the generated code hasn't yet resolved the actual type a in the type Num a => [[a]] , so the entire expression is still represented as a function call taking a (dictionary for) an appropriate Num type.这意味着生成的代码尚未解析类型Num a => [[a]]的实际类型a ,因此整个表达式仍表示为采用(字典)适当Num类型的函数调用。 In other words, it's an unevaluated thunk, and we get:换句话说,这是一个未经评估的 thunk,我们得到:

> :sprint intlist
_

On the other hand, specify the type as Int , and the code is completely different:另一方面,指定类型为Int ,代码完全不同:

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

and so is the :sprint output: :sprint输出也是如此:

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

Similarly, literal strings and explicit lists of characters have completely different representations:同样,文字字符串和显式字符列表具有完全不同的表示形式:

> 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>)
     [])

and the differences in the :sprint output represents artifacts of which parts of the expression GHCi considers evaluated (explicit : constructors) versus unevaluated (the unpackCString# thunks).并且:sprint输出中的差异代表了 GHCi 认为已评估(显式:构造函数)与未评估( unpackCString# )的表达式部分的工件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM