Wandering through the libraries

Mark S. Miller markm@erights.org
Wed, 14 Oct 1998 23:32:07 -0700


At 12:24 AM 10/12/98 -0700, Ka-Ping Yee wrote:
>What i meant was, i don't see why "/" requires a name like "approxDivide"
>when "-" is not "approxSubtract", "*" is not "approxMultiply" etc.  >Whenever
>the result is a floating-point number, we accept the limitations of
>floating-point and live with them...

>> There is no semantics in common between what Java's "/" does on integers
>> and what it does on floating point numbers.  My choice: "/" is shorthand
>> for "approxDivide", and it means "the IEEE double precision floating point
>> number closest (by round to nearest) to the quotient of the two numbers,
>> whatever they are".
>
>This is good, and i think it is reasonable and consistent to simply
>call this "divide" -- given that, for example, even "-" only gives
>you "the IEEE double-precision floating-point number closest to the
>difference of the two numbers" (e.g. 1e45 - 1e-45).


Because when the inputs to "-" or "subtract" are nice exact integers, then
so is the output, but the output of "/" or "approxDivide" is a floating
point number for both common kinds of input.  This is the one case where I
surprising give inexact results even from exact (integer) inputs, so I gave
the operation this name as a warning.


>Hmm.  So are you saying, in E, 5 / 4 == 5/4.0 == 5.0/4.0 == 1.25, and
>5 _/ 4 == 5.0 _/ 4 == 5.0 _/ 4.0 == 1?

Yes.


>I *like* being able to tell the type of the result from the operator
>rather than the operands.  I think this was a good move.

Thanks.  But I expect much grief from this.


>> Let's say that compareTo always returned -1, 0, 1, or NaN.  What would you
>> have "x <= y" expand to?
>
>x compareTo(y) <= 0, i suppose.  But the "<=" here means a
>primitive "<=" in the kernel, not a method call.  I mean,
>it's got to bottom out somewhere.  What i was trying to say
>is that i don't see what the extra indirection buys you since
>doubles are a built-in type anyway.  Surely you don't expect
>people to override the meanings of "isLTZero" and friends?

I'm content to have doubles be a built-in data type, but I recoil in horror
at having "<= 0" be one of the few Kernel E syntactic constructs (or
"special form" for Schemers).  By having *all* interaction between Kernel E
and the data types be by messages, we allow the user to virtualize any data
type (in the sense that OSes virtualize physical memory).  Smalltalk came
close to following this rule perfectly, Act 1, Act 2, and Joule did follow
it perfectly.  I don't know yet about E, but I'd certainly like to.

So getting back to the original problem, If I expanded "x <= y" to 

	x compareTo(y) <= 0

then, what should I expand that into?


>> >I think calling atan2, min, max as methods on a double looks
>> >pretty weird... min and max in particular are probably best
>> >provided with a tuple or an unlimited number of arguments,
>> >aren't they?
>> 
>> Ok, I buy it for min and max.  What's your beef with atan2?  How about cos?
>
>All the one-argument functions like cos and sqrt do make sense
>in a reverse-Polish-esque sort of way.  I'm not screaming to
>have the "atan2" method removed (or even the "min" or "max" methods
>removed for that matter)... i just hope that "x atan2(y)" is not
>the *only* way to call atan2, as it will look a little strange to
>some people.  

Is this TOOWTDI Ping speaking??  The one who (thankfully) argued me out of
having both "size" and "length" in the language?


>Presumably there is some sort of math package
>with all kinds of stuff in it, where min, max, maybe even
>mean, mode, median, stdev -- whatever -- would go...

Given http://java.sun.com/products/jdk/1.2/docs/api/java/lang/Math.html
should I just drop these methods from double?



>i suppose in that case "x min= y"
>would mean something.  But i don't know if it's worth extending
>the (operator)= syntax to (method)= just for that.

No.


>... are there readily-available
>constants for the positive and negative double-precision
>floating-point numbers of greatest and smallest magnitude,
>and +/- infinity for big integers?

E's integer's don't have an infinity or MAX_INT, but IEEE floating point
does, which in E is currently written

	load:java.lang.Double POSITIVE_INFINITY 

to obtain the static "POSITIVE_INFINITY" member of java's Double class.
How we write this in the future depends on how we resolve the java class
naming issues I raised earlier.

Now, I suppose, you're all going to want to know what happens when you
compare (or otherwise mix) an integer and a double?  Well, after beating
y'all over the head with my "If it's accepted by both languages, it must
have the same meaning" principle, how can I admit what I've done?  

When you mix, the right operand is coerced to agree with the left operand,
since, after all, it's the left operand that's being asked to perform the
operation.  In general, an important property of capability programming is
that, for example, in

	bar := foo msg(arg)

the normal assumption (unless stated otherwise) is that foo "stands behind"
the correctness and trustworthiness of bar.  In other words, if the
returned bar does not correctly obey the Bar contract, then, unless the Foo
contract for "msg" disclaims this, this implies that foo has not correctly
implemented the Foo contract.  This means we can normally trust bar as much
as we trust foo (however much that is).

Object languages from Smalltalk to C++ (but curiously not Java) provide for
user-defined numeric types.  These could represent rationals, polynomials,
surreal numbers, or whatever.  All the above compatibly include normal
numbers as a degenerate case of their own theory, so you'd like such
user-defined numbers to be able to interoperate smoothly with integers.
However, user-defined numbers mean numbers that shouldn't necessarily be
trusted.  To motivate this, can you spot the three vulnerabilities in the
following minimal bank (that appears as page 11 of
http://erights.org/doc/e/etalk.pdf )?


define MintMaker() {
    define [sealer,unsealer] := sealingPair()
    define mint {
        to makePurse(balance) {
            define decr(delta ? delta <= balance) {
			balance -= delta
            }
            define purse {
                to getBalance { balance }
                to getDecr { sealer seal(decr) }
                to deposit(amount, src) {
                    unsealer unseal(src getDecr)(amount)
                    balance += amount
                }
            }
        }
    }
}

Don't look until you've thought about it, but the following version cleans
up two of these:


define MintMaker() {
    define [sealer,unsealer] := sealingPair()
    define mint {
        to makePurse(balance) {
            define decr(delta ? (define newBal := balance - delta) >= 0) {
                balance := newBal
            }
            define purse {
                to getBalance { balance }
                to getDecr { sealer seal(decr) }
                to deposit(amount, src) {
                    unsealer unseal(src getDecr)(amount)
                    balance += amount
                }
            }
        }
    }
}



>Are you going to be using the set of allowed characters in URLs
>from RFC 1738 to define the syntax for what comes after the ":"?
>
>I wonder if this will be the first time an RFC becomes part of
>a programming language syntax definition. :)

Actually, I'm using the definition in appendix A of
http://www.ics.uci.edu/pub/ietf/uri/rfc2396.txt but advice on the matter
would be appreciated.


>> within the curlies, "return" is bound to an Ejector.  What do you think
>> about renaming Ejector "EscapeHatch"?
>
>I like that name.  Both names are okay with me, actually, though i
>suppose it's easier to guess what "EscapeHatch" is related to since
>it contains the word "escape".

Ok, it's hereby specified as EscapeHatch (with the implementation to catch
up later).


>Hmmrrm.  Kind of wordy.  I realized just now -- i suppose you could write
>
>    if ([platform] asSet <= ["windows", "winnt", "win95"]) {
>
>(assuming the second argument gets coerced) though i'm not sure how
>apparent the meaning is there.

Sorry, you'll get the wrong coercion.  The left arg is a Mapping, and the
right arg will get coerced by asMapping rather than asSet.  You will be
testing if platform is a 0, 1, or 2.  I agree this is confusing and
accident prone -- suggestions solicited.  In the meantime, put "asSet" on
the right as well.


>Isn't there a simple linear-searching "contains" method for sequences?
>I think that would be generally useful -- for small sets like this,
>it is probably too much effort to hash the keys and convert the thing
>to a mapping before doing a lookup.  This:
>
>   if ["windows", "winnt", "win95"] contains(platform) {
>
>is about as good as you can expect it to read, if the element you're
>testing comes after the set (and i guess you're not going to be able
>to put it in front of the set without special keywords like "in").

Given the newly specified (and unimplemented) mapping pattern, the
following works:

    if (["windows", "winnt", "win95"] asMapping =~ [_ => ==platform] | _) {

which reads "if the specimen contains at least one key-value association
whose value is the same as platform, ..."

It is less clear, less efficient, and takes more characters, but might
stimulate more dinner time conversation.

 
>> >Also, Python permits chaining comparison operators so you
>> >can do a quick-n-easy range test:
>> >
>> >    if 3 < x < y <= 8:
>> >        ...
>[Good explanation of what Python does]

Under consideration.  This one will have to stew for a while.


>I was going to suggest in an earlier message that you consider the
>possibility of "and", "or", "not" operators, ... i was afraid
>you would be too reluctant to rename ...
>Anyway, did i guess right?

Yes.

 
>Well... after all, since it's called a mapping... how about "maps"?
>
>    if mapping maps key {
>        value := mapping[key]
>        ...
>    }

As mentioned out-of-order earlier, I accept "maps", but what should I do
about "containsKey"?  From this group, I know I won't get any resistance to
dropping it...

In any case, I don't have infix verb identifiers.  The above would be written

    if (mapping maps(key)) {
        value := mapping[key]
        ...
    }

Though if you intended 

    if (mapping maps(key)) {
        define value := mapping[key]
        ...
    }

you should consider instead

    if (mapping =~ [==key => value] | _) {
        ...
    }

which reads "if the mapping contains at least one key-value association
whose key is the same as 'key', bind the value to 'value' and ...".  In
this case, it's as clear, eventually probably more efficient (one hash
lookup instead of two), and takes fewer characters.  For this example,
however, I take no position on the dinner time conversation controversy.


>You know... the ellipsis has shown up so many times in my examples
>now, and i bet it will occur in print, too, that i wonder if maybe it
>isn't better to just stick with the backslash for line-continuation.
>It's a familiar beast.  I also saw the ".." operator in your grammar
>table somewhere... what about the possibility of mistyping or
>confusing ".." with "..."?

Yeah, I've always thought the line-continuation-"\" was ugly as sin, and I
thought the line-continuation-"..." would be happy, but I think I'll revert
to convention here.  One less thing to explain and remember!


	Jeez, how do the other language designers do it?  
	This is a lot of work!
	--MarkM