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

Minor error message improvement: record positional fields #42

Open
marcglasberg opened this issue Feb 9, 2024 · 5 comments
Open

Minor error message improvement: record positional fields #42

marcglasberg opened this issue Feb 9, 2024 · 5 comments
Labels
triaging Initial investigation into the issue

Comments

@marcglasberg
Copy link

marcglasberg commented Feb 9, 2024

Currently, I'm getting this error message:

celest\functions\portfolio.dart:38:30: 
The return type of a function must be serializable as JSON. Positional fields are not supported in record types

38 │ Future<(Stock, CashBalance)> sellStock(
   │                              ^^^^^^^^^

Very minor, but maybe the error messages could follow the format in this order: What. Why (when applicable). How to fix it.

Currently you have Why. What and no How.

In What/Why/How format it becomes:

Your function returns a record type with positional fields, which is not serializable as JSON.
Use only named fields in the record type.

A question: Do you feel that anything that doesn't have very "natural" JSON representation should not be allowed? Because technically it is serializable with $1 and $2 just like Dart does:

{ 
   "$1": <first value>, 
   "$2" : <second value>, 
}

Or even:

{"values": [<first value>, <second value>]}

Do you feel this may create problems?

@dnys1 dnys1 added the triaging Initial investigation into the issue label Feb 10, 2024
@dnys1
Copy link
Member

dnys1 commented Feb 10, 2024

Hey @marcglasberg, great feedback on the error messages. I like the model you're describing of what/why/how to fix 👍

Positional record fields can be serialized as you describe, and this is how package:json_serializable handles them. We chose not to support them for the reason that we want Celest APIs to be accessible and debuggable with any API client and the $1, $2 semantics made the API boundary difficult to reason about outside of the hermetic Celest client.

We also felt that using named parameters required a minimal amount of additional code (Stock, CashBalance) -> ({Stock stock, CashBalance balance}) (which can be DRY'd with typedefs) while encouraging better API design. In your example, I can only guess at the meaning of the Stock and CashBalance return values.

Please let me know if we are being too opinionated here, though, and if the use of positional record fields would improve your experience.

@marcglasberg
Copy link
Author

I think your argument about being accessible and debuggable makes perfect sense, and I'm happy with it. But it's not always that we have control over the types we use. Obligatory named params means you won't allow third-party types that use positional parameters. Which may force me to convert to/from the internal third-party classes and my own classes that I can send over to Celest.

Or maybe actually it won't force me to do that! I just need to override the toJson/fromJson with your new extended types override feature, right?

@dnys1
Copy link
Member

dnys1 commented Mar 4, 2024

Yes! I think the solution to this is overrides which can define a custom fromJson/toJson which only applies to your backend.

So given the following type:

// Defined in another package
class BadType {
  const BadType(this.positionalFields);

  final (String a, String b) positionalFields;
}

You could have an override like the following:

@override
extension type FixBadType(BadType it) implements BadType {
  factory FixBadType.fromJson(Map<String, Object?> json) {
    final positionalFields = json['positionalFields'] as Map<String, Object?>;
    return BadType(
      (positionalFields['a'] as String, positionalFields['b'] as String),
    ) as FixBadType;
  }

  Map<String, Object?> toJson() {
    return {
      'positionalFields': {
        'a': positionalFields.$1,
        'b': positionalFields.$2,
      },
    };
  }
}

I will close this for now, but feel free to reopen in the future if custom overrides don't fully solve the problem!

@dnys1 dnys1 closed this as completed Mar 4, 2024
@marcglasberg
Copy link
Author

@dnys1 maybe please still leave it open? We went on a tangent, but the issue was just about a minor improvement in the error message, by applying the What/Why/How format.

@dnys1
Copy link
Member

dnys1 commented Mar 5, 2024

Whoops! Yes, we can

@dnys1 dnys1 reopened this Mar 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triaging Initial investigation into the issue
Projects
None yet
Development

No branches or pull requests

2 participants