Optional argument types

Oliver Hunt oliver at apple.com
Tue Sep 25 12:01:08 PDT 2012


function f(Boolean b) {
   if (b) ...
}

f(false)

has very different semantics from

function f(b) { if (b) ... }

f(false)

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)

--Oliver

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/c89c8545/attachment.html>


More information about the es-discuss mailing list