-
-
Notifications
You must be signed in to change notification settings - Fork 109
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
Implement Type Decorators #1100
base: main
Are you sure you want to change the base?
Conversation
My two lovelace:
So, I strongly believe this type of decorator should live around type definition, not around record field (or if anything, only as a syntactic sugar!). Internally though, it is the type that should carry this information. This will be a lot easier to implement as well, from type-checking to codegen and for the blueprint too. |
@KtorZ the naming thing I understand but I'm not following on the field thing with Right now the plan is to have these only really work during casting that is to say "at the boundary". So like we support two ways of using
the second one I believe fits into what you are describing? and from there is seems harmless to allow it to modify the field. so the only runtime behaviors for this would be that "at the boundary" an extra length check is generated. but the intention is not to have this work outside of this situation because having this behavior generally in other situations like when you instantiate new objects could produce large amounts of unnecessary checks which eat away at your budget in a non-transparent way. if I understood what you're saying, this does indeed do that but also a little more by being available on fields. you can always use this on an alias and then use the alias as the field's type, so not much of a difference functionally speaking. it's important to note as well, that as is implemented I consider the type checking done for this. meaning, I did not plan on doing any other things only the validation rules. after discussing with @MicroProofs we were happy with how it is and then we were going to just do code gen now. i didn't really want the type system aware of these decorators in any significant way which is why I don't perceive them to be refinement types. the length thing is mostly cute. the tag one is actually useful. this means that
would NOT be a I also was going to leave this information completely out of the blueprints. the end goal is just so that people can interop with other datums via controlling the and for the names, although we use the word constructor index in many places, I believe in practice most people call these tags and has been how people have asked for this feature. so it seemed natural to name it |
Sad story then, because the actual interesting feedback is about the field thing 😅 ... Let me give it another try. Clarifying intentAlready, thanks for clarifying the intent: it is not a global type invariant, but only something that happens at the boundary. So right off the bat, I think the syntax might be even more confusing because it suggests a global invariant. To convey the right idea, we probably need the syntax to hint at data somehow.
From there, it becomes clearer that these are only about the underlying serialization rules when interfacing with data. And we can quite easily extend this little DSL to cover, e.g. all of the validation rules specified in CIP-0057 (which would be relatively straightforward with this
The case of literals
I see where you're coming from, and the second part makes sense (and seems also way more approachable indeed). I would however argue that in this particular instance, we should raise a warning (or even an error); because it is straightforward to do. If assigning a literal into something we know to have a specific length, then a warning is the least we can do. We could push it a bit to constants (since they're compile-time executions, we could also very much measure the length of bytes produced from a constant, and carry that information throughout...), but let's leave that as an idea for now. ScopeMy main point in the previous comment was regarding the scope of those decorators. Right now, the scope is on fields and type-aliases. I did miss the type-aliases part initially, though it doesn't really change my point. Since these decorators are about restricting the domain of acceptable inputs, they should belong to types. The constructor annotations are necessary done on fields and they already belong to records (i.e. The bytearray length however, should also be a type-level annotation; specifically internally. Imagine the following (yes, using my suggested syntax to gaslight you into adopting it as well -- although to be frank, I am more attached to the
Now things get a little confusing because, internally, the
If instead, types were carrying this information; then the Plus, the annotations composes this way and the first example would do what one would normally expect. So rather than extending the definition of records or type-aliases; I strongly believe we should augment the definition of Type. Blueprint
Well, blueprints are specifically there to document structural information! The standard was also thought with all kind of validation rules which we never made use of (minimum, maximum, minLength, minItems, etc ...). This is actually a perfect use-case for those. Remember that the point of blueprints is that one can programmatically follow them to build valid input data to a contract. Now, if my contract expects bytearrays to be of length 32, but the blueprint indicates it can take any bytearray, I am going to run into troubles. So now we have an easy way to embed this information into blueprint. And, it becomes quite straightforward if decorators are carried by types 😬 |
Note that, a possible evolution I see with these is now to alleviate some of the restrictions that come with opaque types. With a furnished-enough decorator language, we can possibly specify rather complex deserialisation behaviors and declare some specific opaque types safe. Note This goes way beyond this PR, but just recording it for the sake of having this conversation. Can move to a proper discussions if everyone likes the idea. Imagine the followings:
or
And now, you could use |
@KtorZ yea ok, that's fire. we'll do it like that then, I didn't realize it could go that far. |
I like the idea too seems good and we can remove alias to simplify things. |
Wasn't that the entire goal 😅? |
@KtorZ yes, just linking the issues so I remember to close them |
For a while now people have been asking for a way to modify certain aspects of their types. Some examples include controlling the underlying
Constr
tag for type constructors or enforcing that aByteArray
field is of a specific length.We also plan on supporting
encoding
such that you can ask for a type to be treated as aPlutusData::List
instead of aConstr
. But code gen support for this will come later.There are certain rules that will be enforced during typechecking like which tags can combine with which and whether or not they are being used improperly. The parser is left fairly liberal in this regard, it just accumulates as many tags as it sees and assumes the type checker will sort out what is right or wrong to use where.
Ex:
Custom tags
Enforcing Length