It's not a comparison to bash non-ownership as "very wrong", but arguing that lexical scope and ownership are literally the equivalent things.
Lexical scope allows you to reason about scope on a syntactical level.
Take this code:
let x = 5;
fn foo():
return x \* 2
fn bar():
let x = 10
return foo()
fn baz():
let x = "hello world!"
return foo()
bar()
With lexical scope, you can look at `foo` and you know immediately what it returns, the behaviour of the function is completely local to it and is a syntactical property of the program.
With lexical scope you just don't know what's returned, for all you know x might not even be a number, but could be any type that just happens to be brought into scope.
Ownership is similar, in that you make the existence of a value a local property.
If a 'thing' is inside a variable then you can move it somewhere else, but it also means that it is no longer in that variable. And that tracking of where what is, is a syntactical property, just like with lexical scope.
Clojure has software transactional memory. With ownership it wouldn't need half of the machinery (only that for rollback):
let blocked_accounts = Set()
let account_bob = new ExclusiveBankAccount(200)
let account_alice = new ExclusiveBankAccount(600)
// ExclusiveBankAccount cannot be copied or cloned
fn transfer(source_acc, target_acc, ammount):
if source_acc.deduce(amount.copy()):
target_acc.deposit(amount.copy())
fn block(acc):
let acc_identifier = acc.identifier.copy() // identifiers can be copied and are not exclusive
blocked_accounts.put(acc)
return acc_identifier
fn unblock(acc):
return blocked_accounts.take(acc)
fn do_invalid_stuff():
let good_account = new ExclusiveBankAccount(200)
let bad_account = new ExclusiveBankAccount(200)
block(bad_account)
transfer(bad_account, good_account) // <- this will fail because bad_account was moved by the block function and no longer exists here, it's invalid syntactically
fn do_valid_stuff():
let good_account = new ExclusiveBankAccount(200)
let bad_account = new ExclusiveBankAccount(200)
let ident = block(bad_account)
let restored_account = unblock(ident)
transfer(restored_account, good_account, 100)
The above code makes sure that:
- you can only transfer funds between two accounts in a thread safe way
- you can only transfer funds between accounts that are not blocked
Simply by not allowing something like this on a syntactical level you get a much cleaner understanding of what your code does. After a while you're wondering why we allowed anything else in the first place.
let x = thing();
let y = x; // the thing is moved from x to y here
print(x)
The fact that automatic memory management falls out of this is almost accidental, if you can track where a value is at any given time, you can also track the references to it, and the resources it uses. But that is not what truly makes it amazing, the fact that you can mentally think about digital objects as if they were physical objects is.