如果我定义
λ> data Bar = Bar Int deriving Show
λ> data Foo = Foo Bar deriving Show
和
λ> let foo = trace "foo" Foo (trace "bar" Bar 100)
λ> let two = trace "two" 2
λ> let g (Foo x) y = y
然后我想我理解为什么会得到
λ> g foo two
foo
two
2
但如果我重复一遍,我就会
λ> g foo two
two
2
我不明白为什么foo
似乎没有被评估为g
的第二次调用,特别是因为它显然不是(尚)以某种方式已经可用,因为我可以验证
λ> foo
Foo bar
(Bar 100)
虽然 - 再次,我的困惑 - 重复前一个给出
λ> foo
Foo (Bar 100)
为什么我的foo
表达式在某些情况下似乎已经过评估,而在其他情况下却没有评估?就此而言,为什么我的two
表达式始终需要进行评估?
答案 0 :(得分:9)
归因于two
的类型。让我们检查到目前为止的所有类型:
ghci> :t foo
foo :: Foo
ghci> :t two
two :: Num a => a
啊哈! two
是多态的。因此,其行为取决于实际Num
实例。因此,需要在g
中重新评估。我们可以使用:sprint
:
ghci> :sprint foo
foo = Foo _ -- simplified
这表明我们从未查看过Foo
的内容。 foo
位于weak head normal form。这回答了你的第二个问题。但回到你的第一个。 :sprint two
会发生什么?
ghci> :sprint two
two = _
正如您所看到的,由于其多态性,two
无法访问WHNF。毕竟,哪个 WHNF应该采取什么?您可能希望将其用作Integer
,Currency
或Complex Triple
。
顺便说一下,这是存在单态限制的原因,请参阅"A History of Haskell", section 6.2:
6.2单态限制
早期阶段争议的主要来源是所谓的 “单态限制。”假设
genericLength
有 这个重载类型:genericLength` :: Num a => [b] -> a
现在考虑这个定义:
f xs = (len, len) where len = genericLength xs
看起来len应该只计算一次,但它实际上可以计算两次。为什么?因为我们可以推断出类型
len :: (Num a) => a
;当被字典玷污时 - 通过翻译,len
成为一个被调用一次的函数 对于len
的每次出现,每次出现都可以使用不同的two
类型。
另见this Q&A for more information about the restriction。
话虽如此,如果我们修复ghci> let foo = trace "foo" Foo (trace "bar" Bar 100)
ghci> let two = trace "two" (2 :: Integer)
ghci> let g (Foo x) y = y
的类型,我们可以轻松更改此内容:
:set -XMonomorphismRestriction
现在输出将完全符合您的预期。或者,您可以使用ghci> :set -XMonomorphismRestriction
ghci> let two = trace "two" 2
ghci> :t two
two :: Integer -- due to defaulting rules
启用单态限制,因为默认情况下在当前GHCi版本中禁用它:
$json = '{"users":[{ "user": "Carlos", "age": 30, "country": "Spain" }, { "user": "John", "age": 25, "country": "United States" }, { "user": "Mike", "age": 28, "country": "Canada" }]}';
$arr = json_decode($json, true);
echo "<table><tr><th>user</th><th>age</th><th>country</th></tr>";
foreach($arr['users'] as $item){
echo "<tr><td>".$item['user']."</td><td>".$item['age']."</td><td>".$item['country']."</td></tr>";
}
echo "</table>";
答案 1 :(得分:5)
> :t foo
foo :: Foo
> :t two
two :: Num a => a
第二个是多态,所以它是伪装的功能!每次使用时,您都会评估新的trace
来电。