-
Notifications
You must be signed in to change notification settings - Fork 457
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
[NotForNow] GenType: explore the idea of using namespaces to represent modules. #6117
Comments
It will not work with transpilers like esbuild or Babel. Even if use tsc, it hurts the possibility of tree-shaking of build artifacts. |
That's interesting. Can you expand on that? |
OK, it's outdated. @babel/plugin-transform-typescript and esbuild both support transpiling TS namespaces. However, it is not "fully" supported, and it is not a recommended option. Babel recommends using modules where possible.
I already rely on the GenType to generate TypeScript modules in ES Module format. When I use the library, the bundler can remove the parts I don't use. Module semantics make easy to do that. But, the result of namespace: // generated by tsc
"use strict";
var M;
(function (M) {
function foo() {
// Your function implementation goes here
}
M.foo = foo;
let N;
(function (N) {
function bar(x) {
// Your function implementation goes here
}
N.bar = bar;
})(N = M.N || (M.N = {}));
})(M || (M = {})); Usually, it comes as an IIFE that negates most DCE tools. Most library authors avoid using TS namespaces. The only time it's valid is when used in only type-level and not in runtime code. |
Thanks, this simplifies the design a lot -- no need to explore namespaces further! I'll ask you some more gentype-related questions too. |
You mean only create GenType is actually doing the mapping of the runtime representation as well as adding an alias to the typename. I still have to rely on it. An example code I have.. source: @genType
let hasNoAccounts = async (~countAllMembers) => {
switch await countAllMembers(.) {
| count => Ok(count == 0)
| exception Js.Exn.Error(exn) => Error(#IOError({"exn": exn}))
}
} JS result: async function hasNoAccounts(countAllMembers) {
var count;
try {
count = await countAllMembers();
}
catch (raw_exn){
var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
if (exn.RE_EXN_ID === Js_exn.$$Error) {
return {
TAG: /* Error */1,
_0: {
NAME: "IOError",
VAL: {
exn: exn._1
}
}
};
}
throw exn;
}
return {
TAG: /* Ok */0,
_0: count === 0
};
} GenType result: export const hasNoAccounts: (_1:{ readonly countAllMembers: (() => Promise<number>) }) => Promise<
{ tag: "Ok"; value: boolean }
| { tag: "Error"; value: { readonly exn: Js_Exn_t } }> = function (Arg1: any) {
const result = Council_Service_AccountBS.hasNoAccounts(Arg1.countAllMembers);
return result.then(function _element($promise: any) { return $promise.TAG===0
? {tag:"Ok", value:$promise._0}
: {tag:"Error", value:$promise._0}})
}; BTW I'm pretty sure the output can be simplified. It adds too much overhead today. GenType result in the real-worldexport const verifyMemberSession: <T1>(_1:{
readonly findSession: ((_1:Council_Entity_Session_id) => Promise<(null | undefined | Council_Entity_Session_t)>);
readonly findMember: ((_1:T1) => Promise<(null | undefined | Council_Entity_Member_t)>);
readonly sessionId: (null | undefined | Council_Entity_Session_id);
readonly memberId: (null | undefined | T1)
}) => Promise<
{ tag: "Ok"; value: { readonly member: Council_Entity_Member_t; readonly session: Council_Entity_Session_t } }
| { tag: "Error"; value:
{ NAME: "IOError"; VAL: { readonly exn: Js_Exn_t } }
| { NAME: "InvalidMember"; VAL: { readonly member?: T1 } }
| { NAME: "InvalidSession"; VAL: { readonly session?: Council_Entity_Session_id } } }> = function <T1>(Arg1: any) {
const result = Curry._4(Council_Service_SessionBS.verifyMemberSession, function (Arg11: any) {
const result1 = Arg1.findSession(Arg11);
return result1.then(function _element($promise: any) { return ($promise == null ? undefined : {_RE:$promise._RE, id:$promise.id, seq:$promise.seq, events:$promise.events.map(function _element(ArrayItem: any) { return ArrayItem.tag==="Created"
? Object.assign({TAG: 0}, ArrayItem.value)
: Object.assign({TAG: 1}, ArrayItem.value)}), state:($promise.state == null ? undefined : $promise.state.tag==="Anonymous"
? Object.assign({TAG: 0}, $promise.state.value)
: Object.assign({TAG: 1}, $promise.state.value))})})
}, function (Arg12: any) {
const result2 = Arg1.findMember(Arg12);
return result2.then(function _element($promise1: any) { return ($promise1 == null ? undefined : {_RE:$promise1._RE, id:$promise1.id, seq:$promise1.seq, events:$promise1.events.map(function _element(ArrayItem1: any) { return ArrayItem1.tag==="Created"
? Object.assign({TAG: 0}, ArrayItem1.value)
: ArrayItem1.tag==="SingupApproved"
? Object.assign({TAG: 1}, ArrayItem1.value)
: ArrayItem1.tag==="SingupRejected"
? Object.assign({TAG: 2}, ArrayItem1.value)
: ArrayItem1.tag==="AdminGranted"
? Object.assign({TAG: 3}, ArrayItem1.value)
: ArrayItem1.tag==="AdminRevoked"
? Object.assign({TAG: 4}, ArrayItem1.value)
: ArrayItem1.tag==="JoinedToOrganization"
? Object.assign({TAG: 5}, ArrayItem1.value)
: ArrayItem1.tag==="LeaveFromOrganization"
? Object.assign({TAG: 6}, ArrayItem1.value)
: ArrayItem1.tag==="Reactivated"
? Object.assign({TAG: 7}, ArrayItem1.value)
: Object.assign({TAG: 8}, ArrayItem1.value)}), state:($promise1.state == null ? undefined : $promise1.state.tag==="Requested"
? Object.assign({TAG: 0}, $promise1.state.value)
: $promise1.state.tag==="Rejected"
? Object.assign({TAG: 1}, $promise1.state.value)
: $promise1.state.tag==="Active"
? Object.assign({TAG: 2}, $promise1.state.value)
: Object.assign({TAG: 3}, $promise1.state.value))})})
}, (Arg1.sessionId == null ? undefined : Arg1.sessionId), (Arg1.memberId == null ? undefined : Arg1.memberId));
return result.then(function _element($promise2: any) { return $promise2.TAG===0
? {tag:"Ok", value:{member:{_RE:$promise2._0.member._RE, id:$promise2._0.member.id, seq:$promise2._0.member.seq, events:$promise2._0.member.events.map(function _element(ArrayItem2: any) { return ArrayItem2.TAG===0
? {tag:"Created", value:ArrayItem2}
: ArrayItem2.TAG===1
? {tag:"SingupApproved", value:ArrayItem2}
: ArrayItem2.TAG===2
? {tag:"SingupRejected", value:ArrayItem2}
: ArrayItem2.TAG===3
? {tag:"AdminGranted", value:ArrayItem2}
: ArrayItem2.TAG===4
? {tag:"AdminRevoked", value:ArrayItem2}
: ArrayItem2.TAG===5
? {tag:"JoinedToOrganization", value:ArrayItem2}
: ArrayItem2.TAG===6
? {tag:"LeaveFromOrganization", value:ArrayItem2}
: ArrayItem2.TAG===7
? {tag:"Reactivated", value:ArrayItem2}
: {tag:"Deactivated", value:ArrayItem2}}), state:($promise2._0.member.state == null ? $promise2._0.member.state : $promise2._0.member.state.TAG===0
? {tag:"Requested", value:$promise2._0.member.state}
: $promise2._0.member.state.TAG===1
? {tag:"Rejected", value:$promise2._0.member.state}
: $promise2._0.member.state.TAG===2
? {tag:"Active", value:$promise2._0.member.state}
: {tag:"Inactive", value:$promise2._0.member.state})}, session:{_RE:$promise2._0.session._RE, id:$promise2._0.session.id, seq:$promise2._0.session.seq, events:$promise2._0.session.events.map(function _element(ArrayItem3: any) { return ArrayItem3.TAG===0
? {tag:"Created", value:ArrayItem3}
: {tag:"MemberConnected", value:ArrayItem3}}), state:($promise2._0.session.state == null ? $promise2._0.session.state : $promise2._0.session.state.TAG===0
? {tag:"Anonymous", value:$promise2._0.session.state}
: {tag:"Member", value:$promise2._0.session.state})}}}
: {tag:"Error", value:$promise2._0}})
}; |
Not anymore. In v11, the runtime representation is tunable in the language, and genType does no conversion. |
Polymorphic variants can be specified with quotes. |
I can't remember anything having changed in polymorphic variants in v11. |
I'll move this to v12, and probably beyond, in case it makes sense to revisit when tooling around bundling changes. |
According to this comment, I'm drawing a version of the gentype that only handles types ( I wonder why did gentype include the runtime in the first place? |
It included runtime as the runtime representation required conversion. There's still a bit of runtime for |
ES proposal "module declarations" (currently stage 2) could be more proper target https://github.com/tc39/proposal-module-declarations |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Explore a cleaner way to represent ReScript modules in TypeScript using namespaces
Background
In ReScript, modules are used to organize code and encapsulate related types and values. ReScript modules can contain values (such as functions) and type declarations, allowing users to access them using the syntax
M.t
for a typet
defined inside moduleM
. Modules can also be nested, and ReScript supports functors (functions between modules).Currently, genType maps ReScript modules to TypeScript constructs, but there might be a cleaner way to represent them using TypeScript namespaces.
Proposed Solution
The proposed solution is to represent ReScript modules and their features in TypeScript using namespaces:
Representing ReScript modules: Use TypeScript namespaces to group related types and values.
Representing nested ReScript modules: Use nested TypeScript namespaces.
The text was updated successfully, but these errors were encountered: