It gets complicated as soon as there’s a pointer involved
Do you compare the pointer for equality, or do you follow it and deep-compare what’s on the other side?
What about the next pointer jump? What if there’s a class instance? What if there’s a reference cycle?
Python lets you implement custom logic in __eq__() to manually bridge some of these gaps, but doing that for everything can get out of hand. Also, deeply-traversing large objects to check equality can get arbitrarily expensive. Immutable structures simplify these comparisons (just compare everything as a value, any change will mean a new pointer), but obviously introduce certain constraints.
By tracking observed properties of mutable structures at runtime, and then publishing cache invalidations only when they change, you can:
- Do no extra work to know whether or not a cache has been invalidated
- Automatically and perfectly track everything the function “cares about”
- Track only the things it cares about, so changes to properties it doesn’t actually use won’t invalidate the cache
See the MobX docs for more (I’m not closely familiar with Skip directly, but from what I’ve seen it works on very similar principles): https://mobx.js.org/understanding-reactivity.html