19 ms·
Lisp implemented in Rust macros
- deleted 2y ago[deleted]
- djha-skin 2y agoObligatory reference to Carp[1], the lisp that uses borrow checking; the "rust" of lisps. 1: https://github.com/carp-lang/Carp https://github.com/carp-lang/Carp
- IPqLanQvqw7x 2y ago[flagged]
- MIzOjy4EhuJX 2y ago[dead]
- MrYHdP8nPLiG 2y ago[dead]
- deleted 2y ago[deleted]
- gleenn 2y agoI wish there was a well-supported Lisp in Rust, not just the macros. I wonder how much memory safety you would retain or lose being based in Rust. Is it even possible to leverage the borrow checker any any sane way?
- alilleybrinker 2y agoSteel seems alright: https://github.com/mattwparas/steel https://github.com/mattwparas/steel There are other Lisps too (https://github.com/alilleybrinker/langs-in-rust https://github.com/alilleybrinker/langs-in-rust) though I think they’re less actively maintained.
- robinsonrc 2y agoSteel has worked well for me as far as I’ve used it. It’s easy to get going and the partnership with Helix will surely give it a popularity boost over the next year or so.
- dokyun 2y agoSome Lisp compilers like SBCL are already capable of more extensive compile-time type-checking, but its information that the programmer is up to supply, and tends to be the part of the optimization stage rather than normal, incremental development. Lisp is usually defined by its dynamic nature, and run-time type checking is a big part of this. Making the programmer have to worry about how objects are managed before the fact would conflict with the freedom and expressibility one would expect from such a system. It also makes the compilers themselves simpler in comparison: Normal code without any extra declarations is safe by default, some systems might ignore such declarations for always being 'safe' (say if you're on a bytecode VM like CLISP or a Lisp machine that does hardware type-checking). SBCL compiles code quite quickly--so I've heard others tend to be even faster; the Rust compiler on the other hand is more likely to introduce a young programmer to the concept of thrashing. I really see them as two mutually incompatible worlds although they may not seem to be at first glance. One thing to remember is that Lisp is essentially the flagship "The Right Thing" language, while C is the "Worse is Better" language. Rust is neither, it is something entirely different which I think is overdue for a name, perhaps something that reflects the losing characteristics of both philosophies. (This isn't to discredit the OP article: it's still a cool hack!)
- wild_egg 2y ago> perhaps something that reflects the losing characteristics of both philosophies. Oof. With how much love Rust gets here, I didn't expect to see it being called out like that. How about "The Worse Thing"?
- BoingBoomTschak 2y ago> Some Lisp compilers like SBCL are already capable of more extensive compile-time type-checking, but its information that the programmer is up to supply Which is nice and all, but very much gimped by the glaring holes in CL's typing tooling: you can't create actual types, only derived types (deftype) and struct/class types. The two consequences of that is that you can't type cons-based lists/trees (arguably THE Lisp data structure) because deftype can't be recursive and you can't create parametric types, it's an internal thing only used for https://www.lispworks.com/documentation/HyperSpec/Body/t_array.htm https://www.lispworks.com/documentation/HyperSpec/Body/t_arr... (and not even hash-tables, these are completely untyped!).
- duetosymmetry 2y agoGreenspun's tenth rule strikes again! https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule
- klrtaj 2y agoA great example of the rule is that C++ rediscovers car/cdr at a glacial pace in the template language. In C++26 one can finally get the car of a typename parameter pack with Args...[0]. I've no idea why they don't introduce car/cdr functions and nil for empty parameter packs and allow to store parameter packs instead of the current syntax insanity.
- gpderetta 2y agoC++ metaprogramming was done with cons cells already back in '98. The new syntax provides random access instead, which is completely different from linked lists.
- otabdeveloper4 2y agoStore where? C++ templates are a lambda calculus, there's no notion of memory cells or state.
- jasfas 2y agoIn a struct! Pseudo code for extracting the types from subclasses and calling a multimethod on them: template <typename Result, typename Head, typename ...Tail> struct Foo : Visitor { Result res; UntypedList lst; // This does not exist! Tail... tail; // This is not possible! Foo(UntypedList l; Head hd, Tail... tl) : lst(l), tail(tl) { hd->accept(*this); } void visit(float *f) { res = Foo<Result, Tail...>(append(lst, f), tail)::res; } } }; // Base case that calls the multimethod omitted. There are two issues here: You can't store the parameter pack in Foo() which is later required by the continuation in visit(). And you would have to use tuples and tuple_cat() instead of UntypedList. Why can the compiler not infer the types in such an UntypedList? Why is there no car/cdr for tuples?
- p4bl0 2y agoThat was fun. Thanks for sharing!
- quasigloam 2y agoThanks! It was fun to make. It was also instructive, as I learned that rust analyser doesn't handle macros generating millions of tokens :D
- elif 2y agoHmmmmmmm.... Rustemacs?
- celeritascelery 2y agoI tried doing something similar to this once. I ran into an issue where I couldn’t define symbols with a dash in them (DEFINE MY-FN…). This is because rust will split the tokens on a dash. It was a small thing, but it meant I couldn’t just copy and paste snippets from a real lisp, I had to transform everything to underscore. Is it the same in your implementation?
- quasigloam 2y agoYes, at the moment I just assume that all atoms are rust idents because that makes it easier to implement (I can just match against $x:ident), so it doesn't support dashes in atoms. I guess you could match against `$x:ident $(- $y:ident)*` instead? That should work I think, I'd have to change some details in some of the arms but it seems like that would be possible.
- throwup238 2y agoWouldn’t that match “(foo - bar)” as well as “(foo-bar)”? I don’t think you can get around token whitespace in macro_rules
- quasigloam 2y agoYes it would match both, this would require us to assume that "foo - bar" can only be an atom. It's not a great solution.
- celeritascelery 2y agoIt would, but lisp has prefix operators, so you wouldn’t have to worry about it getting confused.
- quasigloam 2y agoAlthough in a Lisp such as Scheme, you could pass around the negation operator in something like (map - '(1 2 3)), so it would be a valid concern that it might clash.
- brundolf 2y agoWow and it uses macro_rules
- krick 2y agoEveryone is supposed to be cheering for how "fun" it is, but every time I see anything like that I cannot help but think, that I hate the fact it can be implemented in Rust. It never truly was a simple language, but I think it started as something way more manageable than what it has become.
- IshKebab 2y agoNah, it really takes very little for something like this to become possible. I bet you could do it with C macros, which are supposedly simple. (I checked; I win this bet: https://github.com/kchanqvq/CSP https://github.com/kchanqvq/CSP )
- josmar 2y agoIf it's Turing Complete, there will be a LISP for it
- zxexz 2y agoRust is definitely not a simple language, I agree there. I am quite likely a bit naïve here, but I'm having trouble understanding why you hate that this is possible. Now, the macro system definitely can generate near infinitely-complex code, but I'm not getting how implementing a sandboxed lisp using macros is a particularly potent example of the language being less manageable than at its genesis. On another note, the fact that the type system is turing-complete, like with C++ templates or the Haskell type system (variously dependent on which GHC languages extensions you're using...), makes me want to see a lisp implemented that way!
- quasigloam 2y agoImplementing a lisp in the type system would be fun, that's originally what this project was about until I got distracted with macros. Awesome projects like fortraith (https://github.com/Ashymad/fortraith https://github.com/Ashymad/fortraith) already exist, and even far more useful projects like dimensioned (compile time dimensional analysis using type level numbers) are inspiring. These examples, although cool, are probably a worse sign for krick for Rust compared with macro_rules being Turing complete.
- Validark 2y agoThis is super awesome!
- meindnoch 2y agoBut C++ is not a sane language because templates are Turing-complete, right?
- danschuller 2y agoC++ is not a sane language for anyone with a passing familiarity. At least Rusts macros aren't literal text substitutions, a move towards the light.
- sham1 2y agoTo be fair, neither are C preprocessor macros. They're instead token substitutions. It's not that much better, but they're not literally text substitution. They're at least more clever than just that. They're also of course surprisingly powerful, at the expense of being very cludgy.
- pjmlp 2y agoC++ templates, coupled with compile time programming, and eventually static reflection, make it easier than the multiple macro languages from Rust, with the additional dependency on syn crate.
- ramon156 2y agoC++ templates are a hell to develop with, at least macro_expand is a thing in Rust. It's the fact Rust's tooling is so well done.
- pjmlp 2y agoThere are C++ template debugging tools in IDEs.
- keybored 2y agoThere’s Turing Complete and Turing Tarpit.[1] [1] Which one is the Rust macro system? I have no idea.