Discussion: Can we make evaluate easier to find?

The evaluate function is defined in GHC.IO, and exported publicly by Control.Exception. This strikes me as an extremely strange place for it. Can we find another place to put it that would seem more sensible? I don't think it should be removed from Control.Exception, but rather that maybe it should be added somewhere else as well. Digging all around, I've come up with one idea that might work, if we give evaluate a more general type: evaluate :: PrimMonad m => a -> m a evaluate a = primitive (\s -> seq# a s) With this type, we could easily put it in Control.Monad.Primitive. There may be some challenges wiggling module dependencies around, but hopefully not huge ones. David

I went ahead and tried this. It works like a charm, but there's one
minor wrinkle: it seems we'd have to move the declaration of the
PrimMonad class out of the primitive package and into base, perhaps
into GHC.Base, with the IO instance in GHC.Base and the ST instance in
GHC.ST. What do people think?
On Tue, Jan 6, 2015 at 11:36 PM, David Feuer
The evaluate function is defined in GHC.IO, and exported publicly by Control.Exception. This strikes me as an extremely strange place for it. Can we find another place to put it that would seem more sensible? I don't think it should be removed from Control.Exception, but rather that maybe it should be added somewhere else as well.
Digging all around, I've come up with one idea that might work, if we give evaluate a more general type:
evaluate :: PrimMonad m => a -> m a evaluate a = primitive (\s -> seq# a s)
With this type, we could easily put it in Control.Monad.Primitive. There may be some challenges wiggling module dependencies around, but hopefully not huge ones.
David

On Tue, Jan 6, 2015 at 11:36 PM, David Feuer
wrote:
The evaluate function is defined in GHC.IO, and exported publicly by Control.Exception. This strikes me as an extremely strange place for it.
I also find the place strange. If there would be a module with a type class for seq (like Eval), it would be certainly a good place for 'evaluate'.

I agree that Control.Exception seems like a very strange place for `evaluate` to be publicly exported. However, I've never wanted that function except in the context of forcing evaluation of a pure value in order to catch exceptions. I also can't think of any other use cases that aren't more easily solved by seq/bang patterns. If the only use case is in fact exception handling, Control.Exception seems like a pretty good location. On Wed Jan 07 2015 at 11:45:39 AM Henning Thielemann < lemming@henning-thielemann.de> wrote:
On Tue, Jan 6, 2015 at 11:36 PM, David Feuer
wrote: The evaluate function is defined in GHC.IO, and exported publicly by Control.Exception. This strikes me as an extremely strange place for it.
I also find the place strange. If there would be a module with a type class for seq (like Eval), it would be certainly a good place for 'evaluate'. _______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

Isn't it the official way to make sure lazy IO gets performed before a
file is closed, or is ($!) actually safe for that? (I really and truly
can't understand all the details of what goes on in the rather old bug
reports relating to the implementation of evaluate).
On Wed, Jan 7, 2015 at 3:27 PM, John Lato
I agree that Control.Exception seems like a very strange place for `evaluate` to be publicly exported. However, I've never wanted that function except in the context of forcing evaluation of a pure value in order to catch exceptions. I also can't think of any other use cases that aren't more easily solved by seq/bang patterns. If the only use case is in fact exception handling, Control.Exception seems like a pretty good location. On Wed Jan 07 2015 at 11:45:39 AM Henning Thielemann
wrote: On Tue, Jan 6, 2015 at 11:36 PM, David Feuer
wrote: The evaluate function is defined in GHC.IO, and exported publicly by Control.Exception. This strikes me as an extremely strange place for it.
I also find the place strange. If there would be a module with a type class for seq (like Eval), it would be certainly a good place for 'evaluate'. _______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

AKAIK neither one is necessarily safe. You need something like deepseq to fully evaluate the lazy structure. If you are sure that evaluation to WHNF is sufficient for performing all lazy IO, then I think either evaluate or ($!) will work. evaluate is probably to be preferred if IO is available, similar to throw vs throwIO. However I think idioms with seq and ($!) are so common that they need to be supported too. After all, you may have just a 'Monad m' context. John

deepseq is somewhat orthogonal:
shallow: v $! return v
deep: v $!! return v
shallow: evaluate v >>= return
deep: evaluate (force v) >>= return
On Thu, Jan 8, 2015 at 12:11 PM, John Lato
AKAIK neither one is necessarily safe. You need something like deepseq to fully evaluate the lazy structure.
If you are sure that evaluation to WHNF is sufficient for performing all lazy IO, then I think either evaluate or ($!) will work.
evaluate is probably to be preferred if IO is available, similar to throw vs throwIO. However I think idioms with seq and ($!) are so common that they need to be supported too. After all, you may have just a 'Monad m' context.
John

It's a bit orthogonal, but since you brought up lazy IO I thought it was
worth mentioning, as shallow evaluation is insufficient.
On 09:15, Thu, Jan 8, 2015 David Feuer
deepseq is somewhat orthogonal:
shallow: v $! return v deep: v $!! return v shallow: evaluate v >>= return deep: evaluate (force v) >>= return
AKAIK neither one is necessarily safe. You need something like deepseq to fully evaluate the lazy structure.
If you are sure that evaluation to WHNF is sufficient for performing all lazy IO, then I think either evaluate or ($!) will work.
evaluate is probably to be preferred if IO is available, similar to
throwIO. However I think idioms with seq and ($!) are so common that
On Thu, Jan 8, 2015 at 12:11 PM, John Lato
wrote: throw vs they need to be supported too. After all, you may have just a 'Monad m' context.
John

On 2015-01-08 at 18:11:22 +0100, John Lato wrote: [...]
evaluate is probably to be preferred if IO is available, similar to throw vs throwIO. However I think idioms with seq and ($!) are so common that they need to be supported too. After all, you may have just a 'Monad m' context.
One thing that always confused me is the semantics of seq/($!)/($!!) in something like foo = do { x <- getX; rnf x `seq` return x } as in my mental model, `;` or rather (>>=) combines two monadic actions (according to the monad-laws), into a combined monadic action "value" 'Monad m => m a', which then needs to be executed via some monad-runner to have any monadic effect and/or extract the result of type 'a'. But I don't see how the monad-laws tell me anything about when that `seq` is executed (is it evaluated at monadic combination time[1], or only lateron at monadic execution?). Otoh, in the IO-monad, 'evaluate' seems to be quite clear to me in its semantics: it forces WHNF on its argument at execution-time. [1]: what effects does "seq foo ()" perform? If the action was foo' = do { undefined `seq` return () } :: Monad m => m () then "seq foo' ()" will in fact diverge. So in that case, the `seq` is evaluated at monadic combination time; but otoh for foo'' = do { return undefined } :: Monad m => m () "seq foo'' ()" will (for a sane 'return') evaluate to '()' Cheers, hvr
participants (4)
-
David Feuer
-
Henning Thielemann
-
Herbert Valerio Riedel
-
John Lato