You can deal with receiver private state since you are exporting methods on the receiver as part of your type signature. If those methods are called, the receiver can see itself, structural typing works fine and you still have encapsulation. Eg
interface Foo {
fun doFoo()
}
You can call doFoo() on some value, and that value can refer to its private state that doesn’t appear in Foo.
However, if you want to see the private data of an argument, private data has to appear in the signature (or use nominal typing). The easiest example is an equality method that compares private state.
interface Foo {
fun equals(otherFoo: Foo): Boolean
}
The receiver of the equals call can still refer to its private data, but whatever you a provided for otherFoo is only guaranteed to have the equals method. You might be able to deal with this isn’t an opaque type:
interface FooModule {
export type t
fun equals(foo: t, otherFoo: t): boolean
makeFoo(): t
}
But you really aren’t doing structural typing anymore, and basically are using t like you would a name.
You can deal with receiver private state since you are exporting methods on the receiver as part of your type signature. If those methods are called, the receiver can see itself, structural typing works fine and you still have encapsulation. Eg
You can call doFoo() on some value, and that value can refer to its private state that doesn’t appear in Foo.However, if you want to see the private data of an argument, private data has to appear in the signature (or use nominal typing). The easiest example is an equality method that compares private state.
The receiver of the equals call can still refer to its private data, but whatever you a provided for otherFoo is only guaranteed to have the equals method. You might be able to deal with this isn’t an opaque type: But you really aren’t doing structural typing anymore, and basically are using t like you would a name.