Skip to main content
No need to signal edits in the post, the revision history is there if it becomes relevant; Clear up the spelling and emphasis of "IF"
Source Link

Without keeping a big table of all the objects you've copied so far, you can't safely deep copy objects at all if there may be circular references.

where every object always has a copy method

Is this true of everything? In which languages? Doesn't this mess with e.g. lock objects? Streams? Use of the RAII pattern? Handles to operating system objects? Intentional singletons? There's lots of cases where you may legitimately want to prevent objects being copied at all.

EDIT

 

I'd also like to push back on

Shouldn't the default always be optimized for safety and not for performance? Isn't optimizing copy for speed premature optimization?

Deep copy can get arbitrarily expensive. You can't overlook that entirely.

But there are lots of different opinions about "safety". Quite a lot of functional programming and FP influence in modern languages moves towards immutability. Objects once created aren't changed. This is a more mathematical view; if you have f(x) = y then it is always true that f(x) = y, and none of f, x, or y are mutable.

IFIf an object is immutable, there is no point in copying it. You can just pass around references to it. You only ever produce mutated copies, instead of changing values within the object. You start thinking more like value types: nobody asks how many different copies of "2" there are in their program, nor do they care about deep vs shallow copy of "2".

Then we consider the reverse operation: interning. Sometimes people ask how many copies of the string "hello" there are in their program, and would find it convenient if all references to "hello" returned the same object. https://en.wikipedia.org/wiki/String_interning ; this makes deep copy meaningless, because in that situation you only ever want one copy of the interned string.

(Incidentally, it turns out that Java does care about how many copies of "2" there are in your program if you start boxing them - all Int(2) are the same object. But not all Int(2000).)

I expect that when I later on pass the same copy to the same method, it will behave exactly the same but that isn't guaranteed, as the objects the array references may have changed, despite me having never changed them in my code

Immutability fixes this.

In Haskell, the default containers are immutable, and it's quite hard to write a mutable one. You don't add an item to a list, you create a new list containing the contents plus the new item. https://www.fpcomplete.com/haskell/library/containers/

Rust takes a different approach: every reference is annotated with ownership and mutability semantics. So you can call methods confident in the knowledge that they won't mutate particular objects.

Without keeping a big table of all the objects you've copied so far, you can't safely deep copy objects at all if there may be circular references.

where every object always has a copy method

Is this true of everything? In which languages? Doesn't this mess with e.g. lock objects? Streams? Use of the RAII pattern? Handles to operating system objects? Intentional singletons? There's lots of cases where you may legitimately want to prevent objects being copied at all.

EDIT

I'd also like to push back on

Shouldn't the default always be optimized for safety and not for performance? Isn't optimizing copy for speed premature optimization?

Deep copy can get arbitrarily expensive. You can't overlook that entirely.

But there are lots of different opinions about "safety". Quite a lot of functional programming and FP influence in modern languages moves towards immutability. Objects once created aren't changed. This is a more mathematical view; if you have f(x) = y then it is always true that f(x) = y, and none of f, x, or y are mutable.

IF an object is immutable, there is no point in copying it. You can just pass around references to it. You only ever produce mutated copies, instead of changing values within the object. You start thinking more like value types: nobody asks how many different copies of "2" there are in their program, nor do they care about deep vs shallow copy of "2".

Then we consider the reverse operation: interning. Sometimes people ask how many copies of the string "hello" there are in their program, and would find it convenient if all references to "hello" returned the same object. https://en.wikipedia.org/wiki/String_interning ; this makes deep copy meaningless, because in that situation you only ever want one copy of the interned string.

(Incidentally, it turns out that Java does care about how many copies of "2" there are in your program if you start boxing them - all Int(2) are the same object. But not all Int(2000).)

I expect that when I later on pass the same copy to the same method, it will behave exactly the same but that isn't guaranteed, as the objects the array references may have changed, despite me having never changed them in my code

Immutability fixes this.

In Haskell, the default containers are immutable, and it's quite hard to write a mutable one. You don't add an item to a list, you create a new list containing the contents plus the new item. https://www.fpcomplete.com/haskell/library/containers/

Rust takes a different approach: every reference is annotated with ownership and mutability semantics. So you can call methods confident in the knowledge that they won't mutate particular objects.

Without keeping a big table of all the objects you've copied so far, you can't safely deep copy objects at all if there may be circular references.

where every object always has a copy method

Is this true of everything? In which languages? Doesn't this mess with e.g. lock objects? Streams? Use of the RAII pattern? Handles to operating system objects? Intentional singletons? There's lots of cases where you may legitimately want to prevent objects being copied at all.

 

I'd also like to push back on

Shouldn't the default always be optimized for safety and not for performance? Isn't optimizing copy for speed premature optimization?

Deep copy can get arbitrarily expensive. You can't overlook that entirely.

But there are lots of different opinions about "safety". Quite a lot of functional programming and FP influence in modern languages moves towards immutability. Objects once created aren't changed. This is a more mathematical view; if you have f(x) = y then it is always true that f(x) = y, and none of f, x, or y are mutable.

If an object is immutable, there is no point in copying it. You can just pass around references to it. You only ever produce mutated copies, instead of changing values within the object. You start thinking more like value types: nobody asks how many different copies of "2" there are in their program, nor do they care about deep vs shallow copy of "2".

Then we consider the reverse operation: interning. Sometimes people ask how many copies of the string "hello" there are in their program, and would find it convenient if all references to "hello" returned the same object. https://en.wikipedia.org/wiki/String_interning ; this makes deep copy meaningless, because in that situation you only ever want one copy of the interned string.

(Incidentally, it turns out that Java does care about how many copies of "2" there are in your program if you start boxing them - all Int(2) are the same object. But not all Int(2000).)

I expect that when I later on pass the same copy to the same method, it will behave exactly the same but that isn't guaranteed, as the objects the array references may have changed, despite me having never changed them in my code

Immutability fixes this.

In Haskell, the default containers are immutable, and it's quite hard to write a mutable one. You don't add an item to a list, you create a new list containing the contents plus the new item. https://www.fpcomplete.com/haskell/library/containers/

Rust takes a different approach: every reference is annotated with ownership and mutability semantics. So you can call methods confident in the knowledge that they won't mutate particular objects.

added 2244 characters in body
Source Link
pjc50
  • 15.3k
  • 1
  • 37
  • 40

Without keeping a big table of all the objects you've copied so far, you can't safely deep copy objects at all if there may be circular references.

where every object always has a copy method

Is this true of everything? In which languages? Doesn't this mess with e.g. lock objects? Streams? Use of the RAII pattern? Handles to operating system objects? Intentional singletons? There's lots of cases where you may legitimately want to prevent objects being copied at all.

EDIT

I'd also like to push back on

Shouldn't the default always be optimized for safety and not for performance? Isn't optimizing copy for speed premature optimization?

Deep copy can get arbitrarily expensive. You can't overlook that entirely.

But there are lots of different opinions about "safety". Quite a lot of functional programming and FP influence in modern languages moves towards immutability. Objects once created aren't changed. This is a more mathematical view; if you have f(x) = y then it is always true that f(x) = y, and none of f, x, or y are mutable.

IF an object is immutable, there is no point in copying it. You can just pass around references to it. You only ever produce mutated copies, instead of changing values within the object. You start thinking more like value types: nobody asks how many different copies of "2" there are in their program, nor do they care about deep vs shallow copy of "2".

Then we consider the reverse operation: interning. Sometimes people ask how many copies of the string "hello" there are in their program, and would find it convenient if all references to "hello" returned the same object. https://en.wikipedia.org/wiki/String_interning ; this makes deep copy meaningless, because in that situation you only ever want one copy of the interned string.

(Incidentally, it turns out that Java does care about how many copies of "2" there are in your program if you start boxing them - all Int(2) are the same object. But not all Int(2000).)

I expect that when I later on pass the same copy to the same method, it will behave exactly the same but that isn't guaranteed, as the objects the array references may have changed, despite me having never changed them in my code

Immutability fixes this.

In Haskell, the default containers are immutable, and it's quite hard to write a mutable one. You don't add an item to a list, you create a new list containing the contents plus the new item. https://www.fpcomplete.com/haskell/library/containers/

Rust takes a different approach: every reference is annotated with ownership and mutability semantics. So you can call methods confident in the knowledge that they won't mutate particular objects.

Without keeping a big table of all the objects you've copied so far, you can't safely deep copy objects at all if there may be circular references.

where every object always has a copy method

Is this true of everything? In which languages? Doesn't this mess with e.g. lock objects? Streams? Use of the RAII pattern? Handles to operating system objects? Intentional singletons? There's lots of cases where you may legitimately want to prevent objects being copied at all.

Without keeping a big table of all the objects you've copied so far, you can't safely deep copy objects at all if there may be circular references.

where every object always has a copy method

Is this true of everything? In which languages? Doesn't this mess with e.g. lock objects? Streams? Use of the RAII pattern? Handles to operating system objects? Intentional singletons? There's lots of cases where you may legitimately want to prevent objects being copied at all.

EDIT

I'd also like to push back on

Shouldn't the default always be optimized for safety and not for performance? Isn't optimizing copy for speed premature optimization?

Deep copy can get arbitrarily expensive. You can't overlook that entirely.

But there are lots of different opinions about "safety". Quite a lot of functional programming and FP influence in modern languages moves towards immutability. Objects once created aren't changed. This is a more mathematical view; if you have f(x) = y then it is always true that f(x) = y, and none of f, x, or y are mutable.

IF an object is immutable, there is no point in copying it. You can just pass around references to it. You only ever produce mutated copies, instead of changing values within the object. You start thinking more like value types: nobody asks how many different copies of "2" there are in their program, nor do they care about deep vs shallow copy of "2".

Then we consider the reverse operation: interning. Sometimes people ask how many copies of the string "hello" there are in their program, and would find it convenient if all references to "hello" returned the same object. https://en.wikipedia.org/wiki/String_interning ; this makes deep copy meaningless, because in that situation you only ever want one copy of the interned string.

(Incidentally, it turns out that Java does care about how many copies of "2" there are in your program if you start boxing them - all Int(2) are the same object. But not all Int(2000).)

I expect that when I later on pass the same copy to the same method, it will behave exactly the same but that isn't guaranteed, as the objects the array references may have changed, despite me having never changed them in my code

Immutability fixes this.

In Haskell, the default containers are immutable, and it's quite hard to write a mutable one. You don't add an item to a list, you create a new list containing the contents plus the new item. https://www.fpcomplete.com/haskell/library/containers/

Rust takes a different approach: every reference is annotated with ownership and mutability semantics. So you can call methods confident in the knowledge that they won't mutate particular objects.

Source Link
pjc50
  • 15.3k
  • 1
  • 37
  • 40

Without keeping a big table of all the objects you've copied so far, you can't safely deep copy objects at all if there may be circular references.

where every object always has a copy method

Is this true of everything? In which languages? Doesn't this mess with e.g. lock objects? Streams? Use of the RAII pattern? Handles to operating system objects? Intentional singletons? There's lots of cases where you may legitimately want to prevent objects being copied at all.