Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the differences between `evaluate`, `rwhnf` and `seq` and their "deep" counterparts?

In the wiki page about timing computations, there is an example for timing a pure computation. The core idea is to use the functions evaluate, rnf and seq to ensure that the desired computation (1 + y in the example below) is performed between the two calls to getCPUTime:

time :: (Num t, NFData t) => t -> IO ()
time y = do
    start <- getCPUTime
    replicateM_ lim $ do
        x <- evaluate $ 1 + y
        rnf x `seq` return ()
    end   <- getCPUTime

I would like to clarify the use of the functions evaluate, rnf and seq here.

First, evaluate evaluates its argument to weak head normal form, but it seems that its other purpose is to deal with exceptions that may happen during evaluation. I'm not exactly sure what would happen without it, but I'm ready to assume that it's needed for exception handling.

Then, rnf evaluates the value to normal form to make sure that the whole computation happens here and that no unevaluated bits remain in weak head normal form, as a result of the call to evaluate. This may or may not be what one wants, as discussed here, because the additional evaluation may not be necessary.

Finaly, seq makes sure that the expression rnf x is evaluated to weak head normal form before returning return ().

To summarize: evaluate deals with potential exceptions, rnf evaluates x deeply to make sure that the whole computation is timed and seq ensures that rnf x is evaluated in time.

Here are my questions:

  1. Isn't the call to seq redundant with the call to rnf which has already evaluated the value to normal form?
  2. If I'd prefer to time the evaluation to weak head normal form rather than to normal form, may I simply replace rnf with rwhnf? Then would the 3 calls evaluate, rwhnf and seq be redundant such that I could eliminate the use of the last 2 and be happy with just evaluate?
like image 589
Guillaume Chérel Avatar asked Oct 15 '22 07:10

Guillaume Chérel


1 Answers

  1. The call to seq is not redundant because rnf x needs to be evaluated (to WHNF) to evaluate x to NF, and that's seq's job.

  2. Yes, I believe so: just

    evaluate $ 1 + y
    return ()
    

But! If the intention of evaluate here is to handle exceptions I don't think it works because they can be thrown from rnf x just as well. And if it isn't, I don't see the benefit over

rnf (1 + y) `seq` return ()

And both force and evaluate docs suggest a simpler alternative:

evaluate $ force $ 1 + y
return ()

Maybe that page was written before force was available? Even so, unless I am missing something,

evaluate $ rnf $ 1 + y

would do the job correctly.

like image 177
Alexey Romanov Avatar answered Nov 09 '22 23:11

Alexey Romanov