Временная стоимость оператора Haskell `seq`

Этот FAQ говорит, что

Оператор seq

seq :: a -> b -> b

x seq y будет оценивать x, достаточно, чтобы проверить, что это не дно, тогда отбросить результат и оценить y. Это может показаться нецелесообразным, но это означает, что x гарантированно оценивается до рассмотрения y.

Это ужасно приятно Haskell, но означает ли это, что в

x `seq` f x

стоимость оценки x будет выплачена дважды ( "отбросить результат" )?

Ответ 1

Функция seq будет отбрасывать значение x, но поскольку значение было оценено, все ссылки на x "обновлены", чтобы больше не указывать на неоценимую версию x, но для вместо этого укажите на оцениваемую версию. Таким образом, даже если seq оценивает и отбрасывает x, значение также оценивалось для других пользователей x, что не приводило к повторным оценкам.

Ответ 2

Нет, он не вычисляет и не забывает, он вычисляет - что заставляет кешировать.

Например, рассмотрим этот код:

 let x = 1 + 1
 in x + 1

Так как Haskell ленив, это оценивается как ((1 + 1) + 1). Бункер, содержащий сумму куска и одного, внутренний кусок - один плюс один.

Позвольте использовать javascript, ленивый язык, чтобы показать, как это выглядит:

 function(){
   var x = function(){ return 1 + 1 };
   return x() + 1;
 }

Объединение подобных трюков может вызывать переполнение стека, если сделано несколько раз, поэтому seq на помощь.

let x = 1 + 1
in x `seq` (x + 1)

Я лгу, когда я говорю вам, что это оценивается до (2 + 1), но это почти верно - просто так, что вычисление 2 принудительно произойдет до того, как произойдет остальное (но 2 все еще рассчитывается лениво).

Возвращаясь к javascript:

 function(){
   var x = function(){ return 1 + 1 };
   return (function(x){
     return x + 1;
   })( x() );
 }

Ответ 3

Я считаю, что x будет оцениваться только один раз (и результат сохраняется для будущего использования, что характерно для ленивых операций). Такое поведение делает seq полезным.

Ответ 4

Конечно seq сам по себе не "оценивает" все. Он просто записывает зависимость форсирующего порядка. Само форсирование запускается путем сопоставления шаблонов. Когда seq x (f x) принудительно, x будет принудительно первым (memoizing результирующее значение), а затем f x будет принудительно. Haskell ленивая оценка означает, что она запоминает результаты форсирования выражений, поэтому повторная "оценка" (пугающие цитаты здесь) не будет выполнена.

Я помещаю "оценку" в страшные цитаты, потому что это подразумевает оценку полная. В словах Haskell wikibook,

" Значения Haskell очень многоуровневые, "оценка" значения Haskell может означать оценку вплоть до любого этих слоев ".

Повторю: seq сам по себе ничего не оценивает. seq x x не оценивает x при любых обстоятельствах. seq x (f x) ничего не оценивает, когда f = id, в отличие от отчет, который, кажется, говорил.

Ответ 5

Вы всегда можете проверить с помощью unsafePerformIO или trace...

import System.IO.Unsafe (unsafePerformIO)

main = print (x `seq` f (x + x))
  where
    f = (+4)
    x = unsafePerformIO $ print "Batman!" >> return 3