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

Unboxed variants stdlib types #6218

Merged
merged 7 commits into from
Apr 30, 2023
Merged

Conversation

zth
Copy link
Collaborator

@zth zth commented Apr 27, 2023

This changes 3 type definitions in the stdlib:

  1. Js.Json.t is now an untagged variant representing valid JSON
  2. Js.nullable is now an untagged variant representing Value('a) | Null | Undefined
  3. Js.null is now an untagged variant representing Value('a) | Null

I'm trying to do as little as possible here, and the rest will be done in Core. But, in order to be able to make using these types smooth, I'd like the source definitions to be in here so it can be reused everywhere.

I've opted to only do nullable in the global Js scope of all of the nullable types (null, undefined etc). I'm very much open to feedback here, but my reasoning is that that's the most important one, and having multiple definitions with Value/Null/Undefined constructors is going to create a headache inference wise. Choosing just one should let us expose it in the global scope (which will be very useful for interop) without causing headaches.

Changelog coming as soon as we agree this is a good plan.

cc @cristianoc

@zth zth requested a review from cristianoc April 27, 2023 08:18
@zth
Copy link
Collaborator Author

zth commented Apr 27, 2023

CI seems to be failing but I don't really understand why.

@cristianoc
Copy link
Collaborator

cristianoc commented Apr 27, 2023

There are 2 js.ml files, you need to change both.
Why are there 2 js files. That's a great question for another day.

@zth
Copy link
Collaborator Author

zth commented Apr 27, 2023

@cristianoc fixed!

@zth
Copy link
Collaborator Author

zth commented Apr 27, 2023

Running locally against Core here to ensure things work as intended: #6218

Comment on lines +33 to +41
type t =
| False [@as false]
| True [@as true]
| Null [@as null]
| String of string
| Number of float
| Object of t Js.Dict.t
| Array of t array
[@@unboxed]
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't it be kept as an opaque type?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Why?

Copy link
Member

Choose a reason for hiding this comment

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

I think it's better to define a type as an opaque type, hiding its internal structure, so that values can only be manipulated using the functions provided by the module's interface, like the previous Js.Json.t.

Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the advantage of hiding the fact that json is json?
Do you want to not rely on the fact that json is a standard? Perhaps there's some abstraction that's useful in some way? Not sure I get it. But curious about what's the thought.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm also curious about why. I only see upsides, but maybe I'm missing something @mununki .

Copy link
Member

@mununki mununki Apr 30, 2023

Choose a reason for hiding this comment

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

Here is another example: circular reference object

let circularReferencingObj: Js.Json.t = Js.Dict.(
  // making circular referencing object
  ...
)->Js.Json.Object 

Copy link
Collaborator Author

@zth zth Apr 30, 2023

Choose a reason for hiding this comment

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

While true, those type of problems are not new, not introduced by this change, and it's not something making the type abstract magically fixes unless we restrict it to only allowing JSON.parse to return a JSON.t, which seems unpractical.

For example, there are a bunch of functions like string: string => t and object_: Js.Dict.t<t> => t in the JSON module right now that operates on the abstract type, but that has the same problem that you're describing, since they're just identity functions.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we have exhausted the exploration here. Merging.

Copy link
Member

Choose a reason for hiding this comment

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

While I respect the maintainer's decision and the fact that it's been merged, I still don't think the current situation justifies using non-opaque type for Js.Json.t. Rather, it might be possible to justify the existing functions due to the decision to use non-opaque types.

there are a bunch of functions like string: string => t and object_: Js.Dict.t => t in the JSON module right now that operate on the abstract type, but they have the same problem that you're describing, since they're just identity functions.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure what's the issue here.
What I see is a very talented contributor who has shipped awesome features executed extremely well: jsx4 and dynamic loading.
This issue does not seem very important.

jscomp/others/js.ml Outdated Show resolved Hide resolved
@zth zth force-pushed the unboxed-variants-stdlib-types branch from 19fa352 to 7783def Compare April 27, 2023 19:28
type +'a nullable
type +'a nullable =
| Value of 'a
| Null [@as null]
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the Js scope there are 2 things called Value and Null. And Js.Null is the last one listed.
Just pointing this out, and see if that's OK, and if the order is the desired one.
I guess perhaps Core will take care of being the entry point of these and this stuff in Js will only be a "storage" of type definitions not used directly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

My logic is:

  1. Expose them all in the Js namespace. This would become problematic inference wise only for users who do open Js and who start using the new representation.
  2. In Core (which is the future), only expose Nullable.t constructors in the global scope, not Null.t. That way, no clashes with inference will happen there.

The reasoning for 1 is that I want to ensure types are "linked together" correctly, which I assume means I need to duplicate the constructor definitions everywhere people might be using the type for things to line up and be compatible in Core. But maybe this is incorrect?

@@ -81,7 +81,7 @@ add_test("File \"js_json_test.res\", line 22, characters 11-18", (function (para
_0: false
};
}
var ty2 = Js_json.classify(Caml_option.valFromOption(v$1));
var ty2 = Js_json.classify(v$1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This difference means the compiler now unboxes the optional.
Well OK it makes assumptions about values of json type being stored unboxed (in the sense of optionals).
While correct in this case, I think it's incidental. And the option unboxing mechanism needs to be taught about untagged variants.
I will investigate in a separate issue. No need to worry about that here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch!

@cristianoc cristianoc merged commit a7b1ac4 into master Apr 30, 2023
13 checks passed
@zth zth deleted the unboxed-variants-stdlib-types branch April 30, 2023 18:58
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.

None yet

4 participants