Do blocks in Civet are mainly to provide shielded scopes (with local declarations). They're currently also necessary to handle declarations (const/let) in the middle of expressions, but hopefully they won't be necessary eventually - only when the user wants to shield scopes. They also have useful forms like `async do` that let you build Promises using await-style code.
Yes, Civet has taken a lot of syntactic inspiration from LiveScript. At this point, I think we have most of the good features, but we might be missing some. Let us know what you think!
The big difference, of course, is that Civet fully supports TypeScript, and is up-to-date with the latest JavaScript and TypeScript features.
The JSX spec hasn't changed for almost 10 years, and I'd guess there will never be a JSX 2.0. On the other hand, ideas for a better JSX are plentiful (check out the issues on the JSX repo, for example). If the spec never changes, how can we improve the JSX experience? Transpilation!
Civet's futuristic JSX compiles to actual spec-compliant JSX, to it's compatible with all forms of JSX, including React, Solid, etc. We'd like to support other DSLs like Astro and Svelte as well.
Not so sure. JSX is already a transpilation language. Why target a language that's only supported as an input language for other compilers? I don't think this is an LLVM-type scenario.
As an example, Solid's JSX compiler changes multiple times in a year. It's better to target JSX than the compiled form. There are also many JSX compilers. (Even React ships with at least two.)
Good question! I don't have hard numbers, but for larger files, I find that it can take on the order of a second to compile. It's still fast enough to get real-time feedback from TypeScript in VSCode, but it could definitely be faster. There's lots of optimization left to do; for now, we're focusing on features over speed, but we'll get to speed as well. (I am an algorithms guy after all!)
You can also turn off implicit returns in Civet if you don't like them. They also work well with TypeScript annotations: if you annotate a return value of `void`, then there's no implicit return.
I agree it can be easy to make and throw away big arrays if you're not aware of what's going on. But it can also save a lot of time. For loops as arrays are super useful, integrating the equivalent of "map" into the language itself. We also recently added generator versions (for*). JSX is a nice example where for loops as expressions and implicit return are powerful; see e.g. https://civet.dev/reference#code-children
What might work well is a lint rule to error if a loop expression ends an actual function declaration (i.e. not an inline callback), and the function doesn’t explicitly define a return type. I think that catches almost every bad case, aside from the odd memory leak in really unusual edge cases.
In case you wondered how Civet compared to CoffeeScript in these regards:
* `is not` is the textual equivalent of `!==`. You can use `isnt` if you turn on the feature explicitly (or even the weird CoffeeScript `is not` behavior if you want it, mainly for legacy code)
* Implicit returns are turned on by default. They are really useful, most of the time, and don't get in the way much if you use `void` return annotations (which turns them off). But if you don't like them, you can turn them off globally with a compiler flag.
* Civet's compiler is built on very different technology from CoffeeScript's (PEG parsing, similar to Python), and it is much more strict about indentation. None of those weird bugs anymore.
* We do have implicit parentheses and braces and such, but you're free to use explicit parentheses and braces as you like. We encourage people to rename their .ts files into .civet (which mostly just works without any converison) and just embrace the features/syntax they like.
> but you're free to use explicit parentheses and braces as you like
I’ve been eying Civet and I quite like it so far, but I dislike having this option because it means I’ll have to see it in other peoples code. I prefer strict rules for readability.
* `{min, max} := Math` is a destructuring declaration. It's similar to the destructuring assignment `{min, max} = Math` (i.e., `min = Math.min; max = Math.max`), but also declares min and max as const.
* The `operator` prefix means to treat min and max as new infix operators in the rest of the program.
Second line:
Given that min and max are infix operators, `value min ceiling max floor` is equivalent to `max(min(value, ceiling), floor)`. Yes, the latter is gross. That's why we like to write `value min ceiling max floor` instead. Think of it as "value minned with ceilling (i.e. capped at ceiling), then maxed with floor (i.e. prevented from going below floor)".
But we are using indentation style; that's one of the major design principles. In Civet, the body of an "if" can be multiple lines, and it's clear (from indentation) what they're nested under. Also, the body of an "if" statement can be empty, so if you comment out the body, the "if" doesn't apply to anything else. (This is an improvement over Python, which requires non-empty bodies, even if just to say "pass".)
I think there's a reason that Python is among the most popular programming languages, and part of it is the indentation-based syntax and lack of brackets. The core of Civet's syntax (originally inspired by CoffeeScript) is like a combination of JavaScript/TypeScript and Python, the two/three most popular programming languages.
But also, if you like brackets, you can include them! Most JavaScript/TypeScript code is also valid Civet. Just use the features you like.
>I think there's a reason that Python is among the most popular programming languages, and part of it is the indentation-based syntax and lack of brackets.
Perhaps, but IMO that will then be because it looks tempting at first glance like YAML. No matter how much Python I write, I never learned to enjoy this and the fact that I can't select inside/around brackets to quickly manipulate a scope is just so frustrating.
Python used an indentation-based syntax from the beginning and its syntax is particularly optimized for that. In particular, any block statement (formally, a statement followed by a "suite") would end with `:` and that's effectively the only place where a dangling `:` can happen [1], so you can easily recognize such statement from any directions.
In comparison, at least some people would find CoffeeScript and Civet to be hard to read because they solely rely on left-bearing indents. If my eye is pointing to the rightmost column and scanning to left, I wouldn't be sure about any nature of the line until the very first token and thus preceding indent is reached. This problem is not unique but can be somehow alleviated with some tweaks to the syntax. Ruby `if` for example is also prone to this issue but an explicit `end` token keeps it on track in most cases. CoffeeScript did nothing.
[1] The only other case is `lambda ...:` in parenthesized expressions. `lambda` in Python is quite exceptional in its syntax after all...
We're definitely looking for ways to improve ways to specify types! I think destructured typing [https://civet.dev/reference#destructured-typing] is already quite useful, especially for React. On the readability side, if/then/else [https://civet.dev/reference#conditional-types] seems easier to read than ?: ternaries, and "Partial Record Name, Info" seems easier to read than "Partial<Record<Name, Info>>" (implicit type arguments — https://civet.dev/reference#implicit-type-arguments). But we'd love to hear more ideas for features like branded types. Join us in Discord if you're interested!