extracting namespace from a property

classic Classic list List threaded Threaded
27 messages Options
12
Reply | Threaded
Open this post in threaded view
|

extracting namespace from a property

Yuh-Ruey Chen
Is there any standard way to ascertain whether a property of a non-XML
object is qualified by a namespace? This can be useful when enumerating
over an object. I've looked at chapters 4 (fundamental concepts), 12
(namespaces), 14 (expressions), and 19 (native objects) and didn't find
a clear answer.

In chapter 4, both readReference and writeReference delegate the
responsibility of actually reading/setting values in
traits.intrinsic::read and traits.intrinsic::write, respectively. Both
of those are not defined anywhere, so I don't know how QNames are stored
as properties.

In JS1.7, |obj.ns::prop| is equivalent to |obj['ns_uri::prop']| where
ns's URI is 'ns_uri'. That allows something like this:

for (let qk in o1) {
    let ns_uri, k;
    let t = qk.split('::');
    if (t.length == 1) {
        k = t[0];
    } else {
        [ns_uri, k] = t;
    }
    print(ns_uri + '::' + k);
}

Not only is this possibly non-standard, it's cumbersome and does not get
the actual Namespace object.

Chapter 12 doesn't mention this issue at all.

Chapter 14 has production rules for qualifiers, but the evaluation of
them makes use of internal functions and the internal Name datatype from
chapter 4.

Chapter 19 doesn't mention this issue at all.

The meta-objects proposal has namespace() has a method of NominalType,
but properties aren't types.

-Yuh-Ruey

Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Brendan Eich-2
You are right on top of this one. I've proposed a Name object (Jeff  
suggested not using E4X's QName class) to reflect qualified  
identifiers. This is necessary because what you propose is ambiguous  
with respect to existing objects having properties with "::" in their  
names (which has always been allowed; these would be accessed via []  
of course).

Here's the guts of the proposal:

Problem Statement

The for-in loop combined with namespace-qualified names means we need  
a Name class reflecting namespace-qualified identifiers of enumerable  
properties:

let o = {x:1, 2:"two", '@funny':3};
for (let i in o)
   print(i, typeof i);

Per ES1-3, and required for backward compatibility, this script  
should print

x string
2 string
@funny string

Consider this code:

namespace n;
let p = {n::m: 42};
for (let i in p)
   print(i, typeof i);

It would be impossible to index p.n::m by p[i] if i were “m” (if the  
output of this loop were m string). We need something like the QName  
class from E4X (ECMA-357). Jeff proposes we call it Name.
Proposed Solution

The Name class is defined as follows:

class Name {
   function Name(ns : Namespace, id : string)
     : qualifier = ns
     , identifier = id
   {
   }


   intrinsic static function invoke(ns : Namespace, id : string) : Name
     new Name(ns, id);

   public const qualifier  : Namespace,
                identifier : string;
}

An enumerating for-in loop or comprehension must reflect an  
enumerable property name as a Name instance if the property was  
defined with an explicit or default namespace other than public.  
Otherwise, with no or a public namespace, enumerating for-in reflects  
the name as a string per ES1-3 and existing JS implementations.

A property access of the form o[n] where n is of type Name looks for  
a binding in the namespace n.qualifier whose unqualified name is  
n.identifier.

----

The only detailing that remains is what Name.prototype.toString  
should do. Current thinking is something like "[namespace public  
n]::m" for the example in the Problem Statement. Comments welcome.

/be

Reply | Threaded
Open this post in threaded view
|

RE: extracting namespace from a property

Jeff Dyer
Brendan Eich wrote:

> Jeff proposes we call it Name.

Since in ES4 all names are qualified names, why burden the language with
a historical distinction that no longer exists?

Jd

> -----Original Message-----
> From: [hidden email]
[mailto:[hidden email]]

> On Behalf Of Brendan Eich
> Sent: Monday, February 05, 2007 2:33 PM
> To: Yuh-Ruey Chen
> Cc: [hidden email]
> Subject: Re: extracting namespace from a property
>
> You are right on top of this one. I've proposed a Name object (Jeff
> suggested not using E4X's QName class) to reflect qualified
> identifiers. This is necessary because what you propose is ambiguous
> with respect to existing objects having properties with "::" in their
> names (which has always been allowed; these would be accessed via []
> of course).
>
> Here's the guts of the proposal:
>
> Problem Statement
>
> The for-in loop combined with namespace-qualified names means we need
> a Name class reflecting namespace-qualified identifiers of enumerable
> properties:
>
> let o = {x:1, 2:"two", '@funny':3};
> for (let i in o)
>    print(i, typeof i);
>
> Per ES1-3, and required for backward compatibility, this script
> should print
>
> x string
> 2 string
> @funny string
>
> Consider this code:
>
> namespace n;
> let p = {n::m: 42};
> for (let i in p)
>    print(i, typeof i);
>
> It would be impossible to index p.n::m by p[i] if i were "m" (if the
> output of this loop were m string). We need something like the QName
> class from E4X (ECMA-357). Jeff proposes we call it Name.
> Proposed Solution
>
> The Name class is defined as follows:
>
> class Name {
>    function Name(ns : Namespace, id : string)
>      : qualifier = ns
>      , identifier = id
>    {
>    }
>
>
>    intrinsic static function invoke(ns : Namespace, id : string) :
Name

>      new Name(ns, id);
>
>    public const qualifier  : Namespace,
>                 identifier : string;
> }
>
> An enumerating for-in loop or comprehension must reflect an
> enumerable property name as a Name instance if the property was
> defined with an explicit or default namespace other than public.
> Otherwise, with no or a public namespace, enumerating for-in reflects
> the name as a string per ES1-3 and existing JS implementations.
>
> A property access of the form o[n] where n is of type Name looks for
> a binding in the namespace n.qualifier whose unqualified name is
> n.identifier.
>
> ----
>
> The only detailing that remains is what Name.prototype.toString
> should do. Current thinking is something like "[namespace public
> n]::m" for the example in the Problem Statement. Comments welcome.
>
> /be
> _______________________________________________
> Es4-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es4-discuss


Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Yuh-Ruey Chen
In reply to this post by Brendan Eich-2
Brendan Eich wrote:
>    intrinsic static function invoke(ns : Namespace, id : string) : Name
>      new Name(ns, id);
>  

invoke is called when Name is called as a function, right? I've seen
this used before in Enumerator - just want to make sure.

> An enumerating for-in loop or comprehension must reflect an  
> enumerable property name as a Name instance if the property was  
> defined with an explicit or default namespace other than public.  
> Otherwise, with no or a public namespace, enumerating for-in reflects  
> the name as a string per ES1-3 and existing JS implementations.
>  

Although this helps, it would still be a hassle to have to check whether
an enumerated property n is a Name or String. I can't do n.identifier if
n is a String.

How about this: Each enumerated property n is always an instance of
Name. If n doesn't have a qualifier, n.qualifier would be null, while
n.identifier would be n's id. Thus, to get a property's id, n.identifier
would work regardless of whether n has a qualifier. typeof(Name) would
be "string". Name's prototype would be a String, so name instanceof
String is true. These two requirements should be enough to satisfy ES3
compatibility.

> The only detailing that remains is what Name.prototype.toString  
> should do. Current thinking is something like "[namespace public  
> n]::m" for the example in the Problem Statement. Comments welcome.
>
> /be
>  

First, if n.qualifier is null or undefined, toString() should just
return n.identifier. Second, it doesn't really matter to me how
n.qualifier would be represented in toString().

-Yuh-Ruey

Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Brendan Eich-2
On Feb 5, 2007, at 7:05 PM, Yuh-Ruey Chen wrote:

> Brendan Eich wrote:
>>    intrinsic static function invoke(ns : Namespace, id : string) :  
>> Name
>>      new Name(ns, id);
>>
>
> invoke is called when Name is called as a function, right?

Yes.

>> An enumerating for-in loop or comprehension must reflect an
>> enumerable property name as a Name instance if the property was
>> defined with an explicit or default namespace other than public.
>> Otherwise, with no or a public namespace, enumerating for-in reflects
>> the name as a string per ES1-3 and existing JS implementations.
>>
>
> Although this helps, it would still be a hassle to have to check  
> whether
> an enumerated property n is a Name or String. I can't do  
> n.identifier if
> n is a String.

You have to use 'switch type' or equivalent.

> How about this: Each enumerated property n is always an instance of
> Name. If n doesn't have a qualifier, n.qualifier would be null, while
> n.identifier would be n's id. Thus, to get a property's id,  
> n.identifier
> would work regardless of whether n has a qualifier. typeof(Name) would
> be "string". Name's prototype would be a String, so name instanceof
> String is true. These two requirements should be enough to satisfy ES3
> compatibility.

That's far from clear, because ES3 mandates a primitive string, not a  
String object. This will break any for-in loop that compares the  
iterating variable to a saved value of that same variable (since ==  
on Strings and other objects is identity, not string value comparison).

You make a good point about this identity being important:

   'foo' in obj === Name(null, 'foo') in obj

The cost of a Name being cons'ed for every iteration of for-in is  
also troubling, in optimization effort if not in runtime and space if  
one argues that it could be optimized away. SpiderMonkey stores 31-
bit int ids in tagged machine words, not as strings -- never mind as  
anything like Name instances.

/be

Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Peter Hall-4
It seems very counter-productive to introduce a new Name type. Surely
it can only reduce E4X integration with ES4?

Peter


On 2/6/07, Brendan Eich <[hidden email]> wrote:

> On Feb 5, 2007, at 7:05 PM, Yuh-Ruey Chen wrote:
>
> > Brendan Eich wrote:
> >>    intrinsic static function invoke(ns : Namespace, id : string) :
> >> Name
> >>      new Name(ns, id);
> >>
> >
> > invoke is called when Name is called as a function, right?
>
> Yes.
>
> >> An enumerating for-in loop or comprehension must reflect an
> >> enumerable property name as a Name instance if the property was
> >> defined with an explicit or default namespace other than public.
> >> Otherwise, with no or a public namespace, enumerating for-in reflects
> >> the name as a string per ES1-3 and existing JS implementations.
> >>
> >
> > Although this helps, it would still be a hassle to have to check
> > whether
> > an enumerated property n is a Name or String. I can't do
> > n.identifier if
> > n is a String.
>
> You have to use 'switch type' or equivalent.
>
> > How about this: Each enumerated property n is always an instance of
> > Name. If n doesn't have a qualifier, n.qualifier would be null, while
> > n.identifier would be n's id. Thus, to get a property's id,
> > n.identifier
> > would work regardless of whether n has a qualifier. typeof(Name) would
> > be "string". Name's prototype would be a String, so name instanceof
> > String is true. These two requirements should be enough to satisfy ES3
> > compatibility.
>
> That's far from clear, because ES3 mandates a primitive string, not a
> String object. This will break any for-in loop that compares the
> iterating variable to a saved value of that same variable (since ==
> on Strings and other objects is identity, not string value comparison).
>
> You make a good point about this identity being important:
>
>    'foo' in obj === Name(null, 'foo') in obj
>
> The cost of a Name being cons'ed for every iteration of for-in is
> also troubling, in optimization effort if not in runtime and space if
> one argues that it could be optimized away. SpiderMonkey stores 31-
> bit int ids in tagged machine words, not as strings -- never mind as
> anything like Name instances.
>
> /be
> _______________________________________________
> Es4-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es4-discuss
>

Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Jeff Walden-3
Peter Hall wrote:
> It seems very counter-productive to introduce a new Name type. Surely
> it can only reduce E4X integration with ES4?

It seems even worse to me to overload QName to apply both places, since QNames also have a prefix property which makes no sense here.

(Rant: it seems even worse to have namespaces in XML expose prefixes at all, but that ship *long* since sailed.  At the least, yay for E4X making <f:x xmlns:f="g"/> == <g:x xmlns:g="g"/>.)

Jeff

--
Rediscover the Web!
http://snurl.com/get_firefox

Reclaim Your Inbox!
http://snurl.com/get_thunderbird

Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Peter Hall-4
Ok... I didn't realise that. In ActionScript 3, QName has only
localName and uri...

What other implementations of E4X are there, and do they have a prefix
property for QName?

Peter


On 2/15/07, Jeff Walden <[hidden email]> wrote:

> Peter Hall wrote:
> > It seems very counter-productive to introduce a new Name type. Surely
> > it can only reduce E4X integration with ES4?
>
> It seems even worse to me to overload QName to apply both places, since QNames also have a prefix property which makes no sense here.
>
> (Rant: it seems even worse to have namespaces in XML expose prefixes at all, but that ship *long* since sailed.  At the least, yay for E4X making <f:x xmlns:f="g"/> == <g:x xmlns:g="g"/>.)
>
> Jeff
>
> --
> Rediscover the Web!
> http://snurl.com/get_firefox
>
> Reclaim Your Inbox!
> http://snurl.com/get_thunderbird
> _______________________________________________
> Es4-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es4-discuss
>

Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Jeff Walden-3
Er, oops.

There's no prefix property, but there *is* a [[Prefix]] internal property, which implementations may optionally preserve.  So if you use QName for |for (var qn in obj)| and preserve prefixes, you have to also manage some form of [[Prefix]].  The fact that it's not visible except by those methods which say they'll look at |name.[[Prefix]]| makes it slightly better, but only slightly, in my opinion.  It's still added complexity and conflation of the same concept in two different contexts, which is the primary argument I'd make against it.

Jeff

--
Rediscover the Web!
http://snurl.com/get_firefox

Reclaim Your Inbox!
http://snurl.com/get_thunderbird

Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Igor Bukanov-2
In reply to this post by Jeff Walden-3
> (Rant: it seems even worse to have namespaces in XML expose prefixes at all, but that ship *long* since sailed.  At the least, yay for E4X making <f:x xmlns:f="g"/> == <g:x xmlns:g="g"/>.)

Prefixes are optional E4X feature that an implementation may choose
not to support. They are only necessary to preserve the initial
prefixes in the serialized XML. So perhaps E4X namespace can subclass
the Name class?

Regards, Igor

Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Jeff Walden-3
Igor Bukanov wrote:
> Prefixes are optional E4X feature that an implementation may choose
> not to support.

Yeah, that's nice -- problem is that it really only works if everyone agrees not to expose prefixes or the spec mandates it, and since most other things preserve prefixes it's feature compat (everyone expects it or uses something else).  I like the effort, but personally I think it was too late to try, a worse-is-better scenario.


> They are only necessary to preserve the initial
> prefixes in the serialized XML. So perhaps E4X namespace can subclass
> the Name class?

I think I could go for that, although since you can't guarantee |n is Name| forall |for (var n in obj)|, I'm not sure doing so is really a useful gain.

Jeff

--
Rediscover the Web!
http://snurl.com/get_firefox

Reclaim Your Inbox!
http://snurl.com/get_thunderbird

Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Brendan Eich-2
On Feb 15, 2007, at 6:43 AM, Jeff Walden wrote:

> Igor Bukanov wrote:
>> Prefixes are optional E4X feature that an implementation may choose
>> not to support.
>
> Yeah, that's nice -- problem is that it really only works if  
> everyone agrees not to expose prefixes or the spec mandates it, and  
> since most other things preserve prefixes it's feature compat  
> (everyone expects it or uses something else).  I like the effort,  
> but personally I think it was too late to try, a worse-is-better  
> scenario.

Yes, this is a case where ECMA-357 followed wimpy ECMA-262 by  
allowing too much to be decided by the implementation. That makes for  
interop hell until there's a winner, when everyone else must follow  
the leader. One could argue that it's better than the spec (written  
based on one implementation) deciding in advance of adoption and  
experience, but I believe that prefix preservation is what people  
want from E4X. The spec should have just said so.

>> They are only necessary to preserve the initial
>> prefixes in the serialized XML. So perhaps E4X namespace can subclass

(QName, not Namespace, I think Igor meant by "E4X namespace".)

>> the Name class?
>
> I think I could go for that, although since you can't guarantee |n  
> is Name| forall |for (var n in obj)|, I'm not sure doing so is  
> really a useful gain.

If QName <: Name it still helps a bit. But yeah, Name is not related  
to string type as discussed previously, so at least one type test  
would be needed for uses of n in the loop body that did something  
more involved, say parse n as a string, than what almost all such  
loops do, obj[n].

/be


Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Igor Bukanov-2
On 15/02/07, Brendan Eich <[hidden email]> wrote:

> > Igor Bukanov wrote:
> >> Prefixes are optional E4X feature that an implementation may choose
> >> not to support.
> >
> > Yeah, that's nice -- problem is that it really only works if
> > everyone agrees not to expose prefixes or the spec mandates it, and
> > since most other things preserve prefixes it's feature compat
> > (everyone expects it or uses something else).  I like the effort,
> > but personally I think it was too late to try, a worse-is-better
> > scenario.

Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Yuh-Ruey Chen
In reply to this post by Brendan Eich-2
Brendan Eich wrote:

> On Feb 5, 2007, at 7:05 PM, Yuh-Ruey Chen wrote:
> >> An enumerating for-in loop or comprehension must reflect an
> >> enumerable property name as a Name instance if the property was
> >> defined with an explicit or default namespace other than public.
> >> Otherwise, with no or a public namespace, enumerating for-in reflects
> >> the name as a string per ES1-3 and existing JS implementations.
> >>
> >
> > Although this helps, it would still be a hassle to have to check  
> > whether
> > an enumerated property n is a Name or String. I can't do  
> > n.identifier if
> > n is a String.
>
> You have to use 'switch type' or equivalent.
>  

That's hardly ideal. There are some use cases for processing keys,
namely calling some string method on a key or passing a key to a
function expecting a string, and any code doing that is likely going to
break the moment a qualified property is added to an object. Consider:

for (k in o) {
    var prefix = k.substr(0, k.indexOf(':'));
    // do stuff
}

which might be used if an author decided to use pseudo-namespaces
(remember this is ES3 code). Both substr and indexOf are String methods,
so it will only work if k is a string (either primitive or object) or is
structurally compatible with String (at least with respect to substr()
and indexOf() in this example). The proposed Name class doesn't have any
of these methods, so the above code would cause an error. This is only
one type of case, but I'm sure there are other use cases where a string
method is called on a key.

> > How about this: Each enumerated property n is always an instance of
> > Name. If n doesn't have a qualifier, n.qualifier would be null, while
> > n.identifier would be n's id. Thus, to get a property's id,  
> > n.identifier
> > would work regardless of whether n has a qualifier. typeof(Name) would
> > be "string". Name's prototype would be a String, so name instanceof
> > String is true. These two requirements should be enough to satisfy ES3
> > compatibility.
>
> That's far from clear, because ES3 mandates a primitive string, not a  
> String object. This will break any for-in loop that compares the  
> iterating variable to a saved value of that same variable (since ==  
> on Strings and other objects is identity, not string value comparison).
>
> You make a good point about this identity being important:
>
>    'foo' in obj === Name(null, 'foo') in obj
>
> The cost of a Name being cons'ed for every iteration of for-in is  
> also troubling, in optimization effort if not in runtime and space if  
> one argues that it could be optimized away. SpiderMonkey stores 31-
> bit int ids in tagged machine words, not as strings -- never mind as  
> anything like Name instances.
>
> /be
>  

Alright, here's another attempt :) I'm considering the fact that the
difference between string keys and Name keys is only going to matter if
some string method is called on a key or the key is passed to a function
that expects a string.

Define a new type of primitive - I'll call it UnqualifiedKey. It is
basically a string. The only difference is how it is handled when it's
converted to an object. When an UnqualifiedKey primitive is converted to
an object (typically as a temporary object that methods can act upon),
the new Name(null, key) is returned. In terms of efficiency, this isn't
much worse than what happens right now (temporary String objects being
created).

Define another new type of primitive called QualifiedKey, which is
basically a structure that includes a reference to a namespace object
and the identifier string. typeof(qualified_key) == "string" (see (1)
for rationale). When a QualifiedKey primitive is converted to an object,
new Name(namespace, identifier) is returned.

Furthermore, Name will somehow contain all the String methods and
properties, so that Name.stringprop is equivalent to
Name.toString().stringprop. (If not for that toString() bit, Name could
simply have String as its prototype to get all of String's methods.)
Name's prototype would be String (even tho it has to redefine all the
methods of String), so that name_obj instanceof String is true (see (1)
for rationale again). Just like any other object, typeof(name_obj) ==
"object".

Define Name's toString() to be: (this.qualifier?
(this.qualifier.toString() + "::" + identifier) : identifier).

Neither UnqualifiedKey nor QualifiedKey are objects, so a == b isn't
identity equality for them. Instead overload the loose equality operator
so that:
    - if a is a string and b is an UnqualifiedKey or QualifiedKey, then
a == b iff a === String(b) (and vice versa)
    - if both a and b are either UnqualifiedKey or QualifiedKey, then a
== b iff String(a) === String(b)

(1) There's probably some existing code out there like this:

function foo(s) {
    if (typeof s == "string" || s instanceof String) {
       // do something
    }
    else if (typeof s == "number" || s instanceof Number) {
       // do something
    }
    // etc.
}
for (k in obj)
    foo(s);

so typeof(k) should always be "string" and Object(k) instanceof String
should always be true, regardless of whether s represents a qualified
identifier or not.

-Yuh-Ruey
_______________________________________________
Es4-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es4-discuss
Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Brendan Eich-2
On Feb 28, 2007, at 12:26 AM, Yuh-Ruey Chen wrote:

> Brendan Eich wrote:
>>> Although this helps, it would still be a hassle to have to check
>>> whether
>>> an enumerated property n is a Name or String. I can't do
>>> n.identifier if
>>> n is a String.
>>
>> You have to use 'switch type' or equivalent.
>
> That's hardly ideal.

Backward compatibility is that way, sometimes. But it trumps  
incompatible idealism. :-/

> There are some use cases for processing keys,
> namely calling some string method on a key or passing a key to a
> function expecting a string, and any code doing that is likely  
> going to
> break the moment a qualified property is added to an object. Consider:
>
> for (k in o) {
>     var prefix = k.substr(0, k.indexOf(':'));

Such code is already broken by E4X, which extended the type of  
property identifier to be a union with another arm (QName) already.  
But perhaps E4X's spec authors made a mistake. Ok, moving on:

> which might be used if an author decided to use pseudo-namespaces
> (remember this is ES3 code). Both substr and indexOf are String  
> methods,
> so it will only work if k is a string (either primitive or object)  
> or is
> structurally compatible with String (at least with respect to substr()
> and indexOf() in this example). The proposed Name class doesn't  
> have any
> of these methods, so the above code would cause an error. This is only
> one type of case, but I'm sure there are other use cases where a  
> string
> method is called on a key.

Igor suggested something similar in his last message: make String and  
Name both compatible with a structural type. But we can't make String  
<: {qualifier: Namespace, identifier: String} without polluting  
String with 'qualifier' and 'identifier' properties. That seems as  
bad, aesthetically, as anything you are trying to fix, although  
operationally it might not break real-world code.

> Define a new type of primitive - I'll call it UnqualifiedKey. It is
> basically a string. The only difference is how it is handled when it's
> converted to an object. When an UnqualifiedKey primitive is  
> converted to
> an object (typically as a temporary object that methods can act upon),
> the new Name(null, key) is returned. In terms of efficiency, this  
> isn't
> much worse than what happens right now (temporary String objects being
> created).

Adding primitives is the wrong way to go. It spreads complexity  
around the built-in types' conversion methods, instead of leaving the  
hassle for the (few, I claim) for-in loop writers who expect a string  
id.

BTW, primitives are a problem still -- we had hoped to unify string  
and String, boolean and Boolean, etc. (number types are different,  
due to int uint double decimal being the "primitives" while Number is  
the old wrapper class in ES1-3).  Unification is looking too  
incompatible, so we are trying to unify as much as possible and keep  
compatibility. But more on that in a separate message.

> Furthermore, Name will somehow contain all the String methods and
> properties, so that Name.stringprop is equivalent to
> Name.toString().stringprop.

Now that's a good idea, and it makes your counterexample above  
(k.substr) work. I will make Name <: String in the name objects  
proposal.

> (1) There's probably some existing code out there like this:
>
> function foo(s) {
>     if (typeof s == "string" || s instanceof String) {
>        // do something
>     }
>     else if (typeof s == "number" || s instanceof Number) {
>        // do something
>     }
>     // etc.
> }
> for (k in obj)
>     foo(s);
>
> so typeof(k) should always be "string" and Object(k) instanceof String
> should always be true, regardless of whether s represents a qualified
> identifier or not.

Given Name <: String, it makes sense for (typeof n === "string")  
given (n is Name).

I do not propose to add more primitive types. OK?

Thanks for keeping this conversation going.

/be

_______________________________________________
Es4-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es4-discuss
Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Yuh-Ruey Chen
Brendan Eich wrote:

> > There are some use cases for processing keys,
> > namely calling some string method on a key or passing a key to a
> > function expecting a string, and any code doing that is likely  
> > going to
> > break the moment a qualified property is added to an object. Consider:
> >
> > for (k in o) {
> >     var prefix = k.substr(0, k.indexOf(':'));
>
> Such code is already broken by E4X, which extended the type of  
> property identifier to be a union with another arm (QName) already.  
> But perhaps E4X's spec authors made a mistake. Ok, moving on:
>
> > which might be used if an author decided to use pseudo-namespaces
> > (remember this is ES3 code). Both substr and indexOf are String  
> > methods,
> > so it will only work if k is a string (either primitive or object)  
> > or is
> > structurally compatible with String (at least with respect to substr()
> > and indexOf() in this example). The proposed Name class doesn't  
> > have any
> > of these methods, so the above code would cause an error. This is only
> > one type of case, but I'm sure there are other use cases where a  
> > string
> > method is called on a key.
>
> Igor suggested something similar in his last message: make String and  
> Name both compatible with a structural type. But we can't make String  
> <: {qualifier: Namespace, identifier: String} without polluting  
> String with 'qualifier' and 'identifier' properties. That seems as  
> bad, aesthetically, as anything you are trying to fix, although  
> operationally it might not break real-world code.
>  

Yeah, that was going to be my first suggestion - adding qualifier and
identifier to String as a hack.

But that does give me an alternative and much simpler idea: Overload the
Name constructor and function to accept a single parameter, such that
Name(k) == Name(null, k). This would allow code like this:

for (k in o)
    print(Name(k).identifier);

No need for a switch type statement, although that would probably be
more efficient.

> > Define a new type of primitive - I'll call it UnqualifiedKey. It is
> > basically a string. The only difference is how it is handled when it's
> > converted to an object. When an UnqualifiedKey primitive is  
> > converted to
> > an object (typically as a temporary object that methods can act upon),
> > the new Name(null, key) is returned. In terms of efficiency, this  
> > isn't
> > much worse than what happens right now (temporary String objects being
> > created).
>
> Adding primitives is the wrong way to go. It spreads complexity  
> around the built-in types' conversion methods, instead of leaving the  
> hassle for the (few, I claim) for-in loop writers who expect a string  
> id.
>
> BTW, primitives are a problem still -- we had hoped to unify string  
> and String, boolean and Boolean, etc. (number types are different,  
> due to int uint double decimal being the "primitives" while Number is  
> the old wrapper class in ES1-3).  Unification is looking too  
> incompatible, so we are trying to unify as much as possible and keep  
> compatibility. But more on that in a separate message.
>
> > Furthermore, Name will somehow contain all the String methods and
> > properties, so that Name.stringprop is equivalent to
> > Name.toString().stringprop.
>
> Now that's a good idea, and it makes your counterexample above  
> (k.substr) work. I will make Name <: String in the name objects  
> proposal.
>  

Well as I mentioned, although Name would derive from String, it would
still have to redefine all of String's methods. This implies that any
method added to String's prototype would not automatically work with a
Name instance. Unless the way Name delegates String methods via some
__noSuchMethod__/__resolve__ hook.

> > (1) There's probably some existing code out there like this:
> >
> > function foo(s) {
> >     if (typeof s == "string" || s instanceof String) {
> >        // do something
> >     }
> >     else if (typeof s == "number" || s instanceof Number) {
> >        // do something
> >     }
> >     // etc.
> > }
> > for (k in obj)
> >     foo(s);
> >
> > so typeof(k) should always be "string" and Object(k) instanceof String
> > should always be true, regardless of whether s represents a qualified
> > identifier or not.
>
> Given Name <: String, it makes sense for (typeof n === "string")  
> given (n is Name).
>  

Does this mean that in ES4 typeof Object(string) == "string" instead of
"object" (and for other primitive wrappers)? Since Name instances are
objects, typeof name should be "object", unless objects and their
primitive counterparts have been unified with respect to typeof.

> I do not propose to add more primitive types. OK?
>  

I imagine those primitive types to be more of a language implementation
construct, since they're basically strings and Names with special flags
that determine how they're treated in the ToObject conversion.

Well whatever the case, I'd be satisfied with the solution I proposed above.

> Thanks for keeping this conversation going.
>
> /be
>  

My pleasure :)

-Yuh-Ruey
_______________________________________________
Es4-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es4-discuss
Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Brendan Eich-2
On Feb 28, 2007, at 6:28 AM, Yuh-Ruey Chen wrote:

> But that does give me an alternative and much simpler idea:  
> Overload the
> Name constructor and function to accept a single parameter, such that
> Name(k) == Name(null, k). This would allow code like this:
>
> for (k in o)
>     print(Name(k).identifier);

Ok, good idea -- updating the spec now. It is unusual to have  
optional leading arguments, but it makes sense here, and it actually  
corresponds to E4X's QName:

js> qn = new QName('local')
local
js> qn.localName
local
js> qn.uri

js> qn2 = new QName('uri', 'local')
uri::local
js> qn2.uri
uri

> No need for a switch type statement, although that would probably be
> more efficient.

It would, yeah.

> Well as I mentioned, although Name would derive from String, it would
> still have to redefine all of String's methods.

Not so -- see ES1-3, where String.prototype methods except for  
toString and valueOf are intentionally generic. They convert their  
this parameter ToString and then operate on the result of that  
internal type conversion call.

> This implies that any
> method added to String's prototype would not automatically work with a
> Name instance. Unless the way Name delegates String methods via some
> __noSuchMethod__/__resolve__ hook.

Generic methods on String.prototype automatically work, where "work"  
is defined as operate on ToString(n), without hassle. If one adds a  
specific String.prototype method that can't cope with Name, but  
operates on its |this| parameter only if that parameter is exactly a  
string instance already, then it will throw when called on Name --  
which will tell the programmer to extend Name.prototype too (if we do  
decide to make class Name be dynamic).

>> Given Name <: String, it makes sense for (typeof n === "string")
>> given (n is Name).
>>
>
> Does this mean that in ES4 typeof Object(string) == "string"  
> instead of
> "object" (and for other primitive wrappers)?

That would have been the case if we had managed to merge string  
primitive type with String object wrapper, but we can't, so what  
happens instead is the same as today:

js> typeof Object('hi')
object
js> Object('hi') instanceof String
true

> Since Name instances are
> objects, typeof name should be "object", unless objects and their
> primitive counterparts have been unified with respect to typeof.

typeof new Name('hi') === "object", as you would expect.

We can't merge string and String, etc.

/be
_______________________________________________
Es4-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es4-discuss
Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Yuh-Ruey Chen
Brendan Eich wrote:

> > Well as I mentioned, although Name would derive from String, it would
> > still have to redefine all of String's methods.
>
> Not so -- see ES1-3, where String.prototype methods except for  
> toString and valueOf are intentionally generic. They convert their  
> this parameter ToString and then operate on the result of that  
> internal type conversion call.
>
> > This implies that any
> > method added to String's prototype would not automatically work with a
> > Name instance. Unless the way Name delegates String methods via some
> > __noSuchMethod__/__resolve__ hook.
>
> Generic methods on String.prototype automatically work, where "work"  
> is defined as operate on ToString(n), without hassle. If one adds a  
> specific String.prototype method that can't cope with Name, but  
> operates on its |this| parameter only if that parameter is exactly a  
> string instance already, then it will throw when called on Name --  
> which will tell the programmer to extend Name.prototype too (if we do  
> decide to make class Name be dynamic).
>  

Ah, I totally forgot about string methods being generic (AFAIK there's
practically no code out there that has a constructor whose prototype is
String or has a String method as a property of a non-String object).

In standard mode, every class (except maybe host objects) are dynamic,
right? Or at least would every builtin class is dynamic in standard mode?

> >> Given Name <: String, it makes sense for (typeof n === "string")
> >> given (n is Name).
> >>
> >
> > Does this mean that in ES4 typeof Object(string) == "string"  
> > instead of
> > "object" (and for other primitive wrappers)?
>
> That would have been the case if we had managed to merge string  
> primitive type with String object wrapper, but we can't, so what  
> happens instead is the same as today:
>
> js> typeof Object('hi')
> object
> js> Object('hi') instanceof String
> true
>
> > Since Name instances are
> > objects, typeof name should be "object", unless objects and their
> > primitive counterparts have been unified with respect to typeof.
>
> typeof new Name('hi') === "object", as you would expect.
>
> We can't merge string and String, etc.
>
> /be
>  

Hmm...that will be troublesome in the case where a key is passed to a
function that tries to check if the passed argument is a string only via
typeof:

function foo(x) {
    if (typeof x == 'string') {
       // if x is a Name, we won't get here
    }
}

for (k in o) foo(k);

Of course, the function should have |x instanceof String|. But if the
program was designed so that it guaranteed that it never passed String
objects to foo, and foo is passed keys within a for-in loop, and that
for-in loop was iterating over an object containing a qualified
property, we run into backwards compatibility trouble.

On the other hand, the |typeof null == 'null'| change is more likely to
break backwards compat, yet that's currently in ES4. I guess it depends
on how much you're willing to break.

New ES4 code would typically use the |is| operator anyway, and
primitives and wrappers are unified under |is|, right?

-Yuh-Ruey
_______________________________________________
Es4-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es4-discuss
Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Brendan Eich-2
On Mar 1, 2007, at 6:19 AM, Yuh-Ruey Chen wrote:

> Ah, I totally forgot about string methods being generic (AFAIK there's
> practically no code out there that has a constructor whose  
> prototype is
> String or has a String method as a property of a non-String object).

There is code that uses new String, however, so the ToString(this)  
done by each generic method on entry (which results in a string  
primitive) is important.

> In standard mode, every class (except maybe host objects) are dynamic,
> right? Or at least would every builtin class is dynamic in standard  
> mode?

The built-in classes defined in ES3 section 15 are dynamic, but class  
B extends A {} by default is not dynamic. You can have non-dynamic  
subclasses of a dynamic class (necessary since Object is the base  
class of all others and it's dynamic).

IIRC you are allowed to have a dynamic subclass of a non-dynamic  
superclass (Jeff correct me if I'm wrong). dynamic is not inherited,  
and applied to a class, it affects only mutability of instances  
(whether one can add "expandos", i.e. whether the class "seals"  
instances), again if my memory is correct. Others should correct me  
or add more information as needed.

Standard vs. string mode does not change the default for a class that  
lacks an explicit 'dynamic' qualifier.

>> typeof new Name('hi') === "object", as you would expect.
>>
>> We can't merge string and String, etc.
>>
>> /be
>>
>
> Hmm...that will be troublesome in the case where a key is passed to a
> function that tries to check if the passed argument is a string  
> only via
> typeof:
>
> function foo(x) {
>     if (typeof x == 'string') {
>        // if x is a Name, we won't get here
>     }
> }
>
> for (k in o) foo(k);
>
> Of course, the function should have |x instanceof String|. But if the
> program was designed so that it guaranteed that it never passed String
> objects to foo, and foo is passed keys within a for-in loop, and that
> for-in loop was iterating over an object containing a qualified
> property, we run into backwards compatibility trouble.

I've changed the proposal to say that typeof n === "string"; we'll  
see how that flies.

> On the other hand, the |typeof null == 'null'| change is more  
> likely to
> break backwards compat, yet that's currently in ES4.

Concerns about compatibility is giving us pause even for this bug fix  
to make typeof null != 'object' will break anyone who gratuitously  
tests like so:

function isNull(a) {
       return typeof a == 'object' && !a;
}

but I know of now such tests, and spidering various top 1000 web  
sites finds cases that are currently assuming typeof null !=  
'object'. Still the current proposal and spec says we are going to  
attempt this fix, as you note.

> I guess it depends
> on how much you're willing to break.

There's a fine line. If typeof n == 'string' is not helpful, then I  
would prefer to make typeof n == 'object'. Spidering the visible web  
can only produce counter-examples showing how we might help someone  
with an incompatible "bug fix" -- it can't prove that we won't break  
someone else.

> New ES4 code would typically use the |is| operator anyway, and
> primitives and wrappers are unified under |is|, right?

Yes, string <: String, etc.

/be

_______________________________________________
Es4-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es4-discuss
Reply | Threaded
Open this post in threaded view
|

Re: extracting namespace from a property

Brendan Eich-2
On Mar 1, 2007, at 12:07 PM, Brendan Eich wrote:

> Standard vs. string mode does not change the default for a class  
> that lacks an explicit 'dynamic' qualifier.

s/string mode/strict mode/ of course

> There's a fine line. If typeof n == 'string' is not helpful, then I  
> would prefer to make typeof n == 'object'. Spidering the visible  
> web can only produce counter-examples showing how we might help  
> someone with an incompatible "bug fix" -- it can't prove that we  
> won't break someone else.

Assuming spidering does not find evidence that the change will break  
public web content, of course. You can't prove a negative. Still, we  
should try to find for-in ~~> typeof id == 'string' dependencies.  
I'll ask our spider-guy to start looking.

/be

_______________________________________________
Es4-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es4-discuss
12