Use of cast parameter in I7 6M62 data type definitions

I wrote an extension to implement fractions in I7. It’s targeted to 6M62.

I’m having some trouble making use of the cast parameter, which I think means the same as compatible-with in v10.1 (as listed in the definition for Neptune). From experimentation, it seems that for two types K and L, the inclusion of a line like:

cast: L_TY

in the definition of K_TY lets the compiler know that 1) that it is OK within I7 code to supply an L to a phrase expecting a K, and 2) it should expect to be supplied an I6 routine L_TY_to_K_TY to be applied in order to convert a value from an L to a K when doing so.

I put together a fraction type, and it works, but converting to numbers or real numbers requires use of phrases to decide. I would like to be able to set up casting so that the average user can ignore the extra complexity of having to convert to other number types. I tried to define:

FRACTION_TY:
apply-macro:#BASE-KIND
singular:fraction
plural:fractions
...
instance-of:WORD_VALUE_TY
instance-of:SAYABLE_VALUE_TY
...
cast:NUMBER_TY
cast:REAL_NUMBER_TY
...

and use the following test rule:

When play begins (this is the test number casting rule):
    if 4 is less than 6.0, say "4 < 6.0[line break]";
    if 4.0 is less than 6, say "4.0 < 6[line break]";
    if 5 over 2 is less than 2, say "5/2 < 2[line break]";
    if 5 over 2 is greater than 2, say "5/2 > 2[line break]";
    if 3 is less than 5 over 2, say "3 < 5/2[line break]";
    if 3 is greater than 5 over 2, say "3 > 5/2[line break]".

Compilation works but the resulting I6 doesn’t make use of the provided casting routines:

[ R_845 ;
    if (debug_rules) DB_Rule(R_845, 845);
    ! [2: if 4 is less than 6.0]
    if (((REAL_NUMBER_TY_Compare(NUMBER_TY_to_REAL_NUMBER_TY(4), 1086324736) < 0)))
    {! [3: say ~4 < 6.0[line break]~]
        say__p=1;! [4: ~4 < 6.0~]
        ParaContent(); print "4 < 6.0";! [5: line break]
        ParaContent(); new_line; .L_Say1; .L_SayX1;}
    ! [6: if 4.0 is less than 6]
    if (((REAL_NUMBER_TY_Compare(1082130432, NUMBER_TY_to_REAL_NUMBER_TY(6)) < 0)))
    {! [7: say ~4.0 < 6[line break]~]
        say__p=1;! [8: ~4.0 < 6~]
        ParaContent(); print "4.0 < 6";! [9: line break]
        ParaContent(); new_line; .L_Say2; .L_SayX2;}
    ! [10: if 5 over 2 is less than 2]
    if (((FRACTION_TY_Compare((SimplifiedFraction(5, 2)), 2) < 0)))
    {! [11: say ~5/2 < 2[line break]~]
        say__p=1;! [12: ~5/2 < 2~]
        ParaContent(); print "5/2 < 2";! [13: line break]
        ParaContent(); new_line; .L_Say3; .L_SayX3;}
    ! [14: if 5 over 2 is greater than 2]
    if (((FRACTION_TY_Compare((SimplifiedFraction(5, 2)), 2) > 0)))
    {! [15: say ~5/2 > 2[line break]~]
        say__p=1;! [16: ~5/2 > 2~]
        ParaContent(); print "5/2 > 2";! [17: line break]
        ParaContent(); new_line; .L_Say4; .L_SayX4;}
    ! [18: if 3 is less than 5 over 2]
    if (((3 < (SimplifiedFraction(5, 2)))))
    {! [19: say ~3 < 5/2[line break]~]
        say__p=1;! [20: ~3 < 5/2~]
        ParaContent(); print "3 < 5/2";! [21: line break]
        ParaContent(); new_line; .L_Say5; .L_SayX5;}
    ! [22: if 3 is greater than 5 over 2]
    if (((3 > (SimplifiedFraction(5, 2)))))
    {! [23: say ~3 > 5/2[line break]~]
        say__p=1;! [24: ~3 > 5/2~]
        ParaContent(); print "3 > 5/2";! [25: line break]
        ParaContent(); new_line; .L_Say6; .L_SayX6;}
        rfalse;
];

Note that SimplifiedFraction() is the I6 translation for the N over D phrase at the I7 level, which is:

To decide which fraction is (X - number) over (Y - number):
    (- (SimplifiedFraction({X}, {Y})) -).

I6 generation of the above is identical if I insert the casting parameter:

cast: FRACTION_TY

into the definitions for numbers and real numbers.

I’m noting that the test number casting rule’s individual comparisons are being handled slightly differently by the compiler in that, for the lines involving fractions, the ones starting with a fraction make use of FRACTION_TY_Compare() while the ones starting with numbers are just plain I6 comparison operators. This is different from the first two comparisons in the rule, which both correctly make use of REAL_NUMBER_TY_Compare() and NUMBER_TY_to_REAL_NUMBER_TY() regardless of which type comes first.

In the test scenario there is another rule:

When play begins (this is the test real number casting rule):
    if 22 over 3 is less than 7.1, say "22/3 < 7.1[line break]";
    if 22 over 3 is greater than 7.1, say "22/3 > 7.1[line break]";
    if 7.1 is less than 22 over 3, say "7.1 < 22/3[line break]";
    if 7.1 is greater than 22 over 3, say "7.1 > 22/3[line break]".

which translates into I6 (again, the same no matter whether it REAL_NUMBER_TY or FRACTION_TY that gets the casting parameter) as:

[ R_846 ;
    ! [2: if 22 over 3 is less than 7.1]
    if (((REAL_NUMBER_TY_Compare(NUMBER_TY_to_REAL_NUMBER_TY((SimplifiedFraction(22, 3))), 1088631603) < 0)))
    {! [3: say ~22/3 < 7.1[line break]~]
        say__p=1;! [4: ~22/3 < 7.1~]
        ParaContent(); print "22/3 < 7.1";! [5: line break]
        ParaContent(); new_line; .L_Say7; .L_SayX7;}
    ! [6: if 22 over 3 is greater than 7.1]
    if (((REAL_NUMBER_TY_Compare(NUMBER_TY_to_REAL_NUMBER_TY((SimplifiedFraction(22, 3))), 1088631603) > 0)))
    {! [7: say ~22/3 > 7.1[line break]~]
        say__p=1;! [8: ~22/3 > 7.1~]
        ParaContent(); print "22/3 > 7.1";! [9: line break]
        ParaContent(); new_line; .L_Say8; .L_SayX8;}
    ! [10: if 7.1 is less than 22 over 3]
    if (((REAL_NUMBER_TY_Compare(1088631603, NUMBER_TY_to_REAL_NUMBER_TY((SimplifiedFraction(22, 3)))) < 0)))
    {! [11: say ~7.1 < 22/3[line break]~]
        say__p=1;! [12: ~7.1 < 22/3~]
        ParaContent(); print "7.1 < 22/3";! [13: line break]
        ParaContent(); new_line; .L_Say9; .L_SayX9;}
    ! [14: if 7.1 is greater than 22 over 3]
    if (((REAL_NUMBER_TY_Compare(1088631603, NUMBER_TY_to_REAL_NUMBER_TY((SimplifiedFraction(22, 3)))) > 0)))
    {! [15: say ~7.1 > 22/3[line break]~]
        say__p=1;! [16: ~7.1 > 22/3~]
        ParaContent(); print "7.1 > 22/3";! [17: line break]
        ParaContent(); new_line; .L_Say10; .L_SayX10;}
        rfalse;
];

and in this case the compiler seems to have decided to apply NUMBER_TY_to_REAL_NUMBER() to a fraction type where I would expect it to have chosen FRACTION_TY_to_REAL_NUMBER_TY().

Am I doing something wrong, or is there some special compiler-level magic going on here involving the comparison relations (e.g. numerically-greater-than-relation mentioned in the Standard Rules) that can’t be modified?

3 Likes

I never worked out any casting with my new kinds. Most of them probably wouldn’t make any sense to have casting, but maybe closures → phrases would?

This does seem likely. I can’t even see where Inform 10 is implementing it, so it’s probably some deep level chicanery.

Wait, found it: inform/Quasinumeric Relations.w at master · ganelson/inform · GitHub

2 Likes

JSON?
Avoiding manually extracting the value would be nice. Same for avoiding “wrapping” in set key K of O to V.

Hmm. Anys could be auto-cast, but that goes against the philosophy of the extension which is to be safe by default.

I’m not sure what you mean by wrapping there.

To use the phrase To set key (K - text) of (R - JSON reference) to (V - JSON reference), we need to wrap any primitive value in a JSON reference. For instance, set key "foo" of obj to JSON number 5.
So I was imagining an automatic cast number -> JSON ref allowing us to write set key "foo" of obj to 5.

I now understand that it can’t work in the other direction (ref → number, ref → thruth state…) because the compiler doesn’t know what’s really in a JSON ref.

Things have come along way since the original JSON extension. That’s not necessary in the Data Structures extension :slight_smile:.

To set key (key - K) in/of (M - map of value of kind K to value of kind L) to/= (val - L):

To set key (key - value of kind K) in/of (M - map of any to any) to/= (val - value of kind V):
1 Like