A null string will evaluate as True, but is equal to Nil and empty list:
(if "" 1) -> 1
(if (list) 1) -> ()
(if Nil 1) -> Nil
(= "" Nil) -> True
(= "" (list)) -> True
(= (list) Nil) -> Nil
(= (list) "") -> True
(= Nil "") -> True
(= Nil (list)) -> True
I think it would be more useful if a null string evaluated as Nil so you could write code such as:
(if (not "") "empty string") -> "empty string"
Also Nil == empty list, but empty list != Nil
Thanks to this and another ticket, I realized tonight that Nil
== 'Nil
!= "Nil"
. I guess that's correct, but ick.
While I completely agree with this, I worry about backwards compatibility. I'm not sure if there is any existing core code that expects an empty string to be non-nil.
Might be worth making the change and seeing what breaks.
BTW: How does Lisp/Scheme/etc. handle this?
OK - according to http://rosettacode.org/wiki/Empty_string#Common_Lisp empty strings are true in Common Lisp.. I've just downloaded a common lisp for testing and get:
* (if "" 1) -> 1
* (if (list) 1) -> NIL
* (if nil 1) -> NIL
* (eq "" nil) -> NIL
* (eq "" (list)) -> NIL
* (eq (list) nil) -> T
* (eq (list) "") -> NIL
* (eq nil "") -> NIL
* (eq nil (list)) -> T
We can always check for empty string with (= var "")
- as long as all the eq/= operators behave in a sane way!
I'm not sure if there is any existing core code that expects an empty string to be non-nil.
hmm... There are several missions which set the SuccessMsg text to an empty string in order to suppress the default "Mission complete!" text (though that is dealt with in cpp code not TLisp).
I don't understand a lot of what's above but the wingman or auton code uses "" to nullify some of the plyMessage/objSendMessage messages on the screen (can't remember which ones).
(not)
, (if)
, (and)
, and (or)
already consider empty strings to be true. So the proposed change - besides fixing the bug with (eq (list) nil)
- is to make (eq)
/(=)
not consider empty strings equal to nil or empty lists.
I did some regex searches of the core and registered extensions for cases where something is checked for equality with nil, such as [(][eE][qQ]\s+[\x20-\x7E]+\s+[nN][iI][lL]\s*[)]
. I only found one, which makes sense, since (not)
is a simpler way to do the same thing, or should be. There's no easy way to detect checking variables or function calls for equality when one could be nil and the other could be empty string, but it seems unlikely any code does this and relies on the current behavior.
Turns out (=)
and (!=)
consider empty strings equal to nil or empty lists, but (eq)
and (neq)
do not. So only the new ones need to be fixed to match Common Lisp in those cases. But all four fail when the first argument is an empty list and the second is nil.
Also, when there are more than two arguments, it appears each one is compared to the one before it, so:
(eq nil nil (list) (list)) -> True
(eq nil (list) nil (list)) -> Nil
OK, I've made the following changes (in 1.8 Alpha 3):
(eq (list) nil)
now returns True, same as(eq nil (list))
.(= "" nil)
now returns Nil, which is consistent witheq
.- I added all of the expressions @giantcabbage tried in CommonLisp to the unit test, and they all passed.
- I checked the C++ code and it will still handle the cases where we null out some messages (e.g., "Mission complete" and objSendMessage. In those cases, we check for blank strings.