Thoughts on opt-in mutability?
Subra V
vemana.github at gmail.com
Fri Jan 19 00:56:24 UTC 2024
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.
I am content for it to remain on its current trajectory, but I wish to
leave a few thoughts.
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.
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.
I'd rather spend our efforts on pattern matching and value types.
I think the overwhelming majority would agree: Value Types > Pattern
matching > [Day light] > Streamlining immutability.
Let me conclude with one aspect that I haven't seen discussed publicly.
[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.
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.
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.
On Fri, Jan 19, 2024 at 12:32 AM Brian Goetz <brian.goetz at oracle.com> wrote:
> 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.
>
> 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".
>
> 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:
>
> package-private class Foo {
> non-static non-final package-private int x;
> ...
> }
>
> 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.)
>
> I'd rather spend our efforts on pattern matching and value types.
>
>
> On 1/18/2024 1:43 PM, Red IO wrote:
>
> 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.
>
> 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)
>
> An example class would look similar to this :
>
> public immutable class Test {
> private int field1;
> private int field2;
> private int field3;
> private int field4;
> private int field5;
> private int field6;
> private int field7;
> private int field8;
> private mutable int field9;
> private mutable int field10;
> }
>
> Rather to combine default final and default private is another discussion.
>
> A counter argument would be that allowing for any combination of defaults
> would result in a keyword explosion:
> immutable
> mutable
> closed (wip to make a class private default)
> package-private
>
> Just to allow inverting the defaults.
>
> 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.
>
> 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.
>
> That are my thoughts on this topic. As one might have heard out I'm
> undecided on the topic.
>
> Great regards
> RedIODev
>
> On Thu, Jan 18, 2024, 17:56 Brian Goetz <brian.goetz at oracle.com> wrote:
>
>> 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.
>>
>> 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.
>>
>> 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.
>>
>> 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:
>>
>> class X {
>> #pragma default-final
>>
>> ...
>> }
>>
>> 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.
>>
>> 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."
>>
>>
>> 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."
>>
>>
>> On 1/17/2024 10:20 PM, Subra V wrote:
>>
>> Hello Project Amber team,
>>
>> 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).
>>
>> If there's prior relevant material, I'd appreciate a pointer; Dr. Google
>> hasn't uncovered anything of note.
>>
>> 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.
>> ---
>>
>> 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.
>>
>> 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
>>
>> 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
>>
>> 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.
>>
>> 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."
>>
>> Appreciate thoughts.
>>
>> Thank you,
>> Subrahmanyam
>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20240119/58c85a1f/attachment-0001.htm>
More information about the amber-dev
mailing list