Skip to content
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

Feature wish: Support JSX syntax as part of the language #8802

Open
bikallem opened this issue Jul 10, 2019 · 27 comments

Comments

@bikallem
Copy link

commented Jul 10, 2019

It would be awesome if ocaml would support JSX syntax out of the box. ReasonML has support for such syntax as part of the language and it make developing web front-end heavy applications easier and intuitive. Intuitive here means someone who is familiar with xml/html can immediately understands the view logic. Additionally it seems jsx syntax have other uses besides the web front-end programming, e.g. it is being used to develop a desktop GUI framework (https://github.com/revery-ui/revery, ), GUI Editor(oni2 -https://github.com/onivim/oni2), console output(https://reason-native.com/docs/pastel/) and reason-native(https://github.com/reasonml/ReasonNativeProject) to name a few.

Ocaml supporting this feature as part of the language itself (like ReasonML) would mean that one doesn't have to move to using the ReasonML to make use of JSX features. Ocaml is already a great language and adding JSX syntax sugar would make it a more appealing and viable option for these use cases.

After some investigation, the JSX syntax are converted to ocaml function application. For eg. the following JSX,

<div foo={bar}> child1 child2 </div>;

is converted

([@JSX] div(~foo=bar, ~children=[child1, child2], ()));

More information on ReasonML JSX: https://reasonml.github.io/docs/en/jsx

Is this a viable request?i.e. can it be implemented as part of an ocaml grammar? and is there an interest in this feature?

@Octachron

This comment has been minimized.

Copy link
Contributor

commented Jul 11, 2019

This kind of domain specific foreign syntax seems to be a perfect candidate for a ppx extension. A quick search suggest that such extension already exists.

Therefore, I am unconvinced that more support is needed in OCaml syntax itself at this point.

@pmetzger

This comment has been minimized.

Copy link
Member

commented Jul 18, 2019

@Octachron I agree that a ppx is probably the right thing, but google doesn't find such a ppx if I look for "ocaml ppx jsx"; what is it named?

@bikallem Is there something here a PPX can't do that you think is important to have?

@Octachron

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

My opam search suggested https://github.com/cxa/ppx_bsx.

@bikallem

This comment has been minimized.

Copy link
Author

commented Jul 19, 2019

This kind of domain specific foreign syntax seems to be a perfect candidate for a ppx extension. A quick search suggest that such extension already exists.

Therefore, I am unconvinced that more support is needed in OCaml syntax itself at this point.

I agree that support for JSX syntax can be achieved via ppx extension. However, out-of-the-box language support of the JSX syntax should make it (the JSX syntax) consistent across various libraries which use it. AFAICT there are 2 ppxes that support variying levels of JSX like syntax,

  1. https://github.com/cxa/ppx_bsx
  2. https://ocsigen.org/tyxml/4.3.0/manual/ppx

However, neither of the work are compatible with each other and one has to learn/re-learn the syntaxes and its idiosyncrcies again - even though both of the libraries are trying to implement more or less the same syntax and conceptual framework.

Ocaml already has a few 'domain specific syntaxes' support, for eg. the list manipualtion syntax support ::, [], @ and more recently the monad syntax support let*, let+. For the monad syntax support, it will allow library authors to support monadic api without having to author yet another monadic ppx(let%lwt, let%bind, let%map, etc). For the users of these monadic libraries, the learning curve is much reduced since the ocaml language proper has codified the monadic syntaxes, i.e. let* and let+ is bind and map respectively. Net result, the ocaml ecosystem becomes more productive - both the library authors and library users.

As I mentioned in my earlier comment, JSX syntax has proven itself to be very useful for various problem domains. Its uses range from web UI, desktop UI, console UI to mobile app development to name a few domains where it's usage has enhanced the developer experience. The popularity and usefulness of ReasonML and its JSX syntax is a potent proof of that. As such JSX is a multi domain specific language/syntax. The ocaml ecosystem and its users would benefit greatly if it catered for such wide ranging use cases.

I am willing to do the work if someone from the ocaml team is willing to sponsor it and provide some mentorship.

@gasche

This comment has been minimized.

Copy link
Member

commented Jul 19, 2019

No, I don't think there is interest in supporting JSX syntax in the language implementation directly for now -- as @Octachron above correctly hinted at.

A few arguments:

  • If something can be done as a library without changing the compiler implementation itself, it's better to do it as a library (it moves the maintenance cost to another group of people, and it gives a lot more flexibility for release schedules, development practices and ecosystem, etc.). As @Octachron pointed out, a JSX-style syntax is an excellent candidate for a ppx extension, the only thing you need is to embed the JSX syntax within an explicit marker [%jsx ...].

  • If there is no mature JSX syntax extension today, that's not an argument for building it in the compiler, that's an argument for building a mature JSX syntax extension.

  • The Scala people have had XML literals for a while, and they are now arguing that maybe they should be removed. One of the main arguments in favor is that the presence of XML literals has in fact had a negative effect on the creativity of the Scala library ecosystem for XML/HTML, by imposing a centralized slow-moving piece, while they have a vibrant JSON ecosystem that benefited from being in the faster-moving third-party world.

@Drup

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

Generalizing a bit, the issue at hand is that ppxs are very bad at quotations and need to abuse strings:

  • The quoting is verbose and syntactically awkward: [%foo{| .... |}]
  • The antiquotations are messy: [%foo{| ..|}x{|.. |}]

I think it would be very beneficial to have dedicated ppx extension/syntax where the input is unparsed, except for dedicated quotation/antiquotation syntax. [%foo .... %x .... ] for example. The quoted content would then be given as [ `String of string | `Antiquot of expr ] list There is no theoretical problem with doing that, and it would not have the disadvantages of camlp4 (we would still get an AST at the end, so merlin&co would understand). The main problem (and it is a really big one) is finding enough syntactic space.

Tyxml's ppx reimplement all this by hand using the string syntax, which makes it awkward. There are many other use cases. The syntax of the Relit paper is also similar (except they add lot's of typing. Just the syntax would be nice progress already imho).

@gasche JSX is not xml. It looks XML-ish, but it has no semantics and only desugar to function calls. It's not so problematic to have that in a compiler and would not restrict innovation, like the XML syntax in scala.

@bikallem Do keep in mind that tyxml's ppx is not jsx, it's real HTML (with antiquotations). Nevertheless, it's been on my todolist to make it work with reason's jsx, so if you want to make a standardize thing with tyxml, my door is open. ;)

@lpw25

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

@Drup I was thinking about this the other day. One option would be to add anti-quotations to the {|...|} syntax. Something like:

let foo (s : string) =
  {| Hello <|s|> ! |}

where <|..|> is the anti-quotation syntax. The anti-quotes would have match the enclosing quotes like:

let foo (s : string) =
  {xxx| Hello <xxx|s|xxx> ! |xxx}

This is mildly useful on its own, but it is especially useful for ppx because its AST representation is precisely the type you gave above. Allowing for:

[%jsx {|
   <a href=<| String.concat "http://" foo |>> Some link text </a>
|}]

This would be pretty easy to implement. It is not 100% backwards compatible but I think it is unlikely to break much in practice.

@Drup

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

@lpw25 You are proposing to add string interpolation in the language and exploit it in PPXs. I feel this is orthogonal. in particular, if you consider xml-like syntaxes, it's very slightly less awkward, but equally as verbose.

@lpw25

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

@Drup It's not orthogonal, it is precisely the missing syntax you were just lamenting. The confusion is my fault for choosing a poor example, consider instead:

let body = [%jsx {| <b> Some text </b> |}] in
let width = 300 in
[%jsx {|
   <div width=<|width|>> Here is: <|body|> </div>
|}]
@lpw25

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

The point is that outside of ppxes the syntax is just for string interpolation, but inside ppxes it can be used for general anti-quotation. I'm basically just stealing the template literals from JavaScript.

@Drup

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

I understand very well, I just find it still too verbose. You propose to write

[%jsx {|
   <div width=<|width|>> Here is: <|body|> </div>
|}]

Where I would like to write

[%jsx <div width=%width> Here is: %body </div>]

You propose a new bandaid that still abuses string syntax for quotations. I want to make the bulky and useless string syntax go away.

@lpw25

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

Those syntaxes are pretty similar, and the first one seems much easier/possible to add to the language.

The syntax isn't the most important bit, the ease of implementation is the important bit. Currently to implement proper anti-quotations requires either restricting yourself to variables, or using horrible hacks around delimiters and then calling out to the OCaml parser.

@gasche

This comment has been minimized.

Copy link
Member

commented Jul 19, 2019

I must say that I rather agree with @Drup here: if we had a nice syntax for arbitrary-content extensions, I don't think that "how to nicely implement antiquotations?" would be a design problem. Most antiquotation syntaxes can be implemented fairly simply, extension users can standardize on something and share code to support it, why would a specific antiquotation syntax need upstream support?

Re. @Drup's free-form quotation suggestion: we already have [%foo: core-type], [%foo: module-signature] and [%foo? pattern] if I remember correctly, could we have something equivalent to [%foo{| ... |}] but less verbose?

@lpw25

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

could we have something equivalent to [%foo{| |}] but less verbose?

I'm not sure it can be much less verbose if you want to do it properly. You need to consider how you deal with delimitters/escaping. Personally I think that escaping is the wrong approach for quotations of foreign syntax. Using customisable delimiters is a much better for these kinds of things but it requires space to put the extendable part of the delimitter, so you're quickly going to get to a similar number of characters to [%foo{| |}].

@Drup

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

@lpw25 You yourself argued for very terse syntax for let operators, because use would be frequent and if we wanted to get them adopted, we needed something terse. In my opinion, it's exactly the same here. JSX is used precisely because it's very terse and feel part of the syntax.

@gasche That was exactly my idea, yes. There is the escaping problem, as usual. I know Rust has decent support for this, but I don't know the details.

@gasche

This comment has been minimized.

Copy link
Member

commented Jul 19, 2019

One option would be to have something like {%foo ..} (mandatory space after the extension name) which assumes that the braces inside are well-parenthesized, and the {%foo-bar|...|bar} families for the customizable-delimiters version (here I used a dash to signal the delimiter form; we could agree to use another character).

@lpw25

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

Having {%foo bar| ... |bar} be a short-hand for [%foo {bar| ... |bar}] would be pretty consistent with the let%foo ... to [%foo let ...] translations and similar.

@gasche

This comment has been minimized.

Copy link
Member

commented Jul 19, 2019

So we would have {%foo|...|} as the most concise form. I liked having a shorter delimiter-balanced form, but the choice mechanism I proposed above is not very nice.

@lpw25

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

@Drup If we combine @gasche's suggestion with my earlier one you get to:

{%jsx| <div width=<|width|>> Here is: <|body|> </div> |}

Do you think that's terse enough for your liking?

We might be able to get a smaller antiquote syntax, but I think you want to at least have space for putting the customisable delimitter in the opening part so you probably need to use at least 3 characters (2 to open and 1 to close).

@Drup

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

Why do we need the | here ? If } is not part of the quoted syntax, everything should be fine, no ? Otherwise, this looks promising.

@gasche

This comment has been minimized.

Copy link
Member

commented Jul 19, 2019

It could also be, following what I understand to be the standard antiquotation/interpolation syntax for JSX:

{%jsx| <div width={width} Here is: {body} </div> |}
@lpw25

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

@gasche What are you doing for quotes that have a delimiter? It is worth bearing in mind that whilst JSX and other XML style syntaxes don't use it { is an extremely common symbol in syntaxes that we are likely to want to quote.

@Drup

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

I would push the antiquotation syntax aside for a while. It can be implemented externally once we have the basic version, in order to experiment and gather feedback.

Also, I think it should depends on the quoted language. Finding something that works all the time is too hard, or very verbose.

@gasche

This comment has been minimized.

Copy link
Member

commented Jul 19, 2019

The simplest suggestion is just to support {%extname delim|...|delim}, with no special support for antiquotation in the payload. Then the implementor of the extension would be perfectly able to turn {...} inside the payload into antiquotations (or whatever other syntax they wish), wish is perfectly fine as long as |delim} does not occur within the quotation boundaries at all.

In my original proposal I tried to allow something a bit more sophisticated with a different mode where, instead of a family of delimiters, we would have just } as delimiter but assume balancedness in the payload (this is how we handle (*..*) in comments, for example). This would allow to quote all the languages that expect braces to be well-balanced with one less character, but I can't get a design proposal that I really like.

@Drup

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

I also though about using well-balanced content, but given we can't give the user choice of delimiters, this might end up being very annoying for various use cases. I feel like using a similar technique to unescaped string is a good idea.

So, to summarize:

  • {%foo ... } for content without }.
  • {%foo| ... |} or {%foo bar| ... |bar} for content with customized delimiters.

Let me make a PR with a proposal and move the discussion there.

@gasche

This comment has been minimized.

Copy link
Member

commented Jul 19, 2019

Unless the spaces are meaningful in your proposal in other places than after the attribute name (please specify), it seems ambiguous on the meaning of {%foo bar|blah|bar}, is it the first form with payload bar|blah|bar?

@Drup

This comment has been minimized.

Copy link
Contributor

commented Jul 19, 2019

Here it is : #8820

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.