But why should an empty list be falsy? An empty list can be as valid a form of list as a non-empty one. It also seems to me that an empty list shouldn't be nil, since to me, nil should mean "undefined", and an empty list is well defined as an empty list.
Would disambiguation here really make Arc programs less terse? Is that a decision that should be enforced by the language or left to the author?
In my example above, making an empty list truthy would cause this change:
(def map1 (f xs)
"Returns a list containing the result of function 'f' applied to every element of 'xs'."
- (if xs
+ (if (~empty? xs)
(cons (f car.xs)
(map1 f cdr.xs))))
We can argue how important this is, but disambiguation does definitely make Arc programs less terse.
- the assumption baked into this argument is that cdr of an empty list returns an empty list. Switching nil to #f and letting empty list be truthy avoids this problem.
- Good names are important. ~empty? isn't really a fair characterization. Lumen uses (some? xs). There is also another way: Update `no` to be (or (is x nil) (empty x)), and then use (~no xs).
For what it's worth, my approach here is pattern-matching. In Lathe Comforts for Racket I implement a macro `expect` which expands to Racket's `match` like so:
If Arc came with a similar pattern-matching DSL and `expect`, we could write this:
(def map1 (f xs)
(expect xs (cons x xs) ()
(cons (f x) (map1 f xs))))
The line "expect xs (cons x xs) ()" conveys "If xs isn't a cons cell, finish with an empty list. Otherwise, proceed with x and xs bound to its car and cdr."
I agree; an empty list is a value. And when you consider interop with other langs, they will infer it to be some object too, where predicates will see it as a value not the lack of one.