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

Add const BuiltList<T>.empty() #230

Closed
cbenhagen opened this issue Feb 14, 2021 · 6 comments
Closed

Add const BuiltList<T>.empty() #230

cbenhagen opened this issue Feb 14, 2021 · 6 comments
Assignees

Comments

@cbenhagen
Copy link

This would allow us to use it as a default for a named parameter.

Related to #164 which is blocked by missing language features.

@davidmorgan
Copy link
Contributor

Unfortunately this is not as straightforward as it sounds.

BuiltList computes hashCode lazily, so it can't be const. We would have to provide a different implementation class just for the empty const, which likely has other implications.

I realize the lack of const is annoying, I just haven't figured out a good way around it yet :/

One approach for named parameters would be to do

doSomething({Iterable<T> values = const []}) {
  values = BuiltList.of(values);
}

@cbenhagen
Copy link
Author

Thank you for the workaround. Unfortunately this would not work for freezed classes. What other implications would you expect with a different implementation for the empty special case? Just wondering, I am sure you have put a lot of thought in it already.

@davidmorgan
Copy link
Contributor

If a compiler notices that there is only a single implementation of a class in the whole program it can optimize--turning dynamic calls into static calls. I suspect that would apply here.

built_value supports defaulting collections from built_collection to empty collections, maybe freezed could/should do that same?

@derolf
Copy link

derolf commented Feb 18, 2021

As a workaround, I do:

import 'package:built_collection/built_collection.dart';

// ignore: avoid_implementing_value_types
class EmptyBuiltList<E> implements BuiltList<E> {
  const EmptyBuiltList();

  BuiltList<E> get _list => BuiltList<E>();

  @override
  BuiltList<E> operator +(BuiltList<E> other) => _list + other;

  @override
  E operator [](int index) => _list[index];

  @override
  bool any(bool Function(E p1) test) => _list.any(test);

  @override
  List<E> asList() => _list.asList();

  @override
  Map<int, E> asMap() => _list.asMap();

  @override
  Iterable<T> cast<T>() => _list.cast<T>();

  @override
  bool contains(Object? element) => _list.contains(element);

  @override
  E elementAt(int index) => _list.elementAt(index);

  @override
  bool every(bool Function(E p1) test) => _list.every(test);

  @override
  Iterable<T> expand<T>(Iterable<T> Function(E p1) f) => _list.expand(f);

  @override
  E get first => _list.first;

  @override
  E firstWhere(bool Function(E p1) test, {E Function()? orElse}) => _list.firstWhere(test, orElse: orElse);

  @override
  T fold<T>(T initialValue, T Function(T p1, E p2) combine) => _list.fold(initialValue, combine);

  @override
  Iterable<E> followedBy(Iterable<E> other) => _list.followedBy(other);

  @override
  void forEach(void Function(E p1) f) => _list.forEach(f);

  @override
  Iterable<E> getRange(int start, int end) => _list.getRange(start, end);

  @override
  int indexOf(E element, [int start = 0]) => _list.indexOf(element, start);

  @override
  int indexWhere(bool Function(E p1) test, [int start = 0]) => _list.indexWhere(test, start);

  @override
  bool get isEmpty => _list.isEmpty;

  @override
  bool get isNotEmpty => _list.isNotEmpty;

  @override
  Iterator<E> get iterator => _list.iterator;

  @override
  String join([String separator = '']) => _list.join(separator);

  @override
  E get last => _list.last;

  @override
  int lastIndexOf(E element, [int? start]) => _list.lastIndexOf(element, start);

  @override
  int lastIndexWhere(bool Function(E p1) test, [int? start]) => _list.lastIndexWhere(test, start);

  @override
  E lastWhere(bool Function(E p1) test, {E Function()? orElse}) => _list.lastWhere(test, orElse: orElse);

  @override
  int get length => _list.length;

  @override
  Iterable<T> map<T>(T Function(E p1) f) => _list.map(f);

  @override
  BuiltList<E> rebuild(Function(ListBuilder<E> p1) updates) => _list.rebuild(updates);

  @override
  E reduce(E Function(E p1, E p2) combine) => _list.reduce(combine);

  @override
  Iterable<E> get reversed => _list.reversed;

  @override
  E get single => _list.single;

  @override
  E singleWhere(bool Function(E p1) test, {E Function()? orElse}) => _list.singleWhere(test, orElse: orElse);

  @override
  Iterable<E> skip(int n) => _list.skip(n);

  @override
  Iterable<E> skipWhile(bool Function(E p1) test) => _list.skipWhile(test);

  @override
  BuiltList<E> sublist(int start, [int? end]) => _list.sublist(start, end);

  @override
  Iterable<E> take(int n) => _list.take(n);

  @override
  Iterable<E> takeWhile(bool Function(E p1) test) => _list.takeWhile(test);

  @override
  ListBuilder<E> toBuilder() => _list.toBuilder();

  @override
  BuiltList<E> toBuiltList() => _list.toBuiltList();

  @override
  BuiltSet<E> toBuiltSet() => _list.toBuiltSet();

  @override
  List<E> toList({bool growable = true}) => _list.toList(growable: growable);

  @override
  Set<E> toSet() => _list.toSet();

  @override
  Iterable<E> where(bool Function(E p1) test) => _list.where(test);

  @override
  Iterable<T> whereType<T>() => _list.whereType<T>();
}

That way, I can pass it to freezed as default value:

@freezed
abstract class Foo with _$Foo {
  factory Foo({
    @JsonKey() @Default(EmptyBuiltList<int>()) BuiltList<int> bars,
  }) = _Foo;

  factory Foo.fromJson(Map<String, dynamic> json) => _$FooFromJson(json);
}

The annoying piece is that json_serializers don't allow non-literal default values in @JsonKey...

@derolf
Copy link

derolf commented Feb 18, 2021

Unfortunately this is not as straightforward as it sounds.

BuiltList computes hashCode lazily, so it can't be const. We would have to provide a different implementation class just for the empty const, which likely has other implications.

You could store the _hashcode in an expando.

@davidmorgan
Copy link
Contributor

Closing in favour of #164

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants