Re: 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