什么时候我的Haskell表达式被评估?

时间:2015-10-11 21:21:12

标签: haskell lazy-evaluation evaluation

如果我定义

λ> 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表达式始终需要进行评估?

2 个答案:

答案 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应该采取什么?您可能希望将其用作IntegerCurrencyComplex 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来电。