From 415b21761ac98241bb9386acbe1d9eba32177a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Sperber?= Date: Sun, 2 Sep 2018 14:36:31 +0200 Subject: [PATCH 1/8] =?UTF-8?q?WIP,=20killing=20stuff=20that=20requires=20?= =?UTF-8?q?casts,=20requires=20dynamic=20typing=20or=20prevents=20const.?= =?UTF-8?q?=20Duplicating=20lots=20of=20code=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/free_io/mock_io.dart | 4 +- lib/src/applicative.dart | 8 +-- lib/src/applicative_plus.dart | 4 +- lib/src/avl_tree.dart | 59 ++++++++++++---- lib/src/either.dart | 51 ++++++++++++-- lib/src/evaluation.dart | 10 ++- lib/src/foldable.dart | 9 +-- lib/src/free.dart | 11 +-- lib/src/function.dart | 2 +- lib/src/functor.dart | 4 +- lib/src/id.dart | 4 +- lib/src/ihashmap.dart | 44 ++++++++++-- lib/src/ilist.dart | 101 ++++++++++++++++++++-------- lib/src/imap.dart | 71 ++++++++++++++----- lib/src/iset.dart | 29 +++++++- lib/src/ivector.dart | 40 +++++++++-- lib/src/list.dart | 4 ++ lib/src/monad.dart | 14 ++-- lib/src/monad_plus.dart | 6 +- lib/src/option.dart | 83 ++++++++++++++++------- lib/src/order.dart | 6 +- lib/src/plus_empty.dart | 4 +- lib/src/state.dart | 23 +++++-- lib/src/streaming/conveyor.dart | 36 ++++++++-- lib/src/streaming/pipe.dart | 6 +- lib/src/streaming/tee.dart | 18 +++-- lib/src/task.dart | 18 ++++- lib/src/trampoline.dart | 16 ++++- lib/src/traversable.dart | 41 +++++------ lib/src/traversable_monad.dart | 4 +- lib/src/traversable_monad_plus.dart | 2 + lib/src/unsafe/io.dart | 4 +- test/evaluation_test.dart | 17 +++-- test/ilist_test.dart | 2 +- test/laws.dart | 8 +-- test/option_test.dart | 8 +-- test/state_test.dart | 2 +- 37 files changed, 561 insertions(+), 212 deletions(-) diff --git a/example/free_io/mock_io.dart b/example/free_io/mock_io.dart index 53bd9f8..a0e8b48 100644 --- a/example/free_io/mock_io.dart +++ b/example/free_io/mock_io.dart @@ -17,7 +17,7 @@ class _MockFileRef implements FileRef { Evaluation>, IVector, IMap, String> mockReadFile(String fileName) => MockM.gets((counters) => counters[fileName]|0).bind((i) => MockM.asks((inputs) => inputs[fileName]|emptyVector()).bind((vs) => - MockM.pure(vs[i]|null) << MockM.modify((counters) => counters.put(fileName, i+1)))); + MockM.pure(vs[i]|null).bind((x) => MockM.modify((counters) => counters.put(fileName, i+1)).replace(x)))); Evaluation>, IVector, IMap, A> _interpret(Free op) => op.foldMap(MockM, mockIOInterpreter); @@ -57,7 +57,7 @@ Evaluation>, IVector, IMap implements Functor { F get nothing => pure(unit); @override F map(covariant F fa, B f(A a)) => ap(fa, pure(f)); - +/* F traverseA(Traversable g, ga, F f(_)) => g.traverse(this, ga, f); F traverseA_(Traversable g, ga, F f(_)) => g.traverse_(this, ga, f); @@ -35,7 +35,7 @@ abstract class Applicative implements Functor { F replicate(int n, covariant F fa) => sequenceL(new IList.from(new List.filled(n, fa))); F replicate_(int n, F fa) => sequenceL_(new IList.from(new List.filled(n, fa))); - +*/ // Workaround: Dumbing down types in generic liftX to give subclasses a chance to do proper typing... // OMG, it just got worse... not much left of the types since 2.0.0-dev.32.0 :-( @@ -70,8 +70,8 @@ class ComposedApplicative extends Functor with Applicative { } abstract class ApplicativeOps implements FunctorOps { - F pure(B b); +// F pure(B b); F ap(covariant F ff); - @override F map(B f(A a)) => ap(pure(f)); + @override F map(B f(A a));// => ap(pure(f)); } diff --git a/lib/src/applicative_plus.dart b/lib/src/applicative_plus.dart index 1567ff1..570fce6 100644 --- a/lib/src/applicative_plus.dart +++ b/lib/src/applicative_plus.dart @@ -6,6 +6,6 @@ abstract class ApplicativePlus implements Applicative, PlusEmpty { } abstract class ApplicativePlusOps implements ApplicativeOps, PlusEmptyOps { - F prependElement(A a) => cast>(pure(a)).plus(cast(this)); - F appendElement(A a) => plus(pure(a)); + F prependElement(A a);// => cast>(pure(a)).plus(cast(this)); + F appendElement(A a);// => plus(pure(a)); } \ No newline at end of file diff --git a/lib/src/avl_tree.dart b/lib/src/avl_tree.dart index fef8888..3fa737c 100644 --- a/lib/src/avl_tree.dart +++ b/lib/src/avl_tree.dart @@ -4,7 +4,7 @@ part of dartz; // TODO: naive implementation. does too much work and too many allocations. -class AVLTree extends FoldableOps { +class AVLTree implements FoldableOps { final Order _order; final _AVLNode _root; @@ -47,6 +47,32 @@ class AVLTree extends FoldableOps { Iterator iterator() => toIterable().iterator; void forEach(void sideEffect(A a)) => foldLeft(null, (_, a) => sideEffect(a)); + + @override bool all(bool f(A a)) => foldMap(BoolAndMi, f); // TODO: optimize + + @override bool any(bool f(A a)) => foldMap(BoolOrMi, f); // TODO: optimize + + @override A concatenate(Monoid mi) => foldMap(mi, id); // TODO: optimize + + @override Option concatenateO(Semigroup si) => foldMapO(si, id); // TODO: optimize + + @override B foldLeftWithIndex(B z, B f(B previous, int i, A a)) => + foldLeft>(tuple2(z, 0), (t, a) => tuple2(f(t.value1, t.value2, a), t.value2+1)).value1; // TODO: optimize + + @override Option foldMapO(Semigroup si, B f(A a)) => + foldMap(new OptionMonoid(si), composeF(some, f)); // TODO: optimize + + @override B foldRightWithIndex(B z, B f(int i, A a, B previous)) => + foldRight>(tuple2(z, length()-1), (a, t) => tuple2(f(t.value2, a, t.value1), t.value2-1)).value1; // TODO: optimize + + @override A intercalate(Monoid mi, A a) => + foldRight(none(), (A ca, Option oa) => some(mi.append(ca, oa.fold(mi.zero, mi.appendC(a))))) | mi.zero(); // TODO: optimize + + @override int length() => foldLeft(0, (a, b) => a+1); // TODO: optimize + + @override Option maximum(Order oa) => concatenateO(oa.maxSi()); + + @override Option minimum(Order oa) => concatenateO(oa.minSi()); } abstract class _AVLNode { @@ -65,6 +91,7 @@ abstract class _AVLNode { int get balance; Option, A>> _removeMax(); bool get empty; + _NonEmptyAVLNode _unsafeGetNonEmpty(); } class _NonEmptyAVLNode extends _AVLNode { @@ -112,15 +139,15 @@ class _NonEmptyAVLNode extends _AVLNode { final b = balance; if (b < -1) { if (_left.balance < 0) { - return llRotate(cast(_left)); + return llRotate(_left._unsafeGetNonEmpty()); } else { - return doubleLrRotate(cast(_left)); + return doubleLrRotate(_left._unsafeGetNonEmpty()); } } else if (b > 1) { if (_right.balance > 0) { - return rrRotate(cast(_right)); + return rrRotate(_right._unsafeGetNonEmpty()); } else { - return doubleRlRotate(cast(_right)); + return doubleRlRotate(_right._unsafeGetNonEmpty()); } } else { return this; @@ -129,11 +156,11 @@ class _NonEmptyAVLNode extends _AVLNode { _NonEmptyAVLNode llRotate(_NonEmptyAVLNode l) => new _NonEmptyAVLNode(l._a, l._left, new _NonEmptyAVLNode(_a, l._right, _right)); - _NonEmptyAVLNode doubleLrRotate(_NonEmptyAVLNode l) => llRotate(l.rrRotate(cast(l._right))); + _NonEmptyAVLNode doubleLrRotate(_NonEmptyAVLNode l) => llRotate(l.rrRotate(l._right._unsafeGetNonEmpty())); _NonEmptyAVLNode rrRotate(_NonEmptyAVLNode r) => new _NonEmptyAVLNode(r._a, new _NonEmptyAVLNode(_a, _left, r._left), r._right); - _NonEmptyAVLNode doubleRlRotate(_NonEmptyAVLNode r) => rrRotate(r.llRotate(cast(r._left))); + _NonEmptyAVLNode doubleRlRotate(_NonEmptyAVLNode r) => rrRotate(r.llRotate(r._left._unsafeGetNonEmpty())); B foldLeft(B z, B f(B previous, A a)) { final leftResult = _left.foldLeft(z, f); @@ -179,15 +206,15 @@ class _NonEmptyAVLNode extends _AVLNode { if (o == Ordering.EQ) { return some(current._a); } else if (o == Ordering.LT) { - final l = current._left; - if (l is _NonEmptyAVLNode) { + final l = current._left._unsafeGetNonEmpty(); + if (l != null) { current = l; } else { return none(); } } else { - final r = current._right; - if (r is _NonEmptyAVLNode) { + final r = current._right._unsafeGetNonEmpty(); + if (r != null) { current = r; } else { return none(); @@ -202,6 +229,8 @@ class _NonEmptyAVLNode extends _AVLNode { Option max() => _right is _EmptyAVLNode ? some(_a) : _right.max(); bool get empty => false; + + _NonEmptyAVLNode _unsafeGetNonEmpty() => this; } class _EmptyAVLNode extends _AVLNode { @@ -236,6 +265,8 @@ class _EmptyAVLNode extends _AVLNode { @override int get hashCode => 0; bool get empty => true; + + _NonEmptyAVLNode _unsafeGetNonEmpty() => null; } _AVLNode emptyAVLNode() => new _EmptyAVLNode(); @@ -255,7 +286,7 @@ final Foldable AVLTreeFo = new FoldableOpsFoldable(); class _AVLTreeIterable extends Iterable { final AVLTree _tree; _AVLTreeIterable(this._tree); - @override Iterator get iterator => _tree._root.empty ? new _AVLTreeIterator(null) : new _AVLTreeIterator(cast(_tree._root)); + @override Iterator get iterator => new _AVLTreeIterator(_tree._root._unsafeGetNonEmpty()); } class _AVLTreeIterator extends Iterator { @@ -285,7 +316,7 @@ class _AVLTreeIterator extends Iterator { bool _descend() { if (!_currentNode._right.empty) { - _currentNode = cast(_currentNode._right); + _currentNode = _currentNode._right._unsafeGetNonEmpty(); _descendLeft(); return true; } else { @@ -304,7 +335,7 @@ class _AVLTreeIterator extends Iterator { var currentLeft = current._left; while(true) { if (!currentLeft.empty) { - final _NonEmptyAVLNode cl = cast(currentLeft); + final _NonEmptyAVLNode cl = currentLeft._unsafeGetNonEmpty(); _path = cons(current, _path); current = cl; currentLeft = cl._left; diff --git a/lib/src/either.dart b/lib/src/either.dart index eb36511..fb86484 100644 --- a/lib/src/either.dart +++ b/lib/src/either.dart @@ -1,7 +1,7 @@ part of dartz; // Workaround for https://github.com/dart-lang/sdk/issues/29949 -abstract class Either extends TraversableOps*/, R> with FunctorOps*/, R>, ApplicativeOps*/, R>, MonadOps*/, R>, TraversableMonadOps*/, R> { +abstract class Either implements TraversableMonadOps, R> { B fold(B ifLeft(L l), B ifRight(R r)); Either orElse(Either other()) => fold((_) => other(), (_) => this); @@ -13,14 +13,11 @@ abstract class Either extends TraversableOps*/, R> wi bool isRight() => fold((_) => false, (_) => true); Either swap() => fold(right, left); - @override Either pure(R2 r2) => right(r2); @override Either map(R2 f(R r)) => fold(left, (R r) => right(f(r))); @override Either bind(Either f(R r)) => fold(left, f); @override Either flatMap(Either f(R r)) => fold(left, f); @override Either andThen(Either next) => fold(left, (_) => next); - @override G traverse(Applicative gApplicative, G f(R r)) => fold((_) => gApplicative.pure(this), (R r) => gApplicative.map(f(r), right)); - IList> traverseIList(IList f(R r)) => fold((l) => cons(left(l), nil()), (R r) => f(r).map(right)); IVector> traverseIVector(IVector f(R r)) => fold((l) => emptyVector>().appendElement(left(l)), (R r) => f(r).map(right)); @@ -72,6 +69,52 @@ abstract class Either extends TraversableOps*/, R> wi Iterator iterator() => toIterable().iterator; void forEach(void sideEffect(R r)) => fold((_) => null, sideEffect); + + @override B foldMap(Monoid bMonoid, B f(R r)) => fold((_) => bMonoid.zero(), f); + + @override Either mapWithIndex(B f(int i, R r)) => map((r) => f(0, r)); + + @override Either> zipWithIndex() => map((r) => tuple2(0, r)); + + @override bool all(bool f(R r)) => map(f)|true; + + @override bool any(bool f(R r)) => map(f)|false; + + @override Either appendElement(R r) => orElse(() => right(r)); + + @override R concatenate(Monoid mi) => getOrElse(mi.zero); + + @override Option concatenateO(Semigroup si) => toOption(); + + @override B foldLeft(B z, B f(B previous, R r)) => fold((_) => z, (a) => f(z, a)); + + @override B foldLeftWithIndex(B z, B f(B previous, int i, R r)) => fold((_) => z, (a) => f(z, 0, a)); + + @override Option foldMapO(Semigroup si, B f(R r)) => map(f).toOption(); + + @override B foldRight(B z, B f(R r, B previous)) => fold((_) => z, (a) => f(a, z)); + + @override B foldRightWithIndex(B z, B f(int i, R r, B previous))=> fold((_) => z, (a) => f(0, a, z)); + + @override R intercalate(Monoid mi, R r) => fold((_) => mi.zero(), id); + + @override int length() => fold((_) => 0, (_) => 1); + + @override Option maximum(Order or) => toOption(); + + @override Option minimum(Order or) => toOption(); + + @override Either prependElement(R r) => right(r).orElse(() => this); + + @override Either replace(B replacement) => map((_) => replacement); + + Either reverse() => this; + + @override Either> strengthL(B b) => map((a) => tuple2(b, a)); + + @override Either> strengthR(B b) => map((a) => tuple2(a, b)); + + @override Either ap(Either> ff) => ff.bind((f) => map(f)); } class Left extends Either { diff --git a/lib/src/evaluation.dart b/lib/src/evaluation.dart index 820d062..c08be75 100644 --- a/lib/src/evaluation.dart +++ b/lib/src/evaluation.dart @@ -4,7 +4,7 @@ part of dartz; // Binds are stack safe but relatively expensive, because of Future chaining. // Workaround for https://github.com/dart-lang/sdk/issues/29949 -class Evaluation extends FunctorOps*/, A> with ApplicativeOps*/, A>, MonadOps*/, A> { +class Evaluation implements MonadOps, A> { final Monoid _W; final Function2>>> _run; @@ -60,11 +60,15 @@ class Evaluation extends FunctorOps> value(R r, S s) => run(r, s).then((e) => e.map((t) => t.value3)); - @override Evaluation operator <<(Evaluation next) => bind((a) => next.map((_) => a)); - @override Evaluation replace(B replacement) => map((_) => replacement); Evaluation replicate_(int n) => n > 0 ? flatMap((_) => replicate_(n-1)) : pure(unit); + + @override Evaluation> strengthL(B b) => map((a) => tuple2(b, a)); + + @override Evaluation> strengthR(B b) => map((a) => tuple2(a, b)); + + @override Evaluation ap(Evaluation> ff) => ff.bind((f) => map(f)); // TODO: optimize } class EvaluationMonad extends Functor> with Applicative>, Monad> { diff --git a/lib/src/foldable.dart b/lib/src/foldable.dart index 5931d95..4ab7ba6 100644 --- a/lib/src/foldable.dart +++ b/lib/src/foldable.dart @@ -2,7 +2,7 @@ part of dartz; abstract class Foldable { // def foldMap[A, B: Monoid](fa: Option[A], f: A => B): B - B foldMap(Monoid bMonoid, F fa, B f(A a)); + B foldMap(Monoid bMonoid, covariant F fa, B f(A a)); B foldRight(F fa, B z, B f(A a, B previous)) => foldMap>(endoMi(), fa, curry2(f))(z); @@ -68,13 +68,6 @@ abstract class FoldableOps { A intercalate(Monoid mi, A a) => foldRight(none(), (A ca, Option oa) => some(mi.append(ca, oa.fold(mi.zero, mi.appendC(a))))) | mi.zero(); - G collapse(ApplicativePlus ap) => foldLeft(ap.empty(), (p, a) => ap.plus(p, ap.pure(a))); - - G foldLeftM(Monad m, B z, G f(B previous, A a)) => foldRight>(m.pure, (A a, b) => (w) => m.bind(f(w, a), b))(z); - - G foldRightM(Monad m, B z, G f(A a, B previous)) => foldLeft>(m.pure, (b, A a) => (w) => m.bind(f(a, w), b))(z); - - G foldMapM(Monad m, Monoid bMonoid, G f(A a)) => foldMap(monoid(() => m.pure(bMonoid.zero()), cast(m.lift2(bMonoid.append))), f); } class FoldableOpsFoldable extends Foldable { diff --git a/lib/src/free.dart b/lib/src/free.dart index 8874d97..d9f7421 100644 --- a/lib/src/free.dart +++ b/lib/src/free.dart @@ -3,9 +3,7 @@ part of dartz; typedef Free _FreeF(dynamic x); // Workaround for https://github.com/dart-lang/sdk/issues/29949 -abstract class Free extends FunctorOps*/, A> with ApplicativeOps*/, A>, MonadOps*/, A> { - - @override Free pure(B b) => new Pure(b); +abstract class Free implements MonadOps, A> { @override Free map(B f(A a)) => bind((a) => new Pure(f(a))); @@ -33,7 +31,6 @@ abstract class Free extends FunctorOps*/, A> with Appli @override Free flatMap(Free f(A a)) => new Bind(this, (a) => f(cast(a))); @override Free andThen(Free next) => bind((_) => next); - @override Free operator <<(Free next) => bind((a) => next.map((_) => a)); static Free map2(Free fa, Free fb, C fun(A a, B b)) => fa.flatMap((a) => fb.map((b) => fun(a, b))); @@ -51,6 +48,12 @@ abstract class Free extends FunctorOps*/, A> with Appli fa.flatMap((a) => fb.flatMap((b) => fc.flatMap((c) => fd.flatMap((d) => fe.flatMap((e) => ff.map((f) => fun(a, b, c, d, e, f))))))); static Free ifM(Free fbool, Free ifTrue) => fbool.flatMap((bool b) => b ? ifTrue : new Pure(unit)); + + @override Free ap(Free> ff) => ff.bind((f) => map(f)); // TODO: optimize + + @override Free> strengthL(B b) => map((a) => tuple2(b, a)); + + @override Free> strengthR(B b) => map((a) => tuple2(a, b)); } class Pure extends Free { diff --git a/lib/src/function.dart b/lib/src/function.dart index 586be32..e6ea63f 100644 --- a/lib/src/function.dart +++ b/lib/src/function.dart @@ -44,7 +44,7 @@ Function1 constF(B b) => (A a) => b; class Function0TraversableMonad extends Traversable with Applicative, Monad, TraversableMonad { @override Function0 pure(A a) => () => a; @override Function0 bind(Function0 fa, Function0 f(A a)) => () => f(fa())(); - @override G traverse(Applicative gApplicative, Function0 fa, G f(a)) => gApplicative.map(f(fa()), (b) => () => b); + @override B foldMap(Monoid bMonoid, Function0 fa, B f(A a)) => f(fa()); } final Function0TraversableMonad Function0TrM = new Function0TraversableMonad(); diff --git a/lib/src/functor.dart b/lib/src/functor.dart index ccf108b..9250064 100644 --- a/lib/src/functor.dart +++ b/lib/src/functor.dart @@ -24,7 +24,7 @@ class ComposedFunctor extends Functor { abstract class FunctorOps { F map(B f(A a)); - F strengthL(B b) => map((a) => tuple2(b, a)); + F strengthL(B b);// => map((a) => tuple2(b, a)); - F strengthR(B b) => map((a) => tuple2(a, b)); + F strengthR(B b);// => map((a) => tuple2(a, b)); } \ No newline at end of file diff --git a/lib/src/id.dart b/lib/src/id.dart index f065bd5..3a1ee97 100644 --- a/lib/src/id.dart +++ b/lib/src/id.dart @@ -10,7 +10,9 @@ class IdMonad extends Functor with Applicative, Monad { final IdMonad IdM = new IdMonad(); class IdTraversable extends Traversable { - @override G traverse(Applicative gApplicative, fa, G f(a)) => f(fa); + @override B foldMap(Monoid bMonoid, A fa, B f(A a)) => f(fa); + + @override map(A fa, B f(A a)) => f(fa); } final IdTraversable IdTr = new IdTraversable(); diff --git a/lib/src/ihashmap.dart b/lib/src/ihashmap.dart index 58105f4..cf9bf4d 100644 --- a/lib/src/ihashmap.dart +++ b/lib/src/ihashmap.dart @@ -4,7 +4,7 @@ part of dartz; // Unlike IMap, IHashMap doesn't rely on a total ordering of keys, but instead uses hashCode and '==' to insert // and locate key/value pairs in balanced bucket trees, -class IHashMap extends TraversableOps, V> { +class IHashMap implements TraversableOps, V> { final IMap>> _map; IHashMap.internal(this._map); @@ -53,12 +53,6 @@ class IHashMap extends TraversableOps, V> { @override B foldRight(B z, B f(V v, B previous)) => _map.foldRight(z, (kvs, prev) => kvs.foldRight(prev, (kv, pprev) => f(kv.value2, pprev))); - @override G traverse(Applicative gApplicative, G f(V v)) => - _map.foldLeft(gApplicative.pure(new IHashMap.empty()), - (prev, kvs) => kvs.foldLeft(prev, - (pprev, kv) => gApplicative.map2(pprev, f(kv.value2), - (p, v2) => p.put(kv.value1, v2)))); - @override String toString() => "ihashmap{${_map.foldMap(IListMi, (kvs) => kvs.map((kv) => "${kv.value1}: ${kv.value2}")).intercalate(StringMi, ", ")}}"; @override bool operator ==(other) => identical(this, other) || (other is IHashMap && _map == other._map); @override int get hashCode => _map.hashCode; @@ -84,6 +78,42 @@ class IHashMap extends TraversableOps, V> { void forEach(void sideEffect(V v)) => foldLeft(null, (_, v) => sideEffect(v)); void forEachKV(void sideEffect(K k, V v)) => foldLeftKV(null, (_, k, v) => sideEffect(k, v)); + + @override B foldMap(Monoid bMonoid, B f(V a)) => _map.foldMap(bMonoid, (kvs) => kvs.foldMap(bMonoid, (t) => f(t.value2))); + + @override IHashMap mapWithIndex(B f(int i, V a)) => throw "not implemented!!!"; // TODO + + @override IHashMap> zipWithIndex() => mapWithIndex(tuple2); + + @override bool all(bool f(V a)) => foldMap(BoolAndMi, f); // TODO: optimize + + @override bool any(bool f(V a)) => foldMap(BoolOrMi, f); // TODO: optimize + + @override V concatenate(Monoid mi) => foldMap(mi, id); // TODO: optimize + + @override Option concatenateO(Semigroup si) => foldMapO(si, id); // TODO: optimize + + @override B foldLeftWithIndex(B z, B f(B previous, int i, V a)) => + foldLeft>(tuple2(z, 0), (t, a) => tuple2(f(t.value1, t.value2, a), t.value2+1)).value1; // TODO: optimize + + @override Option foldMapO(Semigroup si, B f(V a)) => + foldMap(new OptionMonoid(si), composeF(some, f)); // TODO: optimize + + @override B foldRightWithIndex(B z, B f(int i, V a, B previous)) => + foldRight>(tuple2(z, length()-1), (a, t) => tuple2(f(t.value2, a, t.value1), t.value2-1)).value1; // TODO: optimize + + @override V intercalate(Monoid mi, V v) => + foldRight(none(), (V cv, Option ov) => some(mi.append(cv, ov.fold(mi.zero, mi.appendC(v))))) | mi.zero(); // TODO: optimize + + @override int length() => foldLeft(0, (a, b) => a+1); // TODO: optimize + + @override Option maximum(Order ov) => concatenateO(ov.maxSi()); + + @override Option minimum(Order ov) => concatenateO(ov.minSi()); + + @override IHashMap> strengthL(B b) => map((v) => tuple2(b, v)); + + @override IHashMap> strengthR(B b) => map((v) => tuple2(v, b)); } final Traversable IHashMapTr = new TraversableOpsTraversable(); diff --git a/lib/src/ilist.dart b/lib/src/ilist.dart index ecd425a..8cb4be6 100644 --- a/lib/src/ilist.dart +++ b/lib/src/ilist.dart @@ -3,7 +3,7 @@ part of dartz; // Internally implemented using imperative loops and mutations, for stack safety and performance. // The external API should be safe and referentially transparent, though. -abstract class IList extends TraversableOps with FunctorOps, ApplicativeOps, ApplicativePlusOps, MonadOps, MonadPlusOps, TraversableMonadOps, TraversableMonadPlusOps, PlusOps { +abstract class IList implements TraversableMonadPlusOps { Option get headOption; Option> get tailOption; @@ -16,7 +16,7 @@ abstract class IList extends TraversableOps with FunctorOps newTail); - IList(); + const IList(); factory IList.from(Iterable iterable) { final IList aNil = nil(); @@ -49,30 +49,6 @@ abstract class IList extends TraversableOps with FunctorOps pure(B b) => new Cons(b, nil()); - - @override G traverse(Applicative gApplicative, G f(A a)) { - var result = gApplicative.pure(nil()); - var current = this; - while(current._isCons()) { - final gb = f(current._unsafeHead()); - result = gApplicative.map2(result, gb, (IList a, h) => new Cons(h, a)); - current = current._unsafeTail(); - } - return gApplicative.map(result, (l) => l.reverse()); - } - - @override G traverse_(Applicative gApplicative, G f(A a)) { - var result = gApplicative.pure(unit); - var current = this; - while (current._isCons()) { - final gb = f(current._unsafeHead()); - result = gApplicative.map2(result, gb, (a, h) => unit); - current = current._unsafeTail(); - } - return result; - } - @override IList bind(IList f(A a)) { final IList bNil = nil(); if (!_isCons()) { @@ -151,8 +127,6 @@ abstract class IList extends TraversableOps with FunctorOps reverse() => foldLeft(nil(), (a, h) => new Cons(h, a)); - @override IList empty() => nil(); - @override IList plus(IList l2) => foldRight(l2, (e, p) => new Cons(e, p)); @@ -342,6 +316,75 @@ abstract class IList extends TraversableOps with FunctorOps> sequenceFuture(IList> lfa) => lfa.traverseFuture(id); static State> sequenceState(IList> lsa) => lsa.traverseState(id); + + @override IList mapWithIndex(B f(int i, A a)) { + final IList bNil = nil(); + if (!_isCons()) { + return bNil; + } + int i = 0; + Cons last = new Cons(f(i++, _unsafeHead()), bNil); + if (!_unsafeTail()._isCons()) { + return last; + } + final result = last; + var current = _unsafeTail(); + while (current._isCons()) { + final next = new Cons(f(i++, current._unsafeHead()), bNil); + last._unsafeSetTail(next); + last = next; + current = current._unsafeTail(); + } + return result; + } + + @override IList> zipWithIndex() => mapWithIndex(tuple2); + + @override bool all(bool f(A a)) => foldMap(BoolAndMi, f); // TODO: optimize + + @override IList andThen(IList next) => bind((_) => next); + + @override bool any(bool f(A a)) => foldMap(BoolOrMi, f); // TODO: optimize + + @override IList ap(IList> ff) => ff.bind((f) => map(f)); // TODO: optimize + + @override A concatenate(Monoid mi) => foldMap(mi, id); + + @override Option concatenateO(Semigroup si) => foldMapO(si, id); + + @override B foldLeftWithIndex(B z, B f(B previous, int i, A a)) { + var i = 0; + var result = z; + var current = this; + while (current._isCons()) { + result = f(result, i++, current._unsafeHead()); + current = current._unsafeTail(); + } + return result; + } + + @override Option foldMapO(Semigroup si, B f(A a)) => + uncons(none, (head, tail) => some(tail.foldLeft(f(head), (acc, a) => si.append(acc, f(a))))); + + @override B foldRightWithIndex(B z, B f(int i, A a, B previous)) => + foldRight>(tuple2(z, length()-1), (a, t) => tuple2(f(t.value2, a, t.value1), t.value2-1)).value1; // TODO: optimize + + @override A intercalate(Monoid mi, A a) => + foldRight(none(), (A ca, Option oa) => some(mi.append(ca, oa.fold(mi.zero, mi.appendC(a))))) | mi.zero(); // TODO: optimize + + @override int length() => + foldLeft(0, (a, b) => a+1); // TODO: optimize + + @override Option maximum(Order oa) => concatenateO(oa.maxSi()); + + @override Option minimum(Order oa) => concatenateO(oa.minSi()); + + @override IList replace(B replacement) => map((_) => replacement); + + @override IList> strengthL(B b) => map((a) => tuple2(b, a)); + + @override IList> strengthR(B b) => map((a) => tuple2(a, b)); + } class Cons extends IList { @@ -360,6 +403,8 @@ class Cons extends IList { } class _Nil extends IList { + const _Nil(); + bool _isCons() => false; A _unsafeHead() => throw new UnsupportedError("_unsafeHead called on _Nil"); IList _unsafeTail() => throw new UnsupportedError("_unsafeTail called on _Nil"); diff --git a/lib/src/imap.dart b/lib/src/imap.dart index a0d0d6d..5899dc9 100644 --- a/lib/src/imap.dart +++ b/lib/src/imap.dart @@ -1,12 +1,12 @@ part of dartz; -class IMap extends TraversableOps, V> { +class IMap implements TraversableOps, V> { final Order _order; final _IMapAVLNode _tree; - IMap(this._order, this._tree); + const IMap._internal(this._order, this._tree); - IMap.empty(this._order): _tree = _emptyIMapAVLNode(); + IMap.empty(this._order): _tree = new _EmptyIMapAVLNode(); factory IMap.from(Order kOrder, Map m) => m.keys.fold(new IMap.empty(kOrder), (p, K k) => p.put(k, m[k])); @@ -23,7 +23,7 @@ class IMap extends TraversableOps, V> { factory IMap.fromPairs(FoldableOps> foldableOps, Order kOrder) => foldableOps.foldLeft(new IMap.empty(kOrder), (acc, kv) => kv.apply(acc.put)); - IMap put(K k, V v) => new IMap(_order, _tree.insert(_order, k, v)); + IMap put(K k, V v) => new IMap._internal(_order, _tree.insert(_order, k, v)); Option get(K k) => _tree.get(_order, k); @@ -31,7 +31,7 @@ class IMap extends TraversableOps, V> { Option operator[](K k) => get(k); - IMap modify(K k, V f(V v), V dflt) => new IMap(_order, _tree.modify(_order, k, f, dflt)); + IMap modify(K k, V f(V v), V dflt) => new IMap._internal(_order, _tree.modify(_order, k, f, dflt)); Option> set(K k, V v) { final newMap = setIfPresent(k, v); @@ -40,10 +40,10 @@ class IMap extends TraversableOps, V> { IMap setIfPresent(K k, V v) { final newTree = _tree.setIfPresent(_order, k, v); - return identical(_tree, newTree) ? this : new IMap(_order, newTree); + return identical(_tree, newTree) ? this : new IMap._internal(_order, newTree); } - IMap remove(K k) => new IMap(_order, _tree.remove(_order, k)); + IMap remove(K k) => new IMap._internal(_order, _tree.remove(_order, k)); IList keys() => _tree.foldRight(nil(), (k, v, p) => new Cons(k, p)); @@ -59,30 +59,26 @@ class IMap extends TraversableOps, V> { B foldMapKV(Monoid mi, B f(K k, V v)) => _tree.foldLeft(mi.zero(), (p, k, v) => mi.append(p, f(k, v))); - IMap mapWithKey(V2 f(K k, V v)) => foldLeftKV(new IMap(_order, _emptyIMapAVLNode()), (p, k, v) => p.put(k, f(k, v))); + IMap mapWithKey(V2 f(K k, V v)) => foldLeftKV(new IMap._internal(_order, _emptyIMapAVLNode()), (p, k, v) => p.put(k, f(k, v))); IMap mapKV(V2 f(K k, V v)) => mapWithKey(f); IList> pairs() => _tree.foldRight(nil(), (k, v, p) => new Cons(tuple2(k, v), p)); G traverseKV(Applicative gApplicative, G f(K k, V v)) => _tree.foldLeft(gApplicative.pure( - new IMap(_order, _emptyIMapAVLNode())), + new IMap._internal(_order, _emptyIMapAVLNode())), (prev, k, v) => gApplicative.map2(prev, f(k, v), (IMap p, v2) => p.put(k, v2))); G traverseKV_(Applicative gApplicative, G f(K k, V v)) => _tree.foldLeft(gApplicative.pure(unit), (prev, k, v) => gApplicative.map2(prev, f(k, v), (_1, _2) => unit)); - @override G traverse(Applicative gApplicative, G f(V v)) => traverseKV(gApplicative, (_, v) => f(v)); - - @override G traverse_(Applicative gApplicative, G f(V v)) => traverseKV_(gApplicative, (_, v) => f(v)); - @override B foldMap(Monoid bMonoid, B f(V v)) => _tree.foldLeft(bMonoid.zero(), (p, k, v) => bMonoid.append(p, f(v))); @override B foldLeft(B z, B f(B previous, V v)) => _tree.foldLeft(z, (p, k, v) => f(p, v)); @override B foldRight(B z, B f(V v, B previous)) => _tree.foldRight(z, (k, v, p) => f(v, p)); - @override IMap map(V2 f(V v)) => new IMap(_order, _tree.map(f)); + @override IMap map(V2 f(V v)) => new IMap._internal(_order, _tree.map(f)); Map toMap() => foldLeftKV(new Map(), (p, K k, V v) => p..[k] = v); @@ -129,12 +125,47 @@ class IMap extends TraversableOps, V> { void forEach(void sideEffect(V v)) => foldLeft(null, (_, v) => sideEffect(v)); void forEachKV(void sideEffect(K k, V v)) => foldLeftKV(null, (_, k, v) => sideEffect(k, v)); + + @override IMap mapWithIndex(B f(int i, V a)) => + _tree.foldLeft>>(tuple2(0, emptyMap()), (t, k, v) => t.apply((i, acc) => tuple2(i+1, acc.put(k, f(i, v))))).value2; // TODO: optimize + + @override IMap> zipWithIndex() => mapWithIndex(tuple2); + + @override bool all(bool f(V a)) => foldMap(BoolAndMi, f); // TODO: optimize + + @override bool any(bool f(V a)) => foldMap(BoolOrMi, f); // TODO: optimize + + @override V concatenate(Monoid mi) => foldMap(mi, id); // TODO: optimize + + @override Option concatenateO(Semigroup si) => foldMapO(si, id); // TODO: optimize + + @override B foldLeftWithIndex(B z, B f(B previous, int i, V a)) => + foldLeft>(tuple2(z, 0), (t, a) => tuple2(f(t.value1, t.value2, a), t.value2+1)).value1; // TODO: optimize + + @override Option foldMapO(Semigroup si, B f(V a)) => + foldMap(new OptionMonoid(si), composeF(some, f)); // TODO: optimize + + @override B foldRightWithIndex(B z, B f(int i, V a, B previous)) => + foldRight>(tuple2(z, length()-1), (a, t) => tuple2(f(t.value2, a, t.value1), t.value2-1)).value1; // TODO: optimize + + @override V intercalate(Monoid mi, V v) => + foldRight(none(), (V cv, Option ov) => some(mi.append(cv, ov.fold(mi.zero, mi.appendC(v))))) | mi.zero(); // TODO: optimize + + @override int length() => foldLeft(0, (a, b) => a+1); // TODO: optimize + + @override Option maximum(Order ov) => concatenateO(ov.maxSi()); + + @override Option minimum(Order ov) => concatenateO(ov.minSi()); + + @override IMap> strengthL(B b) => map((v) => tuple2(b, v)); + + @override IMap> strengthR(B b) => map((v) => tuple2(v, b)); } IMap imap(Map m) => new IMap.from(comparableOrder(), m); IMap imapWithOrder(Order o, Map m) => new IMap.from(o, m); -IMap emptyMap() => new IMap(comparableOrder(), _emptyIMapAVLNode()); +IMap emptyMap() => new IMap._internal(comparableOrder(), _emptyIMapAVLNode()); IMap singletonMap(K k, V v) => emptyMap().put(k, v); class IMapMonoid extends Monoid> { @@ -159,8 +190,8 @@ Monoid> imapMi() => imapMonoid(secondSemigro final Traversable IMapTr = new TraversableOpsTraversable(); -abstract class _IMapAVLNode extends FunctorOps<_IMapAVLNode, V> { - _IMapAVLNode(); +abstract class _IMapAVLNode implements FunctorOps<_IMapAVLNode, V> { + const _IMapAVLNode(); _IMapAVLNode insert(Order order, K k, V v); _IMapAVLNode remove(Order order, K k); @@ -184,6 +215,10 @@ abstract class _IMapAVLNode extends FunctorOps<_IMapAVLNode, V B cata(B z, B ifEmpty(B b), B ifNonEmpty(B b, K k, V v, B cataLeft(B b), B cataRight(B b))); _NonEmptyIMapAVLNode _unsafeGetNonEmpty(); + + @override _IMapAVLNode> strengthL(B b) => map((v) => tuple2(b, v)); + + @override _IMapAVLNode> strengthR(B b) => map((v) => tuple2(v, b)); } class _NonEmptyIMapAVLNode extends _IMapAVLNode { @@ -385,7 +420,7 @@ class _NonEmptyIMapAVLNode extends _IMapAVLNode { } class _EmptyIMapAVLNode extends _IMapAVLNode { - _EmptyIMapAVLNode(); + const _EmptyIMapAVLNode(); @override B foldLeft(B z, B f(B previous, K k, V v)) => z; diff --git a/lib/src/iset.dart b/lib/src/iset.dart index 47c5b12..081240c 100644 --- a/lib/src/iset.dart +++ b/lib/src/iset.dart @@ -1,6 +1,6 @@ part of dartz; -class ISet extends FoldableOps { +class ISet implements FoldableOps { final AVLTree _tree; ISet(this._tree); @@ -59,6 +59,33 @@ class ISet extends FoldableOps { Iterator iterator() => _tree.iterator(); void forEach(void sideEffect(A a)) => foldLeft(null, (_, a) => sideEffect(a)); + + + @override bool all(bool f(A a)) => foldMap(BoolAndMi, f); // TODO: optimize + + @override bool any(bool f(A a)) => foldMap(BoolOrMi, f); // TODO: optimize + + @override A concatenate(Monoid mi) => foldMap(mi, id); // TODO: optimize + + @override Option concatenateO(Semigroup si) => foldMapO(si, id); // TODO: optimize + + @override B foldLeftWithIndex(B z, B f(B previous, int i, A a)) => + foldLeft>(tuple2(z, 0), (t, a) => tuple2(f(t.value1, t.value2, a), t.value2+1)).value1; // TODO: optimize + + @override Option foldMapO(Semigroup si, B f(A a)) => + foldMap(new OptionMonoid(si), composeF(some, f)); // TODO: optimize + + @override B foldRightWithIndex(B z, B f(int i, A a, B previous)) => + foldRight>(tuple2(z, length()-1), (a, t) => tuple2(f(t.value2, a, t.value1), t.value2-1)).value1; // TODO: optimize + + @override A intercalate(Monoid mi, A a) => + foldRight(none(), (A ca, Option oa) => some(mi.append(ca, oa.fold(mi.zero, mi.appendC(a))))) | mi.zero(); // TODO: optimize + + @override int length() => foldLeft(0, (a, b) => a+1); // TODO: optimize + + @override Option maximum(Order oa) => concatenateO(oa.maxSi()); + + @override Option minimum(Order oa) => concatenateO(oa.minSi()); } final Foldable ISetFo = new FoldableOpsFoldable(); diff --git a/lib/src/ivector.dart b/lib/src/ivector.dart index dd18298..79e8d61 100644 --- a/lib/src/ivector.dart +++ b/lib/src/ivector.dart @@ -1,6 +1,6 @@ part of dartz; -class IVector extends TraversableOps with FunctorOps, ApplicativeOps, ApplicativePlusOps, MonadOps, MonadPlusOps, TraversableMonadOps, TraversableMonadPlusOps, PlusOps { +class IVector implements TraversableMonadPlusOps { final IMap _elementsByIndex; final int _offset; final int _length; @@ -31,7 +31,7 @@ class IVector extends TraversableOps with FunctorOps, IVector setIfPresent(int index, A a) => new IVector._internal(_elementsByIndex.setIfPresent(_offset+index, a), _offset, _length); - @override IVector pure(B b) => emptyVector().appendElement(b); + IVector pure(B b) => emptyVector().appendElement(b); @override IVector map(B f(A a)) => new IVector._internal(_elementsByIndex.map(f), _offset, _length); @@ -41,7 +41,7 @@ class IVector extends TraversableOps with FunctorOps, @override IVector flatMap(IVector f(A a)) => bind(f); - @override IVector empty() => emptyVector(); + IVector empty() => emptyVector(); @override IVector plus(IVector fa2) { final int l = length(); @@ -59,10 +59,6 @@ class IVector extends TraversableOps with FunctorOps, } } - @override G traverse(Applicative gApplicative, G f(A a)) => - _elementsByIndex.foldLeft(gApplicative.pure(emptyVector()), - (prev, a) => gApplicative.map2(prev, f(a), (IVector p, a2) => p.appendElement(a2))); - Option> traverseOption(Option f(A a)) => _elementsByIndex.foldLeft(some(emptyVector()), (prev, a) => prev.fold(none, (p) => f(a).fold(none, (b) => some(p.appendElement(b))))); @@ -127,6 +123,36 @@ class IVector extends TraversableOps with FunctorOps, Iterator iterator() => _elementsByIndex.valueIterator(); void forEach(void sideEffect(A a)) => foldLeft(null, (_, a) => sideEffect(a)); + + @override IVector> zipWithIndex() => mapWithIndex(tuple2); + + @override bool all(bool f(A a)) => foldMap(BoolAndMi, f); // TODO: optimize + + @override bool any(bool f(A a)) => foldMap(BoolOrMi, f); // TODO: optimize + + @override A concatenate(Monoid mi) => foldMap(mi, id); // TODO: optimize + + @override Option concatenateO(Semigroup si) => foldMapO(si, id); // TODO: optimize + + @override Option foldMapO(Semigroup si, B f(A a)) => + foldMap(new OptionMonoid(si), composeF(some, f)); // TODO: optimize + + @override A intercalate(Monoid mi, A a) => + foldRight(none(), (A ca, Option oa) => some(mi.append(ca, oa.fold(mi.zero, mi.appendC(a))))) | mi.zero(); // TODO: optimize + + @override Option maximum(Order oa) => concatenateO(oa.maxSi()); + + @override Option minimum(Order oa) => concatenateO(oa.minSi()); + + @override IVector andThen(IVector next) => bind((_) => next); + + @override IVector ap(IVector> ff) => ff.bind((f) => map(f)); // TODO: optimize + + @override IVector replace(B replacement) => map((_) => replacement); + + @override IVector> strengthL(B b) => map((a) => tuple2(b, a)); + + @override IVector> strengthR(B b) => map((a) => tuple2(a, b)); } IVector ivector(Iterable iterable) => new IVector.from(iterable); diff --git a/lib/src/list.dart b/lib/src/list.dart index c89dbfe..9394f19 100644 --- a/lib/src/list.dart +++ b/lib/src/list.dart @@ -10,6 +10,7 @@ class ListTraversableMonadPlus extends Traversable with Applicative, @override List empty() => []; @override List plus(List f1, List f2) => new List.from(f1)..addAll(f2); + /* @override G traverse(Applicative gApplicative, List fas, G f(_)) => fas.fold(gApplicative.pure([]), (previous, e) { return gApplicative.map2(previous, f(e), (a, b) { final r = new List.from(cast(a)); @@ -17,6 +18,9 @@ class ListTraversableMonadPlus extends Traversable with Applicative, return r; }); }); + */ + + @override B foldMap(Monoid bMonoid, List fa, B f(A a)) => fa.fold(bMonoid.zero(), (z, a) => bMonoid.append(z, f(a))); } class ListMonoid extends Monoid> { diff --git a/lib/src/monad.dart b/lib/src/monad.dart index 2f7dcae..c984b5a 100644 --- a/lib/src/monad.dart +++ b/lib/src/monad.dart @@ -8,9 +8,9 @@ abstract class Monad implements Applicative { @override F map(covariant F fa, B f(A a)) => bind(fa, (A a) => pure(f(a))); @override F ap(covariant F fa, covariant F ff) => bind(ff, (f(_)) => map(fa, f)); - Monad /** Monad>> **/ composeM(Monad G, Traversable GT) => new ComposedMonad(this, G, GT); + //Monad /** Monad>> **/ composeM(Monad G, Traversable GT) => new ComposedMonad(this, G, GT); } - +/* // Compose Monad> with Monad> and Traversable>, yielding Monad>> class ComposedMonad extends Functor with Applicative, Monad { final Monad _F; @@ -23,21 +23,15 @@ class ComposedMonad extends Functor with Applicative, Monad { @override F bind(F fga, F f(_)) => _F.bind(fga, (G ga) => _F.map(_GT.traverse(_F, ga, f), _G.join)); } - +*/ abstract class MonadOps implements ApplicativeOps { - F pure(B b); F bind(covariant F f(A a)); - @override F map(B f(A a)) => bind((a) => pure(f(a))); + @override F map(B f(A a));// => bind((a) => pure(f(a))); @override F ap(F ff) => cast>>(ff).bind((f) => map(f)); F flatMap(covariant F f(A a)) => bind(f); F andThen(covariant F next) => bind((_) => next); - F operator >>(covariant F next) => andThen(next); - F operator >=(covariant F f(A a)) => bind(f); - F operator <<(covariant F next) => bind((a) => cast(cast(next).map((_) => a))); F replace(B replacement) => map((_) => replacement); - F join() => bind((A a) => cast(a)); - F flatten() => join(); } class MonadOpsMonad extends Functor with Applicative, Monad { diff --git a/lib/src/monad_plus.dart b/lib/src/monad_plus.dart index a7d4127..fd08185 100644 --- a/lib/src/monad_plus.dart +++ b/lib/src/monad_plus.dart @@ -8,10 +8,8 @@ abstract class MonadPlus implements Functor, Applicative, Monad, App } abstract class MonadPlusOps implements MonadOps, ApplicativePlusOps { - F filter(bool predicate(A a)) => bind((t) => predicate(t) ? pure(t) : empty()); - F where(bool predicate(A a)) => filter(predicate); - - F unite(Foldable aFoldable) => bind((ga) => aFoldable.foldLeft(ga, empty(), (p, a) => cast>(p).plus(pure(a)))); + F filter(bool predicate(A a));// => bind((t) => predicate(t) ? pure(t) : empty()); + F where(bool predicate(A a));// => filter(predicate); } class MonadPlusOpsMonadPlus extends Functor with Applicative, ApplicativePlus, Monad, MonadPlus { diff --git a/lib/src/option.dart b/lib/src/option.dart index 06c650e..1cba6a1 100644 --- a/lib/src/option.dart +++ b/lib/src/option.dart @@ -1,6 +1,8 @@ part of dartz; -abstract class Option extends TraversableOps with FunctorOps, ApplicativeOps, ApplicativePlusOps, MonadOps, MonadPlusOps, TraversableMonadOps, TraversableMonadPlusOps, PlusOps { +abstract class Option implements TraversableMonadPlusOps { + const Option(); + B fold(B ifNone(), B ifSome(A a)); B cata(B ifNone(), B2 ifSome(A a)) => fold(ifNone, ifSome); @@ -10,14 +12,13 @@ abstract class Option extends TraversableOps with FunctorOps operator %(ifNone) => toEither(() => ifNone); A operator |(A dflt) => getOrElse(() => dflt); - @override Option pure(B b) => some(b); @override Option map(B f(A a)) => fold(none, (A a) => some(f(a))); @override Option ap(Option> ff) => fold(none, (A a) => ff.fold(none, (Function1 f) => some(f(a)))); @override Option bind(Option f(A a)) => fold(none, f); @override Option flatMap(Option f(A a)) => fold(none, f); @override Option andThen(Option next) => fold(none, (_) => next); - @override G traverse(Applicative gApplicative, G f(A a)) => fold(() => gApplicative.pure(none()), (a) => gApplicative.map(f(a), some)); + IList> traverseIList(IList f(A a)) => fold(() => cons(none(), nil()), (a) => f(a).map(some)); @@ -35,7 +36,6 @@ abstract class Option extends TraversableOps with FunctorOps> sequenceState(Option> osa) => osa.traverseState(id); - @override Option empty() => none(); @override Option plus(Option o2) => orElse(() => o2); @override Option filter(bool predicate(A a)) => fold(none, (a) => predicate(a) ? this : none()); @@ -73,15 +73,61 @@ abstract class Option extends TraversableOps with FunctorOps toIterable() => fold(() => cast(_emptyIterable), (a) => new _SingletonIterable(a)); + Iterable toIterable() => fold(() => const Iterable.empty(), (a) => new _SingletonIterable(a)); Iterator iterator() => toIterable().iterator; void forEach(void sideEffect(A a)) => fold(() => null, sideEffect); + + @override bool all(bool f(A a)) => map(f)|true; + + @override bool any(bool f(A a)) => map(f)|false; + + @override Option appendElement(A a) => orElse(() => some(a)); + + @override A concatenate(Monoid mi) => getOrElse(mi.zero); + + @override Option concatenateO(Semigroup si) => this; + + @override B foldLeft(B z, B f(B previous, A a)) => fold(() => z, (a) => f(z, a)); + + @override B foldLeftWithIndex(B z, B f(B previous, int i, A a)) => fold(() => z, (a) => f(z, 0, a)); + + @override B foldMap(Monoid bMonoid, B f(A a)) => fold(bMonoid.zero, f); + + @override Option foldMapO(Semigroup si, B f(A a)) => map(f); + + @override B foldRight(B z, B f(A a, B previous)) => fold(() => z, (a) => f(a, z)); + + @override B foldRightWithIndex(B z, B f(int i, A a, B previous))=> fold(() => z, (a) => f(0, a, z)); + + @override A intercalate(Monoid mi, A a) => fold(mi.zero, id); + + @override int length() => fold(() => 0, (_) => 1); + + @override Option mapWithIndex(B f(int i, A a)) => map((a) => f(0, a)); + + @override Option maximum(Order oa) => this; + + @override Option minimum(Order oa) => this; + + Tuple2, Option> partition(bool f(A a)) => map(f)|false ? tuple2(this, none()) : tuple2(none(), this); + + @override Option prependElement(A a) => some(a).orElse(() => this); + + @override Option replace(B replacement) => map((_) => replacement); + + Option reverse() => this; + + @override Option> strengthL(B b) => map((a) => tuple2(b, a)); + + @override Option> strengthR(B b) => map((a) => tuple2(a, b)); + + @override Option> zipWithIndex() => map((a) => tuple2(0, a)); } class Some extends Option { final A _a; - Some(this._a); + const Some(this._a); A get value => _a; @override B fold(B ifNone(), B ifSome(A a)) => ifSome(_a); @override bool operator ==(other) => other is Some && other._a == _a; @@ -89,6 +135,7 @@ class Some extends Option { } class None extends Option { + const None(); @override B fold(B ifNone(), B ifSome(A a)) => ifNone(); @override bool operator ==(other) => other is None; @override int get hashCode => 0; @@ -99,30 +146,20 @@ Option some(A a) => new Some(a); Option option(bool test, A value) => test ? some(value) : none(); Option optionOf(A value) => value != null ? some(value) : none(); -class OptionMonadPlus extends MonadPlusOpsMonadPlus fa, B f(A a)) => fa.map(f); @override Option ap(Option fa, Option> ff) => fa.ap(ff); @override Option bind(Option fa, Option f(A a)) => fa.bind(f); + @override Option empty() => none(); + @override Option plus(Option f1, Option f2) => f1.plus(f2); + @override Option pure(A a) => some(a); } -final OptionMonadPlus OptionMP = new OptionMonadPlus(); -MonadPlus> optionMP() => cast(OptionMP); -final Traversable() => cast(OptionTr); - -class OptionTMonad extends Functor with Applicative, Monad { - Monad _stackedM; - OptionTMonad(this._stackedM); - Monad underlying() => OptionMP; - - @override M pure(A a) => cast(_stackedM.pure(some(a))); - @override M bind(M moa, M f(A a)) => cast(_stackedM.bind(moa, (Option o) => o.fold(() => _stackedM.pure(none()), cast(f)))); +class OptionTraversable extends Traversable fa, B f(A a)) => fa.foldMap(bMonoid, f); + @override Option map(Option fa, B f(A a)) => fa.map(f); } -Monad optionTMonad(Monad mmonad) => new OptionTMonad(mmonad); - class OptionMonoid extends Monoid> { final Semigroup _tSemigroup; diff --git a/lib/src/order.dart b/lib/src/order.dart index 62defc1..d6b133a 100644 --- a/lib/src/order.dart +++ b/lib/src/order.dart @@ -48,12 +48,16 @@ Order order(OrderF f) => new _AnonymousOrder(f); Order orderBy(Order o, B by(A a)) => new _AnonymousOrder((A a1, A a2) => o.order(by(a1), by(a2))); class ComparableOrder extends Order { + final Type _tpe; + + ComparableOrder(): _tpe = A; + @override Ordering order(A a1, A a2) { final c = a1.compareTo(a2); return c < 0 ? Ordering.LT : (c > 0 ? Ordering.GT : Ordering.EQ); } - @override bool operator ==(Object other) => identical(this, other) || other is ComparableOrder && runtimeType == other.runtimeType; + @override bool operator ==(Object other) => identical(this, other) || other is ComparableOrder && _tpe == other._tpe; @override int get hashCode => 0; } diff --git a/lib/src/plus_empty.dart b/lib/src/plus_empty.dart index a2f0d07..0b92a43 100644 --- a/lib/src/plus_empty.dart +++ b/lib/src/plus_empty.dart @@ -4,9 +4,9 @@ part of dartz; // might seem pointless to separate from monoid in dart, but clarifies intent abstract class PlusEmpty implements Plus { - F empty(); + F empty(); // () => F[A] } abstract class PlusEmptyOps implements PlusOps { - F empty(); // () => F[A] + //F empty(); // () => F[A] } \ No newline at end of file diff --git a/lib/src/state.dart b/lib/src/state.dart index 7b73384..ef175f3 100644 --- a/lib/src/state.dart +++ b/lib/src/state.dart @@ -3,7 +3,7 @@ part of dartz; // Bind on plain State is *not* stack safe. Composition of StateT with stack safe monad, such as Trampoline, is. // Workaround for https://github.com/dart-lang/sdk/issues/29949 -class State extends FunctorOps*/, A> with ApplicativeOps*/, A>, MonadOps*/, A> { +class State implements MonadOps, A> { final Function1> _run; Tuple2 run(S s) => _run(s); A value(S s) => run(s).value1; @@ -11,7 +11,7 @@ class State extends FunctorOps*/, A> with ApplicativeO State(this._run); - @override State pure(B b) => new State((s) => new Tuple2(b, s)); + State pure(B b) => new State((s) => new Tuple2(b, s)); @override State map(B f(A a)) => new State((S s) => run(s).map1(f)); @override State bind(State f(A a)) => new State((S s) { final ran = run(s); @@ -19,7 +19,14 @@ class State extends FunctorOps*/, A> with ApplicativeO }); @override State flatMap(State f(A a)) => bind(f); @override State andThen(State next) => bind((_) => next); - @override State operator <<(State next) => bind((a) => next.map((_) => a)); + + @override State> strengthL(B b) => map((a) => tuple2(b, a)); + + @override State> strengthR(B b) => map((a) => tuple2(a, b)); + + @override State ap(State> ff) => ff.bind((f) => map(f)); // TODO: optimize + + @override State replace(B replacement) => map((_) => replacement); } class StateMonad extends Functor> with Applicative>, Monad> { @@ -37,7 +44,7 @@ final StateMonad StateM = new StateMonad(); StateMonad stateM() => new StateMonad(); // Workaround for https://github.com/dart-lang/sdk/issues/29949 -class StateT extends FunctorOps*/, A> with ApplicativeOps*/, A>, MonadOps*/, A> { +class StateT implements MonadOps, A> { final Monad _FM; final Function1 _run; @@ -47,15 +54,19 @@ class StateT extends FunctorOps*/, A> with Appl F value(S s) => _FM.map(_run(s), (t) => t.value1); F state(S s) => _FM.map(_run(s), (t) => t.value2); - @override StateT pure(B b) => new StateT(_FM, (S s) => _FM.pure(new Tuple2(b, s))); + StateT pure(B b) => new StateT(_FM, (S s) => _FM.pure(new Tuple2(b, s))); @override StateT map(B f(A a)) => new StateT(_FM, (S s) => _FM.map(_run(s), (Tuple2 t) => t.map1(f))); @override StateT bind(StateT f(A a)) => new StateT(_FM, (S s) => _FM.bind(_FM.pure(() => _run(s)), (F tt()) { return _FM.bind(tt(), (Tuple2 t) => f(t.value1)._run(t.value2)); })); @override StateT flatMap(StateT f(A a)) => bind(f); @override StateT andThen(StateT next) => bind((_) => next); - @override StateT operator <<(StateT next) => bind((a) => next.map((_) => a)); @override StateT replace(B b) => map((_) => b); + @override StateT ap(StateT> ff) => ff.bind((f) => map(f)); // TODO: optimize + + @override StateT> strengthL(B b) => map((a) => tuple2(b, a)); + + @override StateT> strengthR(B b) => map((a) => tuple2(a, b)); } class StateTMonad extends Functor> with Applicative>, Monad> { diff --git a/lib/src/streaming/conveyor.dart b/lib/src/streaming/conveyor.dart index 2f4dd1b..09c6879 100644 --- a/lib/src/streaming/conveyor.dart +++ b/lib/src/streaming/conveyor.dart @@ -9,9 +9,9 @@ typedef Conveyor SinkF(O o); typedef Conveyor ChannelF(I i); // Workaround for https://github.com/dart-lang/sdk/issues/29949 -abstract class Conveyor extends FunctorOps*/, O> with ApplicativeOps*/, O>, ApplicativePlusOps*/, O>, MonadOps*/, O>, MonadPlusOps*/, O>, PlusOps*/, O> { +abstract class Conveyor implements MonadPlusOps, O> { - A interpret(A ifProduce(O head, Conveyor tail), A ifConsume(F req, Function1, Conveyor> recv), A ifHalt(Object err)); + A interpret(A ifProduce(O head, Conveyor tail), covariant A ifConsume(F req, Function1, Conveyor> recv), A ifHalt(Object err)); static Conveyor produce(O head, [Conveyor tail]) => new _Produce(head, tail ?? halt(End)); static Conveyor consume(F req, Function1, Conveyor> recv) => new _Consume(req, recv); @@ -86,6 +86,22 @@ abstract class Conveyor extends FunctorOps*/, O> wi return cast(go(this, nil())); } + static Task> runLogTask(Conveyor cto) { + Task> go(Conveyor cur, IList acc) => + cur.interpret((h, t) => go(t, cons(h, acc)), + (req, recv) => req.attempt().bind((Either e) => go(Try(() => recv(e)), acc)), + (err) => err == End ? new Task(() => new Future.value(acc.reverse())) : new Task(() => new Future.error(err))); + return cast(go(cto, nil())); + } + + static Free> runLogIO(Conveyor, O> cio) { + Free> go(Conveyor, O> cur, IList acc) => + cur.interpret((h, t) => go(t, cons(h, acc)), + (req, recv) => liftF>>(new Attempt(req)).flatMap((Either e) => go(Try(() => recv(e)), acc)), + (err) => err == End ? new Pure(acc.reverse()) : liftF(new Fail(err))); + return cast(go(cio, nil())); + } + Conveyor drain() => interpret((h, t) => t.drain(), (req, recv) => consume(req, (ea) => recv(ea).drain()), @@ -160,7 +176,7 @@ abstract class Conveyor extends FunctorOps*/, O> wi Conveyor tee(Conveyor c2, Conveyor, O3> t) => t.interpret>( (h, t) => produce(h, tee(c2, t)) - ,(side, recv) => side == Tee._getL + ,(side, recv) => side.direction == BothDirection.LEFT ? interpret( (o, ot) => ot.tee(c2, Try(() => recv(right(o)))) ,(reqL, recvL) => consume(reqL, (ea) => recvL(ea).tee(c2, t)) @@ -177,11 +193,21 @@ abstract class Conveyor extends FunctorOps*/, O> wi Conveyor interleave(Conveyor c2) => tee(c2, Tee.interleave()); - Conveyor to(Conveyor> sink) => zipWith(sink, (o, f) => f(o)).flatMap(cast(id)); + Conveyor to(Conveyor> sink) => zipWith(sink, (o, f) => f(o)).flatMap((a) => a as Conveyor); Conveyor through(Conveyor> channel) => zipWith(channel, (o, f) => f(o)).flatMap(cast(id)); Conveyor onto(Conveyor f(Conveyor c)) => f(this); + + @override Conveyor> strengthL(B b) => map((a) => tuple2(b, a)); + + @override Conveyor> strengthR(B b) => map((a) => tuple2(a, b)); + + @override Conveyor andThen(Conveyor next) => bind((_) => next); + + @override Conveyor ap(Conveyor> ff) => ff.bind((f) => map(f)); // TODO: optimize + + @override Conveyor replace(B replacement) => map((_) => replacement); } class _Produce extends Conveyor { @@ -197,7 +223,7 @@ class _Consume extends Conveyor { final F /** really F **/ _req; final Function1, Conveyor> _recv; _Consume(this._req, this._recv); - A2 interpret(A2 ifProduce(O head, Conveyor tail), A2 ifConsume(F req, Function1, Conveyor> recv), A2 ifHalt(Object err)) => ifConsume(_req, cast(_recv)); + A2 interpret(A2 ifProduce(O head, Conveyor tail), A2 ifConsume(F req, Function1, Conveyor> recv), A2 ifHalt(Object err)) => ifConsume(_req, (ea) => _recv(ea.map((a) => a as A))); @override String toString() => "Consume($_req, $_recv)"; } diff --git a/lib/src/streaming/pipe.dart b/lib/src/streaming/pipe.dart index 2f46138..f478f5d 100644 --- a/lib/src/streaming/pipe.dart +++ b/lib/src/streaming/pipe.dart @@ -3,13 +3,13 @@ part of dartz_streaming; class From {} class Pipe { - static final From _get = new From(); + //static final From _get = new From(); static Conveyor, O> produce(O h, [Conveyor, O> t]) => Conveyor.produce(h, t); static Conveyor, O> consume(Function1, O>> recv, [Function0, O>> fallback]) => - Conveyor.consume(cast(_get), (Either ea) => ea.fold( + Conveyor.consume(new From(), (Either ea) => ea.fold( (err) => err == Conveyor.End ? (fallback == null ? halt() : fallback()) : Conveyor.halt(err) ,(I i) => Conveyor.Try(() => recv(i)))); @@ -74,7 +74,7 @@ class Pipe { } static Conveyor, A> skipDuplicates([Eq _eq]) { - final Eq eq = _eq ?? cast(ObjectEq); + final Eq eq = _eq ?? objectEq(); Conveyor, A> loop(A lastA) => consume((A a) => eq.eq(lastA, a) ? loop(lastA) : produce(a, loop(a))); return consume((A a) => produce(a, loop(a))); } diff --git a/lib/src/streaming/tee.dart b/lib/src/streaming/tee.dart index 05e0aee..7c76d6d 100644 --- a/lib/src/streaming/tee.dart +++ b/lib/src/streaming/tee.dart @@ -1,20 +1,28 @@ part of dartz_streaming; -class Both {} +enum BothDirection { + LEFT, + RIGHT +} + +class Both { + final BothDirection direction; + const Both(this.direction); +} class Tee { - static final Both _getL = new Both(); - static final Both _getR = new Both(); + //static final Both _getL = new Both(); + //static final Both _getR = new Both(); static Conveyor, O> produce(O h, [Conveyor, O> t]) => Conveyor.produce(h, t); static Conveyor, O> consumeL(Function1, O>> recv, [Function0, O>> fallback]) => - Conveyor.consume(cast(_getL), (Either ea) => ea.fold( + Conveyor.consume(new Both(BothDirection.LEFT), (Either ea) => ea.fold( (err) => err == Conveyor.End ? (fallback == null ? halt() : fallback()) : Conveyor.halt(err) ,(L l) => Conveyor.Try(() => recv(l)))); static Conveyor, O> consumeR(Function1, O>> recv, [Function0, O>> fallback]) => - Conveyor.consume(cast(_getR), (Either ea) => ea.fold( + Conveyor.consume(new Both(BothDirection.RIGHT), (Either ea) => ea.fold( (err) => err == Conveyor.End ? (fallback == null ? halt() : fallback()) : Conveyor.halt(err) ,(R r) => Conveyor.Try(() => recv(r)))); diff --git a/lib/src/task.dart b/lib/src/task.dart index ab662bc..e3ef3f0 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -1,6 +1,6 @@ part of dartz; -class Task extends FunctorOps with ApplicativeOps, MonadOps, MonadCatchOps { +class Task implements MonadCatchOps { final Function0> _run; Task(this._run); @@ -11,11 +11,25 @@ class Task extends FunctorOps with ApplicativeOps, MonadOps @override Task bind(Task f(A a)) => new Task(() => _run().then((a) => f(a).run())); - @override Task pure(B b) => new Task(() => new Future.value(b)); + Task pure(B b) => new Task(() => new Future.value(b)); @override Task> attempt() => new Task(() => run().then(right).catchError(left)); @override Task fail(Object err) => new Task(() => new Future.error(err)); + + @override Task map(B f(A a)) => new Task(() => _run().then(f)); + + @override Task> strengthL(B b) => map((a) => tuple2(b, a)); + + @override Task> strengthR(B b) => map((a) => tuple2(a, b)); + + @override Task andThen(Task next) => bind((_) => next); + + @override Task ap(Task> ff) => ff.bind((f) => map(f)); // TODO: optimize + + @override Task flatMap(Task f(A a)) => new Task(() => _run().then((a) => f(a).run())); + + @override Task replace(B replacement) => map((_) => replacement); } class TaskMonadCatch extends Functor with Applicative, Monad, MonadCatch { diff --git a/lib/src/trampoline.dart b/lib/src/trampoline.dart index 4500335..add01e4 100644 --- a/lib/src/trampoline.dart +++ b/lib/src/trampoline.dart @@ -2,11 +2,15 @@ part of dartz; // TODO: unify with Free? -abstract class Trampoline extends FunctorOps with ApplicativeOps, MonadOps { - @override Trampoline pure(B b) => new _TPure(b); +abstract class Trampoline implements MonadOps { + Trampoline pure(B b) => new _TPure(b); @override Trampoline map(B f(A a)) => bind((a) => pure(f(a))); @override Trampoline bind(Trampoline f(A a)) => new _TBind(this, f); + @override Trampoline> strengthL(B b) => map((a) => tuple2(b, a)); + + @override Trampoline> strengthR(B b) => map((a) => tuple2(a, b)); + A run() { var current = this; while(current is _TBind) { @@ -22,6 +26,14 @@ abstract class Trampoline extends FunctorOps with ApplicativeO } return cast(cast<_TPure>(current)._a); } + + @override Trampoline andThen(Trampoline next) => bind((_) => next); + + @override Trampoline ap(Trampoline> ff) => ff.bind((f) => map(f)); // TODO: optimize + + @override Trampoline flatMap(Trampoline f(A a)) => new _TBind(this, f); + + @override Trampoline replace(B replacement) => map((_) => replacement); } class _TPure extends Trampoline { diff --git a/lib/src/traversable.dart b/lib/src/traversable.dart index b1234c9..234b7b5 100644 --- a/lib/src/traversable.dart +++ b/lib/src/traversable.dart @@ -2,15 +2,15 @@ part of dartz; abstract class Traversable extends Functor with Foldable { //def traverseImpl[G[_]:Applicative,A,B](fa: F[A])(f: A => G[B]): G[F[B]] - G traverse(Applicative gApplicative, F fa, G f(a)); + //G traverse(Applicative gApplicative, F fa, G f(a)); - G traverse_(Applicative gApplicative, F fa, G f(a)) => gApplicative.map(traverse(gApplicative, fa, f), constF(unit)); + //G traverse_(Applicative gApplicative, F fa, G f(a)) => gApplicative.map(traverse(gApplicative, fa, f), constF(unit)); - G sequence(Applicative gApplicative, F fa) => traverse(gApplicative, fa, cast(id)); + //G sequence(Applicative gApplicative, F fa) => traverse(gApplicative, fa, cast(id)); - G sequence_(Applicative gApplicative, F fa) => traverse_(gApplicative, fa, cast(id)); + //G sequence_(Applicative gApplicative, F fa) => traverse_(gApplicative, fa, cast(id)); - F mapWithIndex(F fa, B f(int i, a)) { + /*F mapWithIndex(F fa, B f(int i, a)) { final M = stateM(); return cast(traverse>(M, fa, (e) => M.get().bind((i) => M.put(i + 1).map((_) => f(i, e)))).value(0)); } @@ -22,38 +22,39 @@ abstract class Traversable extends Functor with Foldable { // def foldMap[A, B](bMonoid: Monoid[B], fa: F[A], f: A => B): B @override B foldMap(Monoid bMonoid, F fa, B f(A a)) => cast(traverse(StateM, fa, (a) => StateM.modify(cast((previous) => bMonoid.append(cast(previous), f(cast(a)))))).state(bMonoid.zero())); +*/ } abstract class TraversableOps extends FunctorOps with FoldableOps { - G traverse(Applicative gApplicative, G f(A a)); + //G traverse(Applicative gApplicative, G f(A a)); - G traverse_(Applicative gApplicative, G f(A a)) => gApplicative.map(traverse(gApplicative, f), constF(unit)); + //G traverse_(Applicative gApplicative, G f(A a)) => gApplicative.map(traverse(gApplicative, f), constF(unit)); - G sequence(Applicative gApplicative) => traverse(gApplicative, cast(id)); + //G sequence(Applicative gApplicative) => traverse(gApplicative, cast(id)); - G sequence_(Applicative gApplicative) => traverse_(gApplicative, cast(id)); + //G sequence_(Applicative gApplicative) => traverse_(gApplicative, cast(id)); - F mapWithIndex(B f(int i, A a)) { + F mapWithIndex(B f(int i, A a)); /* { final M = stateM(); - return cast(traverse>(M, (e) => M.get().bind((i) => M.put(i + 1).map((_) => f(i, e)))).value(0)); - } + return cast(traverse>(M, (e) => M.get().bind((i) => M.put(i + 1).replace(f(i, e)))).value(0)); + }*/ - F zipWithIndex() => mapWithIndex(tuple2); + F zipWithIndex();// => mapWithIndex(tuple2); - @override F map(B f(A a)) => cast(traverse(IdM, f)); + @override F map(B f(A a));// => cast(traverse(IdM, f)); - @override B foldMap(Monoid bMonoid, B f(A a)) => - cast(traverse(StateM, (a) => StateM.modify(cast((previous) => bMonoid.append(cast(previous), f(a))))).state(bMonoid.zero())); + @override B foldMap(Monoid bMonoid, B f(A a));/* => + cast(traverse(StateM, (a) => StateM.modify(cast((previous) => bMonoid.append(cast(previous), f(a))))).state(bMonoid.zero())); */ } class TraversableOpsTraversable extends Traversable { - @override G traverse(Applicative gApplicative, F fa, G f(a)) => fa.traverse(gApplicative, f); + //@override G traverse(Applicative gApplicative, F fa, G f(a));// => fa.traverse(gApplicative, f); @override B foldRight(F fa, B z, B f(A a, B previous)) => fa.foldRight(z, cast(f)); @override B foldMap(Monoid bMonoid, F fa, B f(A a)) => fa.foldMap(bMonoid, cast(f)); @override F map(F fa, B f(A a)) => cast(fa.map(cast(f))); - @override G sequence_(Applicative gApplicative, F fa) => fa.sequence_(gApplicative); - @override G sequence(Applicative gApplicative, F fa) => fa.sequence(gApplicative); - @override G traverse_(Applicative gApplicative, F fa, G f(a)) => fa.traverse_(gApplicative, f); + //@override G sequence_(Applicative gApplicative, F fa) => fa.sequence_(gApplicative); + //@override G sequence(Applicative gApplicative, F fa) => fa.sequence(gApplicative); + //@override G traverse_(Applicative gApplicative, F fa, G f(a)) => fa.traverse_(gApplicative, f); @override Option concatenateO(Semigroup si, F fa) => cast(fa.concatenateO(si)); @override A concatenate(Monoid mi, F fa) => cast(fa.concatenate(mi)); @override Option foldMapO(Semigroup si, F fa, B f(A a)) => fa.foldMapO(si, cast(f)); diff --git a/lib/src/traversable_monad.dart b/lib/src/traversable_monad.dart index ff48dfc..7e4400d 100644 --- a/lib/src/traversable_monad.dart +++ b/lib/src/traversable_monad.dart @@ -1,9 +1,9 @@ part of dartz; abstract class TraversableMonad implements Traversable, Monad { - G traverseM(Applicative gApplicative, F fa, G /** really G> **/ f(a)) => gApplicative.map(traverse(gApplicative, fa, f), (F ffb) => join(ffb)); + //G traverseM(Applicative gApplicative, F fa, G /** really G> **/ f(a)) => gApplicative.map(traverse(gApplicative, fa, f), (F ffb) => join(ffb)); } abstract class TraversableMonadOps implements TraversableOps, MonadOps { - G traverseM(Applicative gApplicative, G /** really G> **/ f(A a)) => gApplicative.map(traverse(gApplicative, f), (F ffb) => cast(ffb).join()); + //G traverseM(Applicative gApplicative, G /** really G> **/ f(A a)) => gApplicative.map(traverse(gApplicative, f), (F ffb) => cast(ffb).join()); } diff --git a/lib/src/traversable_monad_plus.dart b/lib/src/traversable_monad_plus.dart index d4417c4..7674032 100644 --- a/lib/src/traversable_monad_plus.dart +++ b/lib/src/traversable_monad_plus.dart @@ -12,6 +12,7 @@ abstract class TraversableMonadPlus implements Traversable, MonadPlus { } abstract class TraversableMonadPlusOps implements TraversableOps, MonadPlusOps { + /* // TODO: Only requires ApplicativePlus, not MonadPlus Tuple2 partition(bool f(A a)) => foldRight(tuple2(empty(), empty()), (A a, acc) => f(a) @@ -20,4 +21,5 @@ abstract class TraversableMonadPlusOps implements TraversableOps, Mo // TODO: Only requires Foldable and ApplicativePlus F reverse() => foldLeft(empty(), (r, e) => cast>(r).prependElement(e)); + */ } diff --git a/lib/src/unsafe/io.dart b/lib/src/unsafe/io.dart index 1b5338e..c7362a7 100644 --- a/lib/src/unsafe/io.dart +++ b/lib/src/unsafe/io.dart @@ -40,7 +40,7 @@ Future unsafeIOInterpreter(IOOp io) { return new Future.delayed(io.duration, () => unsafePerformIO(io.a)); } else if (io is Gather) { - return io.ops.traverse(FutureM, unsafePerformIO); + return io.ops.traverseFuture(unsafePerformIO); } else { throw new UnimplementedError("Unimplemented IO op: $io"); @@ -49,4 +49,4 @@ Future unsafeIOInterpreter(IOOp io) { Future unsafePerformIO(Free io) => io.foldMap(FutureM, unsafeIOInterpreter); -Future>> unsafeConveyIO(Conveyor, A> conveyor) => unsafePerformIO(IOM.attempt(conveyor.runLog(IOM))); +Future>> unsafeConveyIO(Conveyor, A> conveyor) => unsafePerformIO(IOM.attempt(Conveyor.runLogIO(conveyor))); diff --git a/test/evaluation_test.dart b/test/evaluation_test.dart index ea09b16..15e8a01 100644 --- a/test/evaluation_test.dart +++ b/test/evaluation_test.dart @@ -8,20 +8,19 @@ void main() { //final qc = new QuickCheck(maxSize: 300, seed: 42); test("demo", () async { - final EvaluationMonad M = new EvaluationMonad(tuple2Monoid(IListMi, StringMi)); + final EvaluationMonad, String>, int> M = new EvaluationMonad(tuple2Monoid(ilistMi(), StringMi)); - final Evaluation inc = - M.get() >= ((oldState) { + final inc = + M.get().bind((oldState) { final newState = oldState+1; - return M.put(newState) >> M.write(new Tuple2(IListMP.pure("State transition from $oldState to $newState"), "!")); + return M.put(newState).andThen(M.write(new Tuple2(ilist(["State transition from $oldState to $newState"]), "!"))); }); final Evaluation p = - inc >> - (M.pure("hej") >= (v) => - inc >> - M.get() >= ((s) => (s == 7) ? M.asks((suffix) => v + cast(suffix)) : M.raiseError("Gaah! State wasn't 7!!!")) - ) << inc; + inc.andThen( + M.pure("hej").bind((v) => + inc.andThen(M.get().bind((s) => (s == 7) ? M.asks((suffix) => v + suffix) : M.raiseError("Gaah! State wasn't 7!!!")))) + ).bind((a) => inc.replace(a)); expect(await p.run("!", 5), right(tuple3(tuple2(ilist(["State transition from 5 to 6", "State transition from 6 to 7", "State transition from 7 to 8"]), "!!!"), 8, "hej!"))); expect(await p.run("!", 6), left("Gaah! State wasn't 7!!!")); diff --git a/test/ilist_test.dart b/test/ilist_test.dart index 6ccb455..6efe207 100644 --- a/test/ilist_test.dart +++ b/test/ilist_test.dart @@ -67,7 +67,7 @@ void main() { test('bind', () { qc.check(forall(intILists, - (l) => l.bind((i) => ilist([i, i])) == l.map((i) => ilist([i, i])).join())); + (IList l) => l.bind((i) => ilist([i, i])) == IList.flattenIList(l.map((i) => ilist([i, i]))))); }); test('traverse', () { diff --git a/test/laws.dart b/test/laws.dart index 40dcc96..dbac009 100644 --- a/test/laws.dart +++ b/test/laws.dart @@ -47,7 +47,7 @@ void checkFoldableLaws(Foldable F, Enumeration enumeration, {bool equality(a, b) }); } -void checkFoldableOpsProperties(Enumeration enumeration, {bool equality(a, b): defaultEquality, QuickCheck qc: null}) { +void checkFoldableOpsProperties(Enumeration enumeration, {bool equality(a, b): defaultEquality, QuickCheck qc: null}) { qc = qc != null ? qc : defaultQC; group("foldable ops properties", () { @@ -68,11 +68,11 @@ void checkTraversableLaws(Traversable T, Enumeration enumeration, {bool equality group("traversable laws", () { test("identity traverse", () { - qc.check(forall(enumeration, (fa) => equality(T.traverse(IdM, fa, giveDollar), T.map(fa, giveDollar)))); + //qc.check(forall(enumeration, (fa) => equality(T.traverse(IdM, fa, giveDollar), T.map(fa, giveDollar)))); }); test("purity", () { - qc.check(forall(enumeration, (fa) => equality(T.traverse(IdM, fa, id), fa))); + //qc.check(forall(enumeration, (fa) => equality(T.traverse(IdM, fa, id), fa))); }); // TODO: check naturality @@ -111,7 +111,7 @@ void checkFoldableMonadLaws(Foldable F, Monad M, {bool equality(a, b): defaultEq group("foldable+monad laws", () { test("pure+concatenate identity", () { - qc.check(forall(c.ints, (a) => equality(F.concatenate(NumSumMi, M.pure(a)), a))); + qc.check(forall(c.ints, (int a) => equality(F.concatenate(IntSumMi, M.pure(a)), a))); }); }); diff --git a/test/option_test.dart b/test/option_test.dart index cac617b..02b2d60 100644 --- a/test/option_test.dart +++ b/test/option_test.dart @@ -36,19 +36,19 @@ void main() { expect(Option.sequenceIList(IList.sequenceOption(l2)), ilist([none()])); }); - group("OptionM", () => checkMonadLaws(OptionMP)); + group("OptionM", () => checkMonadLaws(new OptionMonadPlus())); - group("OptionTMonad+Id", () => checkMonadLaws(optionTMonad(IdM))); + //group("OptionTMonad+Id", () => checkMonadLaws(optionTMonad(IdM))); //group("OptionTMonad+IList", () => checkMonadLaws(optionTMonad(IListMP))); - group("OptionM+Foldable", () => checkFoldableMonadLaws(OptionTr, OptionMP)); + group("OptionM+Foldable", () => checkFoldableMonadLaws(new OptionTraversable(), new OptionMonadPlus())); group("OptionMi", () => checkMonoidLaws(new OptionMonoid(NumSumMi), c.ints.map(some))); final intOptions = c.ints.map((i) => i%2==0 ? some(i) : none()); - group("OptionTr", () => checkTraversableLaws(OptionTr, intOptions)); + group("OptionTr", () => checkTraversableLaws(new OptionTraversable(), intOptions)); group("Option FoldableOps", () => checkFoldableOpsProperties(intOptions)); diff --git a/test/state_test.dart b/test/state_test.dart index 3e182f9..fea2415 100644 --- a/test/state_test.dart +++ b/test/state_test.dart @@ -7,7 +7,7 @@ void main() { final M = stateM(); final State st = M.pure("hej"); final State inc = M.modify((n) => n+1); - expect((inc >> inc >> st).map((v) => "$v!").run(5), tuple2("hej!", 7)); + expect((inc.andThen(inc).andThen(st)).map((v) => "$v!").run(5), tuple2("hej!", 7)); }); group("StateM", () => checkMonadLaws(StateM, equality: (a, b) => a.run(0) == b.run(0))); From e3d39929cb443606984c956f23abbc164c6c4840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Sperber?= Date: Sun, 23 Sep 2018 17:13:36 +0200 Subject: [PATCH 2/8] WIP: Furious experimentations trying to get Conveyor working again + performance --- example/free_io/mock_io.dart | 4 ++-- example/streaming_io/example.dart | 15 +++++++++++++-- lib/src/applicative.dart | 9 +++++---- lib/src/either.dart | 10 +++++----- lib/src/foldable.dart | 8 ++++---- lib/src/free.dart | 9 +++++++-- lib/src/imap.dart | 4 ++-- lib/src/io.dart | 3 +++ lib/src/streaming/conveyor.dart | 17 ++++++++--------- lib/src/streaming/source.dart | 10 +++++----- lib/src/unsafe/io.dart | 5 +++-- test/either_test.dart | 2 +- 12 files changed, 58 insertions(+), 38 deletions(-) diff --git a/example/free_io/mock_io.dart b/example/free_io/mock_io.dart index a0e8b48..247645d 100644 --- a/example/free_io/mock_io.dart +++ b/example/free_io/mock_io.dart @@ -68,5 +68,5 @@ Evaluation>, IVector, IMap, IMap, A>>> mockPerformIO(Free io, IMap> input) => _interpret(io).run(input, emptyMap()); -Future, IMap, A>>> mockConveyIO(Conveyor, A> cio, IMap> input) => - _interpret(cio.runLog>(iomc())).run(input, emptyMap()); \ No newline at end of file +Future, IMap, IList>>> mockConveyIO(Conveyor, A> cio, IMap> input) => + _interpret(Conveyor.runLogIO(cio)).run(input, emptyMap()); \ No newline at end of file diff --git a/example/streaming_io/example.dart b/example/streaming_io/example.dart index 14feb8c..d944379 100644 --- a/example/streaming_io/example.dart +++ b/example/streaming_io/example.dart @@ -1,13 +1,15 @@ library streaming_io_example; +import 'dart:async'; + import 'package:dartz/dartz_streaming.dart'; import 'package:dartz/dartz_unsafe.dart'; import 'dart:io'; -main() async { +amain() async { - final pathToThisFile = Platform.script.toFilePath(); + final pathToThisFile = "/Users/bjorns/Documents/Development/github/spebbe/dartz/example/streaming_io/example.dart";// Platform.script.toFilePath(); // Construct Conveyor that: // 1. Reads lines from this file @@ -26,6 +28,8 @@ main() async { // since the Conveyor finishes as soon as the fifth comment is consumed. // The file will be closed as soon as the Conveyor finishes. + print("Here we go!"); + await unsafeConveyIO(firstFiveCommentsInThisFile.to(IO.stdoutWriter)); // Conveyors are pure values, and can safely be reused: @@ -34,3 +38,10 @@ main() async { // ...and composed further: await unsafeConveyIO(firstFiveCommentsInThisFile.map((s) => s.toUpperCase()).to(IO.stdoutWriter)); } + +void main() { + print("Starting!"); + amain(); + //sleep(new Duration(seconds: 1)); + new Future(() => print("Bye!")); +} diff --git a/lib/src/applicative.dart b/lib/src/applicative.dart index 9445e3b..2d4b6b5 100644 --- a/lib/src/applicative.dart +++ b/lib/src/applicative.dart @@ -38,7 +38,7 @@ abstract class Applicative implements Functor { */ // Workaround: Dumbing down types in generic liftX to give subclasses a chance to do proper typing... // OMG, it just got worse... not much left of the types since 2.0.0-dev.32.0 :-( - +/* Function lift(B f(A a)) => cast((F fa) => map(fa, f)); Function lift2(C f(A a, B b)) => (F fa, F fb) => ap(fb, map(fa, curry2(f))); Function lift3(D f(A a, B b, C c)) => (F fa, F fb, F fc) => ap(fc, ap(fb, map(fa, curry3(f)))); @@ -51,11 +51,12 @@ abstract class Applicative implements Functor { F map4(covariant F fa, covariant F fb, covariant F fc, covariant F fd, E f(A a, B b, C c, D d)) => cast(lift4(f)(fa, fb, fc, fd)); F map5(covariant F fa, covariant F fb, covariant F fc, covariant F fd, covariant F fe, EFF f(A a, B b, C c, D d, E e)) => cast(lift5(f)(fa, fb, fc, fd, fe)); F map6(covariant F fa, covariant F fb, covariant F fc, covariant F fd, covariant F fe, covariant F ff, G f(A a, B b, C c, D d, E e, EFF fff)) => cast(lift6(f)(fa, fb, fc, fd, fe, ff)); - - Applicative /** Applicative>> **/ composeA(Applicative G) => new ComposedApplicative(this, G); +*/ + //Applicative /** Applicative>> **/ composeA(Applicative G) => new ComposedApplicative(this, G); } // Compose Applicative> with Applicative>, yielding Applicative>> +/* class ComposedApplicative extends Functor with Applicative { final Applicative _F; final Applicative _G; @@ -68,7 +69,7 @@ class ComposedApplicative extends Functor with Applicative { @override F map(F fga, B f(A _)) => _F.map(fga, (G ga) => _G.map(ga, f)); } - +*/ abstract class ApplicativeOps implements FunctorOps { // F pure(B b); F ap(covariant F ff); diff --git a/lib/src/either.dart b/lib/src/either.dart index fb86484..a705f17 100644 --- a/lib/src/either.dart +++ b/lib/src/either.dart @@ -65,7 +65,7 @@ abstract class Either implements TraversableMonadOps, R // PURISTS BEWARE: side effecty stuff below -- proceed with caution! - Iterable toIterable() => fold((_) => cast(_emptyIterable), (r) => new _SingletonIterable(r)); + Iterable toIterable() => fold((_) => const Iterable.empty(), (r) => new _SingletonIterable(r)); Iterator iterator() => toIterable().iterator; void forEach(void sideEffect(R r)) => fold((_) => null, sideEffect); @@ -151,10 +151,10 @@ class EitherMonad extends MonadOpsMonad> { } final EitherMonad EitherM = new EitherMonad(); -EitherMonad eitherM() => cast(EitherM); +EitherMonad eitherM() => new EitherMonad(); final Traversable EitherTr = new TraversableOpsTraversable(); -Traversable> eitherTr() => cast(EitherTr); - +Traversable> eitherTr() => new TraversableOpsTraversable(); +/* class EitherTMonad extends Functor with Applicative, Monad { Monad _stackedM; EitherTMonad(this._stackedM); @@ -165,4 +165,4 @@ class EitherTMonad extends Functor with Applicative, Monad { } Monad eitherTMonad(Monad mmonad) => new EitherTMonad(mmonad); - +*/ \ No newline at end of file diff --git a/lib/src/foldable.dart b/lib/src/foldable.dart index 4ab7ba6..292e74a 100644 --- a/lib/src/foldable.dart +++ b/lib/src/foldable.dart @@ -30,13 +30,13 @@ abstract class Foldable { A intercalate(Monoid mi, F fa, A a) => foldRight>(fa, none(), (A ca, oa) => some(mi.append(ca, oa.fold(mi.zero, mi.appendC(a))))) | mi.zero(); - G collapse(ApplicativePlus ap, F fa) => foldLeft(fa, ap.empty(), (p, a) => ap.plus(p, ap.pure(a))); + //G collapse(ApplicativePlus ap, F fa) => foldLeft(fa, ap.empty(), (p, a) => ap.plus(p, ap.pure(a))); - G foldLeftM(Monad m, F fa, B z, G f(B previous, A a)) => foldRight>(fa, m.pure, (a, b) => (w) => m.bind(f(w, a), b))(z); + //G foldLeftM(Monad m, F fa, B z, G f(B previous, A a)) => foldRight>(fa, m.pure, (a, b) => (w) => m.bind(f(w, a), b))(z); - G foldRightM(Monad m, F fa, B z, G f(A a, B previous)) => foldLeft>(fa, m.pure, (b, a) => (w) => m.bind(f(a, w), b))(z); + //G foldRightM(Monad m, F fa, B z, G f(A a, B previous)) => foldLeft>(fa, m.pure, (b, a) => (w) => m.bind(f(a, w), b))(z); - G foldMapM(Monad m, Monoid bMonoid, F fa, G f(A a)) => foldMap(monoid(() => m.pure(bMonoid.zero()), cast(m.lift2(bMonoid.append))), fa, f); + //G foldMapM(Monad m, Monoid bMonoid, F fa, G f(A a)) => foldMap(monoid(() => m.pure(bMonoid.zero()), cast(m.lift2(bMonoid.append))), fa, f); } abstract class FoldableOps { diff --git a/lib/src/free.dart b/lib/src/free.dart index d9f7421..5c66b9d 100644 --- a/lib/src/free.dart +++ b/lib/src/free.dart @@ -12,7 +12,7 @@ abstract class Free implements MonadOps, A> { @override Free replace(B replacement) => map((_) => replacement); R fold(R ifPure(A a), R ifSuspend(F fa), R ifBind(Free ffb, _FreeF f)); - +/* Free step() { Free current = this; while(current is Bind) { @@ -24,11 +24,16 @@ abstract class Free implements MonadOps, A> { } return current; } - +*/ MA foldMap(Monad m, M f(F fa)) => cast(/*step().*/fold((a) => m.pure(a), (fa) => f(fa), (ffb, f2) => m.bind(ffb.foldMap(m, f), (c) => f2(c).foldMap(m, f)))); + Future foldMapFuture(Future f(F fa)) { + return /*step().*/ fold((a) => new Future.microtask(() => a), (fa) => f(fa).then((a) { return a as A;}), (ffb, f2) => ffb.foldMapFuture(f).then((c) => f2(c).foldMapFuture(f))); + } + + @override Free flatMap(Free f(A a)) => new Bind(this, (a) => f(cast(a))); @override Free andThen(Free next) => bind((_) => next); diff --git a/lib/src/imap.dart b/lib/src/imap.dart index 5899dc9..7f37d37 100644 --- a/lib/src/imap.dart +++ b/lib/src/imap.dart @@ -63,7 +63,7 @@ class IMap implements TraversableOps, V> { IMap mapKV(V2 f(K k, V v)) => mapWithKey(f); IList> pairs() => _tree.foldRight(nil(), (k, v, p) => new Cons(tuple2(k, v), p)); - +/* G traverseKV(Applicative gApplicative, G f(K k, V v)) => _tree.foldLeft(gApplicative.pure( new IMap._internal(_order, _emptyIMapAVLNode())), @@ -71,7 +71,7 @@ class IMap implements TraversableOps, V> { G traverseKV_(Applicative gApplicative, G f(K k, V v)) => _tree.foldLeft(gApplicative.pure(unit), (prev, k, v) => gApplicative.map2(prev, f(k, v), (_1, _2) => unit)); - +*/ @override B foldMap(Monoid bMonoid, B f(V v)) => _tree.foldLeft(bMonoid.zero(), (p, k, v) => bMonoid.append(p, f(v))); @override B foldLeft(B z, B f(B previous, V v)) => _tree.foldLeft(z, (p, k, v) => f(p, v)); diff --git a/lib/src/io.dart b/lib/src/io.dart index 5b200c3..8b442b6 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart @@ -54,6 +54,9 @@ class Delay extends IOOp { class Attempt extends IOOp> { final Free fa; Attempt(this.fa); + + Either succeed(A a) => right(a); + Either fail(Object err) => left(err); } class Fail extends IOOp { diff --git a/lib/src/streaming/conveyor.dart b/lib/src/streaming/conveyor.dart index 09c6879..eb8ae20 100644 --- a/lib/src/streaming/conveyor.dart +++ b/lib/src/streaming/conveyor.dart @@ -77,7 +77,7 @@ abstract class Conveyor implements MonadPlusOps, O> { final trimmed = cycle.pipe(Pipe.window2()).takeWhile((pair) => pair != sentinel); return trimmed.map((t) => t.value2).flatMap((o) => o.fold(() => halt(End), (v) => produce(v))); } - +/* FLA runLog(MonadCatch monadCatch) { F go(Conveyor cur, IList acc) => cur.interpret((h, t) => go(t, cons(h, acc)), @@ -85,21 +85,21 @@ abstract class Conveyor implements MonadPlusOps, O> { (err) => err == End ? monadCatch.pure(acc.reverse()) : monadCatch.fail(err)); return cast(go(this, nil())); } - +*/ static Task> runLogTask(Conveyor cto) { Task> go(Conveyor cur, IList acc) => cur.interpret((h, t) => go(t, cons(h, acc)), (req, recv) => req.attempt().bind((Either e) => go(Try(() => recv(e)), acc)), - (err) => err == End ? new Task(() => new Future.value(acc.reverse())) : new Task(() => new Future.error(err))); - return cast(go(cto, nil())); + (err) => err == End ? new Task(() => new Future.value(acc.reverse())) : new Task(() => new Future.error(err))); + return go(cto, nil()); } static Free> runLogIO(Conveyor, O> cio) { Free> go(Conveyor, O> cur, IList acc) => cur.interpret((h, t) => go(t, cons(h, acc)), - (req, recv) => liftF>>(new Attempt(req)).flatMap((Either e) => go(Try(() => recv(e)), acc)), + (req, recv) => liftF>(new Attempt(req)).flatMap((e) => go(Try(() => recv(e)), acc)), (err) => err == End ? new Pure(acc.reverse()) : liftF(new Fail(err))); - return cast(go(cio, nil())); + return go(cio, nil()); } Conveyor drain() => @@ -195,7 +195,7 @@ abstract class Conveyor implements MonadPlusOps, O> { Conveyor to(Conveyor> sink) => zipWith(sink, (o, f) => f(o)).flatMap((a) => a as Conveyor); - Conveyor through(Conveyor> channel) => zipWith(channel, (o, f) => f(o)).flatMap(cast(id)); + Conveyor through(Conveyor> channel) => zipWith(channel, (o, f) => f(o)).flatMap((a) => a as Conveyor); Conveyor onto(Conveyor f(Conveyor c)) => f(this); @@ -240,5 +240,4 @@ class _End {} class _Kill {} -final MonadPlus ConveyorMP = new MonadPlusOpsMonadPlus((a) => Conveyor.produce(a), () => Conveyor.halt(Conveyor.End)); -MonadPlus> conveyorMP() => cast(ConveyorMP); +MonadPlus> conveyorMP() => new MonadPlusOpsMonadPlus((a) => Conveyor.produce(a as O), () => Conveyor.halt(Conveyor.End)); diff --git a/lib/src/streaming/source.dart b/lib/src/streaming/source.dart index dddbd8c..a86e927 100644 --- a/lib/src/streaming/source.dart +++ b/lib/src/streaming/source.dart @@ -16,18 +16,18 @@ class Source { static Conveyor resource(F acquire, Conveyor use(R r), Conveyor release(R r)) => eval(acquire).bind((r) => use(r).onComplete(() => release(r))); - static Conveyor fromFoldable(F fo, Foldable foldable) => foldable.collapse(conveyorMP(), fo); + //static Conveyor fromFoldable(F fo, Foldable foldable) => foldable.collapse(conveyorMP(), fo); static F materialize(Conveyor s, ApplicativePlus ap) => s.interpret((h, t) => ap.prependElement(materialize(t, ap), h), (req, recv) => materialize(recv(left(Conveyor.End)), ap), (err) => err == Conveyor.End ? ap.empty() : throw err); - static Conveyor fromIVector(IVector v) => fromFoldable(v, IVectorTr); - static IVector toIVector(Conveyor s) => cast(materialize(s, IVectorMP)); + //static Conveyor fromIVector(IVector v) => fromFoldable(v, IVectorTr); + //static IVector toIVector(Conveyor s) => cast(materialize(s, IVectorMP)); - static Conveyor fromIList(IList v) => fromFoldable(v, IListTr); - static IList toIList(Conveyor s) => cast(materialize(s, IListMP)); + //static Conveyor fromIList(IList v) => fromFoldable(v, IListTr); + //static IList toIList(Conveyor s) => cast(materialize(s, IListMP)); static Conveyor fromStream(Stream s()) => Source.resource(Task.delay(() => new StreamIterator(s())), (StreamIterator it) => Source.eval(new Task(it.moveNext)).repeat().takeWhile(id).flatMap((_) => Source.eval(Task.delay(() => it.current))), diff --git a/lib/src/unsafe/io.dart b/lib/src/unsafe/io.dart index c7362a7..796a453 100644 --- a/lib/src/unsafe/io.dart +++ b/lib/src/unsafe/io.dart @@ -16,9 +16,10 @@ Future unsafeIOInterpreter(IOOp io) { return new Future.value(unit); } else if (io is Attempt) { - return unsafePerformIO(io.fa).then(right).catchError(left); + return unsafePerformIO(io.fa).then((wut) => io.succeed(wut)).catchError((err, s) {print("&&& ${err}, ${s}"); return io.fail(err); }); } else if (io is Fail) { + print("--- ${io.failure}"); return new Future.error(io.failure); } else if (io is OpenFile) { @@ -47,6 +48,6 @@ Future unsafeIOInterpreter(IOOp io) { } } -Future unsafePerformIO(Free io) => io.foldMap(FutureM, unsafeIOInterpreter); +Future unsafePerformIO(Free io) => io.foldMapFuture(unsafeIOInterpreter); Future>> unsafeConveyIO(Conveyor, A> conveyor) => unsafePerformIO(IOM.attempt(Conveyor.runLogIO(conveyor))); diff --git a/test/either_test.dart b/test/either_test.dart index 98d3a18..f8ffb91 100644 --- a/test/either_test.dart +++ b/test/either_test.dart @@ -50,7 +50,7 @@ void main() { group("EitherM", () => checkMonadLaws(EitherM)); - group("EitherTMonad+Id", () => checkMonadLaws(eitherTMonad(IdM))); + //group("EitherTMonad+Id", () => checkMonadLaws(eitherTMonad(IdM))); //group("EitherTMonad+IList", () => checkMonadLaws(eitherTMonad(IListMP))); From ddf3e6935553a9056ce7c9af9338a6ed97d83fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Sperber?= Date: Sun, 30 Sep 2018 14:24:31 +0200 Subject: [PATCH 3/8] =?UTF-8?q?Did=20horrible=20things=20to=20Trampoline?= =?UTF-8?q?=20to=20make=20it=20reasonably=20fast=20again=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/trampoline.dart | 43 ++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/lib/src/trampoline.dart b/lib/src/trampoline.dart index add01e4..aebf452 100644 --- a/lib/src/trampoline.dart +++ b/lib/src/trampoline.dart @@ -5,46 +5,63 @@ part of dartz; abstract class Trampoline implements MonadOps { Trampoline pure(B b) => new _TPure(b); @override Trampoline map(B f(A a)) => bind((a) => pure(f(a))); - @override Trampoline bind(Trampoline f(A a)) => new _TBind(this, f); + @override Trampoline bind(Trampoline f(A a)) => new _TBind(this, cast(f)); @override Trampoline> strengthL(B b) => map((a) => tuple2(b, a)); @override Trampoline> strengthR(B b) => map((a) => tuple2(a, b)); A run() { - var current = this; - while(current is _TBind) { - var fa = cast<_TBind>(current)._fa; - Function f = cast<_TBind>(current)._f; - if (fa is _TBind) { - var fa2 = cast>(fa._fa); - Function f2 = fa._f; - current = new _TBind(fa2, (a2) => new _TBind(cast(f2(a2)), f)); + _TBind current = _unsafeGetTBind(); + if (current == null) { + return _unsafeGetTPure()._a; + } + while(true) { + final fa = current._fa; + final f = current._f; + final fabind = fa._unsafeGetTBind(); + if (fabind != null) { + final fa2 = fabind._fa; + final f2 = fabind._f; + current = new _TBind(fa2, (a2) => new _TBind(f2(a2), f)); } else { - current = cast(f(cast<_TPure>(fa)._a)); + final res = f(fa._unsafeGetTPure()._a); + current = res._unsafeGetTBind(); + if (current == null) { + return cast(res._unsafeGetTPure()._a); + } } } - return cast(cast<_TPure>(current)._a); } @override Trampoline andThen(Trampoline next) => bind((_) => next); @override Trampoline ap(Trampoline> ff) => ff.bind((f) => map(f)); // TODO: optimize - @override Trampoline flatMap(Trampoline f(A a)) => new _TBind(this, f); + @override Trampoline flatMap(Trampoline f(A a)) => new _TBind(this, cast(f)); @override Trampoline replace(B replacement) => map((_) => replacement); + + _TPure _unsafeGetTPure(); + + _TBind _unsafeGetTBind(); } class _TPure extends Trampoline { final A _a; _TPure(this._a); + + @override _TPure _unsafeGetTPure() => this; + _TBind _unsafeGetTBind() => null; } class _TBind extends Trampoline { final Trampoline _fa; - final Function _f; + final Function1> _f; _TBind(this._fa, this._f); + + _TPure _unsafeGetTPure() => null; + _TBind _unsafeGetTBind() => this; } final Monad TrampolineM = new MonadOpsMonad((a) => new _TPure(a)); From 2cf34f905a445092e259981a0d3480a66e74229a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Sperber?= Date: Mon, 3 Dec 2018 14:11:55 +0100 Subject: [PATCH 4/8] Enable more const construction of IList, Option and Either. Make Nil (empty IList) public. Various cleanups. --- lib/src/either.dart | 6 ++++-- lib/src/ilist.dart | 14 +++++++------- lib/src/monad.dart | 2 +- lib/src/option.dart | 4 +--- lib/src/unit.dart | 7 ++++--- pubspec.yaml | 2 +- test/combinators_stubs.dart | 9 ++++++++- 7 files changed, 26 insertions(+), 18 deletions(-) diff --git a/lib/src/either.dart b/lib/src/either.dart index a705f17..96dfca9 100644 --- a/lib/src/either.dart +++ b/lib/src/either.dart @@ -2,6 +2,8 @@ part of dartz; // Workaround for https://github.com/dart-lang/sdk/issues/29949 abstract class Either implements TraversableMonadOps, R> { + const Either(); + B fold(B ifLeft(L l), B ifRight(R r)); Either orElse(Either other()) => fold((_) => other(), (_) => this); @@ -119,7 +121,7 @@ abstract class Either implements TraversableMonadOps, R class Left extends Either { final L _l; - Left(this._l); + const Left(this._l); L get value => _l; @override B fold(B ifLeft(L l), B ifRight(R r)) => ifLeft(_l); @override bool operator ==(other) => other is Left && other._l == _l; @@ -128,7 +130,7 @@ class Left extends Either { class Right extends Either { final R _r; - Right(this._r); + const Right(this._r); R get value => _r; @override B fold(B ifLeft(L l), B ifRight(R r)) => ifRight(_r); @override bool operator ==(other) => other is Right && other._r == _r; diff --git a/lib/src/ilist.dart b/lib/src/ilist.dart index 8cb4be6..a30ebce 100644 --- a/lib/src/ilist.dart +++ b/lib/src/ilist.dart @@ -177,7 +177,7 @@ abstract class IList implements TraversableMonadPlusOps { return false; } } - return otherCurrent is _Nil; + return otherCurrent is Nil; } else { return false; } @@ -402,20 +402,20 @@ class Cons extends IList { @override Option> get tailOption => some(_tail); } -class _Nil extends IList { - const _Nil(); +class Nil extends IList { + const Nil(); bool _isCons() => false; - A _unsafeHead() => throw new UnsupportedError("_unsafeHead called on _Nil"); - IList _unsafeTail() => throw new UnsupportedError("_unsafeTail called on _Nil"); - void _unsafeSetTail(IList newTail) => throw new UnsupportedError("_unsafeSetTail called on _Nil"); + A _unsafeHead() => throw new UnsupportedError("_unsafeHead called on Nil"); + IList _unsafeTail() => throw new UnsupportedError("_unsafeTail called on Nil"); + void _unsafeSetTail(IList newTail) => throw new UnsupportedError("_unsafeSetTail called on Nil"); @override Option get headOption => none(); @override Option> get tailOption => none(); } -IList nil() => new _Nil(); +IList nil() => new Nil(); IList cons(A head, IList tail) => new Cons(head, tail); final MonadPlus IListMP = new MonadPlusOpsMonadPlus((a) => new Cons(a, nil()), nil); diff --git a/lib/src/monad.dart b/lib/src/monad.dart index c984b5a..9e563b6 100644 --- a/lib/src/monad.dart +++ b/lib/src/monad.dart @@ -28,7 +28,7 @@ abstract class MonadOps implements ApplicativeOps { F bind(covariant F f(A a)); @override F map(B f(A a));// => bind((a) => pure(f(a))); - @override F ap(F ff) => cast>>(ff).bind((f) => map(f)); + @override F ap(covariant F ff) => cast>>(ff).bind((f) => map(f)); F flatMap(covariant F f(A a)) => bind(f); F andThen(covariant F next) => bind((_) => next); F replace(B replacement) => map((_) => replacement); diff --git a/lib/src/option.dart b/lib/src/option.dart index 1cba6a1..b0c15da 100644 --- a/lib/src/option.dart +++ b/lib/src/option.dart @@ -183,6 +183,4 @@ class _SingletonIterator extends Iterator { _SingletonIterator(this._singleton); @override A get current => _moves == 1 ? _singleton : null; @override bool moveNext() => ++_moves == 1; -} - -final _emptyIterable = new Iterable.empty(); \ No newline at end of file +} \ No newline at end of file diff --git a/lib/src/unit.dart b/lib/src/unit.dart index 3b09fff..c7b45fb 100644 --- a/lib/src/unit.dart +++ b/lib/src/unit.dart @@ -1,10 +1,11 @@ part of dartz; -abstract class Unit {} -class _ConcreteUnit extends Unit { +class Unit { + const Unit._internal(); @override String toString() => "()"; } -final Unit unit = new _ConcreteUnit(); + +const Unit unit = const Unit._internal(); class UnitMonoid extends Monoid { @override Unit zero() => unit; diff --git a/pubspec.yaml b/pubspec.yaml index ae0febb..b12f9e4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: dartz -version: 0.8.5 +version: 0.9.0 homepage: https://github.com/spebbe/dartz description: Functional Programming in Dart. Purify your Dart code using efficient immutable data structures, monads, lenses and other FP tools. author: Björn Sperber diff --git a/test/combinators_stubs.dart b/test/combinators_stubs.dart index 6fa94b2..61656eb 100644 --- a/test/combinators_stubs.dart +++ b/test/combinators_stubs.dart @@ -6,10 +6,17 @@ import 'enumerators_stubs.dart'; Enumeration> listsOf(Enumeration enumeration) => enumeration.flatMap((a) => new Enumeration(() => new Stream.fromIterable([[], [a], [a, a], [a, a, a]]))); -Enumeration> mapsOf(Enumeration keys, Enumeration values) => keys.flatMap((k) => values.map((v) => {k : v})); +Enumeration> mapsOf(Enumeration keys, Enumeration values) { + List ks = []; + return keys.flatMap((k) { + ks.add(k); + return values.map((v) => ks.fold({}, (acc, k) => acc..[k] = v)); + }); +} final Enumeration ints = new Enumeration(() => new Stream.fromIterable([-2, -1, 0, 1, 2])); final Enumeration strings = new Enumeration(() => new Stream.fromIterable(["", "a", "ab", "abc"])); final Enumeration bools = new Enumeration(() => new Stream.fromIterable([false, true])); + From c64fbb594bf9da05a646950167f15e02d0f0436c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Sperber?= Date: Sun, 16 Dec 2018 19:32:50 +0100 Subject: [PATCH 5/8] Fix warnings --- example/streaming_io/example.dart | 2 -- lib/src/either.dart | 4 ---- 2 files changed, 6 deletions(-) diff --git a/example/streaming_io/example.dart b/example/streaming_io/example.dart index d944379..67bd88c 100644 --- a/example/streaming_io/example.dart +++ b/example/streaming_io/example.dart @@ -5,8 +5,6 @@ import 'dart:async'; import 'package:dartz/dartz_streaming.dart'; import 'package:dartz/dartz_unsafe.dart'; -import 'dart:io'; - amain() async { final pathToThisFile = "/Users/bjorns/Documents/Development/github/spebbe/dartz/example/streaming_io/example.dart";// Platform.script.toFilePath(); diff --git a/lib/src/either.dart b/lib/src/either.dart index 96dfca9..354b2b8 100644 --- a/lib/src/either.dart +++ b/lib/src/either.dart @@ -82,8 +82,6 @@ abstract class Either implements TraversableMonadOps, R @override bool any(bool f(R r)) => map(f)|false; - @override Either appendElement(R r) => orElse(() => right(r)); - @override R concatenate(Monoid mi) => getOrElse(mi.zero); @override Option concatenateO(Semigroup si) => toOption(); @@ -106,8 +104,6 @@ abstract class Either implements TraversableMonadOps, R @override Option minimum(Order or) => toOption(); - @override Either prependElement(R r) => right(r).orElse(() => this); - @override Either replace(B replacement) => map((_) => replacement); Either reverse() => this; From c3568148d1139bc91be01db9093a1d00a5248ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Sperber?= Date: Thu, 28 Mar 2019 20:23:12 +0100 Subject: [PATCH 6/8] Prep for preview release --- CHANGELOG.md | 9 +++++++++ README.md | 2 ++ example/streaming_io/example.dart | 17 ++++------------- lib/src/unsafe/io.dart | 3 +-- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 998b063..6462a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.9.0 WIP + +- Type class hierarchy reworked to be implemented rather than extended +- Removed monad transformers and some other stuff that can't be made type safe on Dart 2 +- IList and Option has some const support +- Dart 2.2.0 support +- Conveyor works on Dart 2! +- TODO: ...more things... + ## 0.8.5 - Compatibility with Dart > 2.3.2 through workaround for https://github.com/dart-lang/sdk/issues/35097 diff --git a/README.md b/README.md index a513ac0..1853033 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Dart 2 has a much stricter type system, making it impossible to pull off the sam Therefore, the open, type class based design of older versions of `dartz` is gradually being replaced by a more closed design, with a fixed set of type class instances interacting through hard coded logic. Some things, such as monad transformers, will probably have to be completely removed. Starting with version 0.8.0 and going forward, more and more of `dartz` will be Dart 2 compatible, at the expense of many breaking API changes. If you are developing for Dart 1, you might be happier sticking with version 0.7.5. +TODO: Document 0.9.0+ + ##### Status for versions >= 0.8.0 * Core design is in flux, with a lot of restructuring going on diff --git a/example/streaming_io/example.dart b/example/streaming_io/example.dart index 67bd88c..14feb8c 100644 --- a/example/streaming_io/example.dart +++ b/example/streaming_io/example.dart @@ -1,13 +1,13 @@ library streaming_io_example; -import 'dart:async'; - import 'package:dartz/dartz_streaming.dart'; import 'package:dartz/dartz_unsafe.dart'; -amain() async { +import 'dart:io'; + +main() async { - final pathToThisFile = "/Users/bjorns/Documents/Development/github/spebbe/dartz/example/streaming_io/example.dart";// Platform.script.toFilePath(); + final pathToThisFile = Platform.script.toFilePath(); // Construct Conveyor that: // 1. Reads lines from this file @@ -26,8 +26,6 @@ amain() async { // since the Conveyor finishes as soon as the fifth comment is consumed. // The file will be closed as soon as the Conveyor finishes. - print("Here we go!"); - await unsafeConveyIO(firstFiveCommentsInThisFile.to(IO.stdoutWriter)); // Conveyors are pure values, and can safely be reused: @@ -36,10 +34,3 @@ amain() async { // ...and composed further: await unsafeConveyIO(firstFiveCommentsInThisFile.map((s) => s.toUpperCase()).to(IO.stdoutWriter)); } - -void main() { - print("Starting!"); - amain(); - //sleep(new Duration(seconds: 1)); - new Future(() => print("Bye!")); -} diff --git a/lib/src/unsafe/io.dart b/lib/src/unsafe/io.dart index 796a453..6c07877 100644 --- a/lib/src/unsafe/io.dart +++ b/lib/src/unsafe/io.dart @@ -16,10 +16,9 @@ Future unsafeIOInterpreter(IOOp io) { return new Future.value(unit); } else if (io is Attempt) { - return unsafePerformIO(io.fa).then((wut) => io.succeed(wut)).catchError((err, s) {print("&&& ${err}, ${s}"); return io.fail(err); }); + return unsafePerformIO(io.fa).then(io.succeed).catchError(io.fail); } else if (io is Fail) { - print("--- ${io.failure}"); return new Future.error(io.failure); } else if (io is OpenFile) { From 98bae257c21f5dd50e0bd23ba29a5ea058aae508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Sperber?= Date: Thu, 25 Apr 2019 18:41:46 +0200 Subject: [PATCH 7/8] =?UTF-8?q?Generic=20cleanups.=20Added=20=E2=80=98ever?= =?UTF-8?q?y=E2=80=99=20aliases=20for=20=E2=80=98all=E2=80=99.=20Added=20a?= =?UTF-8?q?=20couple=20of=20missing=20traverse/sequence=20variants.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/avl_tree.dart | 18 ++++---- lib/src/either.dart | 16 +++---- lib/src/evaluation.dart | 1 - lib/src/foldable.dart | 1 + lib/src/free.dart | 1 - lib/src/id.dart | 6 +-- lib/src/ihashmap.dart | 46 ++++++++++--------- lib/src/ilist.dart | 48 +++++++++++++------- lib/src/imap.dart | 50 +++++++++++---------- lib/src/iset.dart | 19 ++++---- lib/src/ivector.dart | 18 ++++---- lib/src/option.dart | 79 ++++++++++++++++++--------------- lib/src/state.dart | 2 - lib/src/streaming/conveyor.dart | 1 - lib/src/task.dart | 6 +-- 15 files changed, 168 insertions(+), 144 deletions(-) diff --git a/lib/src/avl_tree.dart b/lib/src/avl_tree.dart index 3fa737c..ef016e5 100644 --- a/lib/src/avl_tree.dart +++ b/lib/src/avl_tree.dart @@ -40,15 +40,8 @@ class AVLTree implements FoldableOps { @override String toString() => 'avltree<${toIList()}>'; - // PURISTS BEWARE: side effecty stuff below -- proceed with caution! - - Iterable toIterable() => new _AVLTreeIterable(this); - - Iterator iterator() => toIterable().iterator; - - void forEach(void sideEffect(A a)) => foldLeft(null, (_, a) => sideEffect(a)); - @override bool all(bool f(A a)) => foldMap(BoolAndMi, f); // TODO: optimize + @override bool every(bool f(A a)) => all(f); @override bool any(bool f(A a)) => foldMap(BoolOrMi, f); // TODO: optimize @@ -73,6 +66,15 @@ class AVLTree implements FoldableOps { @override Option maximum(Order oa) => concatenateO(oa.maxSi()); @override Option minimum(Order oa) => concatenateO(oa.minSi()); + + + // PURISTS BEWARE: side effecty stuff below -- proceed with caution! + + Iterable toIterable() => new _AVLTreeIterable(this); + + Iterator iterator() => toIterable().iterator; + + void forEach(void sideEffect(A a)) => foldLeft(null, (_, a) => sideEffect(a)); } abstract class _AVLNode { diff --git a/lib/src/either.dart b/lib/src/either.dart index 354b2b8..fb0e3b9 100644 --- a/lib/src/either.dart +++ b/lib/src/either.dart @@ -1,6 +1,5 @@ part of dartz; -// Workaround for https://github.com/dart-lang/sdk/issues/29949 abstract class Either implements TraversableMonadOps, R> { const Either(); @@ -65,13 +64,6 @@ abstract class Either implements TraversableMonadOps, R @override String toString() => fold((l) => 'Left($l)', (r) => 'Right($r)'); - // PURISTS BEWARE: side effecty stuff below -- proceed with caution! - - Iterable toIterable() => fold((_) => const Iterable.empty(), (r) => new _SingletonIterable(r)); - Iterator iterator() => toIterable().iterator; - - void forEach(void sideEffect(R r)) => fold((_) => null, sideEffect); - @override B foldMap(Monoid bMonoid, B f(R r)) => fold((_) => bMonoid.zero(), f); @override Either mapWithIndex(B f(int i, R r)) => map((r) => f(0, r)); @@ -79,6 +71,7 @@ abstract class Either implements TraversableMonadOps, R @override Either> zipWithIndex() => map((r) => tuple2(0, r)); @override bool all(bool f(R r)) => map(f)|true; + @override bool every(bool f(R r)) => all(f); @override bool any(bool f(R r)) => map(f)|false; @@ -113,6 +106,13 @@ abstract class Either implements TraversableMonadOps, R @override Either> strengthR(B b) => map((a) => tuple2(a, b)); @override Either ap(Either> ff) => ff.bind((f) => map(f)); + + // PURISTS BEWARE: side effecty stuff below -- proceed with caution! + + Iterable toIterable() => fold((_) => const Iterable.empty(), (r) => new _SingletonIterable(r)); + Iterator iterator() => toIterable().iterator; + + void forEach(void sideEffect(R r)) => fold((_) => null, sideEffect); } class Left extends Either { diff --git a/lib/src/evaluation.dart b/lib/src/evaluation.dart index c08be75..d038be9 100644 --- a/lib/src/evaluation.dart +++ b/lib/src/evaluation.dart @@ -3,7 +3,6 @@ part of dartz; // Prestacked Future + Writer + State>> monad. // Binds are stack safe but relatively expensive, because of Future chaining. -// Workaround for https://github.com/dart-lang/sdk/issues/29949 class Evaluation implements MonadOps, A> { final Monoid _W; final Function2>>> _run; diff --git a/lib/src/foldable.dart b/lib/src/foldable.dart index 292e74a..58e514c 100644 --- a/lib/src/foldable.dart +++ b/lib/src/foldable.dart @@ -61,6 +61,7 @@ abstract class FoldableOps { bool any(bool f(A a)) => foldMap(BoolOrMi, f); bool all(bool f(A a)) => foldMap(BoolAndMi, f); + bool every(bool f(A a)) => all(f); Option minimum(Order oa) => concatenateO(new MinSemigroup(oa)); diff --git a/lib/src/free.dart b/lib/src/free.dart index 5c66b9d..ddbc60f 100644 --- a/lib/src/free.dart +++ b/lib/src/free.dart @@ -2,7 +2,6 @@ part of dartz; typedef Free _FreeF(dynamic x); -// Workaround for https://github.com/dart-lang/sdk/issues/29949 abstract class Free implements MonadOps, A> { @override Free map(B f(A a)) => bind((a) => new Pure(f(a))); diff --git a/lib/src/id.dart b/lib/src/id.dart index 3a1ee97..6fef27a 100644 --- a/lib/src/id.dart +++ b/lib/src/id.dart @@ -1,8 +1,8 @@ part of dartz; class IdMonad extends Functor with Applicative, Monad { - @override pure(A a) => a; - @override bind(A fa, B f(A a)) => f(fa); + @override A pure(A a) => a; + @override B bind(A fa, B f(A a)) => f(fa); IList replicate(int n, A fa) => new IList.from(new List.filled(n, fa)); } @@ -12,7 +12,7 @@ final IdMonad IdM = new IdMonad(); class IdTraversable extends Traversable { @override B foldMap(Monoid bMonoid, A fa, B f(A a)) => f(fa); - @override map(A fa, B f(A a)) => f(fa); + @override B map(A fa, B f(A a)) => f(fa); } final IdTraversable IdTr = new IdTraversable(); diff --git a/lib/src/ihashmap.dart b/lib/src/ihashmap.dart index cf9bf4d..a6c00af 100644 --- a/lib/src/ihashmap.dart +++ b/lib/src/ihashmap.dart @@ -57,28 +57,6 @@ class IHashMap implements TraversableOps, V> { @override bool operator ==(other) => identical(this, other) || (other is IHashMap && _map == other._map); @override int get hashCode => _map.hashCode; - // PURISTS BEWARE: side effecty stuff below -- proceed with caution! - - Iterable> pairIterable() => _map.valueIterable().expand((tuples) => tuples.toIterable()); - - Iterator> pairIterator() => pairIterable().iterator; - - Iterable keyIterable() => pairIterable().map((t) => t.value1); - - Iterator keyIterator() => keyIterable().iterator; - - Iterable valueIterable() => pairIterable().map((t) => t.value2); - - Iterator valueIterator() => valueIterable().iterator; - - Iterable> toIterable() => pairIterable(); - - Iterator> iterator() => pairIterator(); - - void forEach(void sideEffect(V v)) => foldLeft(null, (_, v) => sideEffect(v)); - - void forEachKV(void sideEffect(K k, V v)) => foldLeftKV(null, (_, k, v) => sideEffect(k, v)); - @override B foldMap(Monoid bMonoid, B f(V a)) => _map.foldMap(bMonoid, (kvs) => kvs.foldMap(bMonoid, (t) => f(t.value2))); @override IHashMap mapWithIndex(B f(int i, V a)) => throw "not implemented!!!"; // TODO @@ -86,6 +64,7 @@ class IHashMap implements TraversableOps, V> { @override IHashMap> zipWithIndex() => mapWithIndex(tuple2); @override bool all(bool f(V a)) => foldMap(BoolAndMi, f); // TODO: optimize + @override bool every(bool f(V v)) => all(f); @override bool any(bool f(V a)) => foldMap(BoolOrMi, f); // TODO: optimize @@ -114,6 +93,29 @@ class IHashMap implements TraversableOps, V> { @override IHashMap> strengthL(B b) => map((v) => tuple2(b, v)); @override IHashMap> strengthR(B b) => map((v) => tuple2(v, b)); + + + // PURISTS BEWARE: side effecty stuff below -- proceed with caution! + + Iterable> pairIterable() => _map.valueIterable().expand((tuples) => tuples.toIterable()); + + Iterator> pairIterator() => pairIterable().iterator; + + Iterable keyIterable() => pairIterable().map((t) => t.value1); + + Iterator keyIterator() => keyIterable().iterator; + + Iterable valueIterable() => pairIterable().map((t) => t.value2); + + Iterator valueIterator() => valueIterable().iterator; + + Iterable> toIterable() => pairIterable(); + + Iterator> iterator() => pairIterator(); + + void forEach(void sideEffect(V v)) => foldLeft(null, (_, v) => sideEffect(v)); + + void forEachKV(void sideEffect(K k, V v)) => foldLeftKV(null, (_, k, v) => sideEffect(k, v)); } final Traversable IHashMapTr = new TraversableOpsTraversable(); diff --git a/lib/src/ilist.dart b/lib/src/ilist.dart index a30ebce..a23007b 100644 --- a/lib/src/ilist.dart +++ b/lib/src/ilist.dart @@ -227,22 +227,6 @@ abstract class IList implements TraversableMonadPlusOps { static IList flattenOption(IList> oas) => oas.flatMap((oa) => oa.fold(nil, (a) => cons(a, nil()))); - // PURISTS BEWARE: side effecty stuff below -- proceed with caution! - - List toList() => foldLeft([], (List p, a) => p..add(a)); - - Iterable toIterable() => new _IListIterable(this); - - Iterator iterator() => new _IListIterator(this); - - void forEach(void sideEffect(A a)) { - var current = this; - while (current._isCons()) { - sideEffect(current._unsafeHead()); - current = current._unsafeTail(); - } - } - Option> traverseOption(Option f(A a)) { Option> result = some(nil()); var current = this; @@ -298,6 +282,17 @@ abstract class IList implements TraversableMonadPlusOps { return result.map((l) => l.reverse()); } + Free> traverseFree(Free f(A a)) { + Free> result = new Pure(nil()); + var current = this; + while(current._isCons()) { + final gb = f(current._unsafeHead()); + result = result.flatMap((a) => gb.map((h) => new Cons(h, a))); + current = current._unsafeTail(); + } + return result.map((l) => l.reverse()); + } + Option> traverseOptionM(Option> f(A a)) { var result = some(nil()); var current = this; @@ -311,12 +306,14 @@ abstract class IList implements TraversableMonadPlusOps { static Option> sequenceOption(IList> loa) => loa.traverseOption(id); - static Either> sequenceEither(IList> loa) => loa.traverseEither(id); + static Either> sequenceEither(IList> lea) => lea.traverseEither(id); static Future> sequenceFuture(IList> lfa) => lfa.traverseFuture(id); static State> sequenceState(IList> lsa) => lsa.traverseState(id); + static Free> sequenceFree(IList> lfa) => lfa.traverseFree(id); + @override IList mapWithIndex(B f(int i, A a)) { final IList bNil = nil(); if (!_isCons()) { @@ -341,6 +338,7 @@ abstract class IList implements TraversableMonadPlusOps { @override IList> zipWithIndex() => mapWithIndex(tuple2); @override bool all(bool f(A a)) => foldMap(BoolAndMi, f); // TODO: optimize + @override bool every(bool f(A a)) => all(f); @override IList andThen(IList next) => bind((_) => next); @@ -385,6 +383,22 @@ abstract class IList implements TraversableMonadPlusOps { @override IList> strengthR(B b) => map((a) => tuple2(a, b)); + // PURISTS BEWARE: side effecty stuff below -- proceed with caution! + + List toList() => foldLeft([], (List p, a) => p..add(a)); + + Iterable toIterable() => new _IListIterable(this); + + Iterator iterator() => new _IListIterator(this); + + void forEach(void sideEffect(A a)) { + var current = this; + while (current._isCons()) { + sideEffect(current._unsafeHead()); + current = current._unsafeTail(); + } + } + } class Cons extends IList { diff --git a/lib/src/imap.dart b/lib/src/imap.dart index 7f37d37..25f2c0b 100644 --- a/lib/src/imap.dart +++ b/lib/src/imap.dart @@ -104,36 +104,15 @@ class IMap implements TraversableOps, V> { @override String toString() => "imap{${foldMapKV>(ilistMi(), (k, v) => new Cons("$k: $v", nil())).intercalate(StringMi, ", ")}}"; - // PURISTS BEWARE: side effecty stuff below -- proceed with caution! - - Iterable> pairIterable() => new _IMapPairIterable(this); - - Iterator> pairIterator() => pairIterable().iterator; - - Iterable keyIterable() => new _IMapKeyIterable(this); - - Iterator keyIterator() => keyIterable().iterator; - - Iterable valueIterable() => new _IMapValueIterable(this); - - Iterator valueIterator() => valueIterable().iterator; - - Iterable> toIterable() => pairIterable(); - - Iterator> iterator() => pairIterator(); - - void forEach(void sideEffect(V v)) => foldLeft(null, (_, v) => sideEffect(v)); - - void forEachKV(void sideEffect(K k, V v)) => foldLeftKV(null, (_, k, v) => sideEffect(k, v)); - @override IMap mapWithIndex(B f(int i, V a)) => _tree.foldLeft>>(tuple2(0, emptyMap()), (t, k, v) => t.apply((i, acc) => tuple2(i+1, acc.put(k, f(i, v))))).value2; // TODO: optimize @override IMap> zipWithIndex() => mapWithIndex(tuple2); - @override bool all(bool f(V a)) => foldMap(BoolAndMi, f); // TODO: optimize + @override bool all(bool f(V v)) => foldMap(BoolAndMi, f); // TODO: optimize + @override bool every(bool f(V v)) => all(f); - @override bool any(bool f(V a)) => foldMap(BoolOrMi, f); // TODO: optimize + @override bool any(bool f(V v)) => foldMap(BoolOrMi, f); // TODO: optimize @override V concatenate(Monoid mi) => foldMap(mi, id); // TODO: optimize @@ -160,6 +139,29 @@ class IMap implements TraversableOps, V> { @override IMap> strengthL(B b) => map((v) => tuple2(b, v)); @override IMap> strengthR(B b) => map((v) => tuple2(v, b)); + + + // PURISTS BEWARE: side effecty stuff below -- proceed with caution! + + Iterable> pairIterable() => new _IMapPairIterable(this); + + Iterator> pairIterator() => pairIterable().iterator; + + Iterable keyIterable() => new _IMapKeyIterable(this); + + Iterator keyIterator() => keyIterable().iterator; + + Iterable valueIterable() => new _IMapValueIterable(this); + + Iterator valueIterator() => valueIterable().iterator; + + Iterable> toIterable() => pairIterable(); + + Iterator> iterator() => pairIterator(); + + void forEach(void sideEffect(V v)) => foldLeft(null, (_, v) => sideEffect(v)); + + void forEachKV(void sideEffect(K k, V v)) => foldLeftKV(null, (_, k, v) => sideEffect(k, v)); } diff --git a/lib/src/iset.dart b/lib/src/iset.dart index 081240c..5a175f7 100644 --- a/lib/src/iset.dart +++ b/lib/src/iset.dart @@ -52,16 +52,8 @@ class ISet implements FoldableOps { @override String toString() => "iset<${_tree.toIList().map((a) => a.toString()).intercalate(StringMi, ", ")}>"; - // PURISTS BEWARE: side effecty stuff below -- proceed with caution! - - Iterable toIterable() => _tree.toIterable(); - - Iterator iterator() => _tree.iterator(); - - void forEach(void sideEffect(A a)) => foldLeft(null, (_, a) => sideEffect(a)); - - @override bool all(bool f(A a)) => foldMap(BoolAndMi, f); // TODO: optimize + @override bool every(bool f(A a)) => all(f); @override bool any(bool f(A a)) => foldMap(BoolOrMi, f); // TODO: optimize @@ -86,6 +78,15 @@ class ISet implements FoldableOps { @override Option maximum(Order oa) => concatenateO(oa.maxSi()); @override Option minimum(Order oa) => concatenateO(oa.minSi()); + + + // PURISTS BEWARE: side effecty stuff below -- proceed with caution! + + Iterable toIterable() => _tree.toIterable(); + + Iterator iterator() => _tree.iterator(); + + void forEach(void sideEffect(A a)) => foldLeft(null, (_, a) => sideEffect(a)); } final Foldable ISetFo = new FoldableOpsFoldable(); diff --git a/lib/src/ivector.dart b/lib/src/ivector.dart index 79e8d61..ff72e2f 100644 --- a/lib/src/ivector.dart +++ b/lib/src/ivector.dart @@ -116,17 +116,10 @@ class IVector implements TraversableMonadPlusOps { @override String toString() => "ivector[${map((A a) => a.toString()).intercalate(StringMi, ', ')}]"; - // PURISTS BEWARE: side effecty stuff below -- proceed with caution! - - Iterable toIterable() => _elementsByIndex.valueIterable(); - - Iterator iterator() => _elementsByIndex.valueIterator(); - - void forEach(void sideEffect(A a)) => foldLeft(null, (_, a) => sideEffect(a)); - @override IVector> zipWithIndex() => mapWithIndex(tuple2); @override bool all(bool f(A a)) => foldMap(BoolAndMi, f); // TODO: optimize + @override bool every(bool f(A a)) => all(f); @override bool any(bool f(A a)) => foldMap(BoolOrMi, f); // TODO: optimize @@ -153,6 +146,15 @@ class IVector implements TraversableMonadPlusOps { @override IVector> strengthL(B b) => map((a) => tuple2(b, a)); @override IVector> strengthR(B b) => map((a) => tuple2(a, b)); + + + // PURISTS BEWARE: side effecty stuff below -- proceed with caution! + + Iterable toIterable() => _elementsByIndex.valueIterable(); + + Iterator iterator() => _elementsByIndex.valueIterator(); + + void forEach(void sideEffect(A a)) => foldLeft(null, (_, a) => sideEffect(a)); } IVector ivector(Iterable iterable) => new IVector.from(iterable); diff --git a/lib/src/option.dart b/lib/src/option.dart index b0c15da..7a534c3 100644 --- a/lib/src/option.dart +++ b/lib/src/option.dart @@ -28,6 +28,8 @@ abstract class Option implements TraversableMonadPlusOps { State> traverseState(State f(A a)) => fold(() => new State((s) => tuple2(none(), s)), (a) => f(a).map(some)); + Free> traverseFree(Free f(A a)) => fold(() => new Pure(none()), (a) => f(a).map(some)); + static IList> sequenceIList(Option> ola) => ola.traverseIList(id); static IVector> sequenceIVector(Option> ova) => ova.traverseIVector(id); @@ -36,49 +38,15 @@ abstract class Option implements TraversableMonadPlusOps { static State> sequenceState(Option> osa) => osa.traverseState(id); + static Free> sequenceFree(Option> ofa) => ofa.traverseFree(id); + @override Option plus(Option o2) => orElse(() => o2); @override Option filter(bool predicate(A a)) => fold(none, (a) => predicate(a) ? this : none()); @override Option where(bool predicate(A a)) => filter(predicate); - bool isSome() => fold(() => false, (_) => true); - - bool isNone() => !isSome(); - - static Option map2(Option fa, Option fb, C fun(A a, B b)) => - fa.fold(none, (a) => fb.fold(none, (b) => some(fun(a, b)))); - - static Option map3(Option fa, Option fb, Option fc, D fun(A a, B b, C c)) => - fa.fold(none, (a) => fb.fold(none, (b) => fc.fold(none, (c) => some(fun(a, b, c))))); - - static Option map4(Option fa, Option fb, Option fc, Option fd, E fun(A a, B b, C c, D d)) => - fa.fold(none, (a) => fb.fold(none, (b) => fc.fold(none, (c) => fd.fold(none, (d) => some(fun(a, b, c, d)))))); - - static Option map5(Option fa, Option fb, Option fc, Option fd, Option fe, F fun(A a, B b, C c, D d, E e)) => - fa.fold(none, (a) => fb.fold(none, (b) => fc.fold(none, (c) => fd.fold(none, (d) => fe.fold(none, (e) => some(fun(a, b, c, d, e))))))); - - static Option map6(Option fa, Option fb, Option fc, Option fd, Option fe, Option ff, G fun(A a, B b, C c, D d, E e, F f)) => - fa.fold(none, (a) => fb.fold(none, (b) => fc.fold(none, (c) => fd.fold(none, (d) => fe.fold(none, (e) => ff.fold(none, (f) => some(fun(a, b, c, d, e, f)))))))); - - static Option mapM2(Option fa, Option fb, Option f(A a, B b)) => fa.bind((a) => fb.bind((b) => f(a, b))); - - static Function1, Option> lift(B f(A a)) => ((Option oa) => oa.map(f)); - static Function2, Option, Option> lift2(C f(A a, B b)) => (Option fa, Option fb) => map2(fa, fb, f); - static Function3, Option, Option, Option> lift3(D f(A a, B b, C c)) => (Option fa, Option fb, Option fc) => map3(fa, fb, fc, f); - static Function4, Option, Option, Option, Option> lift4(E f(A a, B b, C c, D d)) => (Option fa, Option fb, Option fc, Option fd) => map4(fa, fb, fc, fd, f); - static Function5, Option, Option, Option, Option, Option> lift5(F f(A a, B b, C c, D d, E e)) => (Option fa, Option fb, Option fc, Option fd, Option fe) => map5(fa, fb, fc, fd, fe, f); - static Function6, Option, Option, Option, Option, Option, Option> lift6(G f(A a, B b, C c, D d, E e, F f)) => (Option fa, Option fb, Option fc, Option fd, Option fe, Option ff) => map6(fa, fb, fc, fd, fe, ff, f); - - @override String toString() => fold(() => 'None', (a) => 'Some($a)'); - - // PURISTS BEWARE: side effecty stuff below -- proceed with caution! - - Iterable toIterable() => fold(() => const Iterable.empty(), (a) => new _SingletonIterable(a)); - Iterator iterator() => toIterable().iterator; - - void forEach(void sideEffect(A a)) => fold(() => null, sideEffect); - @override bool all(bool f(A a)) => map(f)|true; + @override bool every(bool f(A a)) => all(f); @override bool any(bool f(A a)) => map(f)|false; @@ -123,6 +91,43 @@ abstract class Option implements TraversableMonadPlusOps { @override Option> strengthR(B b) => map((a) => tuple2(a, b)); @override Option> zipWithIndex() => map((a) => tuple2(0, a)); + + bool isSome() => fold(() => false, (_) => true); + + bool isNone() => !isSome(); + + static Option map2(Option fa, Option fb, C fun(A a, B b)) => + fa.fold(none, (a) => fb.fold(none, (b) => some(fun(a, b)))); + + static Option map3(Option fa, Option fb, Option fc, D fun(A a, B b, C c)) => + fa.fold(none, (a) => fb.fold(none, (b) => fc.fold(none, (c) => some(fun(a, b, c))))); + + static Option map4(Option fa, Option fb, Option fc, Option fd, E fun(A a, B b, C c, D d)) => + fa.fold(none, (a) => fb.fold(none, (b) => fc.fold(none, (c) => fd.fold(none, (d) => some(fun(a, b, c, d)))))); + + static Option map5(Option fa, Option fb, Option fc, Option fd, Option fe, F fun(A a, B b, C c, D d, E e)) => + fa.fold(none, (a) => fb.fold(none, (b) => fc.fold(none, (c) => fd.fold(none, (d) => fe.fold(none, (e) => some(fun(a, b, c, d, e))))))); + + static Option map6(Option fa, Option fb, Option fc, Option fd, Option fe, Option ff, G fun(A a, B b, C c, D d, E e, F f)) => + fa.fold(none, (a) => fb.fold(none, (b) => fc.fold(none, (c) => fd.fold(none, (d) => fe.fold(none, (e) => ff.fold(none, (f) => some(fun(a, b, c, d, e, f)))))))); + + static Option mapM2(Option fa, Option fb, Option f(A a, B b)) => fa.bind((a) => fb.bind((b) => f(a, b))); + + static Function1, Option> lift(B f(A a)) => ((Option oa) => oa.map(f)); + static Function2, Option, Option> lift2(C f(A a, B b)) => (Option fa, Option fb) => map2(fa, fb, f); + static Function3, Option, Option, Option> lift3(D f(A a, B b, C c)) => (Option fa, Option fb, Option fc) => map3(fa, fb, fc, f); + static Function4, Option, Option, Option, Option> lift4(E f(A a, B b, C c, D d)) => (Option fa, Option fb, Option fc, Option fd) => map4(fa, fb, fc, fd, f); + static Function5, Option, Option, Option, Option, Option> lift5(F f(A a, B b, C c, D d, E e)) => (Option fa, Option fb, Option fc, Option fd, Option fe) => map5(fa, fb, fc, fd, fe, f); + static Function6, Option, Option, Option, Option, Option, Option> lift6(G f(A a, B b, C c, D d, E e, F f)) => (Option fa, Option fb, Option fc, Option fd, Option fe, Option ff) => map6(fa, fb, fc, fd, fe, ff, f); + + @override String toString() => fold(() => 'None', (a) => 'Some($a)'); + + // PURISTS BEWARE: side effecty stuff below -- proceed with caution! + + Iterable toIterable() => fold(() => const Iterable.empty(), (a) => new _SingletonIterable(a)); + Iterator iterator() => toIterable().iterator; + + void forEach(void sideEffect(A a)) => fold(() => null, sideEffect); } class Some extends Option { diff --git a/lib/src/state.dart b/lib/src/state.dart index ef175f3..a704347 100644 --- a/lib/src/state.dart +++ b/lib/src/state.dart @@ -2,7 +2,6 @@ part of dartz; // Bind on plain State is *not* stack safe. Composition of StateT with stack safe monad, such as Trampoline, is. -// Workaround for https://github.com/dart-lang/sdk/issues/29949 class State implements MonadOps, A> { final Function1> _run; Tuple2 run(S s) => _run(s); @@ -43,7 +42,6 @@ class StateMonad extends Functor> with Applicative stateM() => new StateMonad(); -// Workaround for https://github.com/dart-lang/sdk/issues/29949 class StateT implements MonadOps, A> { final Monad _FM; final Function1 _run; diff --git a/lib/src/streaming/conveyor.dart b/lib/src/streaming/conveyor.dart index eb8ae20..59d9333 100644 --- a/lib/src/streaming/conveyor.dart +++ b/lib/src/streaming/conveyor.dart @@ -8,7 +8,6 @@ part of dartz_streaming; typedef Conveyor SinkF(O o); typedef Conveyor ChannelF(I i); -// Workaround for https://github.com/dart-lang/sdk/issues/29949 abstract class Conveyor implements MonadPlusOps, O> { A interpret(A ifProduce(O head, Conveyor tail), covariant A ifConsume(F req, Function1, Conveyor> recv), A ifHalt(Object err)); diff --git a/lib/src/task.dart b/lib/src/task.dart index e3ef3f0..85786ca 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -5,7 +5,7 @@ class Task implements MonadCatchOps { Task(this._run); - static Task delay(Function0 f) => new Task(() => new Future(f)); + static Task delay(Function0 f) => new Task(() => new Future.microtask(f)); Future run() => _run(); @@ -25,7 +25,7 @@ class Task implements MonadCatchOps { @override Task andThen(Task next) => bind((_) => next); - @override Task ap(Task> ff) => ff.bind((f) => map(f)); // TODO: optimize + @override Task ap(Task> ff) => ff.bind(map); // TODO: optimize @override Task flatMap(Task f(A a)) => new Task(() => _run().then((a) => f(a).run())); @@ -40,7 +40,7 @@ class TaskMonadCatch extends Functor with Applicative, Monad, @override Task fail(Object err) => new Task(() => new Future.error(err)); - @override Task pure(A a) => new Task(() => new Future.value(a)); + @override Task pure(A a) => new Task(() => new Future.microtask(() => a)); } final MonadCatch TaskMC = new TaskMonadCatch(); From 2559419216cd678d1b01f73c7dfd771f8854f4df Mon Sep 17 00:00:00 2001 From: Modestas Valauskas Date: Sat, 13 Jul 2019 14:48:13 +0200 Subject: [PATCH 8/8] fix #18 --- lib/src/free.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/free.dart b/lib/src/free.dart index 8874d97..e3a9fea 100644 --- a/lib/src/free.dart +++ b/lib/src/free.dart @@ -76,7 +76,7 @@ class FreeMonad extends Functor> with Applicative pure(A a) => new Pure(a); - @override Free bind(Free fa, Free f(A a)) => fa.bind(f); + @override Free bind(Free fa, Free f(A a)) => fa.bind(f); }