2026/06/28

Newest at the top

2026-06-28 13:42:18 +0000 <fp> Wild
2026-06-28 13:42:10 +0000 <tomsmeding> and it uses unsafeInterleaveIO to achieve that
2026-06-28 13:42:04 +0000 <tomsmeding> getContents returns a thunk that does IO when you look at it
2026-06-28 13:41:41 +0000 <tomsmeding> fp: yes, with memoisation, that's the point
2026-06-28 13:41:32 +0000 <tomsmeding> this means that what IO gets executed when depends on the evaluation order of your pure code, which normally is not allowed to happen in Haskell (because it makes reasoning extremely difficult)
2026-06-28 13:41:30 +0000 <fp> perhaps with memoization as well
2026-06-28 13:41:09 +0000 <fp> So in another language, lazy evaluation might look like `var foo = { get() { compute_foo() } }`
2026-06-28 13:41:04 +0000 <tomsmeding> now if you do tricks to put a side effect inside a thunk, now suddenly you have pure code (that looks at the thunk and thus triggers its evaluation) that triggers IO
2026-06-28 13:40:16 +0000 <tomsmeding> but that closure is a pure function, so you don't see it in the output (apart from being faster/slower/terminating)
2026-06-28 13:39:56 +0000 <fp> Ok so getContents is magic in particular
2026-06-28 13:39:54 +0000 <tomsmeding> lazy evaluation means that a part of a value may not actually be computed yet; it's a thunk, i.e. a closure, that gets evaluated whenever you try to look at it
2026-06-28 13:39:28 +0000 <fp> That said, I did see RealWorld, which is cool.
2026-06-28 13:39:06 +0000 <tomsmeding> which is not a REPL and not nearly as magic
2026-06-28 13:39:00 +0000 <tomsmeding> if you have a simple implementation of getContents that just reads its input in IO as "normal", your REPL would be "read all input lines, then execute all of them"
2026-06-28 13:38:36 +0000 <fp> Yeah I tried digging into getContents to see what it does. Suffice to say I didn't understand what was going on
2026-06-28 13:37:53 +0000 <tomsmeding> that requires some unsafeInterleaveIO in getContents
2026-06-28 13:37:37 +0000 <tomsmeding> """pure"""
2026-06-28 13:37:25 +0000 <tomsmeding> fp: that example relies on 'contents' being a pure value that, when you look at it, does some IO to give you the thing you're trying to read
2026-06-28 13:37:09 +0000 <fp> I guess I could simplify with map (show . eval)
2026-06-28 13:36:29 +0000 <tomsmeding> RE(L)P
2026-06-28 13:36:25 +0000 <tomsmeding> yeah but there's only one R and only one P, and the L is inside the E
2026-06-28 13:36:08 +0000 <mauke> map is a loop
2026-06-28 13:36:05 +0000 <tomsmeding> that's lazy IO, though, not just lazy evaluation
2026-06-28 13:35:33 +0000 <tomsmeding> lol
2026-06-28 13:35:25 +0000 <fp> Like there's no loop! my repl is a rep but it still works!
2026-06-28 13:34:55 +0000 <tomsmeding> heh, yes that's fun
2026-06-28 13:34:54 +0000 <fp> Maybe yeah
2026-06-28 13:34:30 +0000 <tomsmeding> possibly with some serialised arguments
2026-06-28 13:34:19 +0000 <tomsmeding> as in, instead of constructing an IO computation in the commanding process and sending it over to the fieldbus process, look at your application and write a normal `fn` that does the whole thing, put it in a list of functions (with a name for each), and have the commanding process tell the fieldbus process to "now execute function abc"
2026-06-28 13:33:48 +0000emilym(~Thunderbi@user/emilym) (Ping timeout: 256 seconds)
2026-06-28 13:33:22 +0000 <fp> The magic is less now, though lazy evaluation still feels crazy to me. That you can make a repl with =getContents >>= \contents -> putStrLn $ (unlines . map show . map eval . lines) contents= is bonkers
2026-06-28 13:33:10 +0000 <tomsmeding> if it's a different process on the same machine, I'd seriously consider if there is way to group your computations into "pre-made programs" that can be triggered by the commanding process as a whole
2026-06-28 13:33:09 +0000chromoblob(~chromoblo@user/chromob1ot1c) chromoblob\0
2026-06-28 13:31:53 +0000chromoblob(~chromoblo@user/chromob1ot1c) (Read error: Connection reset by peer)
2026-06-28 13:31:34 +0000 <fp> And also IO feels like magic sometimes and I wanted to understand it better
2026-06-28 13:30:58 +0000 <fp> Yeah like I have some programs that are running with this fieldbus IO, but it's very timing sensitive, so I really need to move it to a different process (though same machine is probably fine). But then of course I have to develop a way to command IO actions of the other process, which is how I got here
2026-06-28 13:30:01 +0000berke93___(~default@193.108.195.249) (Ping timeout: 266 seconds)
2026-06-28 13:29:39 +0000 <tomsmeding> (I'm assuming "device" here is a separate piece of hardware that doesn't necessarily share a CPU architecture with the "main" computer)
2026-06-28 13:29:05 +0000 <tomsmeding> which you can serde etc
2026-06-28 13:28:55 +0000 <tomsmeding> that means you have to also write the stuff in the .bind() closures in your little language, but the payoff is that the thing you send to the device, and what you execute there, is just a regular old data type
2026-06-28 13:28:42 +0000chromoblob(~chromoblo@user/chromob1ot1c) chromoblob\0
2026-06-28 13:28:21 +0000 <tomsmeding> (I think you already suggested this)
2026-06-28 13:28:10 +0000 <tomsmeding> an intermediate version that is much more feasible is defining a little language that you have an interpreter for on the device
2026-06-28 13:27:57 +0000 <fp> That is a good question to ask, one that I should really consider
2026-06-28 13:27:55 +0000chromoblob(~chromoblo@user/chromob1ot1c) (Ping timeout: 276 seconds)
2026-06-28 13:27:45 +0000 <tomsmeding> and what kind of computation do you actually want to run
2026-06-28 13:27:18 +0000 <tomsmeding> because then the first thing I'd think about is "what kind of computation, aside from the precise representation of the IO operations, can I run on this device"
2026-06-28 13:26:50 +0000 <tomsmeding> fp: can you just dispatch a single primitive IO action to the device, or do you really need to dispatch a larger program?
2026-06-28 13:26:16 +0000 <tomsmeding> I feel like this is wrapping closures in IO instead of IO in closures
2026-06-28 13:26:00 +0000 <hc> (Not that I'm saying I particularly like async rust)