2026/07/04

Newest at the top

2026-07-04 20:35:30 +0000 <tomsmeding> yes, not separating the two makes it simpler in the sense that you don't have to think about the separation. ;)
2026-07-04 20:35:06 +0000 <tomsmeding> if that would be the normal way of doing IO, why would we separate IO and non-IO?
2026-07-04 20:34:39 +0000 <EvanR> do the IO and evaluate the result all at the same time
2026-07-04 20:34:18 +0000 <EvanR> that account of IO makes unsafePerformIO sound simpler than normal IO (one kind of special event associated with an action not two)
2026-07-04 20:32:51 +0000bitdex(~bitdex@gateway/tor-sasl/bitdex) (Ping timeout: 252 seconds)
2026-07-04 20:28:11 +0000bitdex(~bitdex@gateway/tor-sasl/bitdex) bitdex
2026-07-04 20:27:28 +0000merijn(~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 268 seconds)
2026-07-04 20:27:23 +0000haritz(~hrtz@user/haritz) haritz
2026-07-04 20:27:23 +0000haritz(~hrtz@2a01:4b00:bc2e:7000:d5af:a266:ca31:5ef8) (Changing host)
2026-07-04 20:27:23 +0000haritz(~hrtz@2a01:4b00:bc2e:7000:d5af:a266:ca31:5ef8)
2026-07-04 20:20:24 +0000merijn(~merijn@host-cl.cgnat-g.v4.dfn.nl) merijn
2026-07-04 20:16:09 +0000 <tomsmeding> that crazy thunk-inspecting unsafeUnperformIO would have a really amusing failure case though: if it's not a state token application, "Cannot unperformIO: already performed"
2026-07-04 20:15:41 +0000bdkl(~bdkl@user/bdkl) (Quit: bdkl)
2026-07-04 20:14:42 +0000tromp(~textual@2001:1c00:340e:2700:ec0e:2a6c:3177:923e)
2026-07-04 20:14:30 +0000 <int-e> etc.
2026-07-04 20:14:28 +0000 <int-e> You can't have unsafeUnperformIO;
2026-07-04 20:14:14 +0000 <int-e> You can't have unsafePerformIO; the way I'd describe the reason is that IO actions have a "lever" that triggers the underlying IO operations independently of forcing the action's result; unsafePerformIO inextricably ties these two things together. Unless you go crazy and write code that inspects thunks and code to find pending applications of state tokens I suppose.
2026-07-04 20:11:41 +0000 <int-e> (that's also wrong of course)
2026-07-04 20:11:29 +0000 <lambdabot> a -> IO a
2026-07-04 20:11:28 +0000 <int-e> :t Control.Exception.evaluate
2026-07-04 20:11:17 +0000 <monochrom> I don't actually know. Maybe it works out of the box.
2026-07-04 20:09:36 +0000 <EvanR> aw
2026-07-04 20:09:20 +0000merijn(~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 245 seconds)
2026-07-04 20:08:46 +0000 <yahb2> <no output>
2026-07-04 20:08:45 +0000 <tomsmeding> % :unset +s
2026-07-04 20:08:35 +0000 <monochrom> The way GHC encodes IO, it may be impossible.
2026-07-04 20:08:32 +0000 <yahb2> (0.01 secs, 0 bytes)
2026-07-04 20:08:31 +0000 <tomsmeding> % unsafe²id :: a -> a ; unsafe²id = System.IO.Unsafe.unsafePerformIO . unsafeUnperformIO where unsafeUnperformIO = return
2026-07-04 20:07:36 +0000 <EvanR> unsafeSquaredId
2026-07-04 20:07:10 +0000 <EvanR> it gives you id :: IO a -> IO a but pick up an unsafe squared
2026-07-04 20:06:57 +0000 <tomsmeding> well, unsafePerformIO . unsafeUnperformIO = id :: a -> a just fine. :)
2026-07-04 20:06:36 +0000 <monochrom> I was hoping unsafeUnperformIO . unsafePerformIO = id :: IO a -> IO a
2026-07-04 20:06:23 +0000haritz(~hrtz@user/haritz) (Quit: ZNC 1.8.2+deb3.1+deb12u1 - https://znc.in)
2026-07-04 20:06:06 +0000tomsmeding. o O ( return? )
2026-07-04 20:05:54 +0000 <monochrom> Consider unsafeUnperformIO :: a -> IO a >:)
2026-07-04 20:05:26 +0000 <tomsmeding> yeah good point
2026-07-04 20:05:24 +0000 <tomsmeding> uh, oh right, the point was that this is in pure code
2026-07-04 20:05:12 +0000 <EvanR> that's an external shenanigan xD
2026-07-04 20:05:00 +0000 <tomsmeding> EvanR: watch out, you're in IO so you can `catch`
2026-07-04 20:04:44 +0000 <EvanR> whoever gets there when it's a bottom can't observe a difference, without external shenanignas
2026-07-04 20:04:28 +0000Lord_of_Life(~Lord@user/lord-of-life/x-2819915) Lord_of_Life
2026-07-04 20:04:16 +0000 <EvanR> i.e. poor man's IVar
2026-07-04 20:04:02 +0000 <EvanR> if you update an IORef containing a bottom so it doesn't contain a bottom, and it's never updated again / updated with that same value only, then you might make an argument that it's still right
2026-07-04 20:03:24 +0000 <tomsmeding> ah yes, common subexpression elimination :)
2026-07-04 20:02:41 +0000 <monochrom> Usually the surprise is the converse. You run "unsafePerformIO (print "hello" >> return 10) 3 times expecting to print hello 3 times, but clearly GHC is right to let it happen just once (and cache the 10).
2026-07-04 20:02:40 +0000 <brooke2k> woah I hadn't thought of that either, good point... if the internal IORef gets copied to another thread that could get dangerous
2026-07-04 20:02:37 +0000 <tomsmeding> so that's a realistic situation where a pure computation may be evaluated more than once
2026-07-04 20:02:26 +0000merijn(~merijn@host-cl.cgnat-g.v4.dfn.nl) merijn
2026-07-04 20:02:18 +0000 <tomsmeding> (the locking-like technique that prevents this from happening, called "blackholing", is disabled by default because it turned out that enabling it cost more in the vast majority of cases than it won)
2026-07-04 20:01:38 +0000 <tomsmeding> also, if a thread encounters a thunk while another thread has already started evaluating it, the second thread may also start evaluating the thunk in parallel