htmlx-org / HTMLx

One Template to rule them all
588 stars 9 forks source link

Improve syntax consistency #6

Closed mindplay-dk closed 6 years ago

mindplay-dk commented 6 years ago

The syntax of inline statements is rather inconsistent.

{#if porridge.temperature > 100}
  <p>too hot!</p>
{:elseif 80 > porridge.temperature}
  <p>too cold!</p>
{:else}
  <p>just right!</p>
{/if}

Why not simply reserve the words if, elseif, else and endif and avoid the mix of #, : and / symbols?

{if porridge.temperature > 100}
  <p>too hot!</p>
{elseif 80 > porridge.temperature}
  <p>too cold!</p>
{else}
  <p>just right!</p>
{endif}

In my opinion, this is less noisy, easier to read and faster to type.

stalkerg commented 6 years ago

I agree I ask about it in new syntax issue for Svelte too. It's bound with convenient for syntax analyzer.

mindplay-dk commented 6 years ago

Google Closure Templates are a good source of reference, I think. (for the basic syntax - not for the features.)

mindplay-dk commented 6 years ago

I'm not a fan of Svelte's double-braces to qualify printable expressions - the only time it's ambiguous is if somebody wants to name variables like if and else, which they shouldn't be doing in the first place. I think it's completely okay to reserve these as keywords.

arxpoetica commented 6 years ago

One of the main reasons was to try and avoid reserving any words, but maybe if people feel that strongly...

An alternate approach that was floated (that I'm still a fan of) looked something like this:

if (expression) {
    <div>...</div>
} else if (expression2) {
    <div>...</div>
} else {
    <div>...</div>
}

I believe this was rejected for looking a little off, but I personally thought it read just fine. (If one used the Pug syntax though, the two might conflict). I could see why that one isn't popular but there's probably no reason it shouldn't still be in the running.

Beyond that, @Rich-Harris had a few other reasons; I'll let him (re)articulate

Rich-Harris commented 6 years ago

The syntax of inline statements is rather inconsistent

'Inconsistent' is definitely the wrong word to use here! # opens a block, / closes it, and : opens a sub-clause, and that's true for all blocks everywhere. Anything between { and } without punctuation is consistently a JavaScript expression.

There are a few reasons to favour punctuation. Firstly, it's easier to grok the structure of a component when you're just scanning it — your brain doesn't need to stop and ask 'is this a special reserved word?'. The symmetry is clearer: when you see #if, you can scan down vertically until you see {/, rather than seeing a sea of lines beginning {e, which all resemble each other.

(That problem would be worse when you consider eachendeach is both clumsy and visually 'meh'.)

Secondly, reserving words like elseif and each is one of those things that seems like a small deal at the time, but turns out to have a significant cost. As a component author, you're never 100% confident that you're not accidentally going to use a reserved word in your expressions, unless you memorise the list (which tends to grow over time, once you've set the precedent that reserved words are okay) or keep referring to documentation. There is value in having a simple rule that between { and } you can put any JavaScript expression you like: it's simple, clear, and requires no framework- (or HTMLx-) specific knowledge.

Finally, as @stalkerg alluded to, it simplifies the job of tokenizing the language. That's not an overriding concern, but it does have value — for example, it makes it easier to build a language server that allows your editor to suggest else as an autocomplete option when you type {:, which in turn reduces the amount you end up typing.

I'm not a fan of Svelte's double-braces to qualify printable expressions

They were removed in v2 — HTMLx is basically the language Svelte currently uses.

stalkerg commented 6 years ago

Secondly, reserving words like elseif and each is one of those things that seems like a small deal at the time, but turns out to have a significant cost.

I suppose it's a little wrong - we should talk about reserving {elseif and {each . Also, about autocomplete - it will be same with '{e' and maybe better because to write ':' you should press shift+; but 'e' you can write without shift.

I understand, syntax with {:,{#, {/ have some benefits but syntax without it look much clear.

. Firstly, it's easier to grok the structure of a component when you're just scanning it — your brain doesn't need to stop and ask 'is this a special reserved word?'.

If I remember right about the brain - for us much easier to detect whole words instead rarely used in language symbols like :,#,\ . (For example in the Russian language we not use # at all, and on the keyboard in this place we have №)

Rich-Harris commented 6 years ago

If you've just typed a { character then your finger is already on the shift key!

If I remember right about the brain - for us much easier to detect whole words instead rarely used in language symbols like :,#,\ . (For example in the Russian language we not use # at all, and on the keyboard in this place we have №)

If that were true in this context, then we would write HTML like this:

<header>
  <h1>Hello world!<endh1>
  <small>This would get annoying quickly<endsmall>
<endheader>

<main>
  <div>app goes here<enddiv>
<endmain>
mindplay-dk commented 6 years ago

# opens a block, / closes it, and : opens a sub-clause, and that's true for all blocks everywhere

IMO, they're just noise - your brain can interpret words much faster without the added noise, and the words have to be there either way, so.

Anything between { and } without punctuation is consistently a JavaScript expression.

IMO, not really helpful, because your brain will automatically learn to filter out the noisy punctuation anyway - you'll barely be able to see it, because your brain is so used to reading words, and that's how you'll parse the tags anyhow. You won't see the extra characters when you're skimming across the code, but you have the added burden of having to type them.

That problem would be worse when you consider eachendeach is both clumsy and visually 'meh'

I could buy that one, sure - it's maybe easier to visually pair each with /each than each with endeach.

OTOH, else and elseif don't visually pair with if and /if, so it doesn't really solve that problem.

Secondly, reserving words like elseif and each is one of those things that seems like a small deal at the time, but turns out to have a significant cost. As a component author, you're never 100% confident that you're not accidentally going to use a reserved word in your expressions, unless you memorise the list (which tends to grow over time...)

The Javascript keyword list if, else, for, while, etc. has not really grown over time though - it's been stable for 10+ years, and most likely this won't grow or change either.

It's only a small, finite set of reserved keywords - which everyone knows and has memorized from Javascript already anyhow, so there's no learning curve here in the first place.

Besides, I can't recall ever having wanted to use words like for, while, if or else as neither variable-names nor function-names or arguments or anything else.

it makes it easier to build a language server that allows your editor to suggest else as an autocomplete option when you type {:

I don't think most people auto-complete keywords like if or else - neither Storm nor VS Code auto-completes keywords, as far as I'm aware. I wouldn't personally want my IDE to suggest keywords, the longest of which is probably while, which is just 5 characters. I don't think I've ever seen anybody who auto-completes keywords. You'd be the first I've known ;-)

I can respect the fact that this is all subjective and based on preference and experience - but personally, I feel this syntax is already much too punctuation-heavy, especially considering the small number of words/tokens in this fairly small language; the extra tokens don't help make this more readable, and definitely makes it much slower to type.

You have to memorize the keywords anyhow - but now, in addition to that, you have to memorize which symbol goes in front of every keyword. And these are largely keywords you already know from JS - from my point of view, having to learn the proper punctuation, in addition, only increases learning-curve.

Part of the selling point of having a standard for this, is IDE support - which means you'd have syntax-highlighting, which makes the extra punctuation even more redundant.

If punctuation is really necessary for parser and visual disambiguation, how about something more consistent with popular template languages like handlebars and dust, where punctuation is used only to indicate directives and end-of-block directives?

For example:

{#if porridge.temperature > 100}
  <p>too hot!</p>
{#elseif 80 > porridge.temperature}
  <p>too cold!</p>
{#else}
  <p>just right!</p>
{/if}

IMO, this is much easier to understand and memorize, more consistent with popular template languages - and a bit less noisy than the special punctuation for certain directives like #else and #elseif?

Rich-Harris commented 6 years ago

If punctuation made text less easy to parse, then every book would be written in the style of Cormac McCarthy! It's self-evidently untrue.

The suggestion was to add new reserved keywords — that's the thing that I'm saying would be a mistake. That includes each and elseif but could quickly grow: Svelte has #await blocks with :then and :catch sub-clauses, which would mean adding then as a reserved word. I guarantee it wouldn't stop there.

VSCode autocompletes keywords. It helps me avoid typing functoin and export dfeault 20 times a day!

in addition to that, you have to memorize which symbol goes in front of every keyword

That's like saying that you have to remember that / goes in front of a closing div tag, and that / goes in front of a closing main tag, and / goes in front of a closing p tag.

I'm not familiar with Dust, but I just looked at their tutorial and here's an example I found...

<ul>
{#friends}
  <li>{name}, {age}{~n}</li>
{:else}
  <p>You have no friends!</p>
{/friends}
</ul>

Clearly they spent time thinking about this and reached the same conclusion!

For context, this isn't something I just threw together on Monday — this syntax is something that's actively being used in Svelte after lengthy, energetic bikeshedding. It's not going to be perfect from everyone's perspective, but these decisions were reached by consensus, and since it became the official syntax there hasn't been any negative feedback about it being slower to type. I'm satisfied that we made the right decisions.

mindplay-dk commented 6 years ago

in addition to that, you have to memorize which symbol goes in front of every keyword

That's like saying that you have to remember that / goes in front of a closing divtag, and that / goes in front of a closing main tag, and...

XML consistently uses no symbol for opening tags, and the same / for all closing tags. It's a simple syntax rule - it's the same for all tags.

I'm only suggesting the same - consistently use # for the start of a directive, and / for the end of a directive. So the only change I'm suggesting at this point, is don't use a third arbitrary symbol : for some directives - make it easy to spot starting and ending directives.

Javascript consistently uses { and } for either if or else blocks.

Imagine a language using a third set of delimiters for the else block for some reason:

if (foo) {
  ...
} else [
  ...
]

Introducing an arbitrary third delimiter for certain keywords is just not necessary or helpful.

Indentation, of course, will make it easy to see where directives start and end.

Whether you're a program or a person, the : will likely complicate rather than help with parsing.

arxpoetica commented 6 years ago

I'm one of those using {:else} statements in the wild and those little : make it fantastic to scan. It's very easy to know I'm somewhere in the middle and not the {/end} 😆.

mindplay-dk commented 6 years ago

It's very easy to know I'm somewhere in the middle and not the {/end}

Indentation, keywords and color-coding in your IDE isn't enough?

Well, I give up.

arxpoetica commented 6 years ago

Indentation

If, else, and end if are on the same level, so that's no help.

keywords

B'lieve it or not, there's a part of my brain preserved for lexical reading vs symbolic scanning. Those two parts operate very differently. Dunno. Maybe other people's brains work very differently.

and color-coding

The colors are the same! That's no help.

stalkerg commented 6 years ago

If you've just typed a { character then your finger is already on the shift key!

Yes, I agree.

If that were true in this context, then we would write HTML like this:

it's obviously working if you have not so many keywords. In svelte we have only if, endif, each, endeach, await, endawait Again you still can use / like {each}{/each} {if}{/if} yeah I know we will have shift but it's same as html tag.

<ul>
{if}
  <li>{name}, {age}{~n}</li>
{else}
  <p>You have no friends!</p>
{/if}
</ul>

yes, sure you can't use 'if' or 'each' anymore as name for variable but I think it good practics anyway. Another python like option:

<ul>
{if express:}
  <li>{name}, {age}{~n}</li>
{else:}
  <p>You have no friends!</p>
{if}
</ul>

PS but anyway current syntax is ok for me

mindplay-dk commented 6 years ago

I'd want a low-noise syntax that resembles JS as much as possible - I don't agree with the way you prioritize syntax concerns, so I just don't think this project is for me.

Thanks for the discussion.