[BuckleScript 8.1 New Syntax]: String interpolation


#1

https://reasonml.org/docs/reason-compiler/latest/new-bucklescript-syntax says:

  • Interpolated string: from {j|hello ${name}|j} to j`hello ${name}`.
  • `foo bar` for multiline strings. "foo bar" string is now singleline.

Why do interpolated strings need to have that j prefix? Can’t we just allow strings with backquotes both to be multiline and interpolated? (For that matter, why j and not i like “interpolated” or something else altogether?)

In any case, I will not use them as long as they are not typesafe. I have been bitten by this several times in situations like

{j|User: ${user}|j}

with user being a string, but later refactored to a record, resulting in

User: [object Object]

without the compiler catching this. I have therefore removed all string interpolation from my Reason code.

IMHO the way it should work is

  • use just backquotes
  • allow arbitrary expressions within the ${} as long as the type is string

Example:

`User: ${user.firstName}`

#2

The j part in the original ocaml syntax is just like an ocaml attribute. Could have been any letter. A compiler/ppx might pass over it, pick up specific names like j, and transform the string content accordingly.

As for why not just desugar foo to {j|foo|}, it’s because there are existing, edge-case reason callsites that already write {|$foo|} (no interpolation). These would convert over to `foo`, then desugar to {j|$foo|j}}, and now suddenly the code doesn’t type because you need foo in scope =|

The other reason is that desugaring to {j|foo|j} is kinda BuckleScript-specific. During the making of it we intended it to be agnostic. Since now the syntax already is BS-specific, this justification disappears. And the previous one isn’t that good anymore imo. We in general don’t want code to break, but a one-time conversion breakable in order to make the experience much better later on might be worth it.

One last thing is that since this is namespaced, we can still reserve a proper, safer one for the unprefixed version.

Open to reconsider though. Also, your user.firstName example already works. Just without proper unicode support.


#3

Ah, right, thanks! I should have tested this before posting. I had just assumed j`hello ${name}`would work the same as the current {j|$name|j}. :man_facepalming:

But in fact it already allows arbitrary expressions, and the type needs to be string. This is great!

So there is just the question of getting rid of the leading j.

It seems {|$foo|} would not be problematic for conversion anyway, just {|${foo}|}. Couldn’t this somehow be escaped by the conversion script though?


#4

To clarify the multiline string syntax a bit:

let name = `Admin name is ${admin.firstName}  ${admin.lastName}`

is the same as

let name = `Admin name is ` ++ admin.firstName ++  ` ` ++ admin.lastName

So it is just backquotes and we allow arbitrary expression inside ${}.


#5

Great! This is perfect, thanks a lot! :slight_smile:

Then there just remains the issue with Unicode that Cheng mentioned.

Js.log("Sehr schön")
Js.log(`Sehr schön`)
Js.log(j`Sehr schön`)

gives me

Sehr schön
Sehr schön
Sehr schön

Would it be possible to get Unicode to work for all three? This is one of those things that’s very hard to explain to newbies coming from JS…


#6

I like the fact that the new multiline string syntax looks exactly like tagged string literals in JS.

// .js

const style = styled`color: red`;

// .res
let style = styled`color: red`;

I’d also prefer multiline strings to be unicode compatible though


#7

Yes, I think we should work together with bobzhang to make it unicode compatible and drop the j prefix

I also envisioned the following usecase:

sql`select * from myTable`

#8

Oh, tagged templates would be awesome, especially if you find a way to make them type safe.

AFAICT, PPXes in Reason play the about the same role as tagged templates in JS, but less syntax to learn is probably better.


#9

@IwanKaramazow shall I file an issue regarding unicode in the new BuckleScript syntax repo?


#10

Yes, let’s make a plan of action in the issue!


#11

I’m not sure what you mean by

Just without proper unicode support.

This code seems to do what I want:

let str = j`çóðè`;
Js.log(j`Üñî${str} interpolation works`);

#12

It would be great if multi line strings would support indentation. So if the last backtick is on a new line, the indentation of the last backtick would be used for the multi line string.


#13

@jdeisenberg I was referring to his j-less code snippet. It’d be nice to fuse the two but that’d mean even tighter integration with BuckleScript.

@Jfrolich we’re punting on this for now because there might actually be better multiline string solutions from e.g. Python and Swift IIRC that we can take.


#14

@jfrolich Here is an analysis of choices to be made for multi-line strings: https://openjdk.java.net/jeps/326 (see the section titled “Margin Management.”) Their solution is to provide functions to do the alignment, but the things they had to think about are worth reading.


#15

Sure. My approach is exactly how the python and swift one works.


#16

Having a type safe Unicode safe string interpolation mechanism would really be great!


#17

We made some changes to the current template literal syntax providing unicode support and typesafe string interpolation out of the box. Available in master and will be released soon.

let x = `Sehr schön, ${son.name} 🙌`
// parses as
let x = {js|Sehr schön, |js} ++ son.name ++ {js| 🙌|js}

Other variations are still available through use of a tag:

let sql = sql`select * from myTable`
let x = j`foo $bar`

#18

Hi. What is the use case of other tags? Isn’t it better to leave that syntax for supporting real template literals at some point (functions that receive two arrays - text and interpolation values). That would be amazing for mapping to modern JS libraries.


#19

That’s really great! Thanks a lot!

What about

"Sehr schön"

though? Will it be parsed as a Unicode string, too?


#20

For now only template literals (between backticks) are supported. In the long term we should investigate if this is possible.