<div dir="ltr">I suspect you're looking for something like lazy IO (or a similarly unsafe cousin like unsafeInterleaveIO as featured in the definition of fixIO) although I still remain a bit confused.<div><br></div><div>The type of <span style="font-size:12.8px">lazyMap :: (a -> b) -> IO a -> IO b is the same as for fmap, but fmap is already as lazy as it can be without unsafeness:</span></div><div><span style="font-size:12.8px"><br></span></div><div><span style="font-size:12.8px"><font face="monospace, monospace">    fmap (const ()) (return undefined) = return ()</font></span></div><div><span style="font-size:12.8px"><font face="monospace, monospace">    fmap (const ()) undefined = undefined</font></span></div><div><span style="font-size:12.8px"><br></span></div><div><span style="font-size:12.8px">You can't (safely) define lazyMap where lazyMap f ma doesn't have all the real-world effects of ma before returning. If ma has infinitely many effects then it's not going to return, because if it did then you'd have your hands on something of pure type 'b' which continues to have real-world effects as you evaluate it. This is, roughly speaking, lazy IO.</span></div><div><span style="font-size:12.8px"><br></span></div><div><span style="font-size:12.8px">On the other hand, you mention knot-tying and this is precisely what fixIO is for. A slightly contrived example:</span></div><div><span style="font-size:12.8px"><br></span></div><div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">import Control.Monad</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">import Control.Monad.Fix</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace"><br></font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">main :: IO ()</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">main = do</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">  fibs <- fibsIO</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">  print $ fibs !! 10</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace"><br></font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">fibsIO :: IO [Int]</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">fibsIO = mfix $ \fibs -> do</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">  fibs' <- forM [2..10] $ \i -> do</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">    putStrLn $ "Calculating fibs !! " ++ show i</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">    return $ fibs !! (i-1) + fibs !! (i-2)</font></span></div><div style=""><span style="font-size:12.8px"><font face="monospace, monospace">  return $ 0:1:fibs'</font></span></div><div style="font-size:12.8px"><br></div></div><div style="font-size:12.8px"><span style="font-size:12.8px">Note that I have to limit the number of actions for this to work - if it said 'forM [2..] then there'd be infinitely many effects and it'd never return.</span><br></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">Hope that helps,</div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">David</div><div><span style="font-size:12.8px"><br></span></div><div><span style="font-size:12.8px"><br></span></div><div><span style="font-size:12.8px"><br></span></div><div><span style="font-size:12.8px"><br></span></div><div><span style="font-size:12.8px"><br></span></div></div><div class="gmail_extra"><br><div class="gmail_quote">On 19 April 2016 at 00:25, David Feuer <span dir="ltr"><<a href="mailto:david.feuer@gmail.com" target="_blank">david.feuer@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><p dir="ltr">lazyMap :: (a -> b) -> IO a -> IO b, hypothetically.</p>
<p dir="ltr">Let me take a step back. There are two ways I know of to use the result of an IO action. The most common way, by far, is to use >>= to send it on to the next computation. The other notable way is to use fixIO.</p>
<p dir="ltr">No part of the result of an IO action becomes available until that action "completes" (in the shady case of lazy IO, this may be immediate, but I'm ignoring lazy IO for this discussion). This causes the well-known inefficiency of mapM in IO: individual results cannot be consed until the entire computation is complete. It seems to cause much more painful effects for fixIO--lazy programming techniques may simply be unavailable.</p>
<p dir="ltr">For example, it's possible for a pure computation using a queue in a single-threaded fashion to represent that queue as a list, tying the knot to pass the list of all enqueued elements to the beginning. If an IO computation wants to use a queue, this technique does not seem to be available. The knot can be tied with fixIO, but conses are delayed until it's too late--attempting to dequeue an element will block forever.</p>
<p dir="ltr">I'm wondering if there's some way to make this sort of thing work, either with plain Haskell IO or some thin layer built on top.</p><div class="HOEnZb"><div class="h5">
<div class="gmail_quote">On Apr 18, 2016 5:12 PM, "David Turner" <<a href="mailto:dct25-561bs@mythic-beasts.com" target="_blank">dct25-561bs@mythic-beasts.com</a>> wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><p dir="ltr">Sorry, you've lost me. I suspect you're trying to give a minimal example of the problem you're having, but you've stripped away too much context. What are lazyMap and f? At least, what are their types?</p>
<div class="gmail_quote">On 18 Apr 2016 22:03, "David Feuer" <<a href="mailto:david.feuer@gmail.com" target="_blank">david.feuer@gmail.com</a>> wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><p dir="ltr">Consider the implementation of `second` for pairs:</p>
<p dir="ltr">second f ~(a,b) = (a, f b)</p>
<p dir="ltr">Now</p>
<p dir="ltr">fix $ second (3 :)</p>
<p dir="ltr">Will be (undefined, [3,3,....])</p>
<p dir="ltr">Translating this to IO, I'd want</p>
<p dir="ltr">lazyMap f undefined</p>
<p dir="ltr">to produce as much as possible of the result, although it cannot produce the final State# RealWorld token.</p>
<div class="gmail_quote">On Apr 18, 2016 4:47 PM, "David Turner" <<a href="mailto:dct25-561bs@mythic-beasts.com" target="_blank">dct25-561bs@mythic-beasts.com</a>> wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><p dir="ltr">You can't know that the final result of the computation (x `seq` (3:...)) will begin with 3, because sometimes it doesn't! More specifically, it diverges (without yielding the 3) if x diverges.</p>
<p dir="ltr">I don't think this is anything special about mfix: (let x = x `seq` 3:... in x) also diverges for the same reason.</p>
<p dir="ltr">Hope that helps,</p>
<p dir="ltr">David</p>
<div class="gmail_quote">On 18 Apr 2016 21:19, "David Feuer" <<a href="mailto:david.feuer@gmail.com" target="_blank">david.feuer@gmail.com</a>> wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">If<br>
<br>
f :: a -> IO a<br>
<br>
for some a, and I want to use<br>
<br>
mfix f<br>
<br>
then f must not inspect its argument in any way, or the computation<br>
will get stuck. In some cases, this seems a bit harsh. For example,<br>
<br>
mfix (\x -> fmap (3 :) (x `seq` readLn))<br>
<br>
looks perfectly reasonable. There is no need to inspect the return []<br>
action to know that the final result of the computation will begin<br>
with 3:. Is there a lazy IO mapping function somewhere that can work<br>
such magic?<br>
_______________________________________________<br>
Haskell-Cafe mailing list<br>
<a href="mailto:Haskell-Cafe@haskell.org" target="_blank">Haskell-Cafe@haskell.org</a><br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
</blockquote></div>
</blockquote></div>
</blockquote></div>
</blockquote></div>
</div></div></blockquote></div><br></div>