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

Basic values and tests #26

Merged
merged 5 commits into from
Apr 18, 2024
Merged

Basic values and tests #26

merged 5 commits into from
Apr 18, 2024

Conversation

Kukovec
Copy link
Collaborator

@Kukovec Kukovec commented Apr 16, 2024

Partially addresses #23 (missing compound values, e.g. Map/Vec)

Copy link
Contributor

@konnov konnov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we avoid using abstract classes and classes here? There is no need for them. An idiomatic way in TypeScript is to rather use discriminated unions: { kind: 'int32', val: bigint } | { kind: 'bool', val: boolean }.

The reason is that we would run into multiple issues with classes such as instanceof, equality, hashing, etc.

@konnov
Copy link
Contributor

konnov commented Apr 16, 2024

Also, it's quite easy to construct values with discriminated unions like { kind: 'bool', val: true }, instead of instantiating classes.

@Kukovec
Copy link
Collaborator Author

Kukovec commented Apr 16, 2024

Can we avoid using abstract classes and classes here? There is no need for them. An idiomatic way in TypeScript is to rather use discriminated unions: { kind: 'int32', val: bigint } | { kind: 'bool', val: boolean }.

The reason is that we would run into multiple issues with classes such as instanceof, equality, hashing, etc.

And how do you enforce field validity in those discriminated unions? The class constructors make sure u32 values are actually u32, I don't see a way of doing that with your suggestion.

@konnov
Copy link
Contributor

konnov commented Apr 16, 2024

Can we avoid using abstract classes and classes here? There is no need for them. An idiomatic way in TypeScript is to rather use discriminated unions: { kind: 'int32', val: bigint } | { kind: 'bool', val: boolean }.
The reason is that we would run into multiple issues with classes such as instanceof, equality, hashing, etc.

And how do you enforce field validity in those discriminated unions? The class constructors make sure u32 values are actually u32, I don't see a way of doing that with your suggestion.

I am actually not sure about why you want to have field validation there. You definitely do not want validation in many cases, e.g., when copying values. This is typically done somewhere in the parser/reader. If you need validation, simply introduce a validation function that does a switch over different kinds. The intermediate representation is just data.

@Kukovec
Copy link
Collaborator Author

Kukovec commented Apr 16, 2024

Can we avoid using abstract classes and classes here? There is no need for them. An idiomatic way in TypeScript is to rather use discriminated unions: { kind: 'int32', val: bigint } | { kind: 'bool', val: boolean }.
The reason is that we would run into multiple issues with classes such as instanceof, equality, hashing, etc.

And how do you enforce field validity in those discriminated unions? The class constructors make sure u32 values are actually u32, I don't see a way of doing that with your suggestion.

I am actually not sure about why you want to have field validation there. You definitely do not want validation in many cases, e.g., when copying values. This is typically done somewhere in the parser/reader. If you need validation, simply introduce a validation function that does a switch over different kinds. The intermediate representation is just data.

Can you give me an example of such copying, where you would explicitly not want the validity check, because I genuinely don't understand what use case you have in mind.

@konnov
Copy link
Contributor

konnov commented Apr 16, 2024

[...]

Can you give me an example of such copying, where you would explicitly not want the validity check, because I genuinely don't understand what use case you have in mind.

I don't want to get into a discussion between classes and discriminated unions in TypeScript. What I can say is that there are very few cases, where I have seen value in using classes (I mean, TypeScript classes) in TypeScript. Most of the time, plain objects and discriminated unions do the job just fine. Here are just a few things that make TS classes hard to use: useless equality, useless toString() by default, no good support for instanceof (you still have to introduce the kind field).

In your PR, we need something similar to sealed classes in Scala. Discriminated unions provide you with exactly that. You don't want to inherit from values, nor you need other features of classes.

Copy link
Contributor

@konnov konnov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it really much better! Left a few stylistic comments.

Comment on lines 56 to 58
export type IntValue = {
val: bigint
type: string
} & (IntValue_u32 | IntValue_i32 | IntValue_u64 | IntValue_i64 | IntValue_u128 | IntValue_i128)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would not you simply enumerate all cases right here? You still have to define types for IntValue_u32 below.

For instance, this is just immediate and does not require any scrolling:

export type IntValue =
    { val: bigint, type: "u32" }
  | { val: bigint, type: "i32" }
  | { val: bigint, type: "u64" }
  ...

Alternatively, we could do:

export type IntValue =
  { type: "uint", bits: 32 | 64 | 128, val: bigint }
  { type: "int", bits: 32 | 64 | 128, val: bigint }

solarkraft/src/state/value.ts Outdated Show resolved Hide resolved
Comment on lines 71 to 73
export type IntValue_u32 = {
type: "u32"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the value of these definitions?

}

// Byte arrays (Bytes, BytesN)
// The `len` field is present iff the length is fixed (i.e. for BytesN)
// TODO: tests
export class ArrValue implements Value {
export type ArrValue = {
val: IntValue_u32[]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we already know that we have an unsigned u32 here, why don't we just use number? Most likely, you would have to use the array length in loops, which would make it much easier if we are using number.

Copy link
Contributor

@konnov konnov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Let's merge it. I think you would still run into issues when looping over bigints, but we can deal with that later.

@Kukovec Kukovec merged commit fdb5cc3 into main Apr 18, 2024
2 checks passed
@Kukovec Kukovec deleted the jk/values branch April 18, 2024 09:48
@konnov konnov added this to the M1: Transaction extractor milestone May 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants