Civet
Civet copied to clipboard
Suggestion: compact notation for an array of string literals
It's not that uncommon that I want a list of allowed keys or something like that. So not too infrequently I find myself writing
keys := 'skeleton blank house cipher car'.split ' '
which looks a bit goofy, but it's significantly pleasanter to type than
keys := ['skeleton', 'blank', 'house', 'cipher', 'car']
because of all those pinky and ring-finger keystrokes.
LiveScript had a compact notation for this, I think it was
keys = <[skeleton blank house cipher car]>
I don't love the <[ ... ]> as it looks sort of JSX-y, but I do think a compact notation for arrays of literal strings that don't contain whitespace would be pleasant/handy.
Brainstorming some options:
keys := [''skeleton blank house cipher car''] // it's kind of quote-y, riffs on triple quote
choices := ['|'option|choice|has space|freedom] // provides a place for the delimiter
answers := [[[correct dumb thoughtful wrong]]] // when in doubt, triple it; but it does shadow
// an array of one element, which is an array of one element, which is an array of one element
// produced by a chain of function calls. So that's likely unacceptable.
Anyhow, just a thought.
Neat idea! Some random thoughts:
- Operator overloading is something we'd like in the future. Maybe
"skeleton blank house cipher car"|" "->"skeleton blank house cipher car".split(" "). We could have|with a string rhs to convert tosplituntil we have full support for binary operator customization. - For static strings known at compile time we could even generate proper TS types for the split.
Another fun thing we could do is transpile 'skeleton blank house cipher car'.split ' ' to ['skeleton', 'blank', 'house', 'cipher', 'car'], as an optimization, similar to how [0..9] transpiles. This doesn't solve the beauty/notation though.
Another way to write this today would be ssv`skeleton blank house cipher car` for an appropriate function ssv (named to mean space-separated data). I agree it'd be nice to have something built-in though.
Another way to write this today would be
True, and I guess you could type ssv so
keys := ssv`skeleton blank etc`
would get the literal type ['skeleton', 'blank', 'etc'], without too much difficulty. I should probably write that way in the meantime.
Another random syntax possibility:
keys := []skeleton blank house cipher car[]
I'm not saying any of the suggested syntaxes are perfect, but some seem plausibly acceptable. This one is the most compact I've thought of that is currently a parse error and therefore potentially up for grabs.
By the way, it's perhaps useful to reflect on other languages that have this feature:
- Perl:
qw(skeleton blank house cipher car)is shorthand for("skeleton", "blank", "house", "cipher", "car"). You can also use[]s or any matching character likeqw!...!orqw/.../. - Ruby:
%w(skeleton blank house cipher car)is shorthand for["skeleton", "blank", "house", "cipher", "car"]. You can also use[]s or any matching character like%w!...!or%w/.../. - Bash/Zsh:
(skeleton blank house cipher car)is equivalent to("skeleton" "blank" "house" "cipher" "car")(thanks to quoting rules)
qw and %w do look a lot like template syntax, but there's the nifty idea of having arbitrary delimiters (like LaTeX's \verb) which is nice if your strings have particular characters. Admittedly we're breaking on whitespace, so that's already a bit restrictive.
Inspired by your last idea, I wonder about this syntax: []`skeleton blank house cipher car`, as a generalization of template syntax... Although I'm not sure what ${} expansion would mean, especially if this is done at compile time, so maybe this is a false analog... Though we could define some reasonable meaning, e.g.:
[]`skeleton ${key} x${key} ${...more}` → ["skeleton", key, `x${key}`, ...more]
(though perhaps I'm getting carried away)
I am actually pretty comfortable with
[]`a b c`
I think it's pretty suggestive of "make this literal into an array in the most obvious way". Covers my typical use case pretty nicely.
I suppose to avoid the specialness of breaking on whitespace, making it just a default, it could be nice to allow e.g.,
[|]`option A|choice B`
Or maybe better
['|']`option A|choice B`
So that
[foo]`stuff`
really just becomes syntactic sugar for
`stuff`.split(foo)
with a new default of ' ' if foo is not specified (which frankly I am surprised JavaScript does not already allow in split).
Nice, I like that generalization!
I think the default should be /\s+/, not ' ', matching the default for Python's split. (I agree it's annoying that it's not the default in JavaScript.) In the special case of the default, I think it also makes sense to remove an empty first or last item, so that []` a b c ` still produces ['a', 'b', 'c']. This is especially useful for multiple lines:
[]`
option1
option2
option3
`
True, and I guess you could type ssv so
keys := ssv`skeleton blank etc`
would get the literal type ['skeleton', 'blank', 'etc'], without too much difficulty
Well actually, I spoke too soon. I was going to contribute to this discussion some working correctly-typed code for ssv (that would split on /s+/) and also support
ssv('|')`My values|have spaces`
ssv(/[.]+/)`I...like ellipses... very much!`
mostly because I would use it anyway and possibly it could provide a compilation target for
[]`The standard \n thing makes \t 6 words.`
[' ']`Only break \n on spaces to get an array of 12 elements.`
[/[{}() ]/]`You{[bracket buster,] you!}`
But then I ran into https://github.com/microsoft/TypeScript/issues/33304. (And very gratifyingly/amusingly one of the comments on that issue is someone explicitly trying to produce a tagged template replicating ruby %w[foo bar zaz] in Typescript. So we are in fact onto something...)
Oops, who knew? So I have given up on this micro-project for the time being. But in some sense it would make it even more useful/cool if Civet did implement this latest []-template literal notation as just above, which I get the sense the two of you like best from the options floated here, in a type-safe way today.
P.S. About the meaning of interpolation in
[foo]`Some ${substituted} x${pressions} to ponder${bang}`
I personally think that since the usual backticks just deal in strings so this feels "stringy" and feels mostly about literal arrays of literal strings, this syntax should just operate on the string level and so the above should precisely mean
`Some ${substituted} x${pressions} to ponder${bang}`.split(foo)
with the caveat of when foo is absent there's the special-case behavior of splitting on /\s+/ and discarding empty first and last chunks if present.
Moreover, I would suggest it is fine to type as string[] when foo or any interpolation is present. Any finer typing, e.g. if all of the interpolated variables are known constants and/or if foo is just a literal string, say, would be icing on the cake. Re this, note that in TypeScript today
const foo = `bar baz` // types as 'bar baz'
const quux = `quince ${foo}` // types as string
- Ruby:
%w(skeleton blank house cipher car)is shorthand for["skeleton", "blank", "house", "cipher", "car"]. You can also use[]s or any matching character like%w!...!or%w/.../.
@edemaine @gwhitney @STRd6 +1 vote for this!
I use %w[a b c] all the time in Ruby… Also, note that (uppercase) %W[a #{variable} c] allows interpolation, and also there is %d[1 2 3] for an array of integers, amongst other % markers…