JScript .NET Type Coercion Semantics, Part Four: Coercion at last

JScript .NET Type Coercion Semantics, Part Four: Coercion at last

  • Comments 9

Before I get going, a couple notable milestones. 

First, this is post number 200!  Who would have believed that I'd have so much to ramble on about?  ("Anyone who knows you" would be the correct answer to that rhetorical question I suppose.) 

Second, as of Sunday I am now over (cue Dr. Evil) one billion seconds old.  Happy gigasecond to me, happy gigasecond to me…

OK, enough frivolity.  Back to boring facts about JScript .NET's coercibility algorithm.

**************************

Today I'll finish up and give a more precise definition of how JScript .NET determines whether a particular value is coercible to a given type. Then we'll get back into lighter topics.

Suppose we have a value V which we wish to coerce to type T without data loss or error.  For clarity I'll split the algorithm up into two sections, one for "special" types like classes, arrays and enumerations, and one for "primitive value" types like integers and strings.

Suppose T is a reference, enumerated, array, etc, type:

  • If V is null or undefined then V is coercible to T irrespective of T.
  • If T is System.Object then V is coercible to T irrespective of V.
  • If T is a class and V is an instance of T (which includes being an instance of a subclass of T) then V is coercible to T. 
  • If T is an interface and V implements T then V is coercible to T.
  • If T is a non-JScript array of known element type E and V is a JScript array then V is coercible to T if and only if every element of V is coercible to E.
  • If T is a JScript array and V is a rank-one non-JScript array then V is coercible to T.
  • If T is an enumerated type and  V is a member of enumerated type E then V is coercible to T if and only if T and E are the same type.
  • If T is an enumerated type and V is a string and then V is coercible to T if and only if V names a member of T.
  • If T is an enumerated type of implementation type E and V is not a member of an enumerated type then V is coercible to T if and only if V is coercible to E.
  • If T is the System.Type type and V is a class name (not an instance of a class but actually the name of a class) then V is coercible to T.
  • If T is a delegate type and V is a function object (including a closure) then V is coercible to T if and only if V has the same function signature as the delegate.
  • If T defines an appropriate type coercion function then V is coercible to T if and only if the function call doesn't throw an exception.

Now let's consider the primitive type coercion rules.

JScript .NET supports several numeric types: 8, 16, 32 and 64 bit integers in both signed and unsigned flavours, 32 and 64 bit floats and a decimal type.  JScript .NET also supports a "char" type which is an unsigned 16 bit integer that holds a single Unicode UTF-16 value.  JScript .NET also supports a scalar Date/Time type which is treated the same as a signed 64 bit integer unless otherwise noted.

Note that data loss is allowed when coercing anything to Boolean and when coercing strings to numbers.

  • If T is the type of V then V is coercible to T.  (Duh!)
  • If V is undefined or null then V is coercible to T irrespective of T.  (FYI, coercing V to Boolean goes to false.  Coercing V to any numeric type goes to 0. Coercing V to string goes to "".)
  • If V is Boolean then V is coercible to T irrespective of T.  (FYI, coercing a V to a numeric type goes to 0 or 1, coercing V to string goes to "true" or "false".)
  • If V is char then V is coercible to T irrespective of T. (FYI, coercing V to Boolean goes to false if char is \u0000, true otherwise.  Coercing V to numeric goes to the UTF-16 value. Coercing V to string goes to a single-character string.)
  • If T is any numeric type and V is a member of any numeric type then V is coercible to T if and only if V can be coerced to T without overflow or loss of precision.  For instance, if V were the unsigned 8-byte integer 300 then V could be coerced to an unsigned 2-byte integer regardless of the fact that the 8-byte unsigned integer type is not promotable to the 2 byte integer type. Note that if V is, say, the 8-byte float value 0.1 then this is not coercible to a 4-byte float as there will be a loss of precision.  (0.5 would be coercible.)  Recall that the assignability algorithm has a special case to ensure that compile-time constants inferred to be 64 bit floats are assignable to 32 bit floats even if they are not coercible.
  • If T is string and V is of any numeric type then V is coercible to T.  (JScript .NET uses the standard number-to-string routine.  If V is a Date/Time scalar then it uses the appropriate date string.)
  • If T is Boolean and V is of any numeric type then V is coercible to T.  (If V is zero or NaN then it goes to false, otherwise it goes to true.)
  • If T is Boolean and V is a string then V is coercible to T.  (If V is "" then it goes to false, otherwise true.)
  • If T is a date type and V is a string then V is coercible to T if and only if V is parsable as a date.
  • If T is char and V is a string then V is coercible to T if and only if V is a single-character string.
  • If T is a numeric type and V is a string that can be parsed as type T then V is coercible to T
  • If T is a numeric type and V is a string that cannot be parsed as type T then V is coercible to T if and only if V is parsable to an 8-byte float and the resulting float value is coercible to T.

If none of the above apply, V is not coercible to T, and will likely cause a type mismatch exception.

  • > If V is undefined or null then V is coercible to T irrespective of T. (FYI, coercing V to Boolean goes to false. Coercing V to any numeric type goes to 0. Coercing V to string goes to "".)

    If T is a double and V is a String and V is null or undefined, it coerces to NaN.
    If T is a double and V is an Object and V is null, it coerces to 0.
    If T is a double and V is and Object and V is undefined, it coerces to NaN.
    If T is a double and V is something other than an Object or a String and V can be coerced to T and V is null or undefined, it coerces to 0.

    As best as I can tell, at least.
  • In other words, this:

    var d : double;
    var s : String;
    var b : Boolean;
    d = null;
    print (d);
    d = undefined;
    print (d);
    s = null;
    d = s;
    print (d);
    s = undefined;
    d = s;
    print (d);
    b = null;
    d = b;
    print (d);
    b = undefined;
    d = b;
    print (d);

    will print out:

    0
    NaN
    NaN
    NaN
    0
    0
  • Indeed you are correct. I wrote this document a long time ago; I suspect those changes were made after I wrote it. Thanks for keeping me honest!

    I should do a blog entry on NaN semantics sometime -- there is a lot of confusion around them.

  • >> Second, as of Sunday I am now over (cue Dr. Evil) one billion seconds old. Happy gigasecond to me, happy gigasecond to me… <<

    And at least one and, we hope, two more happy gigaseconds to come!! :-D

    But now (keeping in mind the documented horribleness that is time calculation) how do you figure that out? How many decimal places in "a year"? Do you account for leap years? Daylight Savings? (Any/all journeys through Indiana?...kidding.) O:-)
  • > two more

    Two? I don't know about you, but I'm planning on living to 120 at the very minimum. I'm hoping for nanorobots.

    > how do you figure that out?

    Actually working out the exact moment that one is any number of seconds old is not that hard in JScript. The trick is to convert your birth date from local time at birth into UTC, then do the addition of a billion seconds, then convert the result to local time at present location.

    I try to always do date calculations in UTC; it makes it much easier when you don't have to worry about DST or time zone offsets.

  • >>living to 120 at the very minimum

    The mere thought makes me weary! Unless they come up with those nanorobots well before I get that old, to fix my decrepitness.

    Trivia: You apparently share a birthday with the Aluminum Company of America (Alcoa), which was founded on the very cool date of 8/8/1888. (How I know that is that a former housemate works for them and gave me a keychain commemorating their centennial - I see that date several times a day.)
  • No no no! My birth DAY is 30 November 1972, and I was one billion seconds old on Sunday. Alcoa on the other hand was 3.66 billion seconds old on Sunday.

    We don't really share anything in common, Alcoa and me.

    Now, birthDAYS on the other hand, I share with Winston Churchill and Mark Twain.
  • Oy! Decrepitness has clearly overtaken my entire brain. (The good news being that at least now I know that, fully and beyond any shadow of a doubt. *Slinks off...* )
Page 1 of 1 (8 items)