Newlines are not a fine separator; what if you have a definition that you intend to span multiple lines? In any case, the pipes go all the way back to EBNF, since what you are really doing when you specify an ADT is specifying a grammar of types.
I admit to also being a brace weenie; I would very much prefer if all the languages I had to use had them. However, Pyret exists in a tradition of many successful, braceless languages like Python, ML, and Lua.
I feel that the argument for the need for multiline definitions here is a bit weak. You are already putting each definition on its own line, so the definitions are actually fairly close to how their actual usage will look. I feel that if you really need them to be all that long, you are already in weird-style territory, so it isn't such a bad idea to just say "deal with wide files". Overall, it improves ergonomics for the vast majority of use cases, with the only cost being a possible aesthetic annoyance for users who are already writing aesthetically-annoying code.
Lines really aren't much of a thing: they are a single keypress, which generates a newline character and some autogenerated indentation from your editor. There's no real difficulty in breaking things up across two lines versus typing any other extra character (this touches on another issue, which is that I am of the opinion that the concept of code brevity estimation by line count comparison is fundamentally flawed, as code with fewer wider lines is often just cumbersome to type as code with more narrower lines).
That said, please consider the following:
data Color = Red | Black
data Color { Red; Black }
data Color = Red | Black | Green
data Color { Red; Black; Green }
I think the brace + semicolon-newline style actually compares pretty favorably on single-line width (it wins out on line width to an increasing degree as the line gets longer, which is important). However, it is visually more complex, which matters a lot for shorter lines. For this reason, I think that allowing both syntaxes would be ideal. The pipe-based syntax could be encouraged for single lines (newline-termination would sidestep block-end inference issues), with the curly brace based syntax being encouraged for multi-line definitions. There is a slight disadvantage in that now a user would have to know both syntaxes, of course.
edit:
Of course, since Pyret's syntax requires that ADT parameters be specified in parentheses, you can actually omit the pipes in single-line mode, too, and opt to use space-juxtaposition.
I can't respond directly to e12e's comment ("Please don't use equal for assignment" -- https://news.ycombinator.com/item?id=6704229) so I'll slightly abuse responding by doing so at this level.
We don't EVER use = for assignment. For us, = is binding. If you write
fun f(x):
y = x * x
y = x + 2
x - y
end
Pyret will say
I'm confused: y is defined twice
and point to the two bindings.
The goal here is to make the common case work fine, where you create a bunch of distinct local bindings; but when you try to mutate, you have to do it explicitly:
fun f(x):
var y = x * x
y := x + 2
x - y
where:
f(10) is -2 # the test passes
end
The first line inside f says "y is _currently_ this, but it's variable, so look out!", and subsequent mutations use := to change y. Unlike JavaScript (say), mutating a variable that hasn't previously been defined is an error, rather than quietly adding the variable to the set of defined names.
We considered it and decided against it for a few reasons.
- We want := to mean "change". The initial binding is not a change. This is precisely the "bindings and assignments" distinction you're talking about.
- A variable may in fact not be mutated.
- A small point is that we want to localize editing when making a change.
So, the way I read
var y = x * x
is, "y is currently bound to x * x. However, y is a variable, so this binding is not guaranteed to last. Be careful about assuming you know the value of y." If you then see
y := x * z
it reads as "y's value changes to that of x * z".
So: "=" means "introduced a name with this value"; "var" means "but this binding may change"; ":=" means "and yup, it really _did_ change".
From the very beginning we have debated whether or not to make the initial stick optional. We wanted at least a semester of code to review before we made a decision. I personally lean toward making it optional and just have to persuade the others (-:. With that, you would be able to write
data Color: Red | Black | Green;
We did also initially discuss using = instead of : in places where we were defining things (functions, data). That's still not completely out of the question. That would get you to
data Color = Red | Green | Black;
We need a closing delimiter to avoid creating huge ambiguities in the grammar.
I admit to also being a brace weenie; I would very much prefer if all the languages I had to use had them. However, Pyret exists in a tradition of many successful, braceless languages like Python, ML, and Lua.