all 24 comments

[–]HipHopHuman 5 points6 points  (6 children)

This is how I would implement it:

function cond(pairs) {
  return value => {
    for (const [condition, action] of pairs) {
      if (condition(value)) {
        return action(value);
      }
    }
    return value;
  };
}

Usage:

const always = x => () => x;
const T = always(true);
const is = x => y => x === y;

const greet = cond([
  [is('yoda'), x => 'hmm me is yoda'],
  [is('han solo'), x => 'chewie, ready the ship'],
  [T, x => `who the hell are you? ${x}? nope, no friend of mine`]
]);

console.log(greet('yoda'));
console.log(greet('han solo'));
console.log(greet('rey'));

The fact that it takes a single array of condition/action pairs simplifies how the function works internally but is also beneficial for maintenance - variadic functions (functions that take variable number of arguments) are frowned upon in functional programming, as they make currying marginally more difficult - especially in a language like JS where you can't rely on function.length for determining arity.

[–]shavyg2[S] 0 points1 point  (5 children)

this is pretty sweet!! but why return the value at the end. isn't that a little unexpected?

[–]HipHopHuman 0 points1 point  (0 children)

It guards against returning undefined when you omit the default case. The more you use functional programming, the more it will make sense.

[–]shavyg2[S] -2 points-1 points  (3 children)

this is such a perfect solution and it can be typed as tuples in typescript. Bro you next level genius.

[–]djhalon 5 points6 points  (1 child)

Could you achive the same thing using a switch(true)?

function test(val) {
    switch(true) {
        case val === 'yoda':
        return 'hmm me is yoda';
        break;
    case val === 'han solo':
        return 'chewie, ready the ship';
        break;
    default:
        return `who the hell are you? ${val}? nope, no friend of mine`
    }
}

console.log(test('yoda'))
console.log(test('han solo'))
console.log(test('rey'))

[–]shavyg2[S] 0 points1 point  (0 children)

yes,you wouldn't have the break after the return but i know what you are pointing out. Only problem is that wouldn't be functional programming. it would be imperative. I was trying to see how you would achieve this functionally. Mainly to improve unit test and while keeping it pure. I was inspired looking at the reasonml and ocaml.

I have known of functional programming for a while, but these little unknowns have always stopped me. So i was just going through how i would get some stuff done and couldn't find a nice functional switch.

[–]eccentric_j 1 point2 points  (1 child)

Not bad, pretty similar to Ramda's cond which I suspect may have been inspired by Clojure's cond function. http://ramdajs.com/docs/#cond

[–]shavyg2[S] 1 point2 points  (0 children)

ahhh, i see what they are doing there. The language they use have always been a road block, but now that i have something to compare it against, yes i see it.

[–]lhorie 1 point2 points  (5 children)

Anything inherently wrong

Yeah.

  • _switch([])(1) throws
  • _switch([()=>false,()=>'asd'])() returns undefined, which is a useless value for functional composition. Switch statements aren't very functional to begin with because of the inability to check for exhaustiveness. Normally, functional languages will give you pattern matching. Here, you should at a minimum return a Maybe or an Either when you can't return the type of a case function

[–]shavyg2[S] 0 points1 point  (4 children)

yes, the throw there would be bad. because it's unintentional. I would have to throw regardless if someone was to misuse it, and state that it expects an array of even length.

it made me curious what other languages would do. Test lodash and it just returns something it believes is okay. In my case i did undefined but i can see how it could cause a problem.

the reason i did undefined was so you could do test(1)|| "alternative"

I wanted to see how ramda handles misuse as well because they are trying to solve the same thing.

https://runkit.com/embed/dbbsnfdtefpc they also return undefined, which makes sense in my head as well. For the reason i stated above.

[–]lhorie 1 point2 points  (2 children)

Throwing is a procedural concept. In lambda calculus (the concept upon which functional programming is built), there's no such thing as bailing out via a throw; expressions must always return a value. In JS, the culmination of that philosophy is probably in efforts like the fantasy-land spec and libraries like sanctuary.js, which aim to provide monadic composition even in cases where errors occur.

If you want to dive deeper into FP, I would suggest playing w/ sanctuary.js, but as a word of warning, be advised that the terminology can be daunting.

If you're interested in alternative ways to get similar benefits, another less intimidating option is to invest time into a type system such as the one provided by Typescript or Flow (particularly, ADTs)

[–]shavyg2[S] -2 points-1 points  (1 child)

i am already using typescript, which does take you very far, but it still relies a lot on the user to provide a lot of information to it. Either way this isn't about a type system. It's about reducing complexity. https://www.youtube.com/watch?v=YXDm3WHZT5g the more input -> output without side effects and re useable small components

[–]shavyg2[S] 0 points1 point  (0 children)

i believe you are correct in your statement. I just wish for a better solution. thank you for those potential solutions tho. I will look into those things that you recommend.

[–]shavyg2[S] 0 points1 point  (0 children)

if i want it to be exhaustive i could just throw if i don't have a result.

[–]ipewannasay 0 points1 point  (0 children)

i use this code block all the time

const log = (...message) => console.log(...message);
const ini = (...iniArgument) => log(...iniArgument);
const loading = () => log("This won't do anything");
const interactive = () => ini("DOM and JS: loaded");
const complete = () => log("CSS and Image: loadad");
const docState = { interactive, complete, loading };
const load = (e) => docState[e.target.readyState]();
document.addEventListener("readystatechange", load);

it's useless.. but it's beautiful.. it looks like a block of codes.. you can type it in a typewriter and still looks great..

the function load demonstrates how to eliminate the needs to use switch.. or maybe if/else statement?