Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create cascade operator for immutable classes that return mutated copies #430

Open
ThinkDigitalSoftware opened this issue Jun 27, 2019 · 6 comments
Labels
request Requests to resolve a particular developer problem

Comments

@ThinkDigitalSoftware
Copy link

ThinkDigitalSoftware commented Jun 27, 2019

Currently, when working with immutable classes, such as are used in Redux, etc,
a lot of boilerplate code is written for each class to allow for mutated copies. This is usually written in the form of:

class MyClass {
  final String myName;
  final int myAge;
  
  MyClass copyWith({String myName, int myAge}){
    return MyClass(myName: myName ?? this.myName,
                  myAge: myAge ?? this.myAge);
  }
}

Actually, this is used often in Flutter. If we could use the same type of idea as
var myNewClass = myClass..name = "newName"; where myClass retained all the old information would render these entire functions useless and same loads of time.

I know to reuse the cascade operator could be confusing, and single and triple dot notation are already taken...

@lrhn lrhn added the request Requests to resolve a particular developer problem label Jun 27, 2019
@lrhn
Copy link
Member

lrhn commented Jun 27, 2019

You want an easier way to update immutable data.

When the immutable data are just plain Dart classes, it's hard to see how this can be done in a way that generalizes. The mutation needs to know how to create a new object in a side-effect free way in such a way that the object retains its current state, except for a single field being changed.

So, we pretty much need to restrict the applicable classes to ones with only public final fields which are all initialized directly by a constructor, and where the constructor has no body and no initializer list, only initializing formals (so, we might as well require the constructor to be const).
Something of the form:

class MyClass {
  final Type1 field1;
  final Type2 field2;
  const MyClass(this.field1, this.field2);
}

At that point, we could define some syntax like x.field1 := value to be short for let tmp = MyClass(value, x.field2) in x = tmp , and possibly even allow cascades x..field1 := value..field2::= value2 which expands to let tmp1 = MyClass(value, x.field2) in let tmp2 = MyClass(tmp1.field1, value2) in x = tmp2, which a smart compiler can optimize to x = MyClass(value, value2).

Such a restrictive requirement on the class is troublesome. It might be that some classes satisfy the requirement by accident, and is later changed to not satisfy them any more. That will break existing code which uses the feature.
It's also easy to accidentally violate the requirements, and if you don't use the feature in your tests, then you might never notice.

This sounds more like a feature which would fit with an explicitly declared immutable data structure like requested in #314. Then it does make sense to have special behavior for assignment. Basically a.b.c.d := v; would find the last access which is mutable, and if the rest are immutable data-type instances, then it does a deep rewrite of the data.

@kasperpeulen
Copy link

The situation is even worse. It is a lot of boilerplate and it is not even possible to set a value to null with a copyWith method like above.

For this reason I stopped with marking fields final for my data classes, instead I do:

todo.copy()..title = “Use immutables properly”..dueDate = null;

@ThinkDigitalSoftware
Copy link
Author

@lhrn, we already have a static check for when classes become non const compatible. Can't that be reused?

@ThinkDigitalSoftware
Copy link
Author

@kasperpeulen, using two dots instead of copyWith() doesn't seem messy nor does it require any boilerplate. I think @lhrn was describing the code they would write to make this a feature in dart for us.

@kasperpeulen
Copy link

@ThinkDigitalSoftware No I Iike your proposal.

What I dislike is that it is impossible to set a value to null if you have a immutable data structure in Dart today with a copyWith method like you describe.

@ThinkDigitalSoftware
Copy link
Author

Oh, I see. I misread what you were saying. Agreed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
request Requests to resolve a particular developer problem
Projects
None yet
Development

No branches or pull requests

3 participants