Newest at the top
| 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 +0000 | chromoblob | (~chromoblo@user/chromob1ot1c) chromoblob\0 |
| 2026-06-28 13:31:53 +0000 | chromoblob | (~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 +0000 | berke93___ | (~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 +0000 | chromoblob | (~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 +0000 | chromoblob | (~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) |
| 2026-06-28 13:25:44 +0000 | <hc> | Hmm, not having read the backlog thoroughly, but isn't that pretty much what rust's async/"Futures" do? Wrap IO in closures? |
| 2026-06-28 13:25:26 +0000 | <tomsmeding> | because an arbitrary IO action contains calls to primitive IO operations as well as "pure" glue code that decides what next action to take |
| 2026-06-28 13:25:23 +0000 | <fp> | Sure |
| 2026-06-28 13:24:52 +0000 | <tomsmeding> | and serialise, transfer and deserialise the closures |
| 2026-06-28 13:24:42 +0000 | <tomsmeding> | if you want to dispatch the IO computation to a device, and you describe IO like Haskell does, you'll need to also run arbitrary Rust code on the device |
| 2026-06-28 13:23:33 +0000 | <fp> | What I'm imagining is that a user can describe a complex IO action and that can be dispatched to the IO device or to a server that does the IO and they get back either the expected data or a rich error |
| 2026-06-28 13:18:57 +0000 | <fp> | Or generally like an interpreter |
| 2026-06-28 13:18:56 +0000 | <tomsmeding> | is that what you need? |
| 2026-06-28 13:18:54 +0000 | <tomsmeding> | what Haskell's "IO" gives you is a data type for describing arbitrary side-effectful computations in a non-side-effectful language |
| 2026-06-28 13:18:34 +0000 | <fp> | I guess building a stack machine might be more reasonable |
| 2026-06-28 13:18:06 +0000 | <tomsmeding> | hm, no that still has closures everywhere |
| 2026-06-28 13:17:46 +0000 | <tomsmeding> | you might have more luck with the free monad encoding |
| 2026-06-28 13:17:38 +0000 | <fp> | Yeah I'm seeing that |
| 2026-06-28 13:17:27 +0000 | <tomsmeding> | I think this experience is enough to show that Haskell's way of representing an IO computation -- using closures -- is not usable in Rust :p |
| 2026-06-28 13:16:34 +0000 | <fp> | "T may not live long enough" |
| 2026-06-28 13:16:23 +0000 | <fp> | Ultimately, my goal isn't to emulate Haskell per se, but to have a composable Io interface in Rust. For context, I'm testing this out to see if I could make an API for an industrial fieldbus this way |
| 2026-06-28 13:15:28 +0000 | <tomsmeding> | what precisely is the error? ret() owns t, right, so it should be able to move it into the closure |
| 2026-06-28 13:14:42 +0000 | <fp> | No, move doesn't work |
| 2026-06-28 13:14:23 +0000 | <tomsmeding> | and yes my rust is rusty, add Box<> as necessary |
| 2026-06-28 13:13:54 +0000 | <tomsmeding> | also not if you `move || t`? |
| 2026-06-28 13:12:51 +0000 | emilym | (~Thunderbi@user/emilym) emilym |
| 2026-06-28 13:11:45 +0000 | <fp> | like in `fn ret(t: Rc<T>) -> Io<T> { Io(Rc::new(|| t.clone())) }`, t doesn't have a long enough lifetime |
| 2026-06-28 13:10:34 +0000 | <fp> | That actually doesn't work because Fn() -> T is a DST, so you need to Box (or just &, but I won't) it and use dyn, or use a function pointer (but that means capturing closures aren't allowed). And even with Rc and clone, you still run into lifetime issues |
| 2026-06-28 13:09:09 +0000 | <tomsmeding> | lifetimes are 100% distracting from the mechanics of IO :p |
| 2026-06-28 13:08:51 +0000 | <tomsmeding> | add .clone() and Rc<> until it works |
| 2026-06-28 13:08:41 +0000 | <tomsmeding> | because of lifetimes? |
| 2026-06-28 13:08:25 +0000 | <fp> | Yes and that is the root of all evil |
| 2026-06-28 13:08:10 +0000 | <tomsmeding> | try `struct Io<T>(Fn() -> T);` |
| 2026-06-28 13:07:23 +0000 | <tomsmeding> | where's the computation? This is just a T |
| 2026-06-28 13:07:19 +0000 | <tomsmeding> | fp: "struct Io<T>(T);" "/// A computation that, when performed, does some I/O before returning a value of type `T`." |