If you are creating Lisp because you want to create Lisp, like creating Lisp, want to show off creating Lisp, that obviously is not what the Law is about.
Furthermore, if you create Lisp because you know the Law, know it is inevitable, and want to avoid the caveats and missed bars by doing so explicitly, well then that also is not what the Law is about.
But if you are going about your business, focused on your independent goal, realize you need Lisp like lists, and then 250 lines of code later realize you have created a solid Lisp unintentionally? Well, congrats on falling into the trap but not getting hurt!
—
Personally, I have created both Lisp and Forth multiple times for suitable projects where I wanted some flexible runtime scripting. I am not familiar with the standard version libraries of either and don’t need them.
Minimal foundation implementations are extremely easy to create, and eliminate any dependencies or sources of mystery.
Anyone know of any other well designed mininal languages?
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.
C++ templates are a lambda calculus, there's no notion of memory cells or state.
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?
"Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp."
HA HA!
Famous use of that pattern is Arthur's Clarke "Any sufficiently advanced technology is indistinguishable from magic.". As with OP's quote, this is also vague and imprecise but the point of the idea is that there is a gradient towards which advancements in technology bring us towards a situation where the average person no longer understands how it works and it may as well be magic (while not necessarily defining exactly when does that transition happen)
But what is "sufficiently" complicated? Now it's a silly statement that just doesn't pass Popper criterion: even if nobody dares to deny your program is complicated, one can always say it isn't sufficiently complicated, hence the fact it doesn't contain Lisp-interpreter disproves nothing. A man of culture wouldn't use such definitions.
You can use proc_macro::Span.line()/column() [1] to track how much whitespace there is between tokens and merge them, but the macro will have to rename them to valid Rust identities ("foo-bar" to "foo_bar") and make sure there's no collisions.
[1] https://doc.rust-lang.org/proc_macro/struct.Span.html#method...
I'm not seeing any issue? (DEFINE MYᜭFN...) works just fine.
(This isn't to discredit the OP article: it's still a cool hack!)
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"?
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_arr... (and not even hash-tables, these are completely untyped!).
(deftype list-of (type)
`(cons ,type (or null (list-of ,type))))
(typep '(1 2 3 4) '(list-of integer))
=> T
Most of the standard types from which derived types can be made are parametric types, which specialize on their arguments in different ways. They work the same way as macros. One upon a time I wrote a type specifier that allows you to specialize on a lambda expression, using the ordinary 'satisfies' type that only lets you provide named functions: (deftype satisfiesp (function)
(let (predicate)
(cond ((symbolp function)
(setq predicate function))
((functionp function)
(setq predicate (gensym))
(setf (symbol-function predicate) function))
((and (consp function) (eq (car function) 'lambda))
(setq predicate (gensym))
(compile predicate function))
(t (error "~S is neither a lambda expression, function or symbol." function)))
`(satisfies ,predicate)))
Now you can even type-check on lambda expressions, and quasiquotation can sidestep the issue of capturing variables. (defvar foo 'bar)
(typep 'bar `(satisfiesp (lambda (x) (eq x ',foo))))
=> T
https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node44.htmlThere are other Lisps too (https://github.com/alilleybrinker/langs-in-rust) though I think they’re less actively maintained.
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!
Thanks for sharing your project, by the way - between this and the other one you linked, I think I know what my leisure time this weekend is going to consist of...
Hard disagree here. Rust team is continuously making the language easier to work with by removing limitations and making features more orthogonal. A notable example is non lexical lifetimes, impl Trait in return position or async traits. Also they have removed some features eg before it went 1.0, it even had GCed references built in with special syntax.
If you want a language guided by the principle of simplicity Rust was never that and you have plenty of options elsewhere.
(I checked; I win this bet: https://github.com/kchanqvq/CSP )
They're also of course surprisingly powerful, at the expense of being very cludgy.
[1] Which one is the Rust macro system? I have no idea.