This is a phrase I first heard from David Khourshid in his talk at React Rally 2017 Infinitely Better UIs with Finite Automata: "Make impossible states impossible" (super great talk by the way, and xstate is awesome, and David is too). Googling around it looks like it's a pretty popular phrase in the Elm community, though I'm not sure who said it first.
To illustrate what this means, let's checkout at a very simple example:
<Alert>Just FYI</Alert>
<Alert success>It worked!</Alert>
<Alert warning>Head's up</Alert>
<Alert danger>Watch out!</Alert>
You may have used or written a component that has this kind of API. It's nice
and clean (in case you're unfamiliar, in JSX, a non-assigned prop like that is
the same as assigning it to the value "true", so success
is the same as
success={true}
).
Here's where this kind of API falls over. What should I render with this?
<Alert success warning>
It worked!
</Alert>
Should it blend the colors? Should I choose one? Which do I choose? Or should we just yell at the developer trying to do this because they obviously don't know what they're doing? (tip: that last one is NOT what you should do).
The idea of making impossible states impossible basically means that situations and questions like these should never come up. It means that you design APIs that make a clear distinction between the possible states of a component. This makes the component easier to maintain and to use.
So what do we do with our simple example? Whelp, all these different props
represent is the type of alert that should be rendered. So what if instead of
simply accepting the prop itself, we accept a type
prop?
<AlertBetter>Just FYI</AlertBetter>
<AlertBetter type="success">It worked!</AlertBetter>
<AlertBetter type="warning">Head's up</AlertBetter>
<AlertBetter type="danger">Watch out!</AlertBetter>
Now it's impossible to have more states than one because there are only three
valid values for the type
prop (well, four if you count undefined
)! It's
easier to maintain, easier to explain/understand, and harder to mess up.
Everyone wins!
Conclusion
There are various ways to do this effectively and converting a boolean value to an enum is only one such mechanism. It can and does get more complicated, but the concept can seriously simplify your component's and application's state. This is why I recommend you give David's talk a watch (here it is again). And give xstate a solid look as well. There's some good ideas in that! Good luck!
P.S. After publishing this, several people noted that this phrase was the title of a talk from Richard Feldman at Elm Conf. There's also a talk by Patrick Stapfer from ReasonML Munich Meetup. You may also be interested to check out a similar blog post by @stereobooster.
P.S.P.S. Further revelation about the origin of a similar phrase:
Bonus:
Here's a part from my section in the babel 7 blog post that didn't make the final cut but I thought you'd enjoy:
Here's a very simple example of a custom macro that swaps line
and column
imports with the line or column where it appears in the code:
// line-column.macro
module.exports = createMacro(lineColumnMacro)
function lineColumnMacro({ references, babel }) {
references.line.forEach((referencePath) => {
const num = referencePath.node.loc.start.line
referencePath.replaceWith(babel.types.numberLiteral(num))
})
references.column.forEach((referencePath) => {
const num = referencePath.node.loc.start.column
referencePath.replaceWith(babel.types.numberLiteral(num))
})
}
And then this code:
`import {line, column} from './line-column.macro'
`
``console.log(`we're at ${line}:${column}`)
console.log(`and now we're at ${line}:${column}`)``
This will be transpiled into:
console.log(`we're at ${3}:${32}`)
console.log(`and now we're at ${4}:${40}`)
And we don't need to change any config to add that custom transform. Writing and using code transforms is a fair amount easier this way. You can't do everything that a full babel plugin can do, but we're only just getting started with this and look forward to what the community will do with this power.
Play around with the above macro example on
astexplorer.net.
See if you can make it support an import called lineColumn
that is replaced
with the sum of line and column. Just for fun!