Last active
April 12, 2021 13:25
-
-
Save dougpagani/a3e066494d8036cc8b7c51b103c58a25 to your computer and use it in GitHub Desktop.
MEAT & POTATOES: Trying out an idea of using closure-leveraging nested snippets for enhancing readability (name == hoisted helpers?)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// does not work because-hoisting.js | |
function calculateWinnerRecursion(squares, possibleWinConditions) { | |
if (noMorePossibleWinConditions()) { return null } | |
if (winConditionIsMet()) { return theWinner() } | |
return SELF(squares, possibleWinConditions.shift())) | |
// HOISTED HELPERS | |
let SELF = calculateWinnerRecursion | |
let theWinner = () => squares[possibleWinConditions[0][0]] | |
let noMorePossibleWinConditions = () => possibleWinConditions.length === 0 | |
let winConditionIsMet = () => { | |
squares[possibleWinConditions[0][0]] | |
&& squares[possibleWinConditions[0][0]] === squares[possibleWinConditions[0][1]] | |
&& squares[possibleWinConditions[0][0]] === squares[possibleWinConditions[0][2]] | |
} | |
} | |
function calculateWinner(squares) { | |
const WIN_CONDITIONS = [ | |
[0, 1, 2], | |
[3, 4, 5], | |
[6, 7, 8], | |
[0, 3, 6], | |
[1, 4, 7], | |
[2, 5, 8], | |
[0, 4, 8], | |
[2, 4, 6], | |
]; | |
return calculateWinnerRecursion (squares, WIN_CONDITIONS) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function calculateWinnerRecursion(squares, lines) { | |
if (lines.length === 0) { | |
return null | |
} | |
if (squares[lines[0][0]] && squares[lines[0][0]] === squares[lines[0][1]] && squares[lines[0][0]] === squares[lines[0][2]]) { | |
return squares[lines[0][0]]; | |
} | |
else { | |
return calculateWinnerRecursion(squares, lines.shift()) | |
} | |
} | |
function calculateWinner(squares) { | |
const LINES = [ | |
[0, 1, 2], | |
[3, 4, 5], | |
[6, 7, 8], | |
[0, 3, 6], | |
[1, 4, 7], | |
[2, 5, 8], | |
[0, 4, 8], | |
[2, 4, 6], | |
]; | |
return calculateWinnerRecursion (squares, LINES) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function calculateWinnerRecursion(squares, possibleWinConditions) { | |
let RECURSION = calculateWinnerRecursion | |
let thisWinCondition = possibleWinConditions[0] | |
if (noMorePossibleWinConditions()) { return null } | |
if (winConditionIsMet()) { return theWinner() } | |
return RECURSION(squares, possibleWinConditions.slice(1)) | |
// hoisted readability helpers -- need to be all thunks w/o main wrapper | |
function noMorePossibleWinConditions(){return (possibleWinConditions.length === 0)} | |
function theWinner(){return squares[thisWinCondition[0]]} | |
function winConditionIsMet(){return ( | |
squares[thisWinCondition[0]] | |
&& squares[thisWinCondition[0]] === squares[thisWinCondition[1]] | |
&& squares[thisWinCondition[0]] === squares[thisWinCondition[2]] | |
)} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function calculateWinnerRecursion(squares, possibleWinConditions) { | |
function MAIN() { // MEAT() | |
if (noMorePossibleWinConditions()) { return null } | |
if (winConditionIsMet()) { return theWinner() } | |
return RECURSION(squares, possibleWinConditions.slice(1)) | |
} | |
// HELPERS -- no longer need to all be thunks | |
const noMorePossibleWinConditions = possibleWinConditions.length === 0 | |
const theWinner = squares[thisWinCondition[0]] | |
const winConditionIsMet = ( | |
squares[thisWinCondition[0]] | |
&& squares[thisWinCondition[0]] === squares[thisWinCondition[1]] | |
&& squares[thisWinCondition[0]] === squares[thisWinCondition[2]] | |
) | |
const RECURSION = calculateWinnerRecursion | |
const thisWinCondition = possibleWinConditions[0] | |
return MAIN() // MEAT() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function calculateWinnerRecursion(squares, possibleWinConditions) { | |
function MEAT() { | |
if (noMorePossibleWinConditions()) { return null } | |
if (winConditionIsMet()) { return theWinner() } | |
return RECURSION(squares, possibleWinConditions.slice(1)) | |
} | |
// POTATOES -- all hunks so post-base-case-code doesn't get executed | |
const noMorePossibleWinConditions = possibleWinConditions.length === 0 | |
const theWinner = squares[thisWinCondition[0]] | |
const winConditionIsMet = ( | |
squares[thisWinCondition[0]] | |
&& squares[thisWinCondition[0]] === squares[thisWinCondition[1]] | |
&& squares[thisWinCondition[0]] === squares[thisWinCondition[2]] | |
) | |
const RECURSION = calculateWinnerRecursion | |
const thisWinCondition = possibleWinConditions[0] | |
return MEAT() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// We need a double-nester because we need the enclosing | |
// ... scope for the dynamically-changing, UNREFERENCED possibleWinConditions. | |
// We could get away without this, but it would hamper readability, | |
// ... which is the core purpose of the thunks. | |
function calculateWinner(squares) { | |
// could be dynamically-generated win conditions (1:row,2:column,3:diagonal) | |
const WIN_CONDITIONS = [ | |
[0, 1, 2], | |
[3, 4, 5], | |
[6, 7, 8], | |
[0, 3, 6], | |
[1, 4, 7], | |
[2, 5, 8], | |
[0, 4, 8], | |
[2, 4, 6], | |
]; | |
return TRY(WIN_CONDITIONS) | |
function TRY(possibleWinConditions) { | |
function meat(){ | |
if (noMorePossibleWinConditions()) {return null} | |
if (thisWinConditionIsMet()) {return theWinner()} | |
else {return RECUR()} | |
} | |
// HELPER THUNKS | |
const RECUR = () => TRY(possibleWinConditions.slice(1)) | |
const noMorePossibleWinConditions = () => possibleWinConditions.length === 0 | |
const thisWinConditionIsMet = () => ( | |
// could iterate from 0+1->n(thisWinCondition) | |
squareValueOfThisWinConditionPosition(0) | |
&& squareValueOfThisWinConditionPosition(0) === squareValueOfThisWinConditionPosition(1) | |
&& squareValueOfThisWinConditionPosition(0) === squareValueOfThisWinConditionPosition(2) | |
) | |
const thisWinCondition = () => possibleWinConditions[0] | |
const theWinner = () => squareValueOfThisWinConditionPosition(0) | |
const squareValueOfThisWinConditionPosition = (i) => squares[thisWinCondition()[i]] | |
return meat() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"Functions cooked to perfection" -- Thunks + closures is a beautiful thing. | |
Rules: | |
- small (otherwise you lose context that you're even in a nested function b/c you dont notice indentation) | |
- no side effects (especially communicating b/t different nested functions) | |
- no accumulators | |
- only for readability | |
- the function is written first without them | |
- their purpose is to access, as per the closure, variables | |
- good use cases: error reporters, complex conditionals, long-winded getters | |
- dont take in variables -- just use the closure | |
Benefits: | |
- readability | |
- guaranteed to move together (cohesive/tight-coupling) | |
- UNTESTABLE for things which shouldn't be tested | |
- awesome stack traces (named logical bits) | |
We use hunks, like MEAT(), to defer execution until later. | |
Nothing in here (the bits) needs to be deferred. But say, this were a http-request handler... you'd want your reportUnexpectedError() logger function + its noisy preamble where you structure the message/data payload, named & deferred for when it actually is hit. | |
It's all about readability of the meat. And, you want to highlight & lift to the top. | |
Actually there is one that needs to be deferred -- the recursive call. | |
IMPORTANT NOTE: to be safe, it's better to defer everything. That way nothing unexpected happen. For example, if you don't defer a dangerous case (e.g. indexing an empty array), as you normally would with the potatoes mixed-in directly, you may hit an otherwise-impossible calculation. Or, simply spoken, this is the most efficient way to do it since you shouldn't be executing predicated code anyways. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// does not work because-hoisting.js | |
function calculateWinnerRecursion(squares, possibleWinConditions) { | |
// base case -- empty array means no more possible ways to win, game goes on | |
if (possibleWinConditions.length === 0) { | |
return null | |
} | |
// Win condition is met | |
if ( | |
squares[possibleWinConditions[0][0]] | |
&& squares[possibleWinConditions[0][0]] === squares[possibleWinConditions[0][1]] | |
&& squares[possibleWinConditions[0][0]] === squares[possibleWinConditions[0][2]] | |
) { | |
return squares[possibleWinConditions[0][0]] | |
} | |
return calculateWinnerRecursion(squares, possibleWinConditions.shift())) | |
} | |
function calculateWinner(squares) { | |
const WIN_CONDITIONS = [ | |
[0, 1, 2], | |
[3, 4, 5], | |
[6, 7, 8], | |
[0, 3, 6], | |
[1, 4, 7], | |
[2, 5, 8], | |
[0, 4, 8], | |
[2, 4, 6], | |
]; | |
return calculateWinnerRecursion (squares, WIN_CONDITIONS) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment