<div dir="ltr"><div>Dr. Goetz, Thanks for the thoughts. My takeaway is that it is on the back burner waiting on potential triggers like the C# nullability experiment. That it is not being summarily dismissed is encouraging. </div><div><br></div><div>I am content for it to remain on its current trajectory, but I wish to leave a few thoughts.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span style="font-family:monospace;font-size:large">I'll just add that none of this "adds" anything to the language (unlike records, or value types, or pattern matching); it just seeks to streamline the declaration of what the language already supports. That makes it a pretty weak feature. </span></blockquote><div>To my 'language user' eyes, streamlining switch statement into switch expression was a strong feature despite the fact that it didn't add anything to the language (strictly speaking). I think of streamlined immutable classes in a similar way. I recognize that switch expressions got prioritized the way it did because it unlocked the bigger prize of pattern matching, while "streamlining immutability" doesn't have that big prize except perhaps a subset of thankful devs. But, even in isolation, switch expressions were a strong feature in their own right because of the improved cognition, something that "streamlined immutability" could possibly claim as well.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span style="font-family:monospace;font-size:large">I'd rather spend our efforts on pattern matching and value types.</span></blockquote><div>I think the overwhelming majority would agree: Value Types > Pattern matching > [Day light] > Streamlining immutability. </div><div><br></div><div>Let me conclude with one aspect that I haven't seen discussed publicly.</div><div><br></div><div>[Shifting bottlenecks] All of the Amber changes are producing strong network effects that are increasing (in fact, have increased) the set of programs for which Java is a very strong choice. In turn, the bottlenecks in producing such programs have also moved. For example, the other day I needed a command line utility to transform java source code (like inlining a class into another, renaming a class and such). I encoded the changes as a small language [example: RenameClass(NewMain, InlineClass(OldMain, Child1, Child2, Child3)) says: Inline the children classes into a parent class called OldMain and then rename it into NewMain] and wrote a quick parser using parser combinator style in Java. It was a pleasant experience except for the aforementioned [Poor Rapid Prototyping Velocity] in my first post - the bottleneck was fiddling with moving fields around and dealing with the verbosity and effort of immutability. Sealed types and pattern matching were relatively effortless.</div><div><br></div><div>A decade ago, I wouldn't even have chosen Java; Visitors make my head spin. A few years ago (and perhaps still), I've seen Go recommended for command line apps, but I couldn't pull off Parser combinator style with Go effortlessly (lack of first class union types). In other words, Java has gone from Heck No --> Plausible --> even Pleasant in the space of a few Amber releases and the bottleneck in producing the program correspondingly moved from "Visitors" to "Second class Immutability". I know this is just one example, but it is a recurring theme in my practice.</div><div><br></div><div>Like I said before, I am content for things to remain on their current trajectory - I trust the language designers' judgment calls - but I hope this anecdote helps in some little way.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Jan 19, 2024 at 12:32 AM Brian Goetz <<a href="mailto:brian.goetz@oracle.com">brian.goetz@oracle.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>
<div>
<font size="4" face="monospace">I'll just add that none of this
"adds" anything to the language (unlike records, or value types,
or pattern matching); it just seeks to streamline the declaration
of what the language already supports. That makes it a pretty
weak feature. <br>
<br>
People have argued in the past for having explicit versions of
everything that is currently implicit (if you don't say "static"
you get an instance method/field; if you don't say
public/private/protected you get a package-private member). For
most of these, there's no way to explicitly say "instance" or
"package-private". <br>
<br>
We could of course do this; we even reserved the `non-` keyword
naming convention to reduce the bikeshed painting over what to
call them. But we never did any of these, because: we don't
really think anyone is going to use them. No one wants to say:<br>
<br>
package-private class Foo { <br>
non-static non-final package-private int x;<br>
...<br>
}<br>
<br>
So such features are often just to satisfy an abstract sense of
completion, which is to say, mostly useless. (And to add insult
to injury, the bikeshed quotient of such features is very high.)
<br>
<br>
I'd rather spend our efforts on pattern matching and value types.<br>
<br>
</font><br>
<div>On 1/18/2024 1:43 PM, Red IO wrote:<br>
</div>
<blockquote type="cite">
<div dir="auto">One idea could be to make the class declaration
special. In well structured classes all fields should live at
the top so having a class modifier like "immutable" would be
rather obvious even with lots of fields. Also rather a field
declared at line 20 and used in line 650 is mutable already
requires scrolling to the top.
<div dir="auto">
<div dir="auto"><br>
</div>
<div dir="auto">On that note the editor/ide is a more often
used indicator for mutablity of fields by having color or
styling differences in the variable name. (I know relying on
tooling to make things clear is not preferable but it's
kinda already this way)</div>
<div dir="auto"><br>
</div>
<div dir="auto">An example class would look similar to this :</div>
<div dir="auto"><br>
</div>
<div dir="auto">public immutable class Test {</div>
<div dir="auto">private int field1;</div>
<div dir="auto">private int field2;</div>
<div dir="auto">private int field3;<br>
</div>
<div dir="auto">private int field4;<br>
</div>
<div dir="auto">private int field5;<br>
</div>
<div dir="auto">private int field6;</div>
<div dir="auto">private int field7;</div>
<div dir="auto">private int field8;</div>
<div dir="auto">private mutable int field9;</div>
<div dir="auto">private mutable int field10;</div>
<div dir="auto">}</div>
<div dir="auto"><br>
</div>
<div dir="auto">Rather to combine default final and default
private is another discussion. </div>
<div dir="auto"><br>
</div>
<div dir="auto">A counter argument would be that allowing for
any combination of defaults would result in a keyword
explosion:</div>
<div dir="auto">immutable </div>
<div dir="auto">mutable </div>
<div dir="auto">closed (wip to make a class private default) </div>
<div dir="auto">package-private</div>
<div dir="auto"><br>
</div>
<div dir="auto">Just to allow inverting the defaults. </div>
<div dir="auto"><br>
</div>
<div dir="auto">Also c# already has this for immutable fields.
They have the readonly keyword which is equivalent to final
on fields in java but it also works on the class making
every field readonly. The problem with that approach in java
is that we already used final on classes to block
inheritance.</div>
<div dir="auto"><br>
</div>
<div dir="auto">Maybe we could instead drop the inversion
(package-private and mutable) and make it a 1 way decision
like in records. But then the question would be why not just
use a record. </div>
<div dir="auto"><br>
</div>
<div dir="auto">That are my thoughts on this topic. As one
might have heard out I'm undecided on the topic.</div>
<div dir="auto"><br>
</div>
<div dir="auto">Great regards </div>
<div dir="auto">RedIODev </div>
</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Thu, Jan 18, 2024, 17:56
Brian Goetz <<a href="mailto:brian.goetz@oracle.com" target="_blank">brian.goetz@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div> <font size="4" face="monospace">So, you provide several
good arguments here for "Java picked a bad default with
respect to mutability." (It is in good company; Java
picked many bad defaults, such as package visibility
rather than private, classes being extensible rather than
closed, etc.) Let's just take it as given that non-final
is an imperfect default for fields. <br>
<br>
Which brings us to the next question: "now that we realize
the default is bad, should we change it?" This is a much
harder question, because it involves the reality of 10M
developers and billions of lines of code. <br>
<br>
One bit of prior art here that might be relevant (and the
experiment is still playing out) is C#'s flipping of the
nullability default on a per-module basis (with injecting
checking at the boundaries.) This was a bold experiment
and we are interested in seeing the results.<br>
<br>
The problem with trying to fix the mistakes of the past is
that in most cases, you are just creating a fork and
forcing users to deal with both branches. If we had a
directive:<br>
<br>
class X { <br>
#pragma default-final<br>
<br>
...<br>
}<br>
<br>
then now users have to keep track (possibly on a per-file
basis) of where "int x" means final or mutable. This is
adding cognitive load to all users, since almost no Java
developer has the luxury of controlling 100% of the code
they deal with. <br>
<br>
<blockquote type="cite">
<div>A one line distillation of the above is perhaps:
"Immutability is common enough to consider a fast path
at source level; the current slow path has negative
consequences for cognition and devx."</div>
</blockquote>
<br>
Valid, but what this misses is: "having to constantly
reason about whether line #656 of Moo.java is on the fast
path or the slow path has even higher cognitive load." <br>
<br>
</font><br>
<div>On 1/17/2024 10:20 PM, Subra V wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">Hello Project Amber team,
<div><br>
</div>
<div>What are the current thoughts on providing source
level mechanisms for opting-in to mutability rather
than the current opt-out? For example, a notion of
immutable class (not a record) can obviate specifying
"private final" for fields. Opt-in mutability would
perhaps also jive with two recent themes in language
evolution: 1. Functional style 2. More cognition, less
ceremony (switch statement, pattern matching to name a
few). </div>
<div><br>
</div>
<div>If there's prior relevant material, I'd
appreciate a pointer; Dr. Google hasn't uncovered
anything of note.
<div><br>
</div>
<div>I realize that this question has red flags of
"proposing a solution" to "my specific problem". So,
let me clarify that (1) I only write because I
believe there's a reasonable chance that opt-in
mutability is of fairly broad interest. (2) I am not
proposing obviating the need for 'private final' as
a solution; instead, it is meant to be analogous to
saying 'I want String interpolation', a 'solution'
from which language designers carefully teased apart
a general problem and solved it in the form of
String Templates.
<div>---</div>
<div><br>
</div>
<div>I am certain language designers are aware of
this (and plenty more), but in the interest of
intellectual honesty, let me attempt to articulate
problems with default-mutability from my
default-immutable practice.</div>
<div><br>
</div>
<div>1. [Immutable style feels second class] Java
has moved in a functional direction much to my
(and most practitioner's?) pleasure. For
the subset of folks who buy into this direction,
Immutable classes are natural. Yet, they
feel second class in that (1) notions of "private
final" aren't relevant yet pollute (2) It requires
extra work [making all fields final, binding them
in constructor] compared to the default case of
mutability</div>
<div><br>
</div>
<div>2. [Cognitive Cost of Ceremony] For non-record
Immutable classes, I have to (1) Write 'private
final X' and then add a constructor param and set
`this.X = X` in the constructor. (2) Read and
verify `private final` on every field to know that
the class is immutable. Even though IDEs help, it
breaks the flow of thought both when reading and
writing</div>
<div><br>
</div>
<div>3. [Poor Prototyping Velocity] I often find the
ceremony especially frustrating in early phases of
design, especially when using immutable classes.
Imagine I have 20 concepts/fields and I am
attempting to organize them. A common occurrence
is to realize 'ohh this field/method logically
belongs to that other class; let me move it there'
but it is a pretty big chore to do so (even with
Intellij) given that immutability takes extra work
to achieve. Such moves also come with further
transitive ripple effects. All the busy work
perhaps accounts for upwards of 30% of the initial
few hours/days of design and more importantly,
constantly interrupts thoughts. For contrast, if I
just make every field public during the initial
period, it'd probably be a better experience, but
then I need a magic wand to make them all
immutable after the design settles down.</div>
<div><br>
</div>
<div>A one line distillation of the above is
perhaps: "Immutability is common enough to
consider a fast path at source level; the current
slow path has negative consequences for cognition
and devx."</div>
<div><br>
</div>
<div>Appreciate thoughts.</div>
<div><br>
</div>
<div>Thank you,</div>
<div>Subrahmanyam</div>
<div><br>
</div>
</div>
</div>
</div>
</blockquote>
<br>
</div>
</blockquote>
</div>
</blockquote>
<br>
</div>
</blockquote></div></div>