I have

sample :: MVar a -> IO [a]
sample v = do
   a <- takeMVar v
   pure (a:unsafePerformIO (sample v))

which appears to be a legitimate use of unsafePerformIO to me. But I am very interested to know how to avoid it! Is there a pattern for this use already?

share|improve this question
    
How is it more unsafe than the readLn action. After all the result is IO [a] – heisenbug Mar 1 '16 at 23:02
4  
I wouldn't be so sure that this is legitimate use of the Fᴜɴᴄᴛɪᴏɴ ᴛʜᴀᴛ ꜱʜᴀʟʟ ɴᴏᴛ ʙᴇ ɴᴀᴍᴇᴅ. With concurrency in the game, there's all kinds of things that could go wrong if the compiler messes up with referential transparancy assumptions. If sample worked really safely, then it would probably be in the library already. – leftaroundabout Mar 1 '16 at 23:06
2  
If you are going to go with the unsafe route, you should at least use unsafeInterleaveIO here, which is the intended method of implementing lazy IO. Note that getChanContents itself uses unsafeInterleaveIO in order to lazily produce the entire contents of the Chan. – user2407038 Mar 2 '16 at 3:35
up vote 7 down vote accepted

You can implement a similar function using a thread, a Chan and getChanContents:

sample :: MVar a -> IO [a]
sample v = do
   c <- newChan
   forkIO $ forever $ takeMVar v >>= writeChan c
   getChanContents c   

The thread/getChanContents approach is slightly better, since at least you can rely on the MVar being continuously taken. Instead, the unsafePerformIO approach will run takeMVar at impredictable points, making the putMVars blocking in a similarly impredictable way. Of course, the getChanContents approach will buffer all the data, possibly requiring more memory.

However, both approaches are essentially similar to lazy IO, which is best to be avoided, in my opinion.

share|improve this answer
    
I agree with this, in all aspects. – leftaroundabout Mar 1 '16 at 23:10
    
could you explain in which way getChanContents is more predictable than using unsafePerformIO. I guess with other approaches than lazy IO you refer to things like pipes or conduit. – epsilonhalbe Mar 1 '16 at 23:29
1  
@epsilonhalbe One (admittedly non-technical) advantage of getChanContents over manually using unsafePerformIO: it is maintained by experts in both GHC and concurrency; it is also more widely used than a homebrew solution and therefore more likely to be the scrutinee if/when a bug in its implementation creeps in. – Daniel Wagner Mar 1 '16 at 23:35
1  
@epsilonhalbe It's the forever loops that makes that approach more predictable, not the getChanContents. Even if no one demands elements in the returned list, forever will continuously take the MVar and buffer the read values in the channel. So, each putMVar v used elsewhere is guaranteed to always eventually find v empty. – chi Mar 1 '16 at 23:37

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.