There are a few more approaches if you're going for "everything's a foo" minimalism:
- Numbers and strings can be types. You already talk about using numbers as keys (aka types) for array access, so it's a bit surprising you haven't mentioned this option yourself.
- Numbers and strings can be objects whose types happen to be out of scope in user code. As far as the programmer is concerned, the core utilities were constructed with those types in scope, so it makes sense for them to be able to reach inside. I like the thought of everything being a weak table (and this philosophy underlies my garbage collector, Cairntaker), so this is the kind of choice I would make.
- Numbers and strings can be functions. Why not? :-p
Hm... I suppose that might work... yeah you're right that might be a really good idea. Thanks, I didn't think about that. But... hm... I wonder if that'll really work...
---
"Numbers and strings can be objects whose types happen to be out of scope in user code."
That's the problem. If I create the object using an ordinary closure to hide the data, how do I actually get the data when doing things like "lt" and "is" comparisons? I'd have to store it in the object itself, in which case now it's exposed to user code.
---
"Numbers and strings can be functions. Why not? :-p"
Because I'm trying to make a clean separation between data and algorithm. Though I already blur that a little by making $vau return an object. Plus I'm not sure how I'd implement that anyways, could you give an example?
"I'd have to store it in the object itself, in which case now it's exposed to user code."
If the type is out of scope in user code, then there's nothing user code can do to extract that value, right? The user code can't extract a list of types for an object, can it?
---
"Because I'm trying to make a clean separation between data and algorithm. Though I already blur that a little by making $vau return an object. Plus I'm not sure how I'd implement that anyways, could you give an example?"
I don't know what numbers and strings would do as functions, but they could potentially take a secret key and return some other internal representation. This secret key would not be in scope in user code. ;)
Of course, if user code can inspect lexical closure data anyway, it's impossible to hide things this way. I kinda recommend making the %closure key mean "the lexical closure, or nil if it happens to be unavailable."
"If the type is out of scope in user code, then there's nothing user code can do to extract that value, right? The user code can't extract a list of types for an object, can it?"
I was thinking about letting code loop through an object, similar to the "for ... in" loop in JavaScript. An example of where that would be helpful is map, filter, etc.
If I gave that up, then, sure, I could store it in the object and user code wouldn't be able to reach it.
---
"Of course, if user code can inspect lexical closure data anyway, it's impossible to hide things this way. I kinda recommend making the %closure key mean "the lexical closure, or nil if it happens to be unavailable.""
That's right. I think if I'm going to go the "everything is an object" way, then I want everything to be open. Of course, I might change my mind and try some other route...
I think I've figured out what it is that I want in a programming language, at least at a high level. I want a programming language that's basically like Lego blocks. You have these little atomic pieces that are useful in and of themself, but because they share a similar interface, you can plug them together to create bigger pieces.
In other words, it's the Unix philosophy of "write programs that do one thing and do it well" as well as the functional philosophy of "avoiding state". The reason for this is to be able to treat the program as a black box that can be plugged into other black boxes to get the behavior.
I've given up on extensibility and being able to hack other people's code. Why? If their code is insufficient, it's probably easier to just rewrite it from scratch anyways. As long as the new code has the same interface as the old code, it'll work just fine.
So I want a language that has a lot of power and flexibility for defining interfaces, and also a lot of fine-grained power for taking programs and treating them as black boxes. Using objects for everything defeats that, if I allow code to loop through the keys of objects.
But I think my objects + gensyms are basically a simple system that lets Nulan have both monads and comonads. Still a lot to think about and figure out!