ASH typedefs

holatuwol

Developer
Veracity added typedefs to v10.7, and the following is me copying what was written about them because you are all better at writing engine-breaking scripts than I am, since I'm still a newbie to this script-writing thing. ^_~

--------------------------------

A discussion on kolmafia.us has me thinking that user typedefs might make ASH code easier to read and maintain. In particular, holatuwol's new syntax for implicitly passing an object as the first parameter of an invoked function is sort of like method invocation - if it were easier to define the "class".

It could be as simple as this:

Consider two examples from the kolmafia note. Holatuwol's:

Code:
void push( boolean [item] list, string data )
{
    string [int] items = data.split_string( "\\s*,\\s*" );

    foreach key in items
        list[ string_to_item( items[key] ) ] = true;
}

boolean [item] supergarnishes;
supergarnishes.push( "grapefruit, lemon, olive, orange, soda water, strawberry" );

could be:

Code:
// define the "class"
typedef boolean [item] ItemSet;

// define a "method"
void push( ItemSet list, string data )
{
    string [int] items = data.split_string( "\\s*,\\s*" );

    foreach key in items
        list[ string_to_item( items[key] ) ] = true;
}

// make an instance of the "class"
ItemSet supergarnishes;

// invoke the "method"
supergarnishes.push( "grapefruit, lemon, olive, orange, soda water, strawberry" );

or mine:

Code:
boolean [item] supergarnishes;
supergarnishes[ $item[grapefruit] ] = true;
supergarnishes[ $item[lemon] ] = true;
supergarnishes[ $item[olive] ] = true;
supergarnishes[ $item[orange] ] = true;
supergarnishes[ $item[soda water] ] = true;
supergarnishes[ $item[strawberry] ] = true;

foreach key in supergarnishes
    if (item_amount( key ) > 0)
        print( "hey, you have some " + key );

could be:

Code:
// define the "class"
typedef boolean [item] ItemSet;

// define a "method"
void add( itemSet set, string elt )
{
    set[ string_to_item( elt ) ] = true;
}

// define an instance of the "class"
ItemSet supergarnishes;

// invoke the "method"
supergarnishes.add( "grapefruit" );
supergarnishes.add( "lemon" );
supergarnishes.add( "olive" );
supergarnishes.add( "orange" );
supergarnishes.add( "soda water" );
supergarnishes.add( "strawberry" );

foreach key in supergarnishes
    if (item_amount( key ) > 0)
        print( "hey, you have some " + key );

Those are pretty similar, actually...

Code:
// define the "class"
typedef boolean [item] ItemSet;

// define "methods"
void add( itemSet set, string elt )
{
    set[ string_to_item( elt ) ] = true;
}

void push( ItemSet set, string data )
{
    string [int] items = data.split_string( "\\s*,\\s*" );

    foreach key in items
        set.add( items[key] );
}

// define an instance of the "class"
ItemSet supergarnishes;

// And then take your pick on how to initialize it...

Edit: Fixed my own dumb code error from the original note. Veracity.
 

Veracity

Developer
Staff member
I should comment that what I added was, essentially, type aliases to make it easier to give a name to a complicated map. If you give two names to the same data structure, those names are interchangeable.

Code:
typedef boolean [item] Type1;
typedef boolean [item] Type2;

Type1 getMap()
{
    Type2 map;
    return map;
}
...is legal, if pointless.

I could have made such definitions NOT interchangeable - but it would have required a lot more internal changes to ASH to de-alias types all over the place. It didn't seem especially useful, since ASH does not provide user-defined polymorphic functions (functions with the same name and different arguments). If it DID provide that, you might want to do:

Code:
typedef boolean [item] ItemSet;
typedef boolean [item] ItemMap;

void func (ItemSet a)
{
    ...
}

void func (ItemMap b )
{
    ...
}

... and have the appropriate function called depending on what the argument was. But, that still doesn't seem useful: if it's really the same data structure underneath, why define functions with the same name to do something different?

So, the point of this really was to make it easy to "name" complicated maps and such, for code clarity. If you really want something closer to a "class", use records.
 
[quote author=Veracity link=topic=785.msg3795#msg3795 date=1172097413]
I could have made such definitions NOT interchangeable - but it would have required a lot more internal changes to ASH to de-alias types all over the place. It didn't seem especially useful, since ASH does not provide user-defined polymorphic functions (functions with the same name and different arguments). If it DID provide that, you might want to do:
[/quote]

[quote author=holatuwol link=topic=715.msg3358#msg3358 date=1169299631]
I thought I allowed for this several dozen releases ago, but it turns out that I didn't actually implement the final changes which would permit it at the user-level. I've gone ahead and done so and the changes will be present in the next release of KoLmafia.

What this means, in regular terms, is that with the next release of KoLmafia, you can finally start giving your functions the same names, provided the parameters passed to these functions are somehow different. You're even allowed to give them different return types, provided this "use different parameters or different numbers of parameters" condition is met.

For example, the following code will validate in the updated ASH interpreter and print "Integer!" followed by "String!":

void test_me( string s )
{ print( "String!" );
}

void test_me( int i )
{ print( "Integer!" );
}

test_me( 1 );
test_me( "Hello world." );

[/quote]

Could someone please explain this to me? I thought what Holatuwol said (shown above) was introducing polymorphic functions...I'm confused.
 

Veracity

Developer
Staff member
OK, I didn't notice when user-defined polymorphic functions were enabled.

Code:
int test( int arg )
{
   print( "Integer: " + arg );
   return arg * 2;
}

float test( float arg )
{
   print( "Float: " + arg );
   return arg * 2;
}

print( "Returns: " + test( 5 ) );
print( "Returns: " + test( 6.0 ) );

yields:

> poly.ash
Integer: 5
Returns: 10
Float: 6.0
Returns: 12.0

Ignore the "confusing" part of my comment. :D
 
OK so am I using this right?

Code:
typedef int [item] itemset;

int maxint = 2147483647;

//mapname.add(item, quant)
void add(itemset set, item toadd, int quant )
  {
  set [toadd] = quant
  }

//mapname.do_dump(command)
void do_dump(itemset set, string command)
  {
  foreach key in set
    {
    if(set[key] == maxint)
      {
      command = command + "* " + key + ", ";
      }
      else
      {
      command = command + set[key] + " " + key + ", ";
      }
    }
  //trim last camma space pair
  command = substring(command, 0, last_index_of( command, ", " ) - 1);
  cli_execute(command);
  }

void display_dump(itemset set)
  {
  set.do_dump("display put ");
  }

void closet_dump(itemset set)
  {
  set.do_dump("closet put ");
  }
  
void stash_dump(itemset set)
  {
  set.do_dump("stash put ");
  }

itemset display_items;
itemset closet_items;
itemset stash_items;

void execute_cleanup()
  {
  display_items.display_dump();
  closet_items.closet_dump();
  stash_items.stash_dump();
  }
  
//either import the script or create a void main() to populate the maps, and call execute_cleanup

edit my mixing of programming languages strikes again! had to fix := in variable assignment.
edit2 fixed maxint removed comment about it, and added some () to make implied first parameter work.
 
Thanks! Now since I have already started writing scripts to use the newest version, I must go download it! This will make my scripts much easier to understand!
 
Top