Is there a way of passing extra data to the Left lambda in Match without having to add it to every intermediate method?

60 Views Asked by At

Suppose I have a method that takes the Id of a card issuer (think of loyalty cards for shops), and I want to get the status of the customer who made the most recent transaction. Yes this is a silly scenario, but it's easer than trying to describe the real one, and it's good enough to show the problem. The code shown here constitutes a working example, except for the last bit, which is where my question lies.

Here are two very simple types needed to support the workflow...

record CardIssuer(string Id, string Key);

enum CustomerStatus {
  Active,
  Inactive
}

I have some static methods that return an Either<string, T>, where T is specific to the actual method. The versions shown are stupidly simple for clarity...

static Either<string, CardIssuer> GetCardIssuer(string id) =>
 id == "1" ? new CardIssuer("1", "abc") : new CardIssuer(id, "def");

static Either<string, int> GetLatestTransactionId(CardIssuer issuer) =>
  issuer.Id == "1" ? 42 : 666;

static Either<string, CustomerStatus> GetCustomerStatus(int transactionId) =>
 transactionId == 42 ? CustomerStatus.Active : CustomerStatus.Inactive;

I use these in a method like this...

static async Task<String> Process(string cardIssuerId) =>
  await (
    from issuer in GetCardIssuer(cardIssuerId).ToAsync()
    from id in GetLatestTransactionId(issuer).ToAsync()
    from status in GetCustomerStatus(id).ToAsync()
    select status.ToString()
  )
  .Match(status => {
    return status;
  },
  error => {
    return $"Error: {error}";
  });

All of the above works fine, and is all you need to reproduce the simple scenario.

Suppose I now have a requirement that the result is encrypted, using the issuer-specific encryption key held in the Key property of CardIssuer.

Modifying the Right lambda is easy, I just change the last few lines of the query to look like this...

static async Task<String> Process(string cardIssuerId) =>
  await (
    from issuer in GetCardIssuer(cardIssuerId).ToAsync()
    from id in GetLatestTransactionId(issuer).ToAsync()
    from status in GetCustomerStatus(id).ToAsync()
    // Next line changed, along with the first lambda to Match
    select (issuer, status.ToString())
  )
  .Match((CardIssuer issuer, string status) => {
    return Encrypt(status, issuer.Key);
  },
  error => {
    // What do I do here?
  });

This assumes an Encrypt method...

static string Encrypt(string s, string key) =>
  // Do some clever encryption with the key
  "";

However, I don't know how to get the encryption key into the Left lambda.

I know I can do this by modifying each of the Either-returning methods to return a tuple containing the type they currently return, along with a string for the key, however, this means that a significant number of currently reusable (and reused) methods would now have to take in and pass out data that has nothing to do with them.

Is there a better way of passing data to the Left lambda?

0

There are 0 best solutions below