Skip to content

Resources design exercise

jennalwise edited this page Nov 6, 2017 · 15 revisions

Typestate

Most real world problems or scenarios can be solved or modeled with an emphasis on state, sometimes for the better. For example, consider the state chart diagram below used to model the state of a wallet with money and a license.

Wallet State Chart

Resources

For each task the money and license must follow certain properties/restrictions:

  • The money and license cannot be duplicated.

    For example,

    @Resource class Money {...}
    
    class Person {
      Wallet wallet;
    
      // If I find money on the street, I put it in my wallet.
      transaction foundMoney(Money newMoney) {
        Money money = newMoney;
        Money m = newMoney; // COMPILE ERROR: Money cannot be duplicated because it is a @Resource
        Money m = money; // no compiler error
        wallet.addMoney(m);
      }
    }
  • The money and license can only be used once.

    For example,

    @Resource class Money {...}
    
    class Person {
      Wallet wallet;
    
      // If I find money on the street, I put it in my wallet.
      transaction foundMoney(Money money) {
        wallet.addMoney(money);
        wallet.addMoney(money); // COMPILE ERROR: can't consume Money twice because it is a @Resource.
      }
    }
  • The money and license cannot be dropped on the floor.

    • They must be either passed, returned, or released before going out of scope.

Since money and license have the above special rules/restrictions they will be referred to and marked as resources.

Task 1

The Wallet state chart diagram is implemented as a class with state member variables and transaction methods. The state member variables contain their own member variables (that happen to be resources) appropriate to each state and the transaction methods contain one or more transitions (-><state name>) from the current state to another.

However, the code below is incomplete as resources are not being handled properly. Your task is to add to and/or modify the code below (where indicated with // TODO:) to ensure the license and money resources are handled properly by following the rules listed in the Resources section above.

Do not limit yourself to known language constructs; ie. make up any language features you may want. We want to see the kind of code you would want to be able to write.

@Resource class License {...}
@Resource class Money {...}

class Wallet {
  state HasLicenseOnly;
  state HasMoneyOnly;
  state HasLicenseAndMoney;
  state Empty;

  License license available in HasLicenseOnly, HasLicenseAndMoney;
  Money money available in HasMoneyOnly, HasLicenseAndMoney;

  transaction putInMoney(Money newMoney) available in Empty {...}

  // TODO: add money to the wallet, there is not a wallet capacity for money
  // you may modify the method declaration (like with a return statement/type) or the state transition to your liking
  transaction putInMoney(Money newMoney) available in HasLicenseOnly {

    ->HasLicenseAndMoney

  }

  transaction putInLicense(License newLicense) available in HasMoneyOnly {...}
  transaction putInLicense(License newLicense) available in Empty {...}

  // TODO: add a license and money to the wallet, there is not a wallet capacity for money
  // you may modify the method declaration (like with a return statement/type) or the state transition to your liking
  transaction putInLicenseAndMoney(License newLicense, Money newMoney) available in Empty {

    ->HasLicenseAndMoney

  }

  // TODO: remove all the money from the wallet
  // you may modify the method declaration (like with a return statement/type) or the state transition to your liking
  transaction takeOutMoney() available in HasLicenseAndMoney {

    ->HasLicenseOnly

  }

  transaction takeOutMoney() available in HasMoneyOnly {...}
  transaction takeOutLicense() available in HasLicenseOnly {...}
  transaction takeOutLicense() available in HasLicenseAndMoney {...}

  // TODO: remove all the money and the license from the wallet
  // you may modify the method declaration (like with a return statement/type) or the state transition to your liking
  transaction takeOutLicenseAndMoney() available in HasLicenseAndMoney {

    ->Empty

  }
}

Task 2

For this task you will be given various code design options for resource handling based on the Wallet code example from Task 1. Your task is to read and understand each design option, then complete the associated sub-tasks.

Passing Resources

Option 1

Resources are and must be assigned to the state member variables available in the state being transitioned to with the state transition. All the transition state member variables must be assigned at once.

Ex.

@Resource Resource1 { ... }
@Resource Resource2 { ... }

class Bar {
  state A;
  state B;

  Resource1 r1 available in B;
  Resource2 r2 available in B;

  // Option 1
  transaction foo(Resource1 newR1, Resource2 newR2) available in A {
    ->B(r1 = newR1, r2 = newR2)
  }
}

Option 2

Resources are and must be assigned to the state member variables available in the state being transitioned to before the state transition. The transition state member variables are assigned individually and are annotated with the transition state they are currently in the scope of.

Ex.

@Resource Resource1 { ... }
@Resource Resource2 { ... }

class Bar {
  state A;
  state B;

  Resource1 r1 available in B;
  Resource2 r2 available in B;

  // Option 2
  transaction foo(Resource1 newR1, Resource2 newR2) available in A {
    B::r1 = newR1;
    B::r2 = newR2;
    ->B
  }
}

Option 3

Resources are and must be assigned to the state member variables available in the state being transitioned to before the state transition. The transition state member variables are assigned individually.

Ex.

@Resource Resource1 { ... }
@Resource Resource2 { ... }

class Bar {
  state A;
  state B;

  Resource1 r1 available in B;
  Resource2 r2 available in B;

  // Option 3
  transaction foo(Resource1 newR1, Resource2 newR2) available in A {
    r1 = newR1;
    r2 = newR2;
    ->B
  }
}

Option 4

Resources are and must be assigned to the state member variables available in the state being transitioned to after the state transition. The transition state member variables are assigned individually.

Ex.

@Resource Resource1 { ... }
@Resource Resource2 { ... }

class Bar {
  state A;
  state B;

  Resource1 r1 available in B;
  Resource2 r2 available in B;

  // Option 4
  transaction foo(Resource1 newR1, Resource2 newR2) available in A {
    ->B
    r1 = newR1;
    r2 = newR2;
  }
}

Task 2a

Implement each of the Passing Resources design options within the following transaction. Follow the // TODO: when implementing each design option.

class Wallet {

  ...

  License license available in HasLicenseOnly, HasLicenseAndMoney;
  Money money available in HasMoneyOnly, HasLicenseAndMoney;

  // TODO: Finish the transaction using each Passing resources design option to ensure resources are handled properly as indicated in the Resources section.
  // Ensure the transaction adds money and a license to the wallet maintaining a wallet money capacity of $100.
  // Also, make sure the wallet transitions to the correct state if/when necessary.
  transaction putInLicenseAndMoney(License newLicense, Money newMoney) available in Empty returns Money {

    Money overcapacity = new Money(0);

    if (newMoney.value() <= 100) {

    } else {

      double value = newMoney.value();
      double leftover = value - 100;

      overcapacity.add(leftover);
      newMoney.subtract(leftover);

    }

    return overcapacity;
  }

  ...

}
  1. Which of the Passing Resources design options do you prefer?
  2. Why do you prefer that design option over the others?

Returning Resources

Option 1

When resources in the current state do not exist in the state being transitioned to from the current state, they are returned to an unordered collection. The resources can then be referenced by name. Resources can be added to, removed from, and released through the collection.

Ex.

@Resource Resource1 { ... }
@Resource Resource2 { ... }

class Bar {
  state A;
  state B;

  Resource1 r1 available in B;
  Resource2 r2 available in B;

  // Option 1
  transaction foo() available in B returns x {
    x = ->A

    //can access resources by name
    print(x.r1.string());
    print(x.r2.string());

    return x;
  }
}

// add to
x += r1;
// remove from (x.r2 can be read only after this)
Resource2 newR2 = x.r2;
// release single resource
release(x.r1);
// release entire collection
release(x);

// returned unordered collection
resources = Bar.foo();
resources.r1;

Option 2

When resources in the current state do not exist in the state being transitioned to from the current state, they are returned to an ordered tuple collection. The resources can then be referenced by an assigned name. The name assignment syntax when performing the transition (see example below) is required. However, after returning from the transaction the tuple can be named as a whole or the elements named individually. Resources may be removed from or released through the tuple collection.

@Resource Resource1 { ... }
@Resource Resource2 { ... }

class Bar {
  state A;
  state B;

  Resource1 r1 available in B;
  Resource2 r2 available in B;

  // Option 2
  transaction foo() available in B returns (Resource1, Resource2) {
    (returnR1 = r1, returnR2 = r2) = ->A

    //can access resources directly
    print(returnR1.string());
    print(returnR2.string());

    return (returnR1, returnR2);
  }
}

// returned tuple variation 1
(resource1, resource2) = Bar.foo();

// returned tuple variation 2
x = Bar.foo();
// remove from
Resource1 resource1 = x[0];
// release single resource
release(x[0]);
// release tuple collection
release(x);

Task 2b

Implement each of the Returning Resources design options within the following transaction. Follow the // TODO:'s when implementing each design option.

class Wallet {

  ...

  License license available in HasLicenseOnly, HasLicenseAndMoney;
  Money money available in HasMoneyOnly, HasLicenseAndMoney;

  // TODO: Ensure resources are handled properly as indicated in the Resources section by finishing the transaction below with each design decision for Returning Resources and any other necessary code.
  // Ensure the transaction below returns the specified amount of money (if that amount does not exceed the amount of money in the wallet) and a license.
  // If the specified amount exceeds the amount in the wallet then all of the money in the wallet should be returned.
  // You may assume, as in the last task, that a Money resource has methods to add and subtract money from it by passing in the money value of type double to add or subtract.
  // Make sure the transaction transitions to the correct state if/when necessary.
  transaction takeOutLicenseAndMoney(double amount) available in HasLicenseAndMoney {

    if (amount < money.value()) {

    } else {

    }

  }

  ...

}

// TODO: Invoke the transaction defined above and assign the returned resource collection to a new collection of that type.
// TODO: Print the value of the money removed from the wallet.
// You may assume, a Money resource has a method to return its value (as seen in the above transaction).
  1. Which of the Returning Resources design options do you prefer?
  2. Why do you prefer that design option over the others?

Releasing Resources

Option 1

Resources from the current state are released before transitioning from that state to another state where the resources no longer exists.

Ex.

@Resource Resource1 { ... }
@Resource Resource2 { ... }

class Bar {
  state A;
  state B;

  Resource1 r1 available in B;

  // Option 1
  transaction foo() available in B {
    print(r1.string());

    release(r1);

     ->A
  }
}

Option 2

Resources from the current state must be returned to a modifying reference (local to the transaction method) if they do not exist in the state being transitioned to and have not been released beforehand. This modifying reference must be released before it goes out of scope.

Ex.

@Resource Resource1 { ... }
@Resource Resource2 { ... }

class Bar {
  state A;
  state B;

  Resource1 r1 available in B;

  // Option 2
  transaction foo() available in B {
    r1 = ->A // in the case of more than one resource being
             // returned either design option 1 or 2 of
             // returning resources can be used

    print(r1.string());

    release(r1);
  }
}

Task 2c

Implement the Releasing Resources design options within the following transaction where indicated with // TODO's. Follow the instructions in the // TODO's when implementing the options.

class Wallet {

  ...

  License license available in HasLicenseOnly, HasLicenseAndMoney;
  Money money available in HasMoneyOnly, HasLicenseAndMoney;

  // TODO: Ensure the correct resources are released using the design options for Releasing Resources where indicated.
  // This transaction checks whether the license in the wallet has expired and/or the money in the wallet is 0.
  // If either or both of those conditions are true, the license resource and/or the money resource should be released.
  // Make sure the transaction transitions to the correct state if/when necessary.
  transaction clearExpiredLicenseAndZeroMoney() available in HasLicenseAndMoney {

    if (license.expiresDate() < system.date() && money.value() == 0) {
      // TODO: license and money should both be released and an alert about it should be printed to the console using print()
      // Solve this TODO for each design option for Releasing Resources and for a combination of both of them.

    } else if (license.expiresDate() < system.date()) {
      // TODO: license should be released and an alert about it should be printed to the console using print()
      // Solve this using your preferred design option for Releasing Resources

    } else if (money.value() == 0) {
      // TODO: money should be released and an alert about it should be printed to the console using print()
      // Solve this using your preferred design option for Releasing Resources

    }

  }

  ...

}
  1. When would you use the first Releasing Resources design option over the second Releasing Resources design option?
  2. When would you use the second Releasing Resources design option over the first Releasing Resources design option?