Freezed: Support deep copy

Created on 2 Mar 2020  Â·  14Comments  Â·  Source: rrousselGit/freezed

Consider two classes:

@freezed
abstract class Person with _$Person {
  factory Person(String name, int age) = _Person;
}

@freezed
abstract class Company with _$Company {
  factory Company(String name, Person director) = _Company;
}

Now say we want to change the director's name, then we would have to write:

Company company;

company = company.copyWith(
  director: company.director.copyWith(name: 'New name'),
)

This is not ideal, especially on very nested objects.

Another solution would be to, on Company, generate methods to update director directly:

Company company;

company = company.$director.copyWith(name: 'New name')
enhancement

Most helpful comment

I think I'll go with that syntax, as copyWith.director(name: 'Foo') free-up the $director slot for something else

$director may be needed for #60

All 14 comments

Another syntax could be to use Dart's callable classes to achieve:

Company company;

company = company.copyWith.director(name: 'New name')

Which is more readable IMO (and remove that ugly $).

It may even scale with property-specific copy:

company = company.copyWith.name('New name');
company = company.copyWith.director.name('New name');

The benefits of the company.$director.copyWith() approach is that it is more flexible.

It allows:

company.$director(Person(...));
company.$director.copyWith(name: 'Name');

Whereas with company.copyWith.director, trying to achieve the same thing leads to weird names:

company.copyWith.director(Person(...))
company.copyWith.director.copyWith(name: 'Name')

I don't think it is sane to mixt both approaches either.

A third solution could be:

company.copy.director(Person(...))
company.copy.director.with(Person(...))

But this takes an extra reserved keyword slot.

Don't know if this should be part of this issue or if I should create a new one:

@freezed
abstract class Company with _$Company {
  factory Company(String name, List<Person> ceos) = _Company;
}

Now say I wanna change the name of one Person in ceos.

Company company;

var ceos = company.ceos.toList();
ceos[1] = ceos[1].copyWith(name: 'NewName');

company = company.copyWith(ceos: ceos);

This is not very intuitive.

Yes, lists and maps are definitely in the scope of this feature. Maybe not in the first release though.

We can think of:

@freezed
abstract class TodoList with _$TodoList {
  factory TodoList(List<Todo> todos) = _TodoList;
}
@freezed
abstract class Todo with _$Todo {
  factory Todo(String name, bool checked) = _Todo;
}

Which would allow:

TodoList list;

list = list.$todos[0].checked(true);
list = list.$todos.where(...).checked(true);
list = list.$todos([]);

I probably won't work on collections in the first release of such feature.

In the meantime, we can use spread operator and if/for inside collections:

TodoList list;

list = list.todos([...list.todos, Todo()]);
list = list.todos([
  for (final todo in list.todos)
    todo.copyWith(checked: true),
]);

So it's not too bad.

What happens if director is nullable?

@smiLLe nothing special.
What's your concern?

Let's say director is null in my company object.
And director may have a Person assistant.

what happens if i'd call company.$director.$assistant.copyWith(age: 22);

$director would be nullable

So you'd have:

company.$director?.assistant.copyWith(age: 22);

@rrousselGit ok ... that was too obvious :) i need a coffee. right. now....

No worries, that's a fair question! 😄

Thinking about it, the previous issue of:

company.copyWith.director(Person(...))
company.copyWith.director.copyWith(name: 'Name')

This is actually not a problem.

We could instead have:

company.copyWith(director: Person(...))
company.copyWith.director(name: 'Name')

And this logic applies to deeper copies too:

company.copyWith.director(assistant: Assistant(...));
company.copyWith.director.assistant(age: 22);

I think I'll go with that syntax, as copyWith.director(name: 'Foo') free-up the $director slot for something else

$director may be needed for #60

Was this page helpful?
0 / 5 - 0 ratings