AFAICS :t is of class clojure.lang.Keyword and when the Reflector
searches for the right constructor to invoke it can not find one in
class java.io.PrintWriter whose second parameter is of type
clojure.lang.Keyword. Note that this also affects (. Boolean TRUE) which
is mapped to :t as well.
Reflector.isCongruent() checks the parameters against the supplied
arguments and in case of a boolean parameter it checks whether the
argument is null or of class Boolean. Adding a check arg == RT.T (which
is the internal representation of :t) might fix the problem I think (and
also for overloaded methods which contain boolean parameters).
Cheers,
Toralf
However the current state with regards to :t seems somewhat inconsistent
to me. It is not possible to do (new Boolean :t) but something like (.
(new javax.swing.JFrame "Test") (setVisible :t)) is ok, (. Boolean
(valueOf :t)) again is not.
Also note that (new Boolean nil) throws an exception:
user=> (new Boolean nil)
clojure.lang.Compiler$CompilerException: REPL:1: null
at clojure.lang.Compiler.analyzeSeq(Unknown Source)
at clojure.lang.Compiler.analyze(Unknown Source)
at clojure.lang.Compiler.analyze(Unknown Source)
at clojure.lang.Compiler.eval(Unknown Source)
at clojure.lang.Repl.main(Unknown Source)
Caused by: java.lang.NullPointerException
at java.lang.Class.isAssignableFrom(Native Method)
at clojure.lang.Reflector.paramArgTypeMatch(Unknown Source)
at clojure.lang.Compiler.getMatchingParams(Unknown Source)
at clojure.lang.Compiler$NewExpr.<init>(Unknown Source)
at clojure.lang.Compiler$NewExpr$Parser.parse(Unknown Source)
... 5 more
I think the patch Chas posted is still valid because you can then use
nil or :t without type casts. If the NPE in Reflector.paramArgTypeMatch
is fixed, e.g. by putting "if(argType == null) return true;" in the
first line (I think this is justified because null is a valid member of
every type)) then one can do:
user=> (new Boolean :t)
true
user=> (new Boolean nil)
false
user=> (. Boolean (valueOf :t))
true
user=> (. Boolean (valueOf nil))
nil
user=> (new Boolean (boolean :t))
true
user=> (new Boolean (boolean nil))
false
user=> (. Boolean (valueOf (boolean :t)))
true
user=> (. Boolean (valueOf (boolean nil)))
nil
user=> (. (new javax.swing.JFrame "Test") (setVisible :t))
nil
user=> (. (new javax.swing.JFrame "Test") (setVisible nil))
nil
Cheers,
Toralf
Sorry for jumping the gun on this subject - I wasn't aware that this
part is being revised.
> 3) Implicit conversion to boolean is going away.
>
> The need for #3 is demonstrated by your (new Boolean nil) call - there
> is no reason it should resolve to the boolean overload versus the
> String overload.
Hmm. I confess I'm a bit puzzled by this whole boolean thing. I like the
Scheme way of having #t for true and #f for false. Since in Clojure nil
is used to represent false and null it seems to me that treating nil as
a boolean is only logical ;-) So expecting (new Boolean nil) to resolve
to the boolean overload follows directly from nil denoting boolean
falsity. On the other hand the need to typecast nil to boolean looks
weird to me. I wonder what's the advantage of having nil meaning false
and null? Wouldn't it be better to copy Scheme and having a dedicated
false value, e.g. :f which leaves nil for null?
>
> I've made great progress on #2 - now in all the code generated by
> compiling boot.clj there are only 3 reflective calls! And I don't
> consider boot.clj to be overburdened with type hints.
>
Excellent work! I appreciate it very much that you put so much energy
into Clojure. It shows.
Thanks,
Toralf
>> 3) Implicit conversion to boolean is going away.
>>
>> The need for #3 is demonstrated by your (new Boolean nil) call - there
>> is no reason it should resolve to the boolean overload versus the
>> String overload.
>
> Hmm. I confess I'm a bit puzzled by this whole boolean thing. I like the
> Scheme way of having #t for true and #f for false. Since in Clojure nil
> is used to represent false and null it seems to me that treating nil as
> a boolean is only logical ;-) So expecting (new Boolean nil) to resolve
> to the boolean overload follows directly from nil denoting boolean
> falsity. On the other hand the need to typecast nil to boolean looks
> weird to me. I wonder what's the advantage of having nil meaning false
> and null? Wouldn't it be better to copy Scheme and having a dedicated
> false value, e.g. :f which leaves nil for null?
Yes, sometimes I also wonder if having 'nil' for both false and null is a
good thing. Could you explain more?
Maybe it would make sense to change ':t' to 'true'. It goes well with
'nil' and you free up keyword ':t'.
I am using newLISP for some of my projects, so feel free to see:
http://www.newlisp.org/
http://www.newlisp.org/index.cgi?Documentation
http://www.newlisp.org/downloads/newlisp_manual.html
Happy coding, Frantisek :-)
> Wouldn't it be better to copy Scheme and having a dedicated
> false value, e.g. :f which leaves nil for null?
I very strongly agree. It took Scheme until R4RS to get #f and ()
completely disentangled, and everyone in the community agrees that it
was a Good Thing. A few adjustments had to be made: for example,
assoc now returns a cons if it finds something or #f if it finds
nothing, since people were treating the result as boolean quite
frequently.
Similarly, it took the C lineage until Java to get false and 0 and
null disentangled, and I don't know any Java programmer who thinks
that was a mistake. It catches a huge number of errors right away.
(Java and Scheme differ, though, in that anything not #f is still true
in Scheme, whereas it's an error to use anything other than true or
false in conditional contexts in Java.)
Since Clojure does not have to be backward compatible with anything, it should
separate the empty sequence from falsity right away.
> > I've made great progress on #2 - now in all the code generated by
> > compiling boot.clj there are only 3 reflective calls! And I don't
> > consider boot.clj to be overburdened with type hints.
Which ones are they?
> Excellent work! I appreciate it very much that you put so much energy
> into Clojure. It shows.
I also agree very strongly with that.
--
GMail doesn't have rotating .sigs, but you can see mine at
http://www.ccil.org/~cowan/signatures
Agreed. I certainly didn't want to appear fanatic about this topic and I
see the advantages of nil with regards to sequences and conditional
tests. I was just confused by the new requirement to explicitly typecast
(boolean nil) when interacting with the host. This however seems
inevitable because of nil's dual nature which differs from Java's
treatment of null and false. This in turn lead me to look at Scheme's #f
but yes, I agree that exclusively using a single value for logical
falsity would cost expressiveness and given the choice I also prefer a
more expressive Clojure.
So, withdrawing my question about copying Scheme in this area, I would
like to ask another (possibly foolish) one instead--would it make sense
to introduce a second value for logical falsity, so having :t/:f and
being able to use them directly as booleans with Java and still having
nil as it is now?
Thanks,
Toralf
> - They will represent Boolean.TRUE and Boolean.FALSE, and will have
> type Boolean
>
> - only Booleans will implicitly convert to match boolean arguments in
> calls to Java, as now
>
> - Conditionals will test for nil/non-nil, as now
>
> - boolean returns from Java will be converted into nil/:t, as now
>
> - the boolean coercion operator will still be available to transform
> Clojure logical values to Java logical values, as now
Then I suggest "javatrue" and "javafalse", or any shortening or
punctuating of that (jtrue, java-true, whatever) that suits you.
I would also suggest a convenience function "javatruth" that maps
nil/non-nil to javafalse and javatrue, so you can say things like
(java-function (javatruth (another-java-function)))
where java-function expects a Boolean and another-java-function returns one.
Not to question your decision but what do you estimate how big the
performance impact would be?
> I personally would have no problem with them being called true/false,
> TRUE/FALSE etc, but I'm comfortable with the meaning of conditional
> tests being nil-based and not boolean based. I'm concerned that
> newcomers will have incorrect suppositions about the behavior of (if
> FALSE ...).
I would certainly have tried that. :D
Now how to prevent this I don't know. It has to be made clear in the
documentation what TRUE and FALSE are meant for and using non-obvious
names for TRUE and FALSE would probably cause users to read the docs
after they tried the usual suspects but no one likes non-obvious names
for TRUE and FALSE since they are meant to be a convenience.
Thanks,
Toralf
Just my 2 cents: I like Ruby's "true", "false", and "nil" keywords.
Ruby treats both "false" and "nil" as false in boolean expressions;
anything else is considered true. It's very clear when you're
returning a boolean value, but you still get the convenience of using
nil in boolean expressions.
Having true/false/nil map exactly to Java true/false/null (boxed if
necessary) would avoid confusion over the type conversions.
-Stuart
I understand that you (along with many lispers) thing about this
differently. I just want to explain the thought process which
justifies #f, at least for me.
As a schemer, I always saw #f and null (or '()) as two different types
of nothing. #f was really nothing, and null was the list containing no
elements. For example, if I have a string->numbers which converts "1,
2, 3" to (1 2 3), a return value of #f would mean "That string is not
parsable as a list of numbers", while null would mean "I parsed that
string as containing no numbers". I feel uncomfortable when 'nothing'
is indistinguishable from 'a list containing no elements'. A return
type of 'something or nothing' makes sense to me. 'A list containing
elements or a list containing no elements' makes sense to me. 'A list
or nothing' makes sense to me (this cannot be done with nil, as lists
and nothing overlap). 'something or a list containing no elements'
seems like a stretch of logic.
Just my two pieces of sense,
Henk
> I'm not sure we're far off here although the names are different.
What follows should go on the website.