rescript-association / rescript-lang.org

Official documentation website for the ReScript programming language
https://rescript-lang.org
MIT License
1.86k stars 246 forks source link

Document First Class Modules #155

Open ryyppy opened 3 years ago

ryyppy commented 3 years ago

This is an advanced topic.

FCMs are a thing, but we noticed that it's almost always easier to use a function instead of a "packed module". E.g. in React development, some ppl wanted to use the more complex FCM syntax to pass around React components, but actually what they wanted is passing around the make function instead.

So there should be definitely some hint that this is a feature that exist, but simpler language features are oftentimes enough.

jfrolich commented 3 years ago

I don't think passing a make function is a substitution for passing components, in a render function you can't use hooks. (Unless you use it together with React.createElement).

Actually there are some libraries that started to use FCMs, because in some scenarios it's the best solution for passing a bunch of values packed together when there is already a module that contains everything. See for instance graphql-ppx where you need to pass a query, a parse function, serialization function, and variables type (packed in a module by the ppx) to a client implementation (such as useQuery). The new syntax of FCMs is actually quite nice (let result = useQuery(module(MyQuery)) to pass the query).

I do think this is a feature that can be quite easy to use for consumers of libraries, but not necessary needs to be promoted as something to be used in typical code in userland.

ryyppy commented 3 years ago

@jfrolich yes, what i mean is you'd pass a make function and you'd use React.createElement.

There are use cases of course. The goal of this doc is to help ppl understand the feature in the first iteration, later on we check where they are really needed and give some tips.

austindd commented 3 years ago

The goal of this doc is to help ppl understand the feature in the first iteration

In my experience, the most difficult thing about FCMs is understanding how module types work. It might help to start with an explanation of "structural" vs "nominal" types, and to distinguish ReScript's structural module types from "class" types found in mainstream object-oriented languages.

It also might be helpful to compare module types to TypeScript's interface types, since there are many similarities, like structural polymorphism, and the ability to "merge" two or more structures into a new type.

FCMs are not all that complicated. The most significant drawback with them is the requirement of explicit type annotations before you can get to "working code". IMO, this is the same drawback we find with GADTs. With both FCMs and GADTs, the user must come up with the correct type signatures before they can compile a valid implementation, which prevents the common pattern of "playing around" with a problem before settling into a well-defined solution. With FCMs and GADTs, the type signature is necessarily a part of the solution, and it must come first.

In short, the drawbacks usually boil down to premature code rigidity. Aside from that, I don't think they are too difficult to learn...

ryyppy commented 3 years ago

Notice for myself, here's pretty much the whole syntax surface in one snippet:

module type X = {
  type t

  let add: (int, int) => int
}

type f = module(X)

let a = module(
  {
    type t
    let add = (a, b) => a + b
  }: X
)

module B = unpack(a)
kevinbarabash commented 2 years ago

What does unpack do?

kiuKisas commented 2 years ago
type f = module(X)

convert module to first-class module

type a = module ({
...
})

create a first-class module

module B = unpack(a)

convert first-class module to module (in a module scope only)

ryyppy commented 1 year ago

After some discussions it turned out that we don't want to advertise FCMs in the hopes that it's not relevant for ReScript developers. Might be even a candidate for removal (if this is the case, there will be public discussions about it first).

cknitt commented 1 year ago

We are definitely using this feature in a few places, so I would not like to see it being removed. I agree that it is an advanced feature though and am fine with it not being documented for now.

IwanKaramazow commented 1 year ago

Out of curiosity, what are you use cases and what problem does first class problems solve?

ashton commented 10 months ago

If it is an advanced feature, that's one more reason to document it, no? If the lang supports this, and even part of it is already documented on the language manual (functors) why keep the rest of the feature intentionally undocumented?