Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality to view all public declarations in a contract and introspect Type values for interface conformance #2201

Closed
dreamsmasher opened this issue Dec 20, 2022 · 9 comments

Comments

@dreamsmasher
Copy link
Contributor

Issue To Be Solved

Per an internal conversation with Dete, the new NFT proposal requires developers to add a method on their contract that returns all the NFT types defined in the contract. This is error-prone and manual, so it would be nice to include some introspection mechanism in Cadence to save developers from having to enumerate these by hand.

Suggested Solution

We currently expose .contracts.names: [String] on AuthAccount and PublicAccount, but not much else. This issue proposes an extension method on {Auth,Public}Account.contracts:

fun publicDeclarations(): [Type]

that returns a list of all decls (functions, structs, interfaces, resources, etc.) in the contract. Alternatively, we could split each of those different declaration types into their own getters (this example uses dictionaries, but we could simply return an array instead since Types carry the identifiers of their proxied types):

fun publicFunctionDeclarations(): {String: Type}

fun publicStructDeclarations(): {String: Type}

...

By adding these features, developers can query a contract for all its declared structs. The second part of the problem is filtering through these declarations to find out which types actually conform to the NFT interface.

Currently, Type values only have two members: identifer: String and fun isSubType(of otherType: Type): Bool. In order to simplify the NFT V2 spec, Type values should have a method

fun<T> Type<T>.conformsToInterface(_ interface_: Type): Bool

that performs a runtime interface check on the type. This is doable in Cadence itself, e.g.

fun conformsToInterface(_ scrutinee: Type, _ interfaceType: Type): Bool {
  return scrutinee.isSubtype(of: interfaceType)
}

pub struct interface Foo {
  pub bar: Int
}

pub struct Baz: Foo {
  pub var bar: Int
  ...
}

let bazIsFoo = conformsToInterface(Type<Baz>(), Type<AnyStruct{Foo}>())

but ideally, a way should be provided for an interface type to be provided directly. Currently, attempting to instantiate a Type object with an interface gives

Type<Foo>()
// error: invalid use of interface as type
// got `Foo`; consider using `AnyStruct{Foo}`

Interfaces exist in the type namespace, so a new proxy type Interface could serve as the equivalent for interfaces what Type is to types.

With this, conformsToInterface could have the signature

fun<T, I> Type<T>.conformsToInterface(_ interfaceProxy: Interface<T>): Bool
@dreamsmasher
Copy link
Contributor Author

This is a blocker for the implementation of the NFT V2 spec, due to the fact that it replaces the proposed requirement that contract developers manually export a list of all NFT's.

@turbolent
Copy link
Member

Maybe split this issue into two, e.g. by making this an epic and creating two sub-issues, as two separate features are proposed:

  • Add introspection functions for types of contract
  • Add convenience function to check if type implements interface

@turbolent
Copy link
Member

turbolent commented Jan 3, 2023

Re: introspection for types of a contract: Those new definitions can be computed fields, e.g. let publicDeclarations: [Type]

@dete
Copy link

dete commented Jan 5, 2023

I'm not sure we need conformsToInterface if it's basically just an alias for isSubtype. But that's an aesthetic question that I leave to the language design experts, sounds like the functionality we need is already there.

I'd be inclined to start with a single function that returns the publicly defined types (structs and resources). Over time, I'm sure we'll add additional introspection functionality, but the MVP can be a simple list of data types, I think. (My hunch is that it's inappropriate to be able to query privately declared types, even in some future version of introspection. But I could probably be convinced otherwise!)

FWIW- My preference is a function that returns an array vs. a global field, since a global field "looks" mutable (and attempts to modify that array will only fail dynamically, I believe).

@bjartek
Copy link
Contributor

bjartek commented Jan 6, 2023

Nice change!

@dreamsmasher
Copy link
Contributor Author

yeah, I go over the subtype thing a little bit. But my personal opinion is that adding a specific function for checking interface conformance is more clear than using a dummy restricted type. There's also some talks of overhauling/removing restricted types entirely with the proposed auth fields FLIP: onflow/flips#54

@j1010001
Copy link
Member

I love the snappy title of this EPIC

@dsainati1 dsainati1 removed the Good First Issue Good for newcomers label Apr 14, 2023
@j1010001
Copy link
Member

#2233 is nice to have, not blocking new FT/NFT standards

@j1010001
Copy link
Member

j1010001 commented Jun 1, 2023

Old Zenhub EPIC, closing, just keeping the #2233 issue open

@j1010001 j1010001 closed this as completed Jun 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants