Skip to content
This repository has been archived by the owner on Nov 13, 2023. It is now read-only.

Exposing module types #64

Closed
MarcelCutts opened this issue Oct 19, 2018 · 15 comments
Closed

Exposing module types #64

MarcelCutts opened this issue Oct 19, 2018 · 15 comments

Comments

@MarcelCutts
Copy link

MarcelCutts commented Oct 19, 2018

I've been experimenting with mixing ReasonML and regular JS to provide a solid foundation across side effects.

One thing that I would find amazingly useful is being able to run genType on types within instantiated modules so I can expose those to regular flowd components

A reason-apollo example

[@genType]
module GetUser = [%graphql
  {| query getUser { user { firstName, lastName, telephone } }|}
];

module GetUserQuery = ReasonApollo.CreateQuery(GetUser);

[@genType]
type responseType = GetUser.t;

Here responseType ends up being

export opaque type responseType = GetUser_t;

where GetUser_t cannot be resolved.

Is this something that's on the roadmap? Or is it one of those things that is actually rather difficult?

@MarcelCutts MarcelCutts changed the title Exposing nested types Exposing module types Oct 19, 2018
@cristianoc
Copy link
Collaborator

@MarcelCutts What's in GetUser.t? Would a more direct repro just involve writing a small GetUser module directly, or is a functor application required for a small repro?

@MarcelCutts
Copy link
Author

MarcelCutts commented Oct 19, 2018

I had a quick play - a functor is not required. Simplified test case below:

module A = {
  type user = {name: string};
};

[@genType]
type b = A.user;

Generated flow type:

export opaque type b = A_user;

Where "A_user" cannot be resolved.

@cristianoc
Copy link
Collaborator

Adding one more report received for the same underlying issue : "there's no decent way to tag an included modules' type with [@genType]".
The situation is that there's a module you can't modify and ideally would add an annotation to. In this case, user is not annotated so it's not exported.

@cristianoc
Copy link
Collaborator

There's some annotation real estate planned but not used yet. Perhaps the annotation of modules could be used to mean that all the items within are annotated.

The real estate is a bit crowded though, as the annotation on modules could also be used to indicate grouping elements as objects: #63.

cristianoc added a commit that referenced this issue Oct 23, 2018
Now types used by other annotated types, if in the same file, are consider implicitly annotated.

This implements the major new features proposed in #70.

Addresses the repro example in #64.
@cristianoc
Copy link
Collaborator

@MarcelCutts, quite a lot of re-thinking and re-factoring later, see:
a1bf47a

It addresses your little repro -- not sure exactly what the original problem generates, but can take a look if there are more details.

@MarcelCutts
Copy link
Author

MarcelCutts commented Oct 24, 2018

I've been watching the progress, absolutely amazing work!

This solution does indeed tackle the simplified problem, but my original use-case still doesn't quite work as intended.

Summary

  1. We want to fetch data using reason-apollo
  2. Possible responses are pre-typed by introspection of graphql schema
  3. We would like to export these types as part of a ReasonReact component

Now we can do in-file static module declarations great, but it turns out functors are required and can't be subverted in these external libraries.

Test case

A.re

module type AConfig = {type configType;};

module A = {
  module Make = (Conf: AConfig) => {
    type a = Conf.configType;
  };
};

module B =
  A.Make({
    type configType = int;
  });

[@genType]
type c = B.a;

A.re.js

import type {a as B_a} from './B.re';

export opaque type c = mixed;

However B.re does not exist.

Again, I want to emphasis a strong appreciation for the effort on this project. It's fantastic. 👍

cristianoc added a commit that referenced this issue Oct 24, 2018
…lication.

Handles the functor application example in #64.
@MarcelCutts
Copy link
Author

MarcelCutts commented Oct 24, 2018

For additional context, I have dug up the "in anger" source from graphql-ppx.

The type t at the bottom is what holds the response type, and what I want to export into existing React components so they can get similar type safety from one source of truth.

@cristianoc
Copy link
Collaborator

After this 9d99c5e, and the previous commit, it might be worth having another go and see if a more complex repro is required or the one provided was representative.

The last repro goes through now.

@MarcelCutts
Copy link
Author

MarcelCutts commented Oct 24, 2018

No luck yet but it feels tantalisingly close. Our original use case is still not quite good to go.

module GetUser = [%graphql
  {| query getUser { user { firstName, lastName, telephone } }|}
];

module GetUserQuery = ReasonApollo.CreateQuery(GetUser);

[@genType]
type responseType = GetUser.t;
import type {t as MT_Ret_t} from './MT_Ret.re';

export opaque type GetUser_t = mixed;

export type responseType = GetUser_t;

(Where MT_Re.re is not a real separate file but a nested module)

I haven't quite been able to figure out why this one doesn't work but my test case (even when split across files) work great.

I will spend a bit of effort trying to understand and create a suitable trimmed down test case.

@cristianoc
Copy link
Collaborator

cristianoc commented Oct 24, 2018

Btw there’s a little trick: generate the .rei interface automatically, and then look at what the generated types look like.
That’s already quite useful.

cristianoc added a commit that referenced this issue Oct 25, 2018
First-class modules are only supported in a simple form, where in `(val (module A): ModType)` the restriction is that `ModType` is a module type identifier, and noth a path.

Also support module include: `module N = {include M}`.

This might help with #64.
@cristianoc
Copy link
Collaborator

In case it helps: 57628ea for (some) first-class modules, and module include.
So the surface of what's supported is bigger.

@MarcelCutts
Copy link
Author

It works! 🎉

module GetUser = [%graphql
  {| query getUser { user { firstName, lastName, telephone } }|}
];

module GetUserQuery = ReasonApollo.CreateQuery(GetUser);

[@genType]
type responseType = GetUser.t;

now becomes

export type GetUser_MT_Ret_t = {|+user: {|
  +firstName: string, 
  +lastName?: string, 
  +telephone: number
|}|};

export type GetUser_t = GetUser_MT_Ret_t;

export type responseType = GetUser_t;

And everything is fantastic. Thanks again for the diligent effort on this.

@cristianoc
Copy link
Collaborator

Awesome, this was fun. Thanks for the perseverance.
I guess some module include is generated, perhaps to splice in gql fragments.

@cristianoc
Copy link
Collaborator

A double ast transform (ppx plus genType), is probably the most complicated way one could go about generating an object with 3 fields.

@MarcelCutts
Copy link
Author

Since everything is resolved I'm happy to close this issue ✅

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants