Discussion on the "declarative object" experiment

Leo Meyerovich lmeyerov at gmail.com
Mon Apr 25 13:52:26 PDT 2011


Hopefully not too dead of a topic (just catching up on the list...), but

1) I think a lot of this pattern can be encoded at the library level assuming a catch-all operator

2) A practical use case I had wanted to do this for was distributed objects (e.g., batching updates until a commit). 

Either way, again, I think it's a library-level thing assuming an updated meta object protocol.

Regards,

- Leo




On Apr 13, 2011, at 1:37 AM, es-discuss-request at mozilla.org wrote:

> Send es-discuss mailing list submissions to
> 	es-discuss at mozilla.org
> 
> To subscribe or unsubscribe via the World Wide Web, visit
> 	https://mail.mozilla.org/listinfo/es-discuss
> or, via email, send a message with subject or body 'help' to
> 	es-discuss-request at mozilla.org
> 
> You can reach the person managing the list at
> 	es-discuss-owner at mozilla.org
> 
> When replying, please edit your Subject line so it is more specific
> than "Re: Contents of es-discuss digest..."
> Today's Topics:
> 
>   1. Re: [Harmony proxies] Discussion on the "declarative object"
>      experiment (Dmitry A. Soshnikov)
>   2. Re: [Harmony proxies] Discussion on the "declarative object"
>      experiment (David Bruant)
>   3. Re: 'this' is more complicated than I thought
>      (PropertyReferencesbreak equivalences) (Claus Reinke)
> 
> From: "Dmitry A. Soshnikov" <dmitry.soshnikov at gmail.com>
> Date: April 13, 2011 1:18:51 AM PDT
> To: David Bruant <david.bruant at labri.fr>
> Cc: es-discuss at mozilla.org
> Subject: Re: [Harmony proxies] Discussion on the "declarative object" experiment
> 
> 
> On 13.04.2011 12:08, David Bruant wrote:
>> 
>> Le 13/04/2011 09:02, Dmitry A. Soshnikov a écrit :
>>> 
>>> On 12.04.2011 20:41, David Bruant wrote:
>>>> 
>>>> Hi,
>>>> 
>>>> I'd like to share my experience on a recent experiment. First off, I'd like to apologize for the name; "declarative object" doesn't really capture it and may be confusing. Second of all, what I have created doesn't really solve any use case (I'll discuss it), but offers at most some syntactic sugar. Code can be found here : https://github.com/DavidBruant/DeclO
>>>> 
>>>> One idea is to have an object "o". This object can accept any property name (get trap only in my case). Accessing a property will generate an object on-the-fly which itself can accept any property name and generate an object, etc. At the end, the object can be used as a constructor to do something. Example:
>>>> ---
>>>> var a = new o.azerty.are.the.first.letters.of.my.keyboard();
>>>> var b = new o.David.Bruant.writes.JavaScript();
>>>> ---
>>>> The point could be to build something based on the property names (stored in the propNames variable and added with the push l.8). Of course, a constructor taking a string array could be as useful. That's the reason why I said earlier it doesn't solve any use case. The syntax sugar is the only added value I can think of.
>>>> The exact use of the string array is to be defined in the constructTrap function. The pattern is here.
>>>> 
>>> 
>>> Yes, the pattern is interesting, though, really, which practical use-case will it have?
>> I warned from the beginning that I didn't see any :-p
>> More seriously, the main point of sharing my experiment was the discussion on shared handlers for several proxies.
>> 
>>> Currently a one possible I see -- a query to database with building the query itself. Sort of:
>>> 
>>> where
>>>     .name("Dmitry")
>>>     .surname("Soshnikov")
>>>     .active(true)
>> I have tried to think of a lot of use cases. This one, for instance, doesn't even requires proxies (As I understand it). As long as you know the number and names of the all properties (I assume you do by knowing which database table you're querying), ES5 accessor properties could be used. This goes with the restriction that it's likely to be far more memory consumming, but the pattern could be applied the same way.
>> Now that I think about it, I realize that for getters/setters, the exact same object could be used (may depend on the use case). Same for my pattern, I use Proxy.createFunction for each intermediary (l.10), but I could factorize it and return the same proxy each time since traps don't change at all.
>> 
>> Also, I'd like to note that if each of your property is a function call, you don't even need accessor properties. That's what jQuery already does by returning "this".
>> 
>> 
>>> etc (though, it should be callable in contrast with yours implementation). But, it also arguable whether it's so useful.
>> I have no strong opinion on callable or not. It was just an idea that the pattern would better be used as a constructor. The main point of the pattern lies on the use of the get trap in "firstHandler" and "middleHandler". It could be improved by a different call/construct trap (and the use of arguments for these).
>> 
> 
> Yeah, right, that's why I also mentioned that it's arguable whether the pattern is useful to apply to this particular issue. It wast just a guess.
> 
> Regarding shared handlers, if exactly this is the point, we have also think which practical things can we have.
> 
>> 
>>> 
>>> However, your letter made me think on proposing existential operator, which is a syntactic sugar to avoid long testing whether a property exists and only after that to apply it. This already is again used in CoffeeScript, so I'll show the examples:
>>> 
>>> let street = user.address?.street
>>> 
>>> which desugars e.g. into:
>>> 
>>> street = (typeof user.address != "undefined" && user.address != null)
>>>     ? user.address.street
>>>     : undefined;
>> Shouldn't it desugar to "(typeof user.address == "object" && user.address != null)?" ?
>> 
> 
> Yeah, it's also just an example, any (but the most efficient) desugared variant can be used.
> 
> Dmitry.
> 
> 
> 
> From: David Bruant <david.bruant at labri.fr>
> Date: April 13, 2011 1:32:14 AM PDT
> To: "Dmitry A. Soshnikov" <dmitry.soshnikov at gmail.com>
> Cc: es-discuss at mozilla.org
> Subject: Re: [Harmony proxies] Discussion on the "declarative object" experiment
> 
> 
> Le 13/04/2011 10:18, Dmitry A. Soshnikov a écrit :
>> 
>> On 13.04.2011 12:08, David Bruant wrote:
>>> 
>>> Le 13/04/2011 09:02, Dmitry A. Soshnikov a écrit :
>>>> Yes, the pattern is interesting, though, really, which practical use-case will it have?
>>> I warned from the beginning that I didn't see any :-p
>>> More seriously, the main point of sharing my experiment was the discussion on shared handlers for several proxies.
>>> 
>>> (...)
>>> 
>> (...)
>> 
>> Regarding shared handlers, if exactly this is the point, we have also think which practical things can we have.
> The straightforward benefit I see for shared handlers is to avoid one handler instanciation per proxy instanciation. The downside is the brainfuck it produces in terms of scope definition and code organisation (I've worked hard to make my code easy to read. I find it hard nonetheless).
> 
> Do you have other practical things in mind?
> 
> David
> 
> 
> 
> From: "Claus Reinke" <claus.reinke at talk21.com>
> Date: April 13, 2011 1:38:07 AM PDT
> To: <es-discuss at mozilla.org>, "Lasse Reichstein" <reichsteinatwork at gmail.com>
> Subject: Re: 'this' is more complicated than I thought (PropertyReferencesbreak equivalences)
> 
> 
>> The behavior of References isn't as arbitrary or different from other languages as it might seem. It's really a way to specify l-values.
> 
> Not arbitrary, but different, and quite drastically so (as far as
> usage is concerned). Your remarks helped me to pin down the difference (and eliminated two of my questions, thanks!-): References are l-values, but they cannot be used as such, due to forced, implicit conversion curtailing their lifetimes.
> 
> The differences between References and general l-values (_values_ that represent locations where other values may be
> stored) lies in how they may be used and how long they live:
> 
> - l-values are first-class values: they can be passed around,    assigned to variables, stored in data structures; they happen    to support a de-referencing operation, but merely evaluating
>   an l-value does not de-reference it; l-values can be    de-referenced explicitly; some languages implicitly coerce    l-values into r-values (causing de-reference) depending on
>   usage context (this is where the names come from: values    on the left and right hand sides of assignments), but even    those languages tend to provide means to control when    coercions take place
> 
> - References start out as l-values, but don't live long enough
>   to be used as such. They cannot be passed around, stored    in data structures, or assigned to variables; any attempt to    evaluate them leads to immediate de-reference, no matter    whether the usage context expects an l-value or not; there
>   is no way to prevent the implicit de-reference
> 
> It is mostly the implicit coercion in evaluation, combined with
> the early evaluation inherent in Javascript's call-by-value semantics, that breaks those equivalences.
> 
>> In other languages, e.g., C or Java, you have the same problem:
>>  int x = something.prop;
>>  x = 10;
>> is not the same as
>>  something.prop = 10;
> 
> My C has been buried for too long, but were not l-values
> one of C's showcases? Something like this
> 
>   int *x = &(something.prop);
>   *x = 10;
> 
> should work, by making x hold l-values (in case I messed up the syntax beyond recognition: x should be a pointer to int,
> its value being the address of something.prop, so we can use
> the r-value of x as an l-value in the second assignment).
> 
> C allowed us to be explicit about whether we wanted l-values
> or r-values, overriding the default conversions when necessary.
> Some later languages, such as Haskell or Standard ML, dropped
> the implicit coercions entirely, so all de-references are explicit.
> 
> ECMAScript relies on implicit de-reference, but triggers that by
> every evaluation. So we don't have explicit de-reference, we do
> not have C's flexibility for explicit de-reference control, and we
> do not even have C's context-sensitive implicit de-reference.
> 
> Which means that things like
> 
> (1 ? obj.prop : obj.prop) = 3;
> (0, obj.prop) = 2;
> 
> will work in C, but fail in ES (References are very short-lived
> l-values - every operation evaluates and de-references them,
> independent of whether the result is going to be used in an
> l-value or r-value context). 
> Also, in C we can write
> 
> x = &(obj.prop);
> *x = 4;
> 
> to express that we want x to hold l-values, and storing l-values in arrays isn't much different
> 
> int *a[1] = { &obj.prop };
> *(a[0]) = 1;
> 
> In ES, we only have the default-to-r-value path. For instance,
> 
> [obj.prop][0] = 1; 
> will not use obj.prop as an l-value, and there does not seem to be a straightforward alternative for programmers who want to work with ES References as l-values.
>> So far, References as a specification mechanism is just following other languages, and behaving exactly as any seasoned programmer would expect. 
> 
> Does the above explain why a seasoned programmer
> might reasonably expect differently, because ECMAScript behaves differently from other languages?
> 
>> The binding of "this" when calling a Reference value mimics method calls. It does so fine when you treat objects as objects, but not when you try to extract a method from its object .. It's a shallow abstraction, but it does work when you play along with it.
> 
> I was trying to point out that the mechanism works for the
> simple case and is known to confuse programmers for other cases. In particular, I am trying to find out whether the current mechanism is a special case of a more complete mechanism,
> one that works equally well for simple and non-simple cases.
> 
> Since PropertyReferences hold the object the method was
> selected from, all that seems needed is to make References
> survive evalutation, ie, make References first-class values.
> 
> An alternative would be to preserve context-information
> during evaluation: if the result of '(?:)' is to be used as an
> l-value, then evaluation should perhaps not de-reference
> the l-values in the conditional.
> 
> I am less concerned with being able to use '(?:)' on the
> left hand side of assignments, and more with being able
> to use equivalences like '(true ? x : x) <--> x', independent
> of where the expression occurs. Since we can write such
> conditionals on left hand sides, why not make sure that
> they actually work there?
> 
>>> Question 1:     Should a Reference hold on to the current                        property value?
>> Which value? The one the property (if it existed) had when the reference was created? Or the current one - if the Reference survives for any amount of time, the object property could change its value in the meantime.
> 
> Thanks. So the answer probably is 'no' - which value we get depends on when we look behind the reference. 
> I guess I was confused by property accessors not actually accessing the property - once the decision is made to return a Reference instead of the property value, it would only be
> consequent to keep Reference construction and de-reference
> separated. As long as we are able to specify when to pass the reference and when to look up the value behind it.
> 
>>> Question 2:
>>>    If a Reference allows us to recover 'obj' from 'obj.method',
>>>    why does this information have to get lost when passing     it through a variable binding?
>>>    .. could this error source be eliminated by passing the      Reference as a value, instead of only the value component
>>>    without the base object, one step further?
>> Of course it's possible, but personally I prefer to have the "this" object obvious in the call line. That way I know what object the method is being called on. Without it, the loss of context is in the source code, making it harder to read and maintain.
> 
> I'm afraid you won't get that comfort;-) At the moment, programmers can just write the more complicated
> 
>   var short = function(x) { return obj.method(x); };
>   short("hi"); // no 'this' object on the call line
> 
> We can try to make this more readable, and we can try to
> eliminate a common source of bugs, but the rest is between
> you and your team's coding style and style checker.
>>> Question 3:
>>>    It seems that trying to reuse References for 'this' forces
>>>    early calls to GetValue (because users should not have to
>>>    call 'obj.method.valueOf()' or 'obj.property.valueOf()' to      trigger the delayed selection, and because the property
>>>    value is not stored in the PropertyReference).
>> I'm not sure I understand what the problem is here.
> 
> Probably because the description is a bit confusing. I was
> trying to understand why References get eliminated early,
> through calls to GetValue, and was enumerating non-reasons
> before coming to my question:
> 
>>> This loses information that we would like to hold on to -     if we really cannot solve this for References in general,
>>> why not store the 'this'-candidate in the function instead
>>> (similar to the fairly new '[[boundThis]]')?
>> Won't work. The same function can be used in many places at the same time.
> 
> Ah, good point. If functions were constants, we could make copies (sharing the code, but with different this values). But
> they aren't, so we need the 'this'-candidates outside the function.
> 
>> E.g.
>> [obj1.foo, obj2.foo][(Math.random() * 2) | 0]();
> 
> Note that, currently, either selection will have that anonymous first array as 'this', not obj1 or obj2. But
> you were aware of that, right?-)
>>> Question 4: (general version of question 2)
>>>    Why is the origin information in References lost so easily?
>>> 
>>>    It seems that most parts of the spec require GetValue() by
>>>    default, with few exceptions. What would go wrong if the
>>>    available information would be passed on instead (isn't
>>>    it sufficient for the final consumers to call GetValue(),
>>>    provided that the original property value is stored in the
>>>    PropertyReference, to avoid interference)?
>> References is a specification tool. If it survived for an extended amount of time, and visibly so, implementations would have to actually implement something to represent it. As it is now, a reference is found and immediately consumed, which allows implementations to never create it at all, and work directly on the value in r-value contexts, and on the object and property in l-value contexts.
> 
> Yes, and that is my argument. L-values as first-class values is the common way to handle references, whether it is in C, in Haskell, in ML, .., ever since Strachey documented l-values in 1967, and probably longer than that. Once references become visible to programmers, one might as well support them fully. Eliminating
> temporary structures is a common implementation optimization, not limited to References, and not a language spec concern.
> 
>>> Broken equivalences:
> ..
>>>    var x = obj.m; x(); <-/-> obj.m();
>>> 
>>>    obj.m.valueOf() <-/-> obj.m
>> Why should that work? The valueOf function isn't guaranteed to return anything related to the object it's on.
> 
> The default valueOf for Function comes from Object,
> where it is 'ToObject this', which for Object is the input
> argument without conversion. I think..
> 
>> This is exactly correct. Extracting a method from its object will break the connection to the object. Which is kind of expected when you allow any function to be used as both a method and a non-method.
> 
> I expected none of these:
> 
> - Property access is not extraction.
> - Extraction is triggered by constructs that could just
>   as well pass on the property accessor (and probably
>   should, as the alternative leads to runtime errors).
> - Extraction alone will not break the connection, only
>   some forms of triggering extractions will do so.
> - A new connection is established by trying to pass
>   a property accessor through an Array.
> 
>>> I was not aware that just about any code transformation
>>> would be invalidated by the handling of References. If this cannot be fixed, could the specification be more explicit about this, please?
>> When I think of References as l-values, the current behavior actually become the expected one. The only tricky bit is that method-calls actually need an l-value to work correctly.
> 
> Even the l-value part is unusual, as I've tried to show.
> 
> Claus
> 
> 
> 
> 
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110425/01815147/attachment-0001.html>


More information about the es-discuss mailing list