Tuesday 28 April 2020

Switch Expressions 2

Last year I did a short write-up about switch expressions, and I have now something to add.

In effect, C# 8 includes this feature, let's see:

var country = city switch
            {
                "Paris" => "France",
                "Berlin" => "Germany",
                "Stockholm" => "Sweden",
                _ => "France"
            };

Nice, but there's something that initially seemed a bit odd to me, the value-switch order. It would seem more natural to me to write country = switch city {. Well, there's one explanation for the order choice, chaining. Thanks to the value-switch order we can chain switch expressions like this:

            return city switch
            {
                "Paris" => "France",
                "Berlin" => "Germany",
                "Stockholm" => "Sweden",
                _ => "France"
            } 
            switch
            {
                "France" => "French",
                "Germany" => "German",
                "Sweden" => "Swedish",
                _ => "French"
            };

In my post from last year I had shown that though JavaScript lacks switch expressions we could write similar code via object literals like this:

let numStr = "TWO";
let num = {
        "ONE": 1,
        "TWO": 2,
        "THREE": 3
    }[numStr];

Something missing in the above code is a default case. In order to manage that I've generalized the above code into a function (switchFn):

function switchFn(value, body, defaultFn){
 let caseFn = body[value];
 return caseFn !== undefined
 ? caseFn(value)
 : (defaultFn)
 ? defaultFn(value)
 : null;
}

To be used like this:

stringValue = "ONE";
numValue = switchFn(stringValue,{
     "ONE": () => 1,
     "TWO":  () => 2,
     "THREE": () => 3
 },
 () => 10
);
console.log(`numeric value for ${stringValue} is ${numValue}`);

We can chain switchFn calls like this

stringValue = "ONE";
position = switchFn(
 switchFn(stringValue, 
 {
  "ONE": () => 1,
  "TWO":  () => 2,
  "THREE": () => 3
 }, () => 0),
 { 
  1: () => "First",
  2:  () => "Second",
  3: () => "Third"
 }, () => "Unknown"
);
console.log(`position value for ${stringValue} is ${position}`);

The above code is not too immediate to read. If we curry (via lodash) the switchFn function, apart from doing the selection logic reusable, we do the chaining a bit more clear. Notice that as we want to fix the second and third parameter to the partially applied function that we obtain, and keep the first parameter "free", we are using the _ placeholder for it (it's something that I have just learned):

let curriedSwitch = _.curry(switchFn);
let numberSelector = curriedSwitch(_, 
 {
  "ONE": () => 1,
  "TWO":  () => 2,
  "THREE": () => 3
 }, () => 0);

let positionSelector = curriedSwitch(_, 
 { 
  1: () => "First",
  2:  () => "Second",
  3: () => "Third"
 }, () => "Unknown");

stringValue = "TWO";

positionValue = positionSelector(numberSelector(stringValue));

console.log(`position value for ${stringValue} is ${positionValue}`);

Additionally, we can compose the selection functions with the lodash flow function.

let positionFromStringNumber = _.flow(numberSelector, positionSelector);
stringValue = "THREE";
positionValue = positionFromStringNumber(stringValue);
console.log(`position value for ${stringValue} is ${positionValue}`);

No comments:

Post a Comment