Hi, is there a way to force the "full" evaluation of a value (unless seq, which only brings the term to WHNF)? In my case, I have an algebraic data type Foo, and I would like to catch the exceptions that will occur during the evaluation of a value of Foo, for something like: foo <- force makeFoo `catch` \e -> defaultFoo Thanks, Michael
On Mon, Dec 06, 2004 at 10:24:06AM -0500, Michael Walter wrote:
Hi,
is there a way to force the "full" evaluation of a value (unless seq, which only brings the term to WHNF)?
In my case, I have an algebraic data type Foo, and I would like to catch the exceptions that will occur during the evaluation of a value of Foo, for something like:
foo <- force makeFoo `catch` \e -> defaultFoo
DeepSeq seems to be the most portable solution (can't tell you where to find it). There is also module Strategies in package concurrent in GHC. I guess you could also do something like deepSeq with Data.Generics. Best regards, Tomasz
Tomasz Zielonka wrote:
I guess you could also do something like deepSeq with Data.Generics.
Ralf Hinze posted just such an implementation to one of the mailing lists a couple of years back: http://www.mail-archive.com/glasgow-haskell-users@haskell.org/msg03810.html The end of the message seems to be cut off in the archive, so here's a reconstructed version.
module Force where import Generics
class Force a where force :: a -> ()
force{|Unit|} a = a `seq` ()
force{|b :+: c|} a = case a of Inl b -> force b Inr c -> force c
force{|b :*: c|} a = case a of b :*: c -> force b `seq` force c
instance Force Char where force a = a `seq` () instance Force Int where force a = a `seq` ()
eval :: (Force a) => a -> a eval a = force a `seq` a
And here's a slight variation in which force has the type a -> a, eliminating the need for the helper function eval. I'm not sure which version is better.
module Force' where import Generics
class Force a where force :: a -> a
force{|Unit|} a = a
force{|b :+: c|} a = case a of Inl b -> force b `seq` a Inr c -> force c `seq` a
force{|b :*: c|} a = case a of b :*: c -> force b `seq` force c `seq` a
instance Force Char where force a = a instance Force Int where force a = a
I haven't tested either of these. -- Ben
Ben Rudiak-Gould <Benjamin.Rudiak-Gould@cl.cam.ac.uk> writes:
And here's a slight variation in which force has the type a -> a, eliminating the need for the helper function eval. I'm not sure which version is better.
The version with () does less redundant forcing, although the compiler could perhaps optimize them statically. -- __("< Marcin Kowalczyk \__/ qrczak@knm.org.pl ^^ http://qrnik.knm.org.pl/~qrczak/
Marcin 'Qrczak' Kowalczyk wrote:
The version with () does less redundant forcing, although the compiler could perhaps optimize them statically.
I did some tests with both versions in GHC on a large binary tree and they appear to have the same performance (even without -O), so I guess GHC does do this. There's another problem with the a->a version, which is that it's never tail recursive, though perhaps GHC's optimizer can fix that as well. I imagine the a->() version is more likely to compile to good code, but it bothered me that it had such a strange type. But a->a isn't the right type either. In fact I think the right type is (a -> exists b. b). This gets us the best of both worlds in efficiency and conciseness/elegance: instance (Force a, Force b) => Force (a,b) where force (x,y) = force x `seq` force y instance Force Int where force x = x Too bad no Haskell implementation supports it. -- Ben
Here's the latest version of my DeepSeq module. Dean DeepSeq.lhs -- deep strict evaluation support The `DeepSeq` class provides a method `deepSeq` that is similar to `seq` except that it forces deep evaluation of its first argument before returning its second argument. Instances of `DeepSeq` are provided for Prelude types. Other instances must be supplied by users of this module. $Id: DeepSeq.lhs,v 1.5 2002/04/01 20:58:24 heringto Exp $
module DeepSeq where
class DeepSeq a where deepSeq :: a -> b -> b
infixr 0 `deepSeq`, $!!
($!!) :: (DeepSeq a) => (a -> b) -> a -> b f $!! x = x `deepSeq` f x
instance DeepSeq () where deepSeq = seq
instance DeepSeq Bool where deepSeq = seq instance DeepSeq Char where deepSeq = seq
instance (DeepSeq a) => DeepSeq (Maybe a) where deepSeq Nothing y = y deepSeq (Just x) y = deepSeq x y
instance (DeepSeq a, DeepSeq b) => DeepSeq (Either a b) where deepSeq (Left a) y = deepSeq a y deepSeq (Right b) y = deepSeq b y
instance DeepSeq Ordering where deepSeq = seq
instance DeepSeq Int where deepSeq = seq instance DeepSeq Integer where deepSeq = seq instance DeepSeq Float where deepSeq = seq instance DeepSeq Double where deepSeq = seq
instance DeepSeq (a -> b) where deepSeq = seq
instance DeepSeq (IO a) where deepSeq = seq
instance (DeepSeq a) => DeepSeq [a] where deepSeq [] y = y deepSeq (x:xs) y = deepSeq x $ deepSeq xs y
instance (DeepSeq a,DeepSeq b) => DeepSeq (a,b) where deepSeq (a,b) y = deepSeq a $ deepSeq b y instance (DeepSeq a,DeepSeq b,DeepSeq c) => DeepSeq (a,b,c) where deepSeq (a,b,c) y = deepSeq a $ deepSeq b $ deepSeq c y instance (DeepSeq a,DeepSeq b,DeepSeq c,DeepSeq d) => DeepSeq (a,b,c,d) where deepSeq (a,b,c,d) y = deepSeq a $ deepSeq b $ deepSeq c $ deepSeq d y instance (DeepSeq a,DeepSeq b,DeepSeq c,DeepSeq d,DeepSeq e) => DeepSeq (a,b,c,d,e) where deepSeq (a,b,c,d,e) y = deepSeq a $ deepSeq b $ deepSeq c $ deepSeq d $ deepSeq e y instance (DeepSeq a,DeepSeq b,DeepSeq c,DeepSeq d,DeepSeq e,DeepSeq f) => DeepSeq (a,b,c,d,e,f) where deepSeq (a,b,c,d,e,f) y = deepSeq a $ deepSeq b $ deepSeq c $ deepSeq d $ deepSeq e $ deepSeq f y instance (DeepSeq a,DeepSeq b,DeepSeq c,DeepSeq d,DeepSeq e,DeepSeq f,DeepSeq g) => DeepSeq (a,b,c,d,e,f,g) where deepSeq (a,b,c,d,e,f,g) y = deepSeq a $ deepSeq b $ deepSeq c $ deepSeq d $ deepSeq e $ deepSeq f $ deepSeq g y
Dean Herington wrote:
deepSeq :: DeepSeq a => a -> b -> b
I should point out that deepSeq with this type is the composition of two simpler operations: deepSeq = seq . eval where eval :: DeepSeq a => a -> a eval ties a demand for a value to a demand for all its subvalues, while seq ties a demand for a value to a demand for another value of an unrelated type. Of course you can define eval x = x `deepSeq` x instead, so it's largely a matter of taste.
instance DeepSeq (IO a) where deepSeq = seq
This is an interesting instance (which is not to say I think it's wrong). It means the original poster's code won't work. He wanted to write foo <- eval makeFoo `catch` \e -> defaultFoo but makeFoo has a monadic type, so eval makeFoo === makeFoo. -- Ben
In message <41B72630.6000309@cl.cam.ac.uk>, Ben Rudiak-Gould writes:
instance DeepSeq (IO a) where deepSeq = seq
This is an interesting instance (which is not to say I think it's wrong). It means the original poster's code won't work.
There is a bit similar issue with the instance for (a -> b). You might suppose that deepSeq of a function would turn the function to a representation that had the property that no function application would cause any complex computation, instead every function application would just look up from a precomputed (by deepSeq) table of values mapping each element of the domain to the codomain of the function. So I'm thinking the instance should be as follows:
instance (Enum a, Bounded a, DeepSeq b) => DeepSeq (a -> b) where deepSeq f y = foldr deepSeq y [f i | i <- [minBound .. maxBound]]
Though I'm not sure this will work, it might also require that 'f' is memoized to work correctly, such that recomputation of the thunks will not occur if 'y' uses 'f'. The theory behind this is the observation that functions can be thought of as a collection of thunks of the codomain type indexed by the values of the domain. For a practical application that would require this kind of approach, see http://haskell.org/hawiki/ControlOperation, in there, the use of unsafePerformIO is premised on the assumption that everything has been evaluated. So this would require the above kind of instances for deepSeq to allow use of functions in conjunction with the control operation. There are obviously some performance issues with this approach. -- Esa Pulkkinen
At 4:05 PM +0000 12/8/04, Ben Rudiak-Gould wrote:
Dean Herington wrote:
deepSeq :: DeepSeq a => a -> b -> b
I should point out that deepSeq with this type is the composition of two simpler operations:
deepSeq = seq . eval where eval :: DeepSeq a => a -> a
eval ties a demand for a value to a demand for all its subvalues, while seq ties a demand for a value to a demand for another value of an unrelated type.
Of course you can define eval x = x `deepSeq` x instead, so it's largely a matter of taste.
instance DeepSeq (IO a) where deepSeq = seq
This is an interesting instance (which is not to say I think it's wrong). It means the original poster's code won't work. He wanted to write
foo <- eval makeFoo `catch` \e -> defaultFoo
but makeFoo has a monadic type, so eval makeFoo === makeFoo.
-- Ben
Michael's exact intent was not clear to me, but he could consider the following. -- Dean import Prelude hiding (catch) import DeepSeq import Control.Exception (catch, evaluate) deepForce :: DeepSeq a => a -> a deepForce x = x `deepSeq` x deepForceIO :: DeepSeq a => a -> IO a deepForceIO = evaluate . deepForce makeFooGood, makeFooBad, defaultFoo :: Int makeFooGood = 1 makeFooBad = error "bad makeFoo" defaultFoo = 0 try f = (deepForceIO f `catch` \e -> return defaultFoo) >>= print main = try makeFooGood >> try makeFooBad
On 6 Dec 2004, at 15:24, Michael Walter wrote:
Hi,
is there a way to force the "full" evaluation of a value (unless seq, which only brings the term to WHNF)?
In my case, I have an algebraic data type Foo, and I would like to catch the exceptions that will occur during the evaluation of a value of Foo, for something like:
foo <- force makeFoo `catch` \e -> defaultFoo
Depending on what kind of exceptions you are working with, you may prefer to use the 'MonadError' routines over the 'Either' monad. When you work in Either you can be sure that the value has been sufficiently evaluated to check whether or not an error occurs. If you want to catch asynchronous, OS-level exceptions then Either isn't good enough. Jules
participants (7)
-
Ben Rudiak-Gould -
Dean Herington -
Esa Pulkkinen -
Jules Bean -
Marcin 'Qrczak' Kowalczyk -
Michael Walter -
Tomasz Zielonka