How to get to evaluate the net value in the IO monad?

I need to force a net value in an IO monad. I am writing a higher-level interface for C bindings. At the lower level, I have, say newFile functions and freeFile functions. newFile returns some id, an opaque object. I decided on a lower level. You can't do anything about it, but use it to free a file and just figure out something related to that file.

So I (simplified):

 execGetter :: FilePath -> TagGetter a -> IO a execGetter path g = do fid <- newFile path -- 'fid' stands for "file id" let x = runGetter g fid freeFile fid return x 

This is the initial version of the function. We need to calculate x to freeFile . (The code works if I delete freeFile everything is fine, but I want to free the resource, you know.)

The first attempt (we will use seq for a "forced" evaluation):

 execGetter :: FilePath -> TagGetter a -> IO a execGetter path g = do fid <- newFile path let x = runGetter g fid x `seq` freeFile fid return x 

Segmentation error. Go straight to the seq documentation:

The value of seq ab is lower if a is the bottom, and otherwise it is b . seq usually introduced to improve performance, avoiding unnecessary laziness.

Note on evaluation order: the expression seq ab does not guarantee that a will be evaluated before b . The only guarantee provided by seq that both a and b will be evaluated before seq returns the cost. In particular, this means that b can be estimated to a . If you need to guarantee a specific evaluation order, you should use the pseq function from the "parallel" package.

A good point, indeed, I have seen people applying for different things about the evaluation procedure in this case. What about pseq ? Should I depend on parallel just because of pseq , hmm ... maybe there is another way.

 {-# LANGUAGE BangPatterns #-} execGetter :: FilePath -> TagGetter a -> IO a execGetter path g = do fid <- newFile path let !x = runGetter g fid freeFile fid return x 

Segmentation error. Well, this answer does not work in my case. But it offers evaluate , try it too:

 Control.Exception (evaluate) Control.Monad (void) execGetter :: FilePath -> TagGetter a -> IO a execGetter path g = do fid <- newFile path let x = runGetter g fid void $ evaluate x freeFile fid return x 

Segmentation error. Maybe we should use the value returned by evaluate ?

 Control.Exception (evaluate) Control.Monad (void) execGetter :: FilePath -> TagGetter a -> IO a execGetter path g = do fid <- newFile path let x = runGetter g fid x' <- evaluate x freeFile fid return x' 

No, a bad idea. Maybe we could chain seq :

 execGetter :: FilePath -> TagGetter a -> IO a execGetter path g = do fid <- newFile path let x = runGetter g fid x `seq` freeFile fid `seq` return x 

It works. But is it right to do this? Maybe this only works because of some kind of unstable optimization logic? I dont know. If seq binds to the left in this case, then according to this description, both x and freeFile are evaluated when return x returns its value. But then again, which one, x or freeFile is ranked first? Since I am not getting a seg fault, it should be x , but is this result reliable? Do you know how to force x to freeFile correctly?

+7
haskell lazy-evaluation
source share
2 answers

One possible problem is that newFile does some lazy runGetter and that runGetter is a pretty lazy consumer that running seq on its output doesn't make all newFile actually happen. This can be fixed using deepseq instead of seq :

 execGetter :: NFData a => FilePath -> TagGetter a -> IO a execGetter path g = do fid <- newFile path let x = runGetter g fid x `deepseq` freeFile fid return x 

Another possibility that this will concern is that runGetter claims to be clean, but in fact it is not (and is a lazy producer). However, if in this case the correct fix should not use deepseq here, but to exclude the use of unsafePerformIO from runGetter , use:

 execGetter :: FilePath -> TagGetter a -> IO a execGetter path g = do fid <- newFile path x <- runGetter g fid freeFile fid return x 

which should then work without further action with forcing.

+9
source share

Daniel Wagner's answer is suitable for this use case, but sometimes it’s also useful to simply evaluate the list backbone and leave the list items invaluable (and sometimes the list items do not have a reasonable instance of NFData ). You cannot use deepseq for this because it evaluates everything. However, you can combine seq with this function:

 evalList :: [a] -> () evalList [] = () evalList (_:r) = evalList r 
0
source share

All Articles