val and final have stronger meanings when your objects are immutable. True immutability makes reasoning far easier than just referential immutability.
Aren't those members/fields not variables? If so, then those are effectively global not local, so that's a completely different story – I'm talking strictly about local variables.
> True immutability makes reasoning far easier than just referential immutability.
Yep, immutable types and constant global bindings are great.
I still don't understand why you consider having a compiler-enforced restriction on mutation worse than letting readers figure out what was the developer's intent.
In Java, the only "downside" is having to add "final" as a modifier, which is negligible.
In Scala, the alternative is to declare that variable as "var" instead of "val". When would you ever choose var over val if your object isn't supposed to mutate?
> In Scala, the alternative is to declare that variable as "var" instead of "val". When would you ever choose var over val if your object isn't supposed to mutate?
It's an extra keyword, an extra complication in the language – one more binary choice to multiply with all the other options. It would be nice to get rid of the distinction altogether.
As for marking local variable bindings as non-changing, I think it is tremendously helpful. In the (mostly Java) code base I work in daily we use this throughout. The net result is that I can just assume that property for everything, and whenever I see a variable not marked as non-changing I immediately know that something less than obvious is happening.
Given the above, I am naturally a big fan of making non-changing variable bindings (final/cons/val/...) the default and updatable variable bindings the case that should be marked. I would also like to work in a language where immutability of not just the variable binding but also the values themselves was better handled by the language.
A non-trivial number of people would dispute that ;-)
It's hard to argue in good faith that having the compiler catch mistakes rather than finding about them at runtime is a bad thing. It's perfectly possible to argue that it's not worth the perceived development speed slowdown.
I would go so far as to say that I think overly simplistic static type systems don't have much benefit. C's static typing drives me crazy, as there's almost nothing of use that I can express with it. It's the same with Go; I almost never pass an Int when I meant to pass a Bool. In exchange for thoroughly unhelpful type errors I now have to jump through flaming hoops to parse JSON.
It ends up being a bit like JavaScript or Python where both languages lack the ability to specify that something is truly private (though in JS you can use closures to hide things). You generally just use a naming convention to mark a thing as private, and hopefully people have the decency to respect that. It's like that with types in dynamic languages; I can express pretty complex relationships with types and keep the whole thing in my head with out many problems.
That said, languages with powerful type systems like Scala and Haskell are thoroughly worth the effort. I can express almost anything with these type systems, usually with a minimum of fuss. They can protect me from the dreaded NPE, and that's a bug I encounter quite often. They can help me write simpler code that deals with complex shapes of data with their support for pattern matching and TCO. This one is more Haskell related, but the guarantee that everything is immutable and lazy makes it possible for the compiler to do some insanely impressive optimizations.
Scala, Haskell, and Rust have taken a dyed-in-the-wool lover of dynamic languages and made a convert of me. They finally followed through on the promises of safety and productivity that other languages failed to deliver on.
In closing, I'll repeat one last time that all of these are merely the opinions of an insufferable neck beard (me). Even if we disagree, I'm sure you're a very nice person, and I approve of you using whatever languages and tools make you happy and productive.
Aside from the fact that I've never felt scarier, I agree entirely with every single point you just made and thank you for qualifying my broad generalisation.
It was more of a general butt-covering sort of thing. People like to take offense to things on the internet.
But from a writer's, modifier's or refactorer's perspective, what counts is the intent. Was a particular local variable meant to be mutable or immutable? Everytime I write a line of code, I need to watch out whether I mutated a variable which was not meant to be mutated. Or even the case where I myself mutate it unintentionally (by a typo, for example).
In a non-trivial project having a code-base with 100K lines of code, the time and effort spent in this manual analysis can be an overhead that might be well worth avoiding.
"what the parser figured out" != "what the author intended"
> this is a straightforward syntactic property
Not for the reader.
Is there exactly one assignment location that's not in a loop or a conditional?
yes => constant ; no => non-constant
This is neither hard nor unintuitive.
So "var" in global places means "variable", but "var" in local places means "variable or value"?