language
language copied to clipboard
Allow punctuation based alternatives to keywords?
Dart syntax relies on keywords in many situations. For example:
class C<X extends B<X>> extends S with M<X> implements I, J {}
We could introduce punctuation based alternatives to some of the most frequently used keywords. The rationale would be that this is more concise, and it would very quickly be established as a notation for the given purpose. In other words, extends might arguably be more self-explanatory than :, but it won't take long before they are equally readable if we see them all the time.
One example of a language using punctuation for several of the elements shown above is Kotlin:
// Kotlin.
class C<X : B<X>> : S(), I, J {}
Kotlin uses : to indicate that the following type is the bound of the type variable, and also to indicate that the supertypes of the class are specified (that is, an optional superclass which is extended, and zero or more interfaces which are implemented).
Dart cannot directly use the same syntax for superinterfaces because the superclass can be composite (consisting of a superclass and one or more mixins), and the implemented superinterfaces cannot be recognized by being interfaces (it can be an interface class, but it can also be any other kind of class).
However, we could use two colons (one to replace extends and the next one to replace implements):
// Hypothetical Dart.
class C<X : B<X>> : S & M<X> : I, J {}
If a class implements something and doesn't specify a superclass then we'd have the two colons together: class A :: I {}. Similarly, class A {} means class A : Object {}.
There are many other locations in Dart code where a similar thing could be done. For example, required in a declaration of a named parameter could be written as ! (for example, immediately before the name of the parameter). Also Function in a function type could be abbreviated (perhaps Fun, or maybe a plain F would do).
What do you think? The conciseness can be striking, but the punctuation based notation will surely be considered harder to read by some developers..
It is only harder to read as long as one doesn't know that it means. Even so, people can still often deduce what it means by context.
I don't think we should refrain from using symbols because someone will find it hard to read. Anyone can learn APL, so using : for extends and & for with is fine.
Can class A extends B {} and class A implements B {} be distinguished with punctuation?
The S & M syntax may conflict with intersection types if they are ever added in the language.
Edit: I saw the idea to write ::, thank you.
Can
class A extends B {}andclass A implements B {}be distinguished with punctuation?
I'm mainly interested in the overall preferences, I don't claim that anything I mentioned would be a well-designed proposal for actual syntax. If we want to enable this more concise kind of syntax then we'd need to sort out the details, but I'm sure we can do that.
That said, those two declarations would be class A : B {} and class A :: B {}, so they can be distinguished.
The
S & Msyntax may conflict with intersection types if they are ever added
True, but this might even be a non-problem: Mixin application does create a class which is a subtype of the superclass and also a subtype of the mixin, which makes it yet another kind of intersection type. Next, S & M occurring in the position where a superclass is expected would denote a mixin application, and that wouldn't rule out the possibility that I & J denotes an intersection type if it occurs as the bound of a type variable (which is one place where intersection types and similar entities have been requested several types).
You could of course say that class C : ... : I, J (today: class C extends ... implements I, J) also denotes an intersection type because it makes the enclosing class declaration a subtype of I as well as a subtype of J. However, it doesn't seem possible to use , everywhere to denote intersection-type-ish terms, and also not realistic to use & in implements clauses, so we might well end up with a design where both , and & are used for this kind of purpose.
It'll make class declaration shorter. That's always nice.
They're probably not much harder to read. Would have to do some tests.
The distinction between : Foo and :: Foo might be a little too subtle, but it'd be invisible in DartDoc which just shows superclass and interfaces as a separate list. You don't have to always read the source. If you can read the IDE popup when pointing that the source, you can get a nicer format.
I do feel the pain when writing many class declarations at the same time, but it's not something I do that often.
It's not where the majority of my writing happens, so it's not the first place I'd look for shorthand syntax.
The pain of repeating the class name in the subclass extends or implements clause is at least as big, especially if they're all generic classes. Generally, repeating types is much more cumbersome than the keywords we have today, and this wouldn't help with that at all. It doesn't change how much many tokens you write, it's just a shorter keyword in the same position.
I'm not opposed to the idea on any principled ground, but I don't see it as big win, and therefore not as a high priority. Primary constructors will save much more typing than this, and that's eliminating actual repetitions.
(I'd save more characters total if we allow val or let instead of final for local variables. Or I would hyptothetically, compared to the final that I never write today because it's too long.)
I suggest a simpler nomenclature - let's just introduce shortcuts:
::forextends:forimplementswith- remains as is (rarely used, the shortcut is not justified)valforfinal- no
&
let, though a better word than val, has a misfortune of being a verb: let x=0; is fine, but let x; for an uninitialized field or a function parameter is a bit baffling. It's OK for the languages with "types on the right" to declare let x: String, but with the types on the left let String x says it backwards. Maybe it's possible to get used to it though, and read let not as a word, but as a symbol.
It's a philosophical question.
Related: in type arguments, we use the keyword extends though in this context, it doesn't make any difference whether it extends or implements. Maybe :: and : can be made interchangeable inside <...>. Not sure.
I don't like this.
One of Dart's strength is its simplicity and its similarity to other popular languages. Lots of Dart developers come from JS or Java. That'd make Dart harder to pick-up for a significant portion of its user-base.
And I doubt we'd be removing the ability to use extends, because that'd be a huge breaking change.
Meaning we'd end-up with two syntaxes for doing the exact same thing. That'd bring confusion, increase the learning curve, and cause pointless stylistic debates like "extends T or : T" where some will use one over the other.
As someone who had a (mis)fortune to code in java for approx. 20 years (before gotten kicked out for good, which ended the torture), I can tell you that the last thing java programmer wants is another java.
The languages supporting the : notation, in aggregate, have a bigger market share than java.
Everyone who knows java will be thrilled to see the remaining javaisms gone.
The additional rationale for a more concise notation comes from the static interfaces: saying
class C extends X Implements Y, Z static implements Q is quite a mouthful, and not readable at all.
I know this was kind of just an afterthought, but
Also
Functionin a function type could be abbreviated (perhapsFun, or maybe a plainFwould do).
I think that's my favorite idea from this proposal :)
I think in cases like this, it's important to remember that any given line of code is written basically once but read over and over again.
Of course, one could get used to cryptic punctuation and develop mnemonics that work for them, but I feel it's undeniable that human-speakable words make code drastically more accessible.
It's not even about coming from language X or Y. Python is considered one of the most beginner-friendly languages ever, and it's filled with keywords you won't find in other languages, not to mention abandoning some very common punctuation like ++ and ?: in favor of more explicit keywords or statements. Meanwhile, Rust... I shouldn't even have to finish that sentence, but let's just say its syntax is a genuine stumbling block to many developers and can get in the way of learning the actual concepts Rust is trying to teach.
Dart is already using punctuation a lot, with a large increase since null safety. While it's true that class modifiers are really pushing the boundaries of keyword-to-meaningful-token ratios, it shouldn't be ignored that there was extensive discussion around the exact naming and ordering of those modifiers. Replacing them with punctuation would fly in the face of all meaning associated with those choices. There is nothing about :: that implies "this class extends another's features". I can't even think of a symbol for sealed.
In other words,
extendsmight arguably be more self-explanatory than:, but it won't take long before they are equally readable if we see them all the time.
Again, true, but it's that transition period I'm wondering about. Is it really worth alienating so many just to save some characters when typing? Class declarations aren't expressions, and they don't need to look like it. They currently have a ton of valid combinations, and that's not even including extends, implements, with, or on.
In other words, while it's true that humans can easily get used to some symbols and use them sparingly to save space, there's an extreme gap between rules like "use ? for nullability" and class C<X : B<X>> : S & M<X> : I, J {}
@eernstg: I think you made a mistake by presenting the case in an overly abstract form.
// Hypothetical Dart.
class C<X : B<X>> : S & M<X> : I, J {}
Indeed, this can scare off even the most hardcore user. In practice, the components would not be cryptic at all.
Here's a more realistic example (I removed &)
// Hypothetical Dart.
class C<X : num> :Comparable<num> with MyMixin<X> static Decodable<C> {}
There's nothing controversial about the syntax. Compare it with the alternative:
class C<X extends num> implements Comparable<num> with MyMixin<X> static implements Decodable<C> {}
Does anybody think the latter is more readable?
Considering the issue title, another case to take into account:
for (var number in numbers) {}
versus
for (var number : numbers) {}
I also think we could benefit from providing concise alternatives to commonly used syntaxes. I believe concepts that are both (1) widely understood from people coming from all sorts of languages, and (2) frequently occurable would benefit the most from becoming shorter and symbolic e.g. the final keyword could greatly benefit from this. This is because a programmer does not have to learn a new concept, simply a new and very simple notation.
I disagree with the idea that this approach should be avoided because other languages do not do it. This is because it can be a stylistic choice, much of which Dart code already is. Furthermore, Dart has deviated from other common languages in significant and various ways. One example of the class modifier keywords. While there is overlap with different popular languages, looking at the most popular languages all together, it would be incorrect to say these features are common.
I also disagree with the notion that more concise syntax only benefits the writer. Conciseness is also for the reader. By shortening the words and symbols necessary to convey an idea, the reader can (1) take in more information at a glance and (2) have to map semantics to less symbols (less cognitive overhead). Of course it can be overdone, but it can definitely be underdone as well and as of now it is currently underdone given the lack of shorthand syntaxes.
I think conventional mathematics is a great example to follow in this regard. Symbolize simple and widely used concepts while using names for less common ones. Although, I think many symbols are already in use so shortening keywords is primarily the way to.
Here's a more realistic example (I removed
&)// Hypothetical Dart. class C<X : num> :Comparable<num> with MyMixin<X> static Decodable<C> {}There's nothing controversial about the syntax. Compare it with the alternative:
class C<X extends num> implements Comparable<num> with MyMixin<X> static implements Decodable<C> {}
I mean, it starts with the very simple : = extends, but then
:Comparablesomehow equalsimplements Comparable, even though whitespace doesn't usually matter in Dartstatic implementsbecomes juststatic
If the question here is to just make : = extends, or : = in, that's one thing, but asking for an overhaul of the existing syntax is an entirely different thing, and while I can get behind static instead of static implements, I don't know how valuable it is to overload punctuation that has little to none intuitive meaning as opposed to using real human words.
I've never particularly liked Dart's verbosity here, but I suspect the value of improvement here is too marginal to be worth the cost in migration.
It's like trying to move your house one inch to the left. Sure, you might get slightly better sunlight, but you already built the whole dang house and moved your family in.
On the other hand, language with a lot of punctuation looks for me kinda magical. Dart is too verbose, but keywords instead of punctuations makes the intent cleaner.
: and :: meaning two different things in class signature is ambigous for reader.
Also I don't see a value in prefering less conscise syntax for implementing than for inheritance (if composition over inheritance is preferable).
What for me is not clear and unnecessary verbose is:
- why we use
extendsfor narrowing type in generics? It has nothing to do with inheriting base type Maybe more proper than:<T extends Foo>would be just<Foo T> - why we require to wrap in
{}to allow passing arg as named? - why nullable arg / with default value is not treated as optional? https://github.com/dart-lang/language/issues/2232
- why we require to pass both
interfaceandclassand not onlyinterface?
I suppose that there is a lot of places, where similar questions will arise and IMHO it will be better to address them first rather than migrating to not obvious punctuation. Then we could decide if switching to punctuations is still valuable @eernstg sorry for direct ping, but if think that problems with verbosity do not lay here. Overall I am not against punctuation, but they should be unambigous and clear for reader