typescriptblockchain

Are there mutable variables in Midnight's compact language? Is there a workaround for this?


I'm learning compact language and Midnight's tooling (and can't find a tag on either). It is similar to typescript, and I am confused about mutable variables:

I need a way to share a temporary variable within a function (not a public ledger state) between if/ else blocks. Is there currently a workaround for this? Or, are there mutable variables and I'm doing something wrong?

For example, computing a value conditionally and reusing it later in the function:

circuit demo():[] {
    let result: Uint<64>; // 'result' needs to be local and mutable

    if (condition) {
        result = 10;
    } else {
        result = 20;
    }

    return []; 
}

Solution

  • There are no mutable variables (yet). Three ways come to mind for working around this.

    One way to encode them is to define a separate helper for the then and else parts that returns a tuple of all the values that would have been assigned in each part:

    circuit thenPart(): [Uint<64>] { // tuple just for illustration, not necessary
      // Body goes here:
      const result: Uint<64> = 10;
      // Return all the "mutable" values:
      return [result];
    }
    
    circuit elsePart(): [Uint<64>] {
      const result: Uint<64> = 20;
      return [result];
    }
    
    circuit demo(): [] {
      const result: Uint<64> = default<Uint<64>>;  // If you need an initial value.
      const [result0] = condition ? thenPart() : elsePart();
      // Use result0 instead of result below:
      return [];
    }
    

    Another way is passing (possibly different) arguments into the then and else helpers for all parameters and consts they need from demo. The code would be basically the same if you moved the if into a single helper:

    circuit test(condtion: Boolean): [Uint<64>] {
      if (condition) {
        const result: Uint<64> = 10;
        return [result];
      } else {
        const result: Uint<64> = 20;
        return [result];
      }
    }
    
    circuit demo(): [] {
      const result: Uint<64> = default<Uint<64>>;
      const [result0] = test(condition);
      return [];
    }
    

    The last way is instead of abstracting the code in the then and else parts, you abstract the code that comes after the if into a helper:

    circuit tail(result: Uint<64>): [] {
      // This is all the code after the `if/else`:
      return [];
    }
    
    circuit demo(): [] {
      const result: Uint<64> = default<Uint<64>>;
      if (condition) {
        const result0: Uint<64> = 10;
        return tail(result0);
      } else {
        const result1: Uint<64> = 20;
        return tail(result1);
      }
    }
    

    This one might make sense if there's not much code after the if/else, but beware that in the ZKIR (that is, the circuit) backend, the code for tail will be duplicated by inlining it for both branches.