underscore icon indicating copy to clipboard operation
underscore copied to clipboard

Add _.toggle/_.transition/_.fsm to underscore

Open sktguha opened this issue 4 years ago • 5 comments

Hi, I want to propose to add a new function called toggle to underscore. At simple level it would toggle the value given to it. i.e if it is passed true it will return false and if it is passed false it will return true. ex- flag = _.toggle(flag) // if flag was true then it will get set to false

Also additionally we can have another function that if passed custom arguments then it will return a new function which can be used to toggle with those arguments For ex - bitToggler = getTogglingfunction(0,1) Now if we do bit = bitToggler (bit) // If bit had value of 0 it will be set as 1

sktguha avatar May 10 '20 18:05 sktguha

@sktguha Thanks for reaching out.

The toggle function you describe is just _.negate(_.identity). So you can do this:

_.mixin({toggle: _.negate(_.identity)});

flag = _.toggle(flag);
// or
flag = _(flag).toggle();

Regarding getTogglingfunction, please give a real-world example where such a function would be useful.

jgonggrijp avatar May 10 '20 22:05 jgonggrijp

Ok thanks for the explanation regd toggle. I guess then it is already available. Well regd getToggling function, it can be useful when you need to toggle between custom values , for ex - 'block' and 'none' toggle = getTogglingfunction ('block','none') elem.style.display = toggle(elem.style.display)

sktguha avatar May 11 '20 06:05 sktguha

Thanks. Yes, I can see the practical value in that and I think there is no function yet in Underscore that makes this easy.

To make it as general as possible, I would suggest that this function accepts arbitrary mappings rather than only pairs of alternative values:

var changeTrafficLight = getTogglingfunction({
    red: green,
    yellow: red,
    green: yellow
});

var currentState = 'red';
currentState = changeTrafficLight(currentState); // 'green'
currentState = changeTrafficLight(currentState); // 'yellow'
currentState = changeTrafficLight(currentState); // 'red' again

getTogglingfunction('block', 'none') could then be a convenience shorthand for getTogglingfunction({block: 'none', none: 'block'}).

In order to support all value types instead of only strings, it should maybe (also) support a pair of arrays and/or an array of pairs à la _.object:

var matchStart = /<[^/][^>]*>/, matchEnd = /<\/[^>]+>/;
var toggleParsingMode = getTogglingfunction([
    [matchStart, matchEnd],
    [matchEnd, matchStart]
]);

var parsingMode = matchStart; // match SGML opening tags
parsingMode = toggleParsingMode(parsingMode); // match SGML closing tags from now on

This reminds me a bit of a finite state machine à la Machina. We could take this even further in that direction by allowing callers of getTogglingfunction to pass a transitioning function as an optional third entry and by forwarding additional arguments to the transitioning function:

var changeTrafficLight = getTogglingfunction([
    ['red', null, function(signal) {
        return signal === 'carWaiting' ? 'green' : 'red';
    }],
    ['yellow', 'red'],
    ['green', null, function(signal) {
        return signal === 'pedestrianWaiting' ? 'yellow' : 'green';
    }]
]);

var currentState = 'green';
currentState = changeTrafficLight(currentState); // still 'red'
currentState = changeTrafficLight(currentState, 'pedestrianWaiting'); // still 'red'
currentState = changeTrafficLight(currentState, 'carWaiting'); // 'green'
currentState = changeTrafficLight(currentState); // still 'green'
currentState = changeTrafficLight(currentState, 'carWaiting'); // still 'green'
currentState = changeTrafficLight(currentState, 'pedestrianWaiting'); // 'yellow'

Although if people want this kind of functionality, maybe they should just use Machina. I'm only thinking out loud.

Some things to think about:

  • Decide on what this interface should and shouldn't do.
  • How to name it? If we go all the way to transitioning functions, it may be appropriate to call it _.fsm. Otherwise, something like _.transition, _.rename or just _.toggle might be more suiting.
  • What to do if somebody passes a value that isn't in the transition table? For example, var toggle = getTogglingfunction('block', 'none'); toggle('inline').
  • Should this be added to Underscore directly, or should it rather be added to Underscore-Contrib first? Contrib is unmaintained right now, but I'm looking into a way to blow new life into it.

jgonggrijp avatar May 11 '20 11:05 jgonggrijp

Wow that quite a comprehensive feature request. You have raised very good points. I am not sure how the interface should be yet. Do you have any additional thoughts on this ?

sktguha avatar May 20 '20 17:05 sktguha

Other than what I already wrote, no not really. I'm not in a hurry, though. Are you?

jgonggrijp avatar May 20 '20 22:05 jgonggrijp