spebbe / dartz

Functional programming in Dart
MIT License
756 stars 59 forks source link

IList<Option<Tuple2<K,V>>> to IHashMap<K,V> #4

Closed rich-j closed 6 years ago

rich-j commented 6 years ago

I need to convert an IList<Option<Tuple2<K,V>>> to a IHashMap<K,V>. In Scala I would use Seq(Some("a"->1),None).flatten.toMap. In dartz the .flatten() returns runtime error:

CastError: Casting value of type 'Some<Tuple2<Stri…, String>>' to type 'IList' which is incompatible

This could be failing because I'm using DDC Issue #2.

For the .toMap equivalent:

flattenedList.foldLeft( new IHashMap.empty(), (p, h) => p.put(h.value1, h.value2) )

Since IHashMap has iterators that return Tuple2 would you consider an adding similar constructors?

spebbe commented 6 years ago

'flatten'/'join' in dartz is just monadic join (M[M[A]] => M[A] for any monad M), just like 'join' in scalaz. 'flatten' on scala standard collections does allow flattening between different types but does so by using a lot of the cooler features of scala (implicit evidences, higher-kinded types, etc.). Sadly, Dart lacks the necessary features for pulling off the same trick in a manner that is both type safe and extensible. In fact, not even the current OO-style 'flatten' operation in dartz can be implemented in a type safe way and should probably be removed and replaced with static utility functions (where the full type contract can be expressed).

The current alternative 'unite' allows "flattening" any Foldable within any MonadPlus, like in:

  final IList<Option<int>> listOfOptionalInts = ilist([some(1), none(), some(3)]);
  final IList<int> listOfInts = listOfOptionalInts.unite(optionTr());
  print(listOfInts); // => ilist[1, 3];

Of course, this won't yet (ever?) work on Dart 2 DDC... :-(

For just uniting IList and Option, this seems to work on Dart 2 DDC:

  final IList<Option<Tuple2<String, int>>> l = ilist([some(tuple2("one", 1)), none(), some(tuple2("three", 3))]);
  final IList<Tuple2<String, int>> l2 = l.flatMap((o) => o.fold(nil, (t) => cons(t, nil())));
  print(l2); // => ilist[(one, 1), (three, 3)]

For a solution to your specific task that works on Dart 2 DDC, I'm afraid the current solution is a bit more verbose and less generic:

  IHashMap<K, V> toHashMap<K, V>(IList<Option<Tuple2<K, V>>> keysAndValuesMaybe) =>
    keysAndValuesMaybe.foldLeft(new IHashMap.empty(), (map, maybeKV) => maybeKV.fold(() => map, (kv) => kv.apply(map.put)));

  final IList<Option<Tuple2<String, int>>> l = ilist([some(tuple2("one", 1)), none(), some(tuple2("three", 3))]);
  print(toHashMap(l)); // => ihashmap{three: 3, one: 1}

I'm looking at solutions that will make common operations like these less cumbersome in the future. I will probably sacrifice extensibility/genericity in favour of type safety, but we'll see :-) Convenience IMap/IHashMap constructors from lists/vectors/foldables of Tuple2 sounds like a nice addition -- I'll throw that in and close this ticket after the next release!

Also, I might mention that IHashMap is mainly meant to be used as a last resort for when keys aren't already Comparable or for when it is impossible to construct an Order instance for them (= no total ordering exists). For keys that are totally ordered, using IMap offers better performance and a much richer set of operations than does IHashMap. (I know, I know, the documentation is very lacking...)

Cheers,

/Björn

spebbe commented 6 years ago

Added IMap#fromPairs, IHashMap#fromPairs, IList#flattenIList, IList#flattenOption, IVector#flattenIVector and IVector#flattenOption in 0.7.5 + rebased ddc_compat branch.