Making interfaces structural

There’s at least two issues with how interfaces currently work.

  1. Overrides. I’ve argued before how override(A, B) is often unnecessary (Thoughts on override(A, B) syntax). Interfaces are one very clear instance of this. It should never be necessary to write override(AnInterface, ...) because there is nothing to disambiguate in terms of implementation.

  2. Conflicting imports. Contracts often declare their arguments using interface types instead of addresses to be explicit about the functions that are expected from a contract. This creates unnecessary issues when the interface type present at the call site is structurally the same but nominally different from the one used at the function declaration site. Example: https://twitter.com/PaulRBerg/status/1685352565678931969

These two issues could be solved by turning interfaces into a construct that is structurally typed. In this model, a contract that implements the functions needed for an interface would be automatically considered to implement that interface in the type system, and would be automatically cast to it when necessary.

It should still be possible to explicitly state that a contract implements an interface, to have the compiler check that the contract definition satisfies the interface. This should not, however, affect linearization at all (for which there is a now-closed issue #13136).

No syntactic change seems strictly necessary, but there is a question about whether interfaces should still be listed in the is ... part of a contract definition. Currently interfaces are a kind of contract, and it’s debatable whether under this proposal they should continue to be seen as such. The alternative would be to have a form like contract Foo implements IFoo is Parent1, Parent2 .... I would propose to keep them as part of the is ... statement to avoid introducing a new syntactic form.

7 Likes

Big time in favor of this proposal. It would make Solidity a better language to write libraries for.

3 Likes

I’d generally be in favour of disentangling external interfaces from contracts and especially from inheritance - however, I’m a bit sceptical about automatically instantiating contract types to interfaces by merit of them defining the required functions - I’d prefer that to generally remain explicit. You can have structurally identical interfaces that are semantically different or even more easily have two independent interfaces combine to a third interface that semantically differs from the combination of the two - I’d rather make it easier (or possible without pains in the first place) to specifically declare two interfaces identical (which currently is a hassle). I’m wondering whether out-of-declaration statements indicating that contracts implement interfaces would be a good idea - I’m generally wondering whether in the long run, “contracts” may better be defined by explicitly synthesizing an external interface/dispatch that merely acts on separately defined storage areas, even though that would be a more aggressive change to the current language.

This might be true. One possible example that often shows up is the transferFrom(address,address,uint256) function that is defined both in ERC-20 and ERC-721 with completely different semantics (uint256 amount vs uint256 tokenId). However, the interfaces have other functions that clearly draw a distinction, e.g. IERC721 would have ownerOf(uint256). So I don’t think this should be a concern. In fact, if your contract only uses the transferFrom function it is the case that it will also work with ERC-721 tokens and the language would be faithfully representing that.

+1 that overriding interfaces is redundant, and the compiler resolving diffs in interfaces by checking their definitions.

At least, or until resolved, errors like shown in the screenshot below could be more explicit…

1 Like