coffeescript icon indicating copy to clipboard operation
coffeescript copied to clipboard

Request: Support Svelte JS labels

Open n-smits opened this issue 2 years ago • 14 comments

There was no need for JS labels before, but Svelte found a good use for the syntax. Currently it can only be passed with backticks, but it's very limited and unwieldy.

`$: {`
foo 'hi' # must not properly indent
`}`

All other fail

`$:` foo 'hi'
# produces ({ $: foo 'hi' })

`$:`
foo 'hi'
# erroneous
# $:;
# foo('hi');

`$: {` foo 'hi' `}`
# won't compile

Simplest and very Coffee solution would be to spell out the syntax name, but honestly whatevs, whatever names you have left reserved or can reappropriate such as case outside of switch.

label $: foo 'hi' # or = or space
label $
  foo 'hi'
  foo 'hi'

It's true this label use isn't idiomatic and that it's a solitary kinda-framework example. But, it is actually using the standard syntax, and its unidiomatic use became innovative, because unlike before, it's actually a good use. Past addition of JSX could be considered similar, and a good place for it in documentation (since it is after all a rare good example of Js labels, so it's mostly about Svelte).

In my very humble general opinion here, Coffeescript should adapt here and there because Javascript is a hackable text based language for better and for worse. Otherwise is excessively conservative and necessarily keeps the bad part of Js (Js is what it is), while losing the better part (it's hackable, here for the better). Good guideline for when is enough of offshoots should include standard syntax when it's a good use even if an unconventional one. (So-so unconvencional actually, it's not like Svelte is so unheard of.)

n-smits avatar Aug 14 '21 08:08 n-smits

I agree that Svelte is an important growing use-case to support. Thanks for raising the issue.

Unfortunately, label $: foo 'hi' already has a meaning in CoffeeScript, namely, label({"$": foo('hi')}).

The following doesn't have a meaning yet though:

label $
  foo 'hi'

This matches the syntax style of if and for and so on, which seems good. This suggests that the inline syntax could be:

label $ then foo 'hi'

which currently has no meaning, so that seems good.

edemaine avatar Aug 14 '21 13:08 edemaine

One of the primary reasons for adding support for new features is compatibility. Users shouldn’t need to have to choose between CoffeeScript and Svelte, for example; we added support for JSX (and before that, ES6 classes) to enable full compatibility with React. So yes, we should find some way to support this.

I don’t know about this particular proposed syntax, though. Since label $ compiles, it feels awfully brittle for users to know they need a second line (or then) to get the desired JS label syntax rather than label($). I think it would be better if we could find some new symbol that currently doesn’t compile at all, or always throws at the start of a line; but not :=, as that one might get used either for static types or for block assignment, if we ever add either of those.

GeoffreyBooth avatar Sep 18 '21 23:09 GeoffreyBooth

Very happy that this is even being discussed. I'm working on an extension to sveltekit and my way around this (which uses backticks) is the most fragile part of my code.

johndeighan avatar Oct 04 '21 18:10 johndeighan

I don’t know about this particular proposed syntax, though. Since label $ compiles, it feels awfully brittle for users to know they need a second line (or then) to get the desired JS label syntax rather than label($). I think it would be better if we could find some new symbol that currently doesn’t compile at all, or always throws at the start of a line

How about on $? It's an alias for true in Coffee which is kinda confusing...

  1. but on the other hand, word 'on' has other meanings, specifically, it matches meaning of label
  2. it's very short, like JS colon :
  3. it's non breaking (Coffee doesn't allow on foo)
on $ then foo 'hi'
on $
  foo 'hi'

Alternatively, from also satisfies. from $ then foo 'hi'

n-smits avatar Oct 27 '21 06:10 n-smits

The Svelte syntax is $:, which we can’t use because it already compiles; but :$ is available. How do these look?

# https://svelte.dev/tutorial/reactive-declarations
:$ doubled = count * 2

# https://svelte.dev/tutorial/reactive-statements
:$ if count >= 10
  alert 'count is dangerously high!'
  count = 9

GeoffreyBooth avatar Oct 27 '21 17:10 GeoffreyBooth

Ok-ish, because it uses characters not words. Also second example bothers me, look like if-postfix (no then). Otherwise, yeah it works and isn't too difficult to get used to. I would wait a bit more, perhaps something better comes along

n-smits avatar Oct 27 '21 18:10 n-smits

All of the possible words are listed here: https://github.com/jashkenas/coffeescript/blob/ed6733d17746eeafca415cc261cce92e6f840ff6/src/lexer.coffee#L1225-L1264

We can’t add any more reserved keywords without breaking changes (which basically can’t happen anymore). These are mostly spoken for, and the few that aren’t (like enum) would probably get used as part of adding support for TypeScript, if we ever get that far. The few that could get repurposed because of quirks of grammar as to where they can be used, like on and from, don’t really make much sense in this context.

Anything after : is available, so if we want to use a word we could do :label say. But I don’t really consider that more readable than :$. The advantage of words is when they spell out something resembling a readable sentence, and a label prefix doesn’t have an obvious English corollary the way unless or when do.

GeoffreyBooth avatar Oct 27 '21 19:10 GeoffreyBooth

You're right. Actually, now when you put it that way, :anyword is perfect for label. My only reservation is labels didn't get much good usage, but hey who knows.

n-smits avatar Oct 27 '21 19:10 n-smits

If we added a general :label, it would enable adding nifty features like break label (which exists in JS but not CS). I've definitely wanted to break two levels out in the past. And it would be a general solution that solves the Svelte problem.

An alternative syntax would be :label:. I'm not sure whether it's better though.

:$: doubled = count * 2

:$: if count >= 10
  alert 'count is dangerously high!'
  count = 9

edemaine avatar Oct 27 '21 19:10 edemaine

Why the extra colon? As for :label (with or without extra colon), I still like it a week later

n-smits avatar Nov 01 '21 22:11 n-smits

The first thing you should be asking is whether your intent is to support the concept of labels in general, or if what you really want to support is reactive assignments and/or statements. Most older uses for labels were very bad (think 'goto'). However, at the end of this post, I'll show a use case that I used often in Perl.

If it's reactive assignments that you want to support, then here's a syntax that I use in something I'm working on. I have no idea if it would work in CoffeeScript - just something to think about:

doubled <== 2 * count

This means "whenever a variable in the expression following <== changes, recompute the variable 'doubled'

<==
	ctx.fillStyle = $prefs.color
	ctx.fillRect(10, 10, 80, 80)

This means whenever any variable in the enclosed code changes, re-run the code.

Personally, I rarely use the 2nd syntax since it's possible to just call a function using the 1st as long as you pass any variables that you want to trigger a re-run to that function. But then you have to define the function somewhere, so a bit less convenient.

It seems like supporting this syntax, assuming it's converted directly to $: or $: { code block} in the JS would only be supporting svelte programmers. But I think it's also possible to convert it to plain JS code that accomplishes the same thing. A bit complex, I know, but ultimately, svelte does just that.

Now, the argument for simply supporting labels, which would be simpler to implement anyway. Here is a pattern that I've often used in Perl (this may not be valid Perl, but the intent is obvious):

FILE:
for filename in lFiles
   content = readFile(filename)
   lLines = content.split('\n')
   LINE:
   for line in lLines
      next LINE if (line == '__END__')
      procLine(line)

Now, as written, it will skip any lines like 'END'. But if instead, I wanted to skip the rest of the current file, I would just change 'next LINE' to 'next FILE'. The point is that when I have nested loops, being able to put a label on each loop allows me to specify unambiguously which loop iteration I'm prematurely exiting. I think this is basically the only good use case I've found for labels. Well, except for supporting svelte, where it's used less as a true label, and more as a way of tagging code as being 'reactive'.

johndeighan avatar Jan 23 '22 12:01 johndeighan

any update ?

gcxfd avatar Mar 24 '22 17:03 gcxfd

image https://livescript.net/#functions I see livescript support label , but It seems dead ...

gcxfd avatar Mar 25 '22 04:03 gcxfd

I write a coffeescript label patch for svelte : https://github.com/rmw-lib/coffee-label-patch

Make coffeescript support :label syntax (similar to livescript) so that it can be used for svelte.

It's too hard to modify the compiler based on lex, ast, and I did this based on string substitution.

This is just a crude hack, but it does work.

Expect coffee to officially add this syntax.

y = 0

:$ x=y*2

:$ if y>2 then x+=y else x-=y

:$
  if x > y
    x = y/2
  else
    x = y+9
  x += 1

do =>
  :out
    for i in [1,2,3]
      for j in [4,5,6]
        console.log i,j
        if i > 1
          break out
  return

output :

var x, y;

y = 0;

$ : x = y * 2;

$ : y > 2 ? x += y : x -= y;

$ : {
  if (x > y) {
    x = y / 2;
  } else {
    x = y + 9;
  }
  x += 1;
}

(() => {
  var i, j, k, l, len, len1, ref, ref1;
  out : {
    ref = [1, 2, 3];
    for (k = 0, len = ref.length; k < len; k++) {
      i = ref[k];
      ref1 = [4, 5, 6];
      for (l = 0, len1 = ref1.length; l < len1; l++) {
        j = ref1[l];
        console.log(i, j);
        if (i > 1) {
          break out;
        }
      }
    }
  }
})();

it work , i release a preview in @rmw/svelte-preprocess

yarn add -D @rmw/svelte-preprocess @rmw/coffee-label-patch

demo video : https://www.loom.com/share/a45ffe7eeecb4115ad335b7db21f9b04

the code for demo video https://github.com/rmw-lib/svelte-pug-stylus-coffee

and also I create a pull request for svelte-preprocess https://github.com/sveltejs/svelte-preprocess/pull/493

gcxfd avatar Mar 25 '22 06:03 gcxfd