facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
227.56k stars 46.42k forks source link

Replace .jsx language extension with tagged template strings #2401

Closed mikesherov closed 9 years ago

mikesherov commented 9 years ago

Apologies in advance if this is the wrong way to start this discussion, but I couldn't find a more suitable forum or ML to discuss this on...

Now that tagged template strings will be part of ES6, is there any reason other than back compat to not deprecate the .jsx language extension and use template strings?

I may be oversimplifying it, but it seems the upgrade path for users would be to replace things of the format:

<text>

with

jsx`<text>`

Unifying jsx with ES6 would help save effort to make tools intended to work with js work with jsx. For example, over in JSHint and JSCS (both are popular static analysis tools), there have been several tickets about jsx support, which we're happy to field. However, IMO, these efforts would be better spent getting tools ready for ES6 instead. I know FB's own esprima-fb have been contributing back to the upstream esprima, which is awesome, but focusing in on ES6 will help speed up those efforts.

Hopefully, React's recent move to get closer to the ES6 class syntax is a sign that this proposal will be seriously and thoughtfully considered.

mikesherov commented 9 years ago

Here's a POC of jsx-as-template-string implementation: http://www.2ality.com/2014/07/jsx-template-strings.html?m=1

RReverser commented 9 years ago

Reasons why it's not completely possible/useful: http://facebook.github.io/jsx/#why-not-template-literals

mikesherov commented 9 years ago

@RReverser thanks for engaging me in the discussion on twitter. I'd like to note my objections long form here, in a friendly attempt to change your opinion, by highlighting and rebutting some of the points from the article you linked:

It would be possible to use template literals as a syntactic entry point and change the semantics inside the template literal to allow embedded scripts that can be evaluated in scope:

This seems to negate the "possible" portion of your statement: "Reasons why it's not completely possible/useful:"

However, this would lead to further divergence. Tooling that is built around the assumptions imposed by template literals wouldn't work.

This is fine and expected. The world would still need tooling around how to understand the jsx tag, just as it does now. However, doing inside of a template literal means that any tool made to "understand" the jsx tag would help other tooling for other DSLs that don't intend on using {$} either! A BIG WIN.

It would undermine the meaning of template literals.

Not true. Just because a tagged template doesn't make use of {$} doesn't mean it's undermined. Think mustache, handlebars, jrb, etc... all of which forgo {$}.

It would be necessary to define how JSX behaves within the rest of the ECMAScript grammar within the template literal anyway.

As it would for all DSLs, not just ones that make use of {$} or not.

Therefore it's better to introduce JSX as an entirely new type of PrimaryExpression:

This does not follow from the preceding statements. Currently, jsx tooling requires two things: a new parser, and tooling to understand the DSL. If you went the "{$} free template literal route", only the tooling to understand the DSL would be required.

By requiring an additional parser, you're requiring all tooling to be able to except custom parsing, and understand grammar.

It also means you can't use jsx with other transpilers easily. Opting into ES7 or harmony features requires unnecessary overhead.

The argument that coffescript/typescript etc. are guilty of the same crime doesn't make this any better, because those ARE completely different languages. .jsx could EASILY be made into real js if it's willing to accept that not all template literals need to use {$}.

phloe commented 9 years ago

@mikesherov You do realise that the "POC" you linked would create actual DOM elements on render - thereby eliminating any performance gains allowed by the virtualDOM?

mikesherov commented 9 years ago

@rasmusfl0e no, I didn't realize that at all. Thanks for letting me know.

However, that's just a POC that could be augmented to work the way react actually works with a virtual DOM. It's implementation details.

I'm just trying to find out if it would be possible to use template strings instead, not suggest that the POC is the actual replacement. @RReverser raised some points about language semantics which I'm still trying to illuminate and understand. That's the real meat of this issue.

The argument about the POC using actual DOM does not actually factor into the discussion. It's just a POC to see if the syntax could be handled well enough by template strings.

syranide commented 9 years ago

@mikesherov Unless there's a static transformation for the template string then it's intrinsically a bad idea I would say, the hint is in the name "template string". That would require run-time parsing and there would be no way to actually translate the tag names to locally defined classes (unless you want to use variables for that too, but then we've left HTML so far behind that we could just use JS instead).

mikesherov commented 9 years ago

unless you want to use variables for that too, but then we've left HTML so far behind that we could just use JS instead).

I fail to see how {$} is too far from HTML, but {} isn't?

sebmarkbage commented 9 years ago

Not true. Just because a tagged template doesn't make use of {$} doesn't mean it's undermined.

The point is that this would leave MyComponent as an unused variable:

var MyComponent = ...;
return jsx`<MyComponent />`;

A linter or minifier would both be useless on this code. Probably also a type system. Perhaps you could do code highlighting but even that would not be very helpful because we would expect highlighters to work well on this code.

Worst case, the minifier just proceeds without error because there's no reason to think that anything inside the template literal referred to anything in the outer scope. You don't even notice it before it hits production.

At best they could start assuming special semantics about the content of template literals. Both of these undermine the meaning of template literals.

By requiring an additional parser, you're requiring all tooling to be able to except custom parsing, and understand grammar.

Yes. As in my example above, it's better if any incompatible tooling fails early than tries to proceed under the wrong assumptions.

It also means you can't use jsx with other transpilers easily.

The only thing that is needed is for our parser to be compatible so that we can transpile only the JSX calls and leave the rest untouched so you can pipe it to another parser. We're pretty quick on updating the parser for new syntax that is being proposed for standardization (e.g. async/await etc). There are also third-party forks of other parties like @RReverser 's awesome work on acorn.

If you're expecting to use experimental syntax, then you can still use it with something like Sweet.js which is ideal for mixing various experimental local DSLs.

You can still build one of these JSX template literal systems for yourself. If it becomes popular enough, we might adopt it. So far, it seems that most people prefer to use either JSX, function calls or a third-party language.

I think JSX shows that JS is lacking certain syntactical features and that pure JSON isn't a very readable format for these data structures. Hopefully this will spark some ideas to introduce a new object literal form to JS. Even if that is not based on XML-like syntax.

I'll close out this issue because there's nothing new here so there's nothing actionable. Feel free to keep discussion going and we'll reopen if some new information arrives.

mikesherov commented 9 years ago

We're pretty quick on updating the parser for new syntax that is being proposed for standardization (e.g. async/await etc).

Yes, but the entire JS tool chain and ecosystem isn't as quick.

I think JSX shows that JS is lacking certain syntactical features

Well, syntactic noise is the roadblock really. JS template strings CAN handle jsx, you're just left with extra jsx and extra ${} in certain cases which React deems to be too noisy. I can respect that opinion, even if I disagree :-). To use your example:

var MyComponent = ...;
// instead of...
return <MyComponent />;
// do...
return x`${MyComponent}`;

For the complicated example from the "why not template strings" post, it's just slightly noisier if you make the tag just be x and switch from >< to /:

// Template Literals
var box = x`
  ${Box}/
    ${
      shouldShowAnswer(user) ?
      x`${Answer} value=${false}/no/${Answer}` :
      x`${Box.Comment}/
         Text Content
        /${Box.Comment}`
    }
  /${Box}`;

vs.

// JSX
var box =
  <Box>
    {
      shouldShowAnswer(user) ?
      <Answer value={false}>no</Answer> :
      <Box.Comment>
         Text Content
      </Box.Comment>
    }
  </Box>;

and that pure JSON isn't a very readable format for these data structures. Hopefully this will spark some ideas to introduce a new object literal form to JS. Even if that is not based on XML-like syntax.

I would agree with this sentiment. JSON isnt the solution. A less noisy tagged template syntax would be.

Thanks for the thorough response. I do think its a bit unfortunate that syntactic noise is ultimately what is preventing template strings from being adopted over a whole new primaryExpression. Perhaps one day you'll reconsider.

mikesherov commented 9 years ago

I'm not even sure if switching from <tag></tag> to ${tag}//${tag} actually even works, but I was simply trying to make a point about syntactic terseness being the actual blocker.

sebmarkbage commented 9 years ago

@mikesherov, familiarity is also an issue to be overcome.

I just want to make it clear that nothing would make me happier than a standardized solution to this issue. I think custom DSLs and even custom high-level libraries should be avoided if possible.

Not just because of the tooling issue but because the issue of having one more thing to learn.

However, it says a lot that many smart people tend to think that the familiarity and terseness of JSX is worth the downside of complicating the tooling story and increasing the language surface area.

Some people are on the opposite side and don't think the tooling/compatibility is an issue. They just don't like the look of it.

That indicates that the issue remains. Perhaps, the solution is better editors that allow you to customize what your code looks like? Then the source code representation doesn't matter. We can all use our own syntax preference. Unfortunately, IDE tooling isn't there yet. At least not in terms of popularity.

mikesherov commented 9 years ago

I just want to make it clear that nothing would make me happier than a standardized solution to this issue. I think custom DSLs and even custom high-level libraries should be avoided if possible.

In this case, it is Possible, just seemingly not desired.

However, it says a lot that many smart people tend to think that the familiarity and terseness of JSX is worth the downside of complicating the tooling story and increasing the language surface area.

There are an equal number of smart people on the other side. I'm not discounting jsx at all, I just don't think appeal to unnamed authority is a good argument here. I would say that these smart people may not have thought about using template strings fully, but they're unnamed and anonymous, so I have no rebuttal there.

That indicates that the issue remains. Perhaps, the solution is better editors that allow you to customize what your code looks like? Then the source code representation doesn't matter. We can all use our own syntax preference. Unfortunately, IDE tooling isn't there yet. At least not in terms of popularity.

This is no different from transpilation anyway. The solution for me would be allow the user to define the default template string parser and define the boundary markers for expressions. Then the terseness argument is all but gone.

sebmarkbage commented 9 years ago

In this case, it is Possible, just seemingly not desired.

Possible, in the sense, that we can't possibly deprecate it without an uprising. :)

I just don't think appeal to unnamed authority is a good argument here.

I didn't mean that as an authority but as the populous. I.e. the majority sentiment.

The solution for me would be allow the user to define the default template string parser and define the boundary markers for expressions.

Macros.

mikesherov commented 9 years ago

Thanks again for this discussion! Means a lot to me when sensitive topics can be discussed civilly and freely without attitudes getting in the way :-)

Possible, in the sense, that we can't possibly deprecate it without an uprising. :)

I know the feeling! I've lived through deprecating tons of stuff in jQuery and the user outcry in response. Then we built the jQuery-migrate plugin that did the work for you.

If deprecation of jsx format was ever seriously considered, I'd gladly write the migration tools along with an army of folks who would help make the switch easy for devs.

I didn't mean that as an authority but as the populous. I.e. the majority sentiment.

Appeal to majority opinion is no better than appeal to authority. Also, I'd dispute that a majority think its preferable over language unification.

mikesherov commented 9 years ago

Macros.

If that's what it took, I suppose.

sebmarkbage commented 9 years ago

Appeal to majority opinion is no better than appeal to authority. Also, I'd dispute that a majority think its preferable over language unification.

I should've clarified, that I meant within the React community. I'm sure the larger JS community would prefer unification, but they don't have to bare the cost.

Even within the React community, it's those with large code bases that suffer. Not those with a few components or higher level abstractions.

The apparent cost of switching to a less familiar/terse syntax is higher than the pain they're feeling from divergent tooling.

I would say that these smart people may not have thought about using template strings fully

We've explained these considerations in earlier posts, in person at various companies using React, and internally at Facebook. So I don't think these people are unfamiliar with the issues. It's just that they're not hurt by it personally.

I'd gladly write the migration tools along with an army of folks who would help make the switch easy for devs.

We already have that tool. It's the transpiler itself. :) The upgrade path is not an issue. We break things all the time and make sure to provide an upgrade path.

The problem is that the people that would bare the cost of working with a subjectively worse syntax doesn't currently hurt from it. By trying to deprecate it, we would only make their lives worse.

mikesherov commented 9 years ago

We've explained these considerations in earlier posts, in person at various companies using React, and internally at Facebook. So I don't think these people are unfamiliar with the issues.

This is fair. I was talking about an anonymous majority group at the time I made that statement prior to you clarifying.

The problem is that the people that would bare the cost of working with a subjectively worse syntax doesn't currently hurt from it. By trying to deprecate it, we would only make their lives worse.

Aye. Trust me I understand this concern. This is why my initial question asked "other than back compat".

This is my personal opinion and I have no evidence to support it, but I think people would get over it, and it would only make their lives worse temporarily. The resulting benefits from a unified ecosystem would be felt far and wide, despite devs knowing the connection or not.

Anyway, I think I've beaten this dead horse enough, and I've taken up enough of your time.

Thanks again for the discussion, and I hope you reconsider in the future.

jussi-kalliokoski commented 9 years ago

Somewhat relevant: https://github.com/jussi-kalliokoski/react-no-jsx (a JSON-type alternative syntax to JSX). :)

syranide commented 9 years ago

@jussi-kalliokoski You mention React.createElement() as being too much boiler-plate, that's why React.createFactory exists, explicitly for non-JSX users. Your syntax is also reponsible for significant amounts of unnecessary allocations. As of 0.12 ReactElements are plain objects, I don't think you can provide manually created ones just yet, but that's the intention IIRC.

jussi-kalliokoski commented 9 years ago

@syranide: React.createFactory() is boilerplate too, especially if the component contains a lot of HTML elements. As for unnecessary allocations, productivity > performance most of the time. You can always use React.createElement() directly in the parts of your code where this becomes a problem.

andreypopp commented 9 years ago

@jussi-kalliokoski

As for unnecessary allocations, productivity > performance most of the time.

I think that's one of the reasons JSX exists!

jussi-kalliokoski commented 9 years ago

@andreypopp

I think that's one of the reasons JSX exists!

No question about it. I have no hate for JSX, in fact I use it in a reasonable-sized project at work. It is however a significant tradeoff in productivity, it gives somewhere and takes somewhere else because of the compile step. To give some examples, we have pretty nice style rules in the project, but we can't enforce them with tools like JSCS and JSHint because of tool incompatibility. There are bridging tools, but they make sacrifices in either causing the linters to ignore the things in JSX blocks completely (which is not nice because for example for pure JS my vim setup is such that whenever I save a file it jumps to the next linting error, but with these tools you'd be free to make typos inside the JSX blocks without the editor noticing anything) or just preprocess the code before passing to linter, which leads to compiler-generated style violations (because it wouldn't make sense to be able to configure JSX output to your style rules).

Another problem is errors - we're using gulp-webpack with jsx-loader and every time there is a compilation error for JSX, the error message only tells you what line it was on and omits the file that caused this. I should've filed a bug about this a long time ago, but I always forget once I manage to eventually figure out what was causing the error. :)

Pre-processing steps are always a pretty serious tradeoff, and the effect is magnified when there are other tools at play. As much as I used to love it, this is the reason I eventually gave up on CoffeeScript as well. :/ JSX is far less horrible than CoffeeScript in this sense though, the errors make sense and the transformation is so lightweight that you don't really even need source maps for proper debugging.

gcanti commented 9 years ago

@jussi-kalliokoski I agree, we had a similar idea

https://github.com/gcanti/uvdom