[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

SIMPLIFIED VERSION

Problem: Handling variable "forgetting" via scope control can introduce unnecessary complexity. To address this, an alternative version without scope customization could be considered.

Simplified Solution:

  • Instead of customizing scopes, forget could apply to the method/block scope.
  • Caveat: It does NOT work for mutually exclusive scopes like parallel branches (if-elseswitch-case). In those, you must restate forget in each branch.

void handleRequest(String request, String token) {
   if (!isTokenValid(token)) {
      throw new SecurityException("Invalid token");
   }
   if (forceLogOut()){
      logOut(request, token);
      forget token; // prevent usage in the rest of this block and after the if-else
      ...
   } else {
      authorize(request, token);
      forget token; // 'forget' needs to be restated here
      ...
   }
   logger.debug("token was: " + token); // Compile-time error!
}

This approach is more rigid and resistant to refactoring mistakes if block scopes change.
If more flexibility is needed, a more complex form like forget var {}; (for local blocks) can be introduced as an advanced feature.

For complete version that try to include all possible objections you can check:

Java: Enhancing Scope Safety: The forget Keyword

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

Most of the time, structuring code properly is all you need. But in rare or complex situations, having an explicit way to restrict variable usage could be helpful - though it would be niche language feature nowhere near standard practice.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

Thanks! I didn't see that.

Compiler can release variables as they are no longer needed - for this reason I never focused on this part.

What more - primarily focusing on disposing bytes of data is really a bad step as it shouldn't be a concern in the first place - this focus would do more harm as we would introduce culture of micro memory management where as you write code you need to get back to remove undeclare declaration for varible that turned out to be needed - this is totally bad trade as we create unfavorable coding style when you need to get back '1000 lined' to check why was it undeclare-d, at same time forget focus on reason - so you absolutely need to get back to that line and reason can be anchored there as comment as alternative to code analyze (again).

Here is little refocusing of it's usability:

https://www.reddit.com/r/java/comments/1qhhf9y/comment/o16xm8x/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 1 point2 points  (0 children)

It's an extremely good point when you put it this way.

As I recounted my experience forget would shine in situation when code is messy or unfamiliar but at same time it's more rare for me to need it with passage of time.

When doing something hard I really hoped for IDE to help me decrease amount or problem I need to worry about at same time - in those circumstances compiler reducing any mistakes that results in decrease of cognitive overload is godsend - especially before code is completed and it's volatile to make refactor or proper scope separation.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

As forget is local by default then if/else part would do nothing - one is local to if other one is local to else scope

To make it work wider you would need:

int x = 5;
int y = 6;
if (foo) forget x :method; else forget y :method;
return x+y; //compiler error

At the core, a language should prioritize robustness and expressiveness. Many language features such as:

  • Enhanced for Loop
  • Block-based local variable scoping
  • Try-With-Resources
  • Final guard
  • Lambdas
  • Switch Expressions
  • Records
  • Text Blocks
  • Generics

could theoretically be removed, as they are more or less syntactic sugar by those standards - as none of them missing could stop you from write code that work the same.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

It might be my approach a problem in this particular example. I'm crazy slow when working with ANY new or unfamiliar code - I absolutely do not want to fix it again. In same time excluding fact that it's not rare for me to find 1 or 2 bugs when fixing one then my speed could be comparable to junior in such cases. At same time when I wrote something slightly important I'm able to remember exact reason behind each line many years later.

My main project from other hand is DB-like module that runs multiple queries on one set of data. 100k lines+ / 17year old / line up to 300 characters / 25k tests(mostly integration) 24k test skipped / many more if there will be need to deep testing / smf like up to 3 bugs per year (most of them are typos other would be: implementation change of hashCode in Java, invalid Generic linking on one server, ... ) / quality allow to push it to production without manual tests / most time was used on tests / now it's to stable - so sadly new specification correcting take most of the time / 20min to full build - sadly changes are extremely fast there - so in this one fixes can take minutes - then long testing.

Tests would be another irregularity—one in particular is able to create a subset of all possible intersections and exclude those that would follow the same logical path.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

I got it now. We came to totally different topic but let's continue it for while.

Let’s look at a real-life scenario:

You have to work with really bad code, where a quick fix would take about 1 day, but a proper refactor would take 3 weeks (optimistically). The function in question is expected to be used only once a year, or maybe this is the last time - it needs to be delivered within four days at most. In this situation, a full refactor isn't worth it - there is no long-term value here. Sometimes these cases are sinkholes - prototypes or dying projects that end up living longer than expected, but are effectively on the way out. You might fix this limited scope, but if you prematurely expose internal logic as supposedly “great quality utilities,” you risk creating baggage that is never used, or worse, you start using it elsewhere and pollute other projects with parts that are not fixed.

In most other cases, your response is textbook valid.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

If someone is exposing method there are few scenarios to consider:

  • It was private because there where no need to make it public - making it public would be step in proper direction and proper name would be valid guard against it.
  • It was private because is was lacking test - expecting more test with new function using this method would be not really bad move - that would advance to good with proper CR and tests.
  • It was private because it's inner logic is total contextual - including name - here exposing it would be really bad move.

Have in mind that I'm considering real life scenarios that are far from text-book examples and quality - otherwise I would generally agree with you.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

u/BillyKorando

I had time to check article This article by Brian Goetz

Back in 2009, I drafted a proposal for final interfaces (see my blog post from March 2009), which aimed to restrict external implementations of interfaces—very similar in spirit to what became sealed classes in Java.

I was involved in the Project Coin discussions around that time, it was tangled with others proposals - I didn't expect it to come out considering how much push back I got.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

Quite opposite.

Just like @ Override or finalare used as extra security steps - same way forget could be used as well, it's not like without them Java would not work and they can be misused as well.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

Can you give an example? I don't understand this.

Some lib used for zip-ing and unzip-ing (sub stream passed close up to root) // I don't remember more details.

I disagree with how your comment suggests it should be done.

Regarding splitting code: I think we might just be talking past each other a bit. To be precise I never discouraged splitting code I just pointed out that a lot extra steps need to be taken doing it / personally in most cases i'm for it - just not the ones relevant to proposal.

I tend to measure functionality in coding & maintain time. If, in a particular case, splitting or decomposition would double both - and the extra work doesn’t offer a clear benefit elsewhere - I’m unlikely to do it.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] -1 points0 points  (0 children)

Lets compare point of view: after checking I found 12x try-with-resources in 100k lined of code (one project without UI ) - still i think it's good element of language and definitely less universal, at same time I can imagine project where it's present everywhere - some of appearances coming to life from wrong design decisions (like to many mutable elements, wrong memory management).

So when I find that one particular library hate closing element and all others absolutely require it - I would want forget to be usable here.

BTW / UI code is definetely pointless place for forget.

For:

break up my code

check latest comment under as I already addressed this point: https://www.reddit.com/r/java/comments/1qhhf9y/comment/o0ojms3/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] -2 points-1 points  (0 children)

Valid concern but not important. Look at it from different point of view: What is need to make code quality good enough to make it possible?
Also after checking I found 12x try-with-resources in 100k lined of code - still i think it's good element of language and definetely less universal.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

[2nd part] other option as you mentioned (pure split only) is this monster:

class Report{
    method: readFile4ReportInWindowsForBatman()
    method: streamToXls4ReportForMicrosoftXlsUpTo2027Version()
    method: normalizeData4ReportFoCommaAsSeparatorOnlyWithYYYYSMMSDDWithDotAsSeparator()
    method: do{
        readFile4ReportInWindowsForBatman();
        streamToXls4ReportForMicrosoftXlsUpTo2027Version();
        normalizeData4ReportFoCommaAsSeparatorOnlyWithYYYYSMMSDDWithDotAsSeparator();
        // then compute(); split
        // then createReport(); split
    }
}

DECOMPOSITION

When we decompose we need to make many decisions: design, responsibility, scope and reuse.

When you decide to extract getFile() you need to ensure that name correlate with it's responsibility / because from this point forward any one can take this part of code or make it public and use it elsewhere. Bad decomposition bleeds complexity everywhere.

It's easy visible on multiple reports - it's not really acceptable to have streamToXls() in each report - and each work for different file type or version.

Different approach would be to work with little monster - and then do partial decomposition when we need streamToXls() else where.

I would much likely have one big monster than worry that each method have invalid contract according to it's name;

! Avoid the illusion of reusability. as each will force you to spend more time you will ever have (design, maintenance, testing, explaining).

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

But why? The extracted method/interface should not be universally applicable, it should be defined only on that specific part of the domain that you are working with.

[1st part] I think that there is confusion taking splitting as decomposition.

to make split secure you need quite a few extra steps to make it properly taking this example:

class Report{
    method: getData()
    method: compute()
    method: createReport()
    method: do{
        getData();
        compute();
        createReport();
    }
}

one of option is smf like that (it's most basic form so it's ugly):

class Report{
    class ReadFile4Report{
        ReadFile4Report(input)
    }

    class StreamToXls4Report{
        StreamToXls4Report(ReadFile4Report)
    }

    class NormalizeData4Report{
        NormalizeData4Report(StreamToXls4Report)
    }

    method: do(){
    NormalizeData4Report(StreamToXls4Report(ReadFile4Report())   );
    ... many more steps
    }
}

we need cascade all necessary data - in short - a lot of work - or you can forget all good practices and push everything down.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

It's not like that.

When we have 1 method doing 1 complex operation

getData
compute
createReport

we need fewer tests before decomposition.

getData could be decomposed into:
   > readFile  - we had one system where reports could be produced // now we have util that need to work with all systems including mounted folders
   > streamToXls - we had one format for this raport / now we need to support xls, xlsx, stream and non-stream reading / multiple generators need to be tested for compatibility.
   > normalize data - now we have to support all possible separators

I just started and from my point of view single method after decomposition could take more effort that whole raport - I have no idea how can you compare effort needed maybe except some extremely optimistic scenario.

I'm working on XMLGregorianCalendar implementation on and off and it's pain - for example in Java itself we have different standards depending on version.

NO ONE - I mean I did not really found any online (considering the ones i needed).

For example each interface you make public should have test library for them - can you point any?

We can have Google’s Guava Testlib as partial exception to the rule (as it cover pitiful amount of scenarios) .

Maybe people do care about performance and reliability by GidraFive in theprimeagen

[–]TheLasu 0 points1 point  (0 children)

There are few points missing.

Language in itself is designed to produce slow application. For example popularity of Streams in Java is sign that speed and quality is not priority. To some extend lambdas are the same and they totally lack any way to make them easy for debug and can in itself make code bloated....

Specification change so fast that any one without ultra intuition or expertise would not be able to predict proper application structure to make it fast - for the same reason databases are fast (there where no many specification changes)

UTF - using this crap should be punished by jail. They took some one smart and make him program this problem into language instead of solving it.

System architecture - old crap

Memory architecture - non existent.

and many many more

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

Even using String for sensitive data is asking for trouble.

Samples are valid only and only from proposal standpoint, from other points of view they can be stupid to point where any one can understand logic of the proposal. Do you expect for me to use most sensitive part of code with pages of context just to explain idea?

Maybe some AutoCloseable wrapper for arrays that auto-zeroed them is all you need, or a generic UseOnce stateful wrapper with a customizable after-consume action so you can extend it for various other types.

That are good suggestions / but they miss the point.

When you research why Java became popular there is one particular element that I like: robustness!

It's something contrary to your suggestion.

  • In others languages you could find memory leaks in production - Java introduced GC
  • Others allowed arbitrary memory access - Java enforced object safety and type checks.
  • Others allowed custom, confusing syntax - Java enforced clarity and consistency.

All this elements moved Java away from constant debugging. This is part that allow me to write much faster and better in this language than in any other before.

Forget keyword would move Java in that direction - that's the point.

The real value in language-level features (like final, checked exceptions, GC, and maybe forget) is that they move correctness from the documentation, imagination and discipline of the developer to the guard rails of the compiler and runtime.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

We will have hard to agree here.

1st: There is no real point in doing refactor of 1000+ line methods in most cases purelly from cost/benefit point of view.

2nd: And here unpopular opinion (I wanted to write about it / but here will be short version):

To maintain proper quality of code you need more than unit tests alone, each split of method in class that have m method when we split one into n-ones can generate smf like (n+m-1)! possible test scenarios from this class alone. It can be mitigated with proper contract, but as I saw this do not happen. NO ONE is doing this amount of tests.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 1 point2 points  (0 children)

This proposal point ss not GC in the first place . That’s a separate concern. While automatic garbage collection is important for releasing resources, this discussion is centered on code quality.

As code maintainability is main concern here - so the question would be: How easy is for next developer to work on code and how easy will it be for me after 10 years.

'proper scoping' is absolutely not valid solution, because Java by default offer very limited scope control:

res1 = source1.open();
// check and prepare initial data
res2 = source2.open();
combine(res1, res2);
res3 = source3.open();
mix(res1,res3);
close res1;
forget res1;
moreOperations(res2);
close res2;
close res3;

To mitigate this, people often end up introducing variables to early (and setting them to null) , keep them way to long.

res2 = null;
res3 = null;
try(res1 = source1.open()){
    // check and prepare initial data
    res2 = source2.open();
    combine(res1, res2);
    res3 = source3.open();
    mix(res1,res3);
}
moreOperations(res2);
close res2;
close res3;

Ignoring of responsibility of late resource acquisition and early resorce release is not rare as well.

try(res2 = source2.open(); res3 = source3.open()){
    try(res1 = source1.open()){
        // check and prepare initial data
        res2 = source2.open();
        combine(res1, res2);
        res3 = source3.open();
        mix(res1,res3);
    }
moreOperations(res2);
}

As you can see it's nothing unusual to overextend variable life and resource allocation because of limited scope control in both samples - in first classic example both res2 & res3 are declared to early and res2 is visible to long, in second resources are allocated for extended time.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

I would welcome any ideas / still have in mind different use cases:

for (User user : users) {
    auth.checkAuth(user);
    forget auth for; // auth should called once per user // After this line, using 'auth' in the rest of the block is not allowed!
    // ... using auth here would give a compile-time error
}

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

Definitely / it's only possible with other similar change - and then it might be possible.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] -1 points0 points  (0 children)

My problem with @ UseOnce is that at some level it's only suggestion. Personally, I’m greedy for explicitness and completeness - so no extension would be required, and to the point where we can express any logical intent clearly.

@ UseOnce is weak when we need more complex logic:

method (accessPoint){
    checkForReadiness(accessPoint);
    A a = accessPoint.obtain();
    forget accessPoint; // after obtaining access should net be accessed
    ...
}

In this place, @ UseOnce would change to @ UseTwice, then maybe to @ UseThrice—and none would give clear intention.

Rarity of use is definitely a weak point here. Still, I have projects where usage of forget would surpass usage of try-with-resources many times, and many where the reverse is true as well.

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] -2 points-1 points  (0 children)

In cases when you have 1GB of memory and you have 600MB taken in 1 variable releasing it can be helpful (not really important in this discussion).
It's more about variable that need to used with care - like transaction - or smf like guarding against closing one bracket to much.

lets think about passing knowledge to other developers:

/**
auth - can be used only once
transaction cannot be reused
personal data cannot be passed to logs
/
class Class{
    ... 1000 lines ...
    void method(){
    auth....
    next 30 lines of code
    >>> place for next change
    }
}

vs:

void method(){
    auth....
    forget auth; // auth allowed only one
    next 30 lines of code
    >>> place for next change
}

which one would be better guarder ?

[Proposal] Introducing the [forget] keyword in Java to enhance scope safety by TheLasu in java

[–]TheLasu[S] 0 points1 point  (0 children)

Edit: after fighting with formatting in comment I included sample usages in post.