Numbers in Kawa (was: GSOC | Extending Common Lisp support)
Jamison Hope
jrh@theptrgroup.com
Fri Apr 27 21:05:00 GMT 2012
On Apr 27, 2012, at 12:24 PM, Helmut Eller wrote:
>>> It's also in line with tradition to
>>> emit a warning at compile time but still generate code that will
>>> raise
>>> an runtime error. IMO Kawa acts quite gracefully here.
>>>
>>> A different story is this example:
>>>
>>> (define (test)
>>> (let ((x :: int 10.5))
>>> (* x x)))
>>>
>>> Kawa returns 110 which seems quite wrong.
>>
>> The difference here with int instead of integer is of course due to
>> how coercions to primitive types are handled [and (*:intValue 110.25)
>> returns 110]. It does seem like things should be more consistent
>> between primitive and Object numbers.
>>
>> But I don't really see a return value of 110 as being wrong, per
>> se: the
>> program itself is flawed, since 10.5 is not an int, so we're
>> already in
>> nasal demon territory, but 110 is the nearest int to (* 10.5 10.5),
>> so
>> the result seems fairly gracious. What would you have expected?
>
> I'd expect a compile time warning and an error at runtime. Automagic
> coercion from float to int surprises me. E.g. (string-ref "abc" 0.6)
> should be an error instead of returning #\a or #\b.
That does seem erroneous. I feel like (string-ref "abc" 1.0) should
work,
though [with a warning], so perhaps it should be an error iff the float
isn't already integer-valued?
> One could also argue that either 11 or 10 is the nearest integer to
> 10.5
> so the result should either be 121 or 100. 110 is quite odd, as the
> coercion happens after the multiplication, but the type declaration is
> lexically before the multiplication.
That's true. It's not obvious that (let ((x :: int 10.5)) (* x x)) is
different from (let ((x (as int 10.5))) (* x x)). Both issue a warning
"saw float where int expected" but the latter returns 100 because it
actually performs int multiplication.
>> I will note that the compiler has taken the extra step to infer
>> that if
>> x is an int, then (* x x) will be an int, too. That would not be a
>> valid
>> inference in Common Lisp.
>
> AFAIU, int in Kawa is intentionally not the same as (signed-byte 32)
> in
> CL. It's a different type so that operations like +, * etc. can have
> "methods" that wrap around. I think the boxed representation of an
> int
> is java.lang.Integer and the boxed representation of (unsigned-byte
> 32)
> would be something like Fixnum or Bignum. The trouble is that
> automatic
> coercions from ints to other number types aren't that well specified.
Worse, the numeric conversions aren't consistent. IMHO if you can
convert
to an int, you should be able to convert to an integer, too.
#|kawa:1|# (as int 10.0)
/dev/stdin:1:9: warning - saw float where int expected
10
#|kawa:2|# (integer? 10.0)
#t
#|kawa:3|# (as integer 10.0)
/dev/stdin:3:13: warning - type DFloNum is incompatible with required
type integer
/dev/stdin:3:1: warning - cannot convert literal (of type
gnu.math.DFloNum) to Type integer
Value '10.0' has wrong type (DFloNum) (expected: integer)
at gnu.kawa.lispexpr.LangObjType.coerceIntNum(LangObjType.java:340)
at atInteractiveLevel$3.run(stdin:3)
at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:299)
at gnu.expr.ModuleExp.evalModule(ModuleExp.java:200)
at kawa.Shell.run(Shell.java:279)
at kawa.Shell.run(Shell.java:194)
at kawa.Shell.run(Shell.java:175)
at kawa.repl.main(repl.java:884)
Perhaps if DFloNum (or a superclass?) had a method like:
> public boolean isInexactInteger()
> {
> return (0.0 == Math.IEEEremainder(doubleValue(), 1.0));
> }
and IntNum's asIntNumOrNull(Object value) included a clause
> if (value instanceof DFloNum && ((DFloNum)value).isInexactInteger())
> return IntNum.make(((DFloNum) value).longValue());
[time passes...]
That seems to work:
#|kawa:1|# (as int 10.0)
/dev/stdin:1:9: warning - saw float where int expected
10
#|kawa:2|# (as integer 10.0)
/dev/stdin:2:13: warning - type DFloNum is incompatible with required
type integer
10
#|kawa:3|# (let ((x ::integer 10.0)) (* x x))
/dev/stdin:3:20: warning - type DFloNum is incompatible with required
type integer
100
#|kawa:4|# (let ((x ::integer 10.5)) (* x x))
/dev/stdin:4:20: warning - type DFloNum is incompatible with required
type integer
/dev/stdin:4:27: warning - cannot convert literal (of type
gnu.math.DFloNum) to Type integer
Value '110.25' has wrong type (DFloNum) (expected: integer)
at gnu.kawa.lispexpr.LangObjType.coerceIntNum(LangObjType.java:340)
at atInteractiveLevel$4.run(stdin:4)
at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:299)
at gnu.expr.ModuleExp.evalModule(ModuleExp.java:200)
at kawa.Shell.run(Shell.java:279)
at kawa.Shell.run(Shell.java:194)
at kawa.Shell.run(Shell.java:175)
at kawa.repl.main(repl.java:884)
But that still won't work with doubles or Doubles, so perhaps
the IEEEremainder test should be a static method of IntNum,
like
> public static boolean isIntegerValued(Number value)
> {
> return (0.0 == Math.IEEEremainder(value.doubleValue(), 1.0));
> }
and the test in asIntNumOrNull
> if (value instanceof Number
> && (value instanceof Integer || ...
> isIntegerValued((Number)value)))
Sorry for hijacking your thread, Charlie.
-J
--
Jamison Hope
The PTR Group
www.theptrgroup.com
More information about the Kawa
mailing list