spebbe / dartz

Functional programming in Dart
MIT License
749 stars 60 forks source link

Question About IList And Append #90

Closed ibcoleman closed 2 years ago

ibcoleman commented 2 years ago

Hi There,

Been finding Dartz invaluable in my Dart/Flutter project. I've been able to muddle through some of the more esoteric corners, but every once in a while I run into something I can't quite understand. For example, I'm trying to figure out how to append a single element to an IList.

I took a naive stab at it:

   final IList<Asset> assetListWithOneElement = IListMi.append(assetList, asset);

...then realized you need to append IList to IList (?), so tried:

  final IList<Asset> assetListWithOneElement = IListMi.append(assetList, IList.from([asset]));

... which seemed closer, but the compiler fails with a type mismatch. append here is returning IList<dynamic>. So I thought maybe if I gave Dart a few hints as to the type:

    final IList<Asset> assetList = IList<Asset>.from(<Asset>[]);
    final IList<Asset> assetListPlusOne = IListMi.append<Asset>(
        assetList, IList<Asset>.from(<Asset>[asset])
    );

Still getting the error message A value of type 'IList<dynamic>' can't be assigned to a variable of type 'IList<Asset>'

Surely I'm doing something obviously wrong here. I pored over the tests for IList but couldn't see an obvious alternative.

Thoughts?

Thanks!

--Ian

spebbe commented 2 years ago

Hi @ibcoleman!

IList#appendElement is probably the simplest way to achieve what you want. Please be aware that since IList is implemented as a singly linked cons list , appending is O(n) and can become a performance problem on very large collections. Prepending to the beginning of an IList, using IList#prependElement or cons is however super efficient (O(1)). If you need to do a lot of appending on large collections, consider using IVector instead (O(log n) append and prepend).

The constant IListMi is a remnant from the Dart 1 days and, as you have already noticed, will lose type information :( You can use ilistMi<A>() instead to construct type safe IList monoid instances.

Here are some examples:

  ilist([1, 2, 3]).appendElement(4);                   // => ilist([1, 2, 3, 4])
  ilist([1, 2, 3]).prependElement(0);                  // => ilist([0, 1, 2, 3])
  cons(0, ilist([1, 2, 3]));                           // => ilist([0, 1, 2, 3]) (same as prependElement)
  ilist([1, 2]).plus(ilist([3, 4]));                   // => ilist([1, 2, 3, 4])
  ilistMi<int>().append(ilist([1, 2]), ilist([3, 4])); // => ilist([1, 2, 3, 4]) (same as plus)

Hope that helps!

ibcoleman commented 2 years ago

Thanks @spebbe! And thanks for the incredibly useful library, too!