That's because ((fn (nil) nil) 10) is supposed to be the same as ((fn (()) nil) 10), which destructures the first 0 elements from 10, treating it as a degenerate cons list.
I actually use this behavior in practice, 'cause it's useful to have at least some parameter syntax that doesn't bind any variables, rather than just using a conventional name like "ignored" or "_". Most of the time I use it in a much-too-big 'withs block, where I abuse one of the bindings for sanity-checking or other side effects. ^^ I wouldn't worry about it too much, though.
On the other hand, nil's behavior makes it more verbose to determine whether something's usable as a local variable; you have to check (and x (isa x 'sym) (~ssyntax x)). In Lathe, I define 'anormalsym to do just that. (It might be more accurate to change it to (and (isa x 'sym) (~in x nil t 'o) (~ssyntax x)).)
Speaking of (~ssyntax x), ssyntax is usable for local variable names, but naturally, you can only get the values from the Racket side:
Personally, I'd like for ssyntax-burdened names in parameter lists to use user-defined parameter syntaxes, like custom forms of destructuring and whatnot. And then, just for axiomatic simplicity's sake, I'd like things like 'o to be implemented using the same system, using ssyntax-burdened names like ".o".
"That's because ((fn (nil) nil) 10) is supposed to be the same as ((fn (()) nil) 10)"
nil is such a weird value... it's false, a symbol, and an empty list all at once. I actually had to do some hacky stuff to get () and '() and nil to behave properly.
"Speaking of (~ssyntax x), ssyntax is usable for local variable names, but naturally, you can only get the values from the Racket side:"
I plan to implement ssyntax at the reader level in my interpreter, so that shouldn't be an issue.
"nil is such a weird value... it's false, a symbol, and an empty list all at once."
Perhaps nil should serve as the base case for every type, rather than just some of them. In Arc, it already does this for booleans, lists and strings (""), but it could be extended to support tables (#hash()), numbers (0), symbols (||) etc. Then it would have more consistency as a concept.
I've run into issues with nil the symbol vs nil the value. It's caused some issues when I wanted to use arc's sym type to represent variable names in a class compiler project I'm working on. If the variable's name is nil, all kinds of things stop working, and I've had to resort to calling some scheme functions directly to get proper handling of symbols.
I wish that 'nil and nil could be kept somewhat separate, with the first being just a symbol, and the second being equivalent to the base value for all data structures. Otherwise the symbol type is broken and inconsistent, since you cannot accurately (or at least completely) represent code as data.
However, I can easily define nil so that it's type is 'cons, but it would throw an error if you try to assign to the car or cdr. That may break code that assumes that 'cons != nil though.
Actually, I could represent nil as an actual 'cons cell, so that assigning to the car or cdr would work. Crazy? Probably. Especially since nil is a singleton and you can't create more nil's, so it would be a global change.
Right now, nil does have a type of 'sym, but it seems weird to treat it mostly like an empty cons cell, but not have it's type be 'cons. So I figured I could play around with it and try giving it a type of 'cons and see how badly it breaks stuff.
"Actually, I could represent nil as an actual 'cons cell, so that assigning to the car or cdr would work. Crazy?"
That's a bit crazy. :)
PicoLisp does something reminiscent. Every one of its data structures (numbers, symbols, nil and conses) is implemented using the low-level cons cell structure (i.e. a pair of machine words). [1] They talk about nil's representation fulfilling its dual nature as both a symbol whose value is nil and a list whose car and cdr are nil; both the symbol predicate and the list predicate return true when applied to nil:
: (sym? NIL)
-> T
: (lst? NIL)
-> T
I'm not sure that they let nil's car and cdr be assignable though, because "NIL is a special symbol which exists exactly once in the whole system." [2]
My code has a lot of [string:or _ "nil"] to handle that kind of stuff. ^_^ I've just kinda accepted it ever since http://arclanguage.org/item?id=10793.
I do 'string lists into strings a lot, but that would continue to work if 'nil and '() were separate values.
If the variable's name is nil, all kinds of things stop working
Can you give an example? Are you trying to use nil as a variable name in Arc, or simply to have a data structure representing variables where some of those variables are named nil?
I'm creating a compiler for a class, and I've been representing variable names in the ast with symbols. I could use strings instead, and may end up doing so, but symbols seemed a more elegant solution.
So nil would be an ubertype that basically just means "empty"? One issue with that is (is 0 nil) would be t... I think it's useful to not always treat 0 as the same as nil.
Not sure about empty tables, though... maybe it would be fine to treat those as nil. We already treat an empty list as nil, after all.
Actually, what you're suggesting is very similar to how languages like JavaScript and Python do it, with 0 null undefined NaN "" etc. being falsy. So in JS, you can do this:
var foo = 0;
if (foo) {} // will not be evaluated
foo = "";
if (foo) {} // will not be evaluated
foo = NaN;
if (foo) {} // will not be evaluated
On the other hand, both those languages support actual Booleans, and they treat an empty array as true.