Scala is a fun language, but consistency is not one of its ecosystem's strengths.
Grain of salt: again, I'm new to Scala, but I'd hate to think of it as a language that can only be good once you're Stockholm'd.
I agree with you on one level. But taking a step back, and looking at it from a much longer time perspective than is practical in business - is the problem really the programming languages giving us too much choice?
Constraining how a programming language does something because we're not yet "there" in terms of software engineering does seem a bit backwards.
That said, I don't see anything wrong with this proposal. It makes more likely that code structure mirrors its layout, which is a good thing. Even if it's not always used, it's an improvement where it is.
However, special cases in the syntax increase the complexity and I would always argue against that.
And that says nothing about third party libs that one can't control. Again, I am new to the ecosystem, but with languages like C#, it seems that the comparatively fewer language constructs streamline API patterns.
def f =
def g =
...
(long code sequence)
...
end f // optional
while
println("z")
true
do
println("x")
println("y")
end while // optional
package p with
object o with
class C extends Object
with Serializable with
val x = new C with
def y = 3
val result =
if x == x then
println("yes")
true
else
println("no")
false
end C // optional
end o // you guessed it: optional
Edit: somebody already brought this up in the comments on Github: https://github.com/lampepfl/dotty/issues/2491#issuecomment-3...For now, to quote the authors, it's a: 'Research platform for new language concepts and compiler technologies for Scala'
So this is exactly the right place to test this sort of concept.
And this is a huge breaking change: if they make it you won't be able to convert your Scala 2.1X code to Dotty/Scala 3 by simply fixing all the compiler errors; you'll either need a foolproof automated conversion tool or to hand-audit your entire codebase.
They do make some other breaking changes in 2.xx releases (which come out about every 1.5 - 2 years!), but I wouldn't really call them point releases, given that the 2.x hasn't changed in over a decade - that's like saying Python shouldn't make breaking changes in Python 2.6 -> 2.7. They don't generally make breaking changes in actual point releases (2.XX.yy).
Also, they're building an automated conversion tool (https://www.scala-lang.org/blog/2016/10/24/scalafix.html) for Dotty. As I said, compare this to Python 3. The Scala -> Dotty rewriter should be able to be more complete than 2to3 was however, mostly because they're not fixing many ambiguities like 2to3 was with encoding. Their rewriter is also based on a full sophisticated framework that can parse or unparse multiple versions of Scala, including Dotty, in one build.
Hopefully being able to rewrite a much, much higher percentage of code and the backporting of changes into Scala 2.13+ will make Dotty adoption happen faster (than Python 3) when it comes.
But I looked more carefully and the proposal is more balanced. It looks more like Haskell than Python -- and Haskell's syntax is one of the best ever invented, in my opinion (too bad I am not yet that smart to casually emit production-grade Haskell code).
So, I'm fine with that.
This seems like yet another great way to make that worse.
- Remove the common leading whitespace of both strings.
- The remaining parts of both strings must be all tabs, all spaces, or empty.
This is the least restrictive rule which catches all indent ambiguities.
2. Indent-based syntax is great for imperative languages, but not so good for functional languages with very long expressions. It's not clear how to indent stuff like "a.b(x).c(y).d.e(z)", where a-e x-z may be long expressions. In LISP, the all-parenthesis syntax was so simple, and so hard for humans to parse without help, that indentation was nailed into EMACS and everybody did it that way. The indentation wasn't significant, but it was standardized.
Here's word wrap in Rust:
s.lines()
.map(|bline| UnicodeSegmentation::graphemes(bline, true) // yields vec of graphemes (&str)
.collect::<Vec<&str>>())
.map(|line| wordwrapline(&line, maxline, maxword))
.collect::<Vec<String>>()
.join("\n")
Note that the first "collect" is one level deeper in parentheses than the second one.
How would you do that with indentation only?I feel like you can get rid of the inner one too but forget the right combinator.
use self::itertools::join;
use self::itertools::Itertools;
....
s.lines()
.map(|bline| UnicodeSegmentation::graphemes(bline, true)
.collect::<Vec<&str>>())
.join(|line| wordwrapline(&line, maxline, maxword),"\n")
error[E0061]: this function takes 1 parameter but 2 parameters were supplied.Rust is picking the wrong version of "join". There's one in Iter with one parameter and one in Itertools with two parameters. Haven't figured out how to get the one from Itertools yet. The obvious syntax, ".Itertools::join(...)" gets "error: expected `<`, found `join`".
Without either function overloading or member function qualification, how do you do this?
enum Tree[T]
Branch(t: Tree[T])
Leaf(t: T)
Compare that with preset day Scala: sealed trait Tree[T]
case class Branch(t: Tree[T]) extends Tree[T]
case class Leaf(t: T) extends Tree[T]
In general with this proposal the `case` keyword could be implied in any pattern matching block. That alone would be a big win wrt to reducing keyword noise, something the MLs have enjoyed for decades. enum Tree[T] {
Branch(t: Tree[T])
Leaf(t: T)
}
The main benefit here involves using a block instead of "extends", and using "enum" instead of the odd use of a class hierarchy. enum Tree[T] {
case Branch(t: Tree[T])
case Leaf(t: T)
}
https://github.com/lampepfl/dotty/issues/1970 foo match {
x: Bar =>
(y: Int) => x.num + y
x: Baz => ...
}
With significant whitespace the first block of indented code would mark a `case` pattern, with subsequent indents belonging to the matched pattern.With braces I suspect the `case`less version becomes more difficult to parse. Otherwise why require `case` in pattern matches?
Not that that's necessarily a bad thing; it's good to have some popular languages that follow a less conservative approach, if only to get some real-world experience with different strategies for dealing with the tradeoffs between keeping a language modern and maintaining backwards compatibility with legacy code.
The language is idiosyncratic and multiparadigm enough as it is, I'd say.
CoffeeScript attempts to become more concise by removing delimiters and making things more implicit. I don't think that actually adds much value.
In a sense, it was the equivalent of trying to simplify traffic by removing lane delimiters and street signaling. You could somehow imagine they're there, but it's better if you can see them.
I'm a huge proponent of TypeScript and critic of using CoffeeScript in 2017, so while you may not(but may!) agree that TypeScript brings significant value over raw ES6+ I definitely relate with CofeeScript not bringing enough value to the table to warrant such a divergence in syntax. I will say though that pre-ES6+ it really tidied up a few things like classes, this binding, etc.
I've argued for and successfully migrated CoffeeScript projects to TypeScript, but I'll be the first to admit it is "ugly" compared to CoffeeScript. F# is a pretty elegant language IMHO, and it's success using significant white space is cited early in the post. If we can have all the added VALUE of Scala AND a tidier, potentially optional syntax then why not?
Allow both indentation-based and bracket-based syntax. Have a tool like scalafmt/goformat that freely converts between the two on a per-file and per-project basis.
When you're writing your own code, use the indentation-based syntax. You can paste in bracket-based code anywhere, hit the auto-format key on your IDE or run the commandline formatter and everything becomes nice and indentation-based.
When you're writing books, libraries, example projects, SO posts, use the bracket-based syntax. That way people who read your book/library source/example/SO post can freely copy and paste into their own projects.
This has minimal impact on bracket-based syntax diehards; everything they read and write stays the same. They won't even see the new syntax if they don't want to.
This has been a problem for me so infrequently that I can't even recall an incident where it wasn't so trivial to fix that I did it unconsciously.
Maybe if the website/ebook has messed up formatting or you're pasting from some monstrosity of a PDF - but in the real world - not an issue.
Maybe my editor (PyCharm) is doing clever things to protect me from this - who knows...
And copy-pasting is essential to writing code; if anybody tells you otherwise, they are lying. :)
But with braces, you can do consistency checks, such as that they are properly nested. In a similar way, you can do consistency checks with indentation. For example:
line 1
line 2
line 3
The third line is wrong, the transition from line 1 to line 2 introduced an indentation step, and the third line is neither left nor right.Language spec [1], replacing the standard library [2], etc.
[1] Disallow implicit conversions between unrelated types https://github.com/lampepfl/dotty/pull/2060
[2] Remove parallel collections from scala-library https://github.com/scala/scala/pull/5603
Is it known (as in quantitatively) if significant indentation is a good thing, or a bad thing?
Subjectively, languages with significant indentation (Python, F#, Nim, Coffeescript, Haskell, etc) are often thought to have nicer-looking syntax.
If you like significant invisible characters, use Python. No need to introduce this into other languages.
Given the creator of this proposal it feels very odd to take this stance. He created the language the proposal was made for. How vocal Odersky is or isn't in the community, it's very likely the precise degree of vocal he should/can be.
Also, it's a proposal to explore a topic. Odersky is a language designer and he wants to explore what his creation can be. You're being awfully dismissive of someone exploring their own project.