Introducing the probability switch!

I'm going to start this blog talking about a pattern that has appearing in my programming for some time now.

The scenario is as follows. You have a function and want it to, probabilistically, behave differently, every time you call it. One example is a mutation function, in the domain of evolutionary programming, which is expected to perform a different action among a set of possibilities, as illustrated by the following code.

      function mutate(somethingToMutate) {
        let option = Random.randomInt(4); // generate integer in the range [0,3]
        if (option === 0) { // exchange something
          exchangeSomething(somethingToMutate);
        } else if (option === 1) { // add something
          addSomething(somethingToMutate);
        } else if (option === 2) { // remove something
          removeSomething(somethingToMutate);
        } else { // change something
          changeSomething(somethingToMutate);
        }
      }
      

In the code above, which could also be a switch statement, each option has a chance of 25%. The problem with the code above is that it is not easy to see the programmer intention right away. Also, it lacks flexibility. For instance, if we want to change the probabilities, we could increase the random range, but it would only increase the chance of the last option. Altough it is possible to change the probabilities by accepting a range for each possibility it is cumbersome, to say the least.

With that scenario in mind, this is my proposed pattern:

      function mutateCode(somethingToMutate) {
        let selectedOption = 
          LanguageConstruct.probabilitySwitch(
            // exchange something
            exchangeSomething(somethingToMutate),
            0.25,
            // add something
            addSomething(somethingToMutate),
            0.25,
            // remove something
            removeSomething(somethingToMutate),
            0.25,
            // change something
            changeSomething(somethingToMutate),
            0.25
          );
      }
      

Much better huh? My thoughts exactly... :)

And the implementation, in case you're wondering, is:

      // call format: 
      //    LanguageConstruct.probabilitySwitch(func1, prob1, func2, prob2,..., funcN, probN)
      LanguageConstruct.probabilitySwitch = function(...functionsAndProbabilities) {
        Assert.assertIsArray(functionsAndProbabilities);
        Assert.assert(functionsAndProbabilities.length%2===0, 
                      'Expecting an even number of parameters');
        
        let functions = [];
        let probabilities = [];
        for (var i=0;i Assert.assertIsFunction(func, 
                                  'Expected (func1, prob1, func2, prob2,..., funcN, probN)'));
        probabilities.forEach(prob => Assert.assertIsNumber(prob, 
                                  'Expected (func1, prob1, func2, prob2,..., funcN, probN)'));

        let probabilitiesSum = probabilities.reduce((sum, value) => sum + value, 0);
        Assert.assert(probabilitiesSum, 'Probability sum must be 1');

        //each one select a number based on its probability and the
        //one with the bigger number wins.
        let generatedValues = probabilities.map(prob => Math.random() * prob);
        let selectedIndex = generatedValues.indexOfGreaterValue();
        functions[selectedIndex]();
      };
      

The last point is: wouldn't it be nice to have this cool feature interwoven in the language itself? Then you could use it like a first class language construct, like:

      function mutateCode(somethingToMutate) {
        switchProbability(somethingToMutate) {
            case 0.25:
                // exchange something
                exchangeSomething(somethingToMutate)
                break;
            case 0.25:
                // add something
                addSomething(somethingToMutate),
                break;
            case 0.25:
                // remove something
                removeSomething(somethingToMutate),
                break;
            case 0.25:
                // change something
                changeSomething(somethingToMutate),
                break;
        }
      }
      

Actually, this is possible by "transpiling" the code before execution, with a tool like Sweet.js.

And...That's it for the first post! Did you like it? Leave your comments bellow... Oh no... I'm sorry, this blog doesn't have this functionality yet... Well, you can hit me at @andrers52 and tell me what you think in 140 characters or less! :).

Happy Coding!

André