Giter Club home page Giter Club logo

Comments (1)

 avatar commented on June 14, 2024

The provided example does showcase some of the issues related to immutability, but it falls short in demonstrating the pitfalls of mutable objects when used in hashed collections like HashSet.

The reason your test cases might be passing (i.e., expect(equatableImmune1, equatableImmune2);) is that you are comparing objects that were effectively created to be the same, and the Equatable package is performing deep equality checks. The test doesn't simulate a scenario where you would change an object after it has been inserted into a hashed collection, which is where the issues with mutability usually arise.

Here's a modified example to showcase the point. This version shows how mutable objects can cause issues when their state changes after they've been added to a HashSet:

void main() {
  final setWithImmutable = HashSet<_EquatableTest>();
  final setWithMutable = HashSet<_EquatableTestNonImmutable>();

  final immutableObj = _EquatableTest(values: HashSet.from([1, 2, 3]));
  final mutableObj = _EquatableTestNonImmutable(values: HashSet.from([1, 2, 3]));

  // Add objects to sets
  setWithImmutable.add(immutableObj);
  setWithMutable.add(mutableObj);

  print('Before mutation:');
  print('Immutable set contains object: ${setWithImmutable.contains(immutableObj)}');  // Should print true
  print('Mutable set contains object: ${setWithMutable.contains(mutableObj)}');  // Should print true

  // Mutate the internal state of the objects
  final mutatedImmutableObj = immutableObj.copyWith(values: HashSet.from([4, 5, 6]));
  mutableObj.values = HashSet.from([4, 5, 6]);

  print('After mutation:');
  print('Immutable set contains object: ${setWithImmutable.contains(immutableObj)}');  // Should still print true
  print('Mutable set contains object: ${setWithMutable.contains(mutableObj)}');  // Should print false
}

class _EquatableTest extends Equatable {
  final HashSet values;

  const _EquatableTest({required this.values});

  @override
  List<Object?> get props => [values];

  _EquatableTest copyWith({
    HashSet? values,
  }) {
    return _EquatableTest(
      values: values ?? this.values,
    );
  }
}

class _EquatableTestNonImmutable extends Equatable {
  HashSet values;

  _EquatableTestNonImmutable({required this.values});

  @override
  List<Object?> get props => [values];
}

In this example, you'll see that after mutating the internal HashSet of the mutable object, the main HashSet (setWithMutable) can no longer recognize it. This is because its hash code changes when its internal state changes. On the other hand, the immutable object remains recognizable by the main HashSet (setWithImmutable) even after "mutation", as it actually results in a new object while keeping the original object unchanged.

Here is another example:

void main() {
  // Mutable object test
  final mutableSet = <_Mutable>{};
  final mutable = _Mutable(value: 1);
  
  // Add mutable object to set
  mutableSet.add(mutable);
  print('Mutable contains before mutation: ${mutableSet.contains(mutable)}'); // Output: true
  
  // Mutate object
  mutable.value = 2;
  
  // Check if set still contains the object
  print('Mutable contains after mutation: ${mutableSet.contains(mutable)}'); // Output: false

  // Immutable object test
  final immutableSet = <_Immutable>{};
  var immutable = _Immutable(value: 1);

  // Add immutable object to set
  immutableSet.add(immutable);
  print('Immutable contains before mutation: ${immutableSet.contains(immutable)}'); // Output: true

  // "Mutate" object by creating a new one
  immutable = immutable.copyWith(value: 2);

  // Check if set still contains the object
  print('Immutable contains after mutation: ${immutableSet.contains(immutable)}'); // Output: false

  // Check if set still contains the original object
  print('Immutable contains original after mutation: ${immutableSet.contains(_Immutable(value: 1))}'); // Output: true
}

class _Mutable {
  int value;
  _Mutable({required this.value});
  
  @override
  int get hashCode => value;
  
  @override
  bool operator ==(Object other) =>
      identical(this, other) || (other is _Mutable && other.value == value);
}

class _Immutable {
  final int value;
  _Immutable({required this.value});

  _Immutable copyWith({int? value}) {
    return _Immutable(value: value ?? this.value);
  }
  
  @override
  int get hashCode => value;
  
  @override
  bool operator ==(Object other) =>
      identical(this, other) || (other is _Immutable && other.value == value);
}

from equatable.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.