Optional argument types

Dmitry Soshnikov dmitry.soshnikov at gmail.com
Tue Sep 25 12:09:39 PDT 2012


On Tue, Sep 25, 2012 at 12:01 PM, Oliver Hunt <oliver at apple.com> wrote:

> function f(Boolean b) {
>    if (b) ...
> }
>
> f(false)
>
> has very different semantics from
>
> function f(b) { if (b) ... }
>
> f(false)
>
>
Yeah, this seems the best example, forgot about it; thanks.



> Likewise eliding boxing is very very hard if anything happens to mutate
> any portion of the proto chain for String, etc.
>
> You also run into semantic issue if you do:
>
> Foo = String
>
> function f(Foo h) { .... }
>
> Should this box?  If it should box then what we're saying is that the
> semantic behaviour of
>
> function f(Foo h)
>
> is
>
> function f(h) {
>     if (!Object.isObject(h)) h = new Foo(h)
>     if (!(h instanceof Foo)) throw ...;
>     ...
> }
>
> Which is the horror of C++ implicit constructor conversions
>
> If we say that
>
> Foo = String;
> function f(Foo h) ..
>
> is semantically different from
>
> function f(String h)
>
> then we're implying that String has become a context dependent keyword
> (which is what i meant to imply for my prior primitive type names)
>
>

Yeah, this one is too; thanks.

Yes, it's actually harder to do (though unfortunately based on the existing
language issues, not the application issues. But we cannot ignore them of
course).

In this view, yes, JS is not ready to go with this in production and at
standard level. And as for custom projects -- then, that's said, can solve
this issue in own manner with pre-processors.

Thanks again,

Dmitry


>
> On Sep 25, 2012, at 11:37 AM, Dmitry Soshnikov <dmitry.soshnikov at gmail.com>
> wrote:
>
> On Tue, Sep 25, 2012 at 11:18 AM, Oliver Hunt <oliver at apple.com> wrote:
>
>> I'm very concerned about the interaction of boxed primitives and these
>> type guards, especially in the context of multiple global objects (i.e. the
>> standard browser environment).
>>
>> I'm not sure what the best solution to this is -- the [[NativeBrand]]
>> concept worries me as it hides the potential for different types being
>> passed into an ostensibly singly typed variable.  Likewise I'd be worried
>> about instanceof causing problems for the exact opposite reason (eg.
>> instanceof Array not working across globals).
>>
>> I'm not sure what the spec currently says w.r.t implicit boxing off the
>> top of my head, but I would be prefer that implicit boxing would not be
>> allowed.
>>
>> Then we could have at least a few primitive types: number, bool[ean],
>> string, function, say.  The primitive names would take precedence over the
>> result of any lookup, and if someone wanted to force a lookup then they
>> could use ""'s.  This also would mean that Number and number as types would
>> be (correctly) distinguished.
>>
>> I'm unsure what the default values for a primitive typed variable should
>> be, but I'm generally opposed to TDZs.
>>
>>
> In real practice though, I don't think there can be the cases when
> "strings" or `new Strings` are worth to distinguish and to work separately
> in the code (I mean exactly to route the code differently based on it).
> That's literally -- I cannot imagine any practical example of it (OK, I
> cannot imaging even a theoretical example of routing the code based on
> primitive vs. box-values).
>
> The only thing when "string" vs `new String` arrives is exactly the errors
> of trying to _test the type_ of passed arguments as:
>
> if (typeof foo == "string") { ... }
>
> and then their `new String` doesn't work for them. Then they switch to
> [[NativeBrand]] checks instead.
>
> So for `foo(String bar) { ... }` in real practice both, "string" and `new
> String` should pass I think.
>
> P.S.: actually, the one theoretical/practical distinguishing example is
> when a programmer say want to save some state on the passed value, e.g.:
>
> foo(String bar) {
>   bar.count  = 10;
> }
>
> It won't work for primitives (since an intermediate wrapper is destroyed
> after property resolution). But this is kind of the hint for the programmer
> himself that he should pass then `new String` always there, but not just
> "strings".
>
> And foo(String bar) for `new Strings`, and foo(string bar) for "strings"
> interesting by itself, but introduces new keywords, and also keeps logical
> separation b/w primitives vs. boxes. In real practice again, programmers
> don't even think about it in application tasks and if so, try to avoid.
>
> Dmitry
>
>
>
>> --Oliver
>>
>> On Sep 25, 2012, at 10:56 AM, Dmitry Soshnikov <
>> dmitry.soshnikov at gmail.com> wrote:
>>
>> On Tue, Sep 25, 2012 at 7:37 AM, Andreas Rossberg <rossberg at google.com>wrote:
>>
>>> On 25 September 2012 15:31, Andrea Giammarchi
>>> <andrea.giammarchi at gmail.com> wrote:
>>> > That's a hell of a question ... shapes speaking I'd say structural,
>>> since
>>> > AFAIK shapes are those boosted up more, isn't it?
>>> >
>>> > That would solve String VS string and Array VS Arguments which is, I
>>> > believe, kinda desired.
>>> >
>>> > Which one would you chose ?
>>>
>>> I assume that most people would probably prefer structural types in
>>> principle, but the problem is that they induce far, far more expensive
>>> runtime checking (easily an order of magnitude). Which is why guards
>>> and trademarks were proposed as a more conservative, nominal
>>> mechanism.
>>>
>>> Generally speaking, retrofitting something type-like on an untyped
>>> language is a *very* hard problem. It has been tried with many
>>> languages and has succeeded for very, very few. You can read lots and
>>> lots of research papers on the subject.
>>>
>>> Fortunately, though, we have top-notch expertise on that topic on
>>> TC39, e.g. Sam TH. ;)
>>>
>>
>> I believe all this is true, and this is why I underlined in the first
>> letter that it's kind of not a "type system" by itself, but only the
>> runtime (yes, runtime) check of the "type-tags" of the argument values. And
>> only the argument values.
>>
>> That's said, introducing the types for casual vars, like:
>>
>> let x :: Number = 10;
>>
>> pushes the responsibility of type carrying to the variable. Thus changing
>> the semantics of the environments model, where the vars don't carry the
>> type, but just reference to the values which carry the type. The line
>> above, if to accept such a "type system" would look like x = "foo" is the
>> error then.
>>
>> Having this "type hints" only for arguments, reduces the problem only for
>> checking the "type-tags" of the arguments, in the function's prologue. And
>> it can be done only in runtime (a function can be applied in different
>> ways, and it's not possible to determined lexically with what "types" of
>> arguments, so only the runtime).
>>
>> By "type-tags" I mean a generic way of testing. Not the `typeof` result,
>> not the `instanceof`. But e.g. the [[NativeBrand]] (for natives) +
>> `instanceof` for custom constructors (the later is just an extension).
>>
>> This gives the ability not to think about it as a "type system", but just
>> as "type hints for arguments". Because again, after the arguments have been
>> checked for the "type-tag" at activation, the argument vars themselves can
>> be rebound to the values of different types, underlining that the variable
>> are not related with the types, and the type annotations for function
>> arguments are just type annotations for checking the types of passed values
>> at only activation.
>>
>> This is kind of scheme I have in mind. I understand all the consequences
>> of (not)optimizations and runtime coasts, but if devs are need these type
>> checks, then they do this anyway manually (and usually in the prologue of
>> the functions) at runtime today. So providing this would make it just
>> implicit. Still with understanding that this is a runtime check, which may
>> decrease the speed of function execution -- but it's already decreased once
>> they do this manually anyway.
>>
>> This model btw is used in PHP as well (type hints only for function
>> arguments, but any var can be reassigned to the value of different type,
>> underlining that this not a "type system").
>>
>> Today's check to which we can compile our sources with a build-tool is:
>>
>> function foo(String bar, Widget baz) { ... }
>>
>> is:
>>
>> if (bar.[[NativeBrand]] != "String") throw TypeError(...);
>> if (!(baz instanceof Widget)) throw TypeError(...);
>>
>> (it's easy to distinguish which type-tag, either [[NativeBrand]] or the
>> instanceof, to use in order to check which value, natives are known, and
>> all others may be checked with the instanceof). Thus, the later is just an
>> extension, we can check only for natives, not bothering with
>> constructors/classes.
>>
>> Anyways, since it's hard to implement it now and since it may coast too
>> much in respect of VMs optimizations, I think any big project may solve
>> this problem with own implementations with pre-processors/compilers. Thus,
>> some exact syntactic form (if it will be widely adopted by some big
>> community) can be then reused for standardization.
>>
>> P.S.: and to think about type-system in JS is hard, since it doesn't have
>> it. It has just a mess with `typeof` vs. `instanceof` vs. [[NativeBrand]]
>> vs. "ducks" (aka "[[NativeBrand]]-like objects"). And therefore, again,
>> those "type-hints" for args are not about a "type-system".
>>
>> Dmitry
>> _______________________________________________
>> 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/20120925/8f2f5afc/attachment-0001.html>


More information about the es-discuss mailing list