We can indeed see a let-expression as it were a sole contextual with-expression known in some languages, e.g, Python. So, there is a translation on syntax-level for:
let X = M
N
as:
with M as X:
N
With that general idea in mind, we can apply some sort of ownership on let-expressions if the target language supports:
- Abstract types, either through Existential types or Rank-N polymorphism
- HM-like type inference
To be sound, such languages will perform an escape analysis checking if some lexically introduced abstract type would escape its definition scope. Said that, we can pass such abstract type as it were a ghost type, for example, or even deliver a concrete type as an abstract one. The point is to the value to carry this locally introduced type information, together with that, type systems will disallow such value from escaping the abstract type definition's scope (surely, we must disable implicit substituitions such unification, this is the why of encoding the value as an "abstract one").