The only other language where I’ve seen this is Factor, with its “undo” word. I presume they work similarly, so I can speak to the general idea.
It’s not that the compiler can invert a hash, or automatically derive a logarithm function from an exponentiation function, or anything like that.
Essentially, if you have a composition of functions in a “pipeline” (h ∘ g ∘ f):
[ f g h ]
Then to get the inverse of the composition, you just invert each function and reverse the whole thing (f⁻¹ ∘ g⁻¹ ∘ h⁻¹):
[ f g h ] undo
[ \ h undo \ g undo \ f undo ]
“undo” is defined for a set of primitive words. If there isn’t an inverse defined for some function you used in the composition, then it fails; however, you can just define a custom inverse for your function. And obviously this relies on having some kind of reified representation of functions available.
“undo” can be used for things like pattern-matching: a destructuring function, which takes some value and produces its fields, is the inverse of a constructor, which takes the fields and constructs a value.
It’s not that the compiler can invert a hash, or automatically derive a logarithm function from an exponentiation function, or anything like that.
Essentially, if you have a composition of functions in a “pipeline” (h ∘ g ∘ f):
Then to get the inverse of the composition, you just invert each function and reverse the whole thing (f⁻¹ ∘ g⁻¹ ∘ h⁻¹): “undo” is defined for a set of primitive words. If there isn’t an inverse defined for some function you used in the composition, then it fails; however, you can just define a custom inverse for your function. And obviously this relies on having some kind of reified representation of functions available.“undo” can be used for things like pattern-matching: a destructuring function, which takes some value and produces its fields, is the inverse of a constructor, which takes the fields and constructs a value.