status | flip | authors | sponsor | updated |
---|---|---|---|---|
implemented |
134 |
Supun Setunga ([email protected]) |
Supun Setunga ([email protected]) |
2023-10-24 |
A previous FLIP (https://github.com/onflow/flips/blob/main/cadence/20230503-improve-conformance.md) improved interface conformance by allowing conditions defined in other interfaces to coexist with a default function implementation coming from a different interface.
This FLIP proposes to relax this restriction further, by also allowing empty function declarations defined in other interfaces to coexist with a default function implementation coming from a different interface.
Assume there are two interfaces, Receiver
interface declares an empty isSupportedVaultType
function.
The second interface Vault
conforms to the Receiver
interface and provides a default implementation
to the isSupportedVaultType
function.
access(all) resource interface Receiver {
access(all) view fun isSupportedVaultType(): {Type: Bool}
}
access(all) resource interface Vault: Receiver { // Static error
access(all) view fun isSupportedVaultType(): {Type: Bool} {
// Implementation...
}
}
access(all) resource VaultImpl: Vault {}
Currently, this reports an error saying `isSupportedVaultType` function of `Vault` conflicts with a function with the same name in `Receiver`
.
However, it is possible to make this work by adding a dummy condition to the function in Receiver
.
e.g.
access(all) resource interface Receiver {
access(all) view fun isSupportedVaultType(): {Type: Bool} {
pre { true }
}
}
access(all) resource interface Vault: Receiver { // OK
access(all) view fun isSupportedVaultType(): {Type: Bool} {
// Implementation...
}
}
access(all) resource VaultImpl: Vault {}
Likewise, restricting empty declarations from coexisting with default functions doesn't really add any value or safety, as it can be workaround by adding a condition. Rather, it only reduces the composability.
It can be useful to have interfaces that only declare the function, while another interface defines default functions. Relaxing the current restriction allows user to use this pattern.
When interface inheritance is supported, the chance of running into similar situations would be more frequent.
Here it is proposed to relax the current restriction and allow emtpy function declarations defined in one interface to coexist with a default function implementation coming from another interface.
access(all) resource interface Receiver {
access(all) view fun isSupportedVaultType(): {Type: Bool}
}
access(all) resource interface Vault: Receiver { // This would be valid
access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
// Implementation...
}
}
access(all) resource VaultImpl: Vault {} // This would be valid
Note that, above would be equivalent to, and hence is also allowed to have:
access(all) resource interface Receiver {
access(all) view fun isSupportedVaultType(): {Type: Bool}
}
access(all) resource interface Vault {
access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
// Implementation...
}
}
access(all) resource VaultImpl: Vault, Receiver {} // This would be valid
However, to avoid any confusion of overriding of default functions, forbid defining an empty function declaration in the descendant, if there is already a default function from an inherited interface. i.e:
access(all) resource interface Receiver {
access(all) view fun isSupportedVaultType(): {Type: Bool} {
// Implementation...
}
}
access(all) resource interface Vault: Receiver { // Static error
access(all) view fun isSupportedVaultType(): {Type: Bool}
}
Some concerns were discussed in: onflow/cadence#2471
None
None
None
This change is trivial in the implementation.
This proposal suggests a relaxation of an existing restriction. Hence, no existing codes would break.
None
None
None
None