3 ms·
I've done the "whole new thing" before, too. 20ish years ago, tho I only have a few fragments of what I worked on back then: https://github.com/rdaum/mica https
by cmrdporcupine 3y ago
I've done the "whole new thing" before, too. 20ish years ago, tho I only have a few fragments of what I worked on back then: https://github.com/rdaum/mica https://github.com/rdaum/mica being one of them I found on an old drive. Not complete.
But sticking with compatibility has allowed me to enforce development discipline, basically. And then I'll move it onwards from there. I have many ideas. And a product concept (if I could find an investor...). But I felt it important to start with a thing with existing foundation and use cases before doing the exciting stuff/changes.
Re: world state / transactions -- yeah, basically all I/O and mutations happen in a transactional context, and then at commit time conflicts are resolved; if they're not resolve-able, the transaction is retried in a new state. As for overhead, yes potentially maybe a lot, but it's also a solvable problem; this is how an MVCC SQL database (like, even Postgres) works. TLDR it's likely inefficient now, but I believe I can make it efficient. And I think it's the best way to solve the shared world state problem and still meet user's expectations of consistency. Original LambdaMOO had a global interpreter lock and only one user was mutating the world at a time, in their given tick slice. Not gonna scale.
Re: the MOO client, it's `rmoo.el`: https://github.com/lisdude/rmoo https://github.com/lisdude/rmoo -- it's been around for a long time (25, 30 years?) and it and/or MOO.el (another emacs one) are how/why I learned emacs in the first place. I had to minor patch my local copy to make it work with emacs 29.1, though I don't know elisp well enough to say whether my patch is the right thing to do or not, so...
EDIT: I just re-read and saw you asked whether each new verb call creates a new state, and actually it's not that. It's each new command or network transaction, which can in fact lead to a chain of verb (method) calls. Recall a MUD has a command loop, so basically a transaction begins at command entry, and commits when all the invoked methods/verbs provoked from that complete.
- derefr 3y ago> Original LambdaMOO had a global interpreter lock and only one user was mutating the world at a time, in their given tick slice. Not gonna scale. I’ve previously seen (abandoned) attempts to convert the Erlang runtime directly into a MOO runtime. Each command being a concurrent actor-process would make a lot of sense to me. (Though a semantics where all commands are MVCC against the at-inception world-state and will rollback or retry on conflict, kind of moots having any kind the advantage of a runtime based on message-passing, since you’re not going to be passing messages between separate concurrent “sessions.”)
- cmrdporcupine 3y agoYeah the whole actor thing is kind of orthogonal to the question of how you handle the shared world state (which is what makes a MOO a MOO.) There are parts of my runtime that look Erlang-ish, Actor-ish, because that's a concurrency pattern that Rust does well with, in general. In the end a MOO is just a database with fancy stored procedures. And the original code is not a particular... good one. Handling serialization conflicts is what databases have to do as one of their core abilities, and LambdaMOO just tries to punt on the whole question.
- smirya0 3y agohow did you come to pick Rust (just curious, vs Elixir, Java with Loom, etc)?
- cmrdporcupine 3y agoThese days Rust is both my day job language (I work on systems for autonomy in tractors) and my hobby language. It's my preferred tool. I have attempted similar systems in C++ before, and got fairly far along at times. But Rust really solves a lot of problems, I tear my hear out a lot less. This is a "systems" type of application; this is a database and compiler and a virtual machine and language runtime. I'm not super concerned with optimization at this point, but I will likely have to be at some point. Being closer to the metal is key for me on a project like this. I'm likely to e.g. rip out RocksDB and replace it with my own database engine at some point (something I work on here and there as another side project, unpublished still.) I will also likely need a better garbage collection solution. With Rust I have options. The community of libraries available for Rust is good. The right tools are there. And the language is expressive enough to make it work well. That said, I have mulled writing the "host" portion -- the layer that manages network sockets and web endpoints, in Elixir/Erlang: The 'daemon' portion of the system sits behind an RPC layer built around ZeroMQ -- so that layer can be restarted/upgraded without losing active network connections. Or can sit on a separate machine for scalability reasons, etc. So it's entirely possible to split what I'm calling the "host" (network facing layer, what we used to call a "frontend" when I worked at Google, but most people think of that term as referring to a UI layer) from the backend "daemon." Anyways for me this project ties together a bunch of interests in systems programming I've had for years. It becomes a kind of synthesis project.
- crooked-v 3y agoTalking about SQL, now I find myself wondering how practical it would be to try and build an entire MU* engine inside a database, with db functions for input and something like an events table with Postgres real-time replication for output.
- cmrdporcupine 3y agoI think you'd find that the SQL parser layer is "getting in the way", but on the whole that's effectively what something like LambdaMOO can be properly distilled to. It has in theory all the layers of a database. But because it was all written as hobby/student code it doesn't really do any of it all that systematically. But yes one approach could be to take something like Postgres and write an alternative stored procedure implementation, and give it access directly to the query execution later bypassing the SQL parser maybe.