FLIP: Cadence Extensions/Attachments

We discussed briefly in the Language Design Meeting the benefits/drawbacks of having dedicated syntax for attachment addition, removal and access. I’d like to summarize the reasons why I prefer the dedicated syntax:

  1. It makes clear that attachments are not a first class-value. We want to only allow attachments to be constructed inside of the same expression that attaches them, which is much harder to enforce and more confusing to users if that expression is just a function call. With the attach syntax, we can simply enforce in attach e1 to e2 that e1 is always a function call to an attachment constructor. However, if we had an attach method on resources/structs, we would need to enforce in e1.attach(e2) that e2 is always an attachment constructor call. It is less immediately obvious, I would argue, in the latter case why e2 cannot be an arbitrary expression, since this looks to a user just like a regular function call.

    To make matters worse, functions defined on structs/resources can be stored in variables: let x = s.foo, for example, when foo is a function on s. It becomes dramatically more complex to enforce that attach's argument must be an attachment constructor call when attach can be aliased like this: let x = r.attach, followed by an x(e) call. We would need to require that e's argument be an attachment constructor call, but to raise an error here is much more opaque to the user than if this were simply a dedicated syntax form.

  2. Removal and access of attachments on resource/structs is done by type rather than value: remove A from v and v[A]. These semantics would be fairly unusual to implement with functions, as the argument is a type rather than a value. It might be possible to support this with a type argument like v.remove<A>() or v.getAttachment<A>(), but this IMO is confusing because it conflates the behavior of type arguments and proper arguments. Compare to a function like account.borrow, which takes both a path argument as well a type argument. The path argument determines the actual runtime value that is returned, whereas the type argument simply restricts the type with which the returned value has statically. Accessing and removing attachments however has its runtime behavior depend on the type provided; v[A] and v[I] do different things at runtime, for example, even if A conforms to interface I. This is fundamentally different to what a type parameter is intended to do, so IMO it does not make sense to provide the attachment type as a function type argument this way.

    Relatedly, accessing and removing an attachment requires a nominal type expressing an attachment; which we are able to enforce in the remove A from v and the v[A] expressions. These simply require a type name rather than a type itself. However, if we were to use a type parameter style as discussed above, we would need to arbitrarily restrict these type parameters to only be attachment names, rather than arbitrary types as are normally valid in type arguments. This would be unusual behavior for imo little to no benefit.

I have submitted a new PR to make some further updates to the proposal. As I see things now there are 3 remaining questions that need resolution, all of which are fairly minor points regarding the details of the proposal:

  1. Is the current access control scheme wherein type restrictions are used to limit which attachments can be accessed on a type sufficient? Or do we need something else to allow users to limit who can access attachments on their resources?

  2. Should attach be an expression that returns a new value or a statement that modifies the existing value (see discussion on PR)?

  3. Given how this feature is intended to be used, is attachments still a good name for this feature? Or would it be more sensible to rename this again, perhaps back to extensions? Note that this would only change the name (and potentially the syntax for attach expressions back to extend or something else appropriate for the name), not the semantics at all.

Sorry I couldn’t attend to the last meeting, I had a crazy week.

It makes clear that attachments are not a first class-value. We want to only allow attachments to be constructed inside of the same expression that attaches them, which is much harder to enforce and more confusing to users if that expression is just a function call.

Attachments being not first class value part makes me feel like they are more of a Type (when I say Type I mean not the MetaType but Int in var x: Int ) So actually then attach e1 to e2 makes a lot of sense. ( though I personally prefer, extend e1 with e2 but it is not important in this discussion)

PS: Though attachments being not first class value ( as they will be able to hold resources ) makes me a bit nervous to be honest.

1 Like