
187Composition Lens
Example in code
JSON.parse() is the inverse of JSON.stringify():
let object   = anyObject(); 
let asString = JSON.stringify(object); 
let parsed   = JSON.parse(asString); 
assert(_.isEqual(object, parsed));
In  the  coee  shop  orders,  removeAddIn() is the inverse of 
addAddIn():
let coffee = anyCoffee(); 
let addIn  = anyAddIn(); 
assert(_.isEqual(coffee.addAddIn(addIn).removeAddIn(addIn), 
                 coffee));
Does the reverse hold? is addAddIn() the inverse of 
removeAddIn()? Let’s write the test:
let coffee = anyCoffee(); 
let addIn  = anyAddIn(); 
assert(_.isEqual(coffee.removeAddIn(addIn).addAddIn(addIn), 
                 coffee));
If we can nd a single test case where this doesn’t hold, the prop-
erty doesn’t hold. What if coee has no add-ins?
let coffee = { size: "mega", roast: "burnt", addIns: {}};
Now when you  remove an  add-in, it’s  a  no-op  (it does  nothing). 
But when you add the add-in aerwards, then it will have it. The 
result will be:
{ size: "mega", roast: "burnt", addIns: { almond: 1 }}
When the coee does not have the add-in, the property does not 
hold. But it does hold when it does have the add-in. This means we 
can write a partial property:
let coffee = anyCoffee(); 
let addIn  = anyAddIn(); 
if(coffee.hasAddIn(addIn)) 
  assert(_.isEqual(coffee.removeAddIn(addIn) 
                         .addAddIn(addIn), 
                   coffee)); 
else assert(true);
We prefer total properties, but 
this is probably as good as 
you can get for this property. 
Another option is to allow for 
negative add-ins, which would 
allow you to go below zero. 
However,  I  don’t  recommend 
this because the resulting cof-
fee  isn’t  meaningful  anymore. 
What does it mean to have -3 
soy milks?