JSX syntax and QuasiQuoters in PureScript

It would be nice to use JSX syntax within PureScript. I do not know to what extent this is easy or not to implement and desirable.

I have seen these projects that could serve as references, both abandoned:

A JSX to Elm compiler

https://github.com/pzavolinsky/elmx

A JSX parser for Reflex (Haskell)

https://github.com/dackerman/reflex-jsx

The latter uses quasiquoters.

Would it be useful to have QuasiQuoters in PureScript?
@paf31 proposed 5 years ago: hxxps://github.com/purescript/purescript/issues/1643

The idea of QuasiQuoters and custom DSL is very seductive but gives the impression that it has not seen much use in Haskell.

What is your opinion?

4 Likes

I might be misreading, but I think that proposal is for there to be support for purescript quasi-quotes in the compilers implementation, not in purescript code?

I think what youā€™re asking for is meta-programming of Template Haskells strength (although not necessarily the same approach): Template PureScript?

Iā€™m personally not too bothered about not using a JSX/HTML syntax directly in the code, although it would be nice

2 Likes

@jy14898 surely I misunderstood @paf31ā€™s proposal

I have done some research on the subject and have also tried to implement some ideas of how to allow JSX syntax together with PureScript code in the same file but using a different extension, for example .pursx. For this you need a custom parser and for development a custom language-server

All of that seems quite complicated. At the end of the day it is very easy to combine PureScript code and Javascript files in the same project. There is not much to be gained by combining the two syntaxes.

I answer to myself :slight_smile:

1 Like

I think the Hamlet library in Haskell may also be useful as a reference: https://www.yesodweb.com/book/shakespearean-templates

This is my view as well. Itā€™s of course unfortunate that you donā€™t get the same level of type safety if your templates are written in JS rather than PureScript, and I think not being required to switch to a different file to edit your templates would be an improvement, but I think the amount of effort to get some kind of macro system going in PureScript in order to support JSX is just very difficult to justify compared to other things we could be doing.

5 Likes

Very interesting the link of Hamlet, Thanks for sharing

Yes, better to use the few human resources that PureScript has in other better things than trying to make JSX more typesafe.

I think the ā€œbestā€ solution is to have an ESLint mom telling you ā€œkid donā€™t do thatā€ :slight_smile:
I found these rules that ā€œforceā€ you to write more functional code in TypeScript

@hdgarrood you know a ā€œbetterā€ approach?

Personally, I have a bias towards writing templates in the host language (so in this case, PureScript). That way you get to use all of PureScriptā€™s nice features for things like iterating over collections, extracting reusable code into functions, (all the things that templating languages tend not to be so good at), and you get type safety, all the editor tooling still works, etc etc. So that would be my recommendation unless thereā€™s some context which makes that unworkable, such as your templates needing to be maintained by designers who donā€™t know PureScript.

I havenā€™t written enough JavaScript/TypeScript recently to be able to comment much on the approach of using lint rules like that. It does at least seem worth a try, but youā€™ll need to tell TypeScript what the types of the data youā€™re passing to your templates are if you want to benefit from its type checking, and Iā€™m not sure how easy that will be.

5 Likes

A QuasiQuotter would open up many use cases that are currently hard to do in PureScript. GraphQL and SQL query validation are the two biggest ones that are not discussed in this thread.

4 Likes

Iā€™d like to suggest that the reason why JavaScript needs JSX (or TSX) is that itā€™s hard to declare DOM trees in JavaScript, and that reveals the weakness of JavaScript, not the power of JSX.

I think almost every external domain-specific language reveals the failure of the host language. Lately Iā€™ve been translating a lot of TSX into PureScript React Basic Hooks and I find that the translation goes pretty much one-to-one line by line. Each line of TSX can be translated into an equivalent line of PureScript, with about the same amount of syntax. For the problem domain of declaring DOM trees, the expressive power of general-purpose PureScript is about equal to the expressive power of domain-specific TSX.

We donā€™t need a special domain-specific DOM tree declaration language in PureScript, because we already have PureScript. And thatā€™s great, because not needing a DSL to express what we want to express means we also donā€™t need extra special DSL toolchains and IDE support and a whole new syntax to learn.

9 Likes

Let me spam with this snippet once again here - maybe someone will find it useful. We use this a bit inefficient syntax (but I count this as a pretty efficient ā€œDSLā€ ;-)) when writing react-basic JSX and it allows us to have clearer tree structure because children array ends up outside of the props record:

jsx = div $ { children: _, classNames: "class-8" }
  [ div $ { children: _, classNames: "class-9" }
     [ ...
     ]
  , div $ ...
     [
     ]
  ]
10 Likes

I donā€™t follow how declaring DOM trees is harder in pure JS/TS than PureScript. For example, react-basic in PureScript:

R.button
  { onClick: capture_ $ self.setState \s -> s { counter = s.counter + 1 }
  , children: [ R.text (self.props.label <> ": " <> show self.state.counter) ]
  }

vs

pure JS/TS:

h(
  "button",
  { onClick: () => this.setState({ counter: this.state.counter + 1 }) },
  this.props.label,
  ": ",
  this.state.counter
);

I donā€™t see much difference here.

1 Like

@jamesbrock

I think almost every external domain-specific language reveals the failure of the host language.

As we know, eDSL are dialects created specifically to declare (declaratives) or specify (imperative) the domain of a concrete problem following the principles: simple, expressive and concise. In my opinion it is not possible to develop a general language that covers all domains and at the same time complies with the three principles. The limitation or failure is in the capacity that the current average human being would have to understand it.

That being said, PureScript is a great language that follows all three of the above principles and integrates quite well with other DSLs, like JSX.

@utkarshkukreti

I donā€™t follow how declaring DOM trees is harder in pure JS / TS than PureScript. For example, react-basic in PureScript:

PureScript for web development is not the problem if you are a Fullstack developer. But it is not a viable syntax for designers and frontend specialists used to syntax derived from HTML.

<button onClick = {() => updateCounter (s => s + 1)}>
  {label}: {counter}
</button>

This snippet is understood by anyone with a minimal base of HTML.

2 Likes

I think youā€™re in agreement with me. I was addressing this statement by jamesbrock:

by saying you can write DOM using functions in JS exactly like PureScript.


Agreed and that is what I prefer (and use) as well.

Iā€™m very late to the party but jsx support in purescript is also about familiarity of the syntax. Many people know html by heart and can read it without extra brain drain.
Considering Purescript is mostly targetted at javascript code that runs in the browser having jsx support would grow a lot of interest in the language and people willing to create usable UI frameworks.

1 Like

Personally, I would also like to have the option to write JSX. But I think for people who are familiar with HTML and not with PureScript, it hardly offers any advantages. As a PureScript beginner, you have to overcome significant obstacles before you even get to write the first lines of JSX.
(At least thatā€™s how it was for me)
And if youā€™ve made it that far, youā€™ll also be content with a DSL in pure PureScript.
Or you may enjoy writing your own DSL.

I also think that an HTML professional could understand and apply this at a certain level:

markup āˆ· Array Element
markup = [
    Div |- className "elem-1"
        |- onClick (\_ -> pure unit)
        |- noChild,
    Div |- className "elem-2"
        |- children [
        Div |- className "elem-2-1"
            |- __onConfirm
            |- noChild,
        H1  |- id "headline"
            |- Child |- Content "Hello"
        ]
    ]
    where __onConfirm = onClick (\_ -> pure unit)

Maybe weā€™d better find a way to teach the common formatters how to indent special HTML-like PureScript code to make/keep the structure clearer.

2 Likes

Iā€™d love to see something like this if you could actually copy&paste HTML - if not I donā€™t really see any advantages as I donā€™t really mind having a few [] and some data-constructors in there.

If anything some form of OverloadedStrings (no more ClassName wrappers) would do a lot more for me :wink:

Thereā€™s nothing stopping you from writing your own util that handles the ClassName wrapper:

myClass :: String -> ...
myClass clazz = H.class_ $ coerce clazz

yes thatā€™s true I guess

I agree on the obsticles if you are a programmer, but a lot of designers modify code in a team based just on html amd tailwind, for example.

I have mostly typescript/scala background and wanted to escape the type verbosity of ts. Right now, there is (for me at least) 4 interesting players in the compiles-to-js world. Rescript, Reason, Gleam and Purescript. Reason lacks nice cross platform tooling, Gleam lacks elixir-like protocols/type classes and jsx and Purescript falls only short on the latter.

Many people know html by heart

JSX isnā€™t HTML tho. It has some superficial similarities having a common XML base but has a lot of gotchas to where you will need to learn JSX specifically (canonical example being class vs. className). Teams of all flavors have used HAML, Pug, et al. for ages, & itā€™s belittling to assert that designer or whoever cannot learn or figure out how to use a PureScript DSL as @christianwish has pointed outā€“some will even prefer indentation to remembering closing tags. Anecdotally, I remember once being one of those folks having primary HTML/CSS experience before I did development with almost no more overhead using a Pug (then Jade) loop + variables vs. a Jinja template loop + variables. You can also create compiler errors regardless of the syntax being a XML dialect or notā€“and sometimes the missing variable error or whatever will be easier to understand from a PureScript perspective than deeper in a template language abrstraction.

Considering Purescript is mostly targetted at javascript code

Hopefully this perception will evolve soon & as alternative back-ends get better uptake :slight_smile:

1 Like

Having used quasi-quoters in Haskell and JSX for the frontend for many years, I agree with @jamesbrock; the added complexity in tooling does not pay off in the long term. We can observe this by the proliferation of JavaScript tools, bringing significant complexity that adds very little value. PureScript is powerful enough for a broad range of use cases without the need for additional compilation tools.

3 Likes