
On Wed, 2009-11-18 at 09:17 +0000, Simon Marlow wrote:
So the main difference is that with the current formulation of deepseq, you need to explicitly force the result in order to use it, either with a pattern match, another seq, or a pseq. If we used (a -> b -> b) then the top-level forcing is "built-in".
Let's look at an example instance; here (1) is the current deepseq, (2) is deepseq :: a -> b -> b
instance (DeepSeq a, DeepSeq b) => DeepSeq (a,b) where -- (1) deepseq (a,b) = deepseq a `seq` deepseq b -- (2) deepseq (a,b) = deepseq a . deepseq b
They're both fairly similar. Most instances follow this pattern, with seq being replaced by (.).
You could argue that (a -> b -> b) is "doing more" than (a -> ()), because it has a kind of built-in continuation (Luke's point). I buy that, although (a -> ()) has a strange-looking unit type in the result and you have to use it in conjunction with seq.
I think the most important thing is to make the public interface that people use most frequently simple and easy to remember. Thus I suggest the primary function people use should be deepseq :: DeepSeq a => a -> b -> b because then all that users have to remember is: "deepseq --- like seq but more so!" That's it. Users already know how to use seq, so now they know how to use deepseq too.
(1) generates slightly better code with GHC, because it compiles seq directly into a case expression, whereas (.) involves a thunk. If deepseq is inlined all the way down, then it would turn into the same thing either way.
I don't feel terribly strongly, but I have a slight preference for the current version.
If it so happens that it is more convenient or faster to make the class and instances use the (a -> ()) style then that is fine. We can give the class method a different name. Presumably people have to write Deepseq instances much less frequently than they use deepseq. Duncan