reasonml-q-a icon indicating copy to clipboard operation
reasonml-q-a copied to clipboard

What is the proper mental model for Reason decorators?

Open mrispoli24 opened this issue 4 years ago • 5 comments

So I just wanted to make sure I'm thinking about the Reason decorators properly.

When we have something like this:

[@react.component]
let make = (~children) => {
  ...
}

What's going on here, are these behaving like js decorators:

@get
function myFunc() {
 ...
}

I've seen some examples with two decorators:

[@bs.module myModule] [@react.component]
let make = (~myprop, ~children) => { ... }

Is this just passing one into another almost like piping in reverse:

make |> myModule |> react.component

I know javascripts prototype system under the hood vs the syntax we actually end up writing can leave you with the wrong mental model of what is going on (ie. classes). Just want to make sure that I'm not thinking of this part the wrong way.

mrispoli24 avatar Feb 14 '20 15:02 mrispoli24

Attributes like [@xyz] are doing compile-time code generation. [@react.component] has a nice explanation here: https://reasonml.github.io/reason-react/docs/en/components#reactcomponent

In general they take the piece of code they are attached to, transform them into another piece of code, and pass that along to the next piece of the build. Think of them almost as middleware for code. Incoming code is a 'request', the attribute is the 'middleware' that transforms the request into another one. They can be chained together just like middlewares.

yawaramin avatar Feb 14 '20 15:02 yawaramin

Adding to @yawaramin, one thing that helped with my mental model when coming from JS is that it's kind of like a Babel plugin or a macro in that it can transform code or generate entirely new code, BUT, it does this before typechecking, which to me is an incredibly important distinction.

This means that the code that's transformed will be type-checked, which is not the case with any solution I know of in JS-land. This is very very powerful. It's one of the things I personally feel enable me to do things I just cannot do in JS/TS with the same quality assurance, which for me is a very big thing.

zth avatar Feb 14 '20 17:02 zth

Reason decorators have absolutely no meanings by themselves and can go (almost) anywhere. They're just visual anchors. Some post-processing step would pick them up and transform the code into the desired form. Could they transform the code without those anchors? Yes. Though they wouldn't know what code to transform =P. Plus, it's good manner to have visual anchors where you might expect the code not to be what it finally looks like.

Two decorators together aren't piping anything at all. It's just, 2 meaningless decorators, on the same piece of code. Now, in that specific example, the react.component decorator is picked up by the ReasonReact-specific post-processing step. Then the bs.module decorator is picked up by the BuckleScript post-processing step. You could also add another decorator, [@blablabla], to those two. It won't do anything because nothing picks up it (just to drive the point home).

The precise name for these post-processing steps are called ppx. We heavily discourage them in userland, not only because they're macros, but also because they're macros that aren't easy to write nor to maintain, nor to carry around, nor to easily version, etc. More like a necessary evil. Reason philosophically discourages macros in favor of just using the actual language whenever you can. Though despite that, we might think of some better macro systems if only just to avoid ppx.

chenglou avatar Feb 14 '20 18:02 chenglou

The type-checking is a good point, and yes, it naturally falls out of Reason having a type checker. Though just because we have a type checker doesn't mean this ppx thing is good enough already; far from it. Because it's not very cool when someone ships a ppx, and you use it, and then you get some type error because of ppx design error said person made. Having ppx themselves be type checked (note: not the same thing as giving ppx access to a program's types!) at writing time would have been excellent. But that's another amount of yak shaving.

chenglou avatar Feb 14 '20 18:02 chenglou

Despite the fact that it's easy to fall into ppx trap by adding too many of them I think there are valid use-cases that are hard to write by hand or require a lot of boilerplate. One such ppx is graphql_ppx which allows you to generate typesafe GraphQL queries based on types of your query provided by GraphQL server.

baransu avatar Feb 15 '20 19:02 baransu