Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #18 #19

Closed
wants to merge 9 commits into from
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions example/free_io/mock_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class _MockFileRef implements FileRef {
Evaluation<String, IMap<String, IVector<String>>, IVector<String>, IMap<String, int>, String> mockReadFile(String fileName) =>
MockM.gets((counters) => counters[fileName]|0).bind((i) =>
MockM.asks((inputs) => inputs[fileName]|emptyVector<String>()).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<String, IMap<String, IVector<String>>, IVector<String>, IMap<String, int>, A> _interpret<A>(Free<IOOp, A> op) =>
op.foldMap(MockM, mockIOInterpreter);
Expand Down Expand Up @@ -57,7 +57,7 @@ Evaluation<String, IMap<String, IVector<String>>, IVector<String>, IMap<String,
return _interpret(io.a);

} else if (io is Gather) {
return io.ops.traverse(MockM, _interpret);
return io.ops.traverseEvaluation(ivectorMi(), _interpret);

} else {
return MockM.raiseError("Unimplemented IO op: $io");
Expand All @@ -68,5 +68,5 @@ Evaluation<String, IMap<String, IVector<String>>, IVector<String>, IMap<String,
Future<Either<String, Tuple3<IVector<String>, IMap<String, int>, A>>> mockPerformIO<A>(Free<IOOp, A> io, IMap<String, IVector<String>> input) =>
_interpret(io).run(input, emptyMap());

Future<Either<String, Tuple3<IVector<String>, IMap<String, int>, A>>> mockConveyIO<A>(Conveyor<Free<IOOp, dynamic>, A> cio, IMap<String, IVector<String>> input) =>
_interpret(cio.runLog<Free<IOOp, A>>(iomc())).run(input, emptyMap());
Future<Either<String, Tuple3<IVector<String>, IMap<String, int>, IList<A>>>> mockConveyIO<A>(Conveyor<Free<IOOp, dynamic>, A> cio, IMap<String, IVector<String>> input) =>
_interpret(Conveyor.runLogIO(cio)).run(input, emptyMap());
17 changes: 9 additions & 8 deletions lib/src/applicative.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ abstract class Applicative<F> implements Functor<F> {
F get nothing => pure(unit);

@override F map<A, B>(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);
Expand Down Expand Up @@ -35,10 +35,10 @@ abstract class Applicative<F> implements Functor<F> {
F replicate<A>(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 :-(

/*
Function lift<A, B>(B f(A a)) => cast((F fa) => map(fa, f));
Function lift2<A, B, C>(C f(A a, B b)) => (F fa, F fb) => ap(fb, map(fa, curry2(f)));
Function lift3<A, B, C, D>(D f(A a, B b, C c)) => (F fa, F fb, F fc) => ap(fc, ap(fb, map(fa, curry3(f))));
Expand All @@ -51,11 +51,12 @@ abstract class Applicative<F> implements Functor<F> {
F map4<A, A2 extends A, B, B2 extends B, C, C2 extends C, D, D2 extends D, E>(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<A, A2 extends A, B, B2 extends B, C, C2 extends C, D, D2 extends D, E, E2 extends E, EFF>(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<A, A2 extends A, B, B2 extends B, C, C2 extends C, D, D2 extends D, E, E2 extends E, EFF, EFF2 extends EFF, G>(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<F> /** Applicative<F<G<_>>> **/ composeA(Applicative G) => new ComposedApplicative(this, G);
*/
//Applicative<F> /** Applicative<F<G<_>>> **/ composeA(Applicative G) => new ComposedApplicative(this, G);
}

// Compose Applicative<F<_>> with Applicative<G<_>>, yielding Applicative<F<G<_>>>
/*
class ComposedApplicative<F, G> extends Functor<F> with Applicative<F> {
final Applicative<F> _F;
final Applicative<G> _G;
Expand All @@ -68,10 +69,10 @@ class ComposedApplicative<F, G> extends Functor<F> with Applicative<F> {

@override F map<A, B>(F fga, B f(A _)) => _F.map(fga, (G ga) => _G.map(ga, f));
}

*/
abstract class ApplicativeOps<F, A> implements FunctorOps<F, A> {
F pure<B>(B b);
// F pure<B>(B b);
F ap<B>(covariant F ff);

@override F map<B>(B f(A a)) => ap(pure(f));
@override F map<B>(B f(A a));// => ap(pure(f));
}
4 changes: 2 additions & 2 deletions lib/src/applicative_plus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ abstract class ApplicativePlus<F> implements Applicative<F>, PlusEmpty<F> {
}

abstract class ApplicativePlusOps<F, A> implements ApplicativeOps<F, A>, PlusEmptyOps<F, A> {
F prependElement(A a) => cast<ApplicativePlusOps<F, A>>(pure(a)).plus(cast(this));
F appendElement(A a) => plus(pure(a));
F prependElement(A a);// => cast<ApplicativePlusOps<F, A>>(pure(a)).plus(cast(this));
F appendElement(A a);// => plus(pure(a));
}
61 changes: 47 additions & 14 deletions lib/src/avl_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ part of dartz;

// TODO: naive implementation. does too much work and too many allocations.

class AVLTree<A> extends FoldableOps<AVLTree, A> {
class AVLTree<A> implements FoldableOps<AVLTree, A> {
final Order<A> _order;
final _AVLNode<A> _root;

Expand Down Expand Up @@ -40,6 +40,34 @@ class AVLTree<A> extends FoldableOps<AVLTree, A> {

@override String toString() => 'avltree<${toIList()}>';

@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

@override A concatenate(Monoid<A> mi) => foldMap(mi, id); // TODO: optimize

@override Option<A> concatenateO(Semigroup<A> si) => foldMapO(si, id); // TODO: optimize

@override B foldLeftWithIndex<B>(B z, B f(B previous, int i, A a)) =>
foldLeft<Tuple2<B, int>>(tuple2(z, 0), (t, a) => tuple2(f(t.value1, t.value2, a), t.value2+1)).value1; // TODO: optimize

@override Option<B> foldMapO<B>(Semigroup<B> si, B f(A a)) =>
foldMap(new OptionMonoid(si), composeF(some, f)); // TODO: optimize

@override B foldRightWithIndex<B>(B z, B f(int i, A a, B previous)) =>
foldRight<Tuple2<B, int>>(tuple2(z, length()-1), (a, t) => tuple2(f(t.value2, a, t.value1), t.value2-1)).value1; // TODO: optimize

@override A intercalate(Monoid<A> mi, A a) =>
foldRight(none<A>(), (A ca, Option<A> 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<A> maximum(Order<A> oa) => concatenateO(oa.maxSi());

@override Option<A> minimum(Order<A> oa) => concatenateO(oa.minSi());


// PURISTS BEWARE: side effecty stuff below -- proceed with caution!

Iterable<A> toIterable() => new _AVLTreeIterable(this);
Expand All @@ -65,6 +93,7 @@ abstract class _AVLNode<A> {
int get balance;
Option<Tuple2<_AVLNode<A>, A>> _removeMax();
bool get empty;
_NonEmptyAVLNode<A> _unsafeGetNonEmpty();
}

class _NonEmptyAVLNode<A> extends _AVLNode<A> {
Expand Down Expand Up @@ -112,15 +141,15 @@ class _NonEmptyAVLNode<A> extends _AVLNode<A> {
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;
Expand All @@ -129,11 +158,11 @@ class _NonEmptyAVLNode<A> extends _AVLNode<A> {

_NonEmptyAVLNode<A> llRotate(_NonEmptyAVLNode<A> l) => new _NonEmptyAVLNode(l._a, l._left, new _NonEmptyAVLNode(_a, l._right, _right));

_NonEmptyAVLNode<A> doubleLrRotate(_NonEmptyAVLNode<A> l) => llRotate(l.rrRotate(cast(l._right)));
_NonEmptyAVLNode<A> doubleLrRotate(_NonEmptyAVLNode<A> l) => llRotate(l.rrRotate(l._right._unsafeGetNonEmpty()));

_NonEmptyAVLNode<A> rrRotate(_NonEmptyAVLNode<A> r) => new _NonEmptyAVLNode(r._a, new _NonEmptyAVLNode(_a, _left, r._left), r._right);

_NonEmptyAVLNode<A> doubleRlRotate(_NonEmptyAVLNode<A> r) => rrRotate(r.llRotate(cast(r._left)));
_NonEmptyAVLNode<A> doubleRlRotate(_NonEmptyAVLNode<A> r) => rrRotate(r.llRotate(r._left._unsafeGetNonEmpty()));

B foldLeft<B>(B z, B f(B previous, A a)) {
final leftResult = _left.foldLeft(z, f);
Expand Down Expand Up @@ -179,15 +208,15 @@ class _NonEmptyAVLNode<A> extends _AVLNode<A> {
if (o == Ordering.EQ) {
return some(current._a);
} else if (o == Ordering.LT) {
final l = current._left;
if (l is _NonEmptyAVLNode<A>) {
final l = current._left._unsafeGetNonEmpty();
if (l != null) {
current = l;
} else {
return none();
}
} else {
final r = current._right;
if (r is _NonEmptyAVLNode<A>) {
final r = current._right._unsafeGetNonEmpty();
if (r != null) {
current = r;
} else {
return none();
Expand All @@ -202,6 +231,8 @@ class _NonEmptyAVLNode<A> extends _AVLNode<A> {
Option<A> max() => _right is _EmptyAVLNode ? some(_a) : _right.max();

bool get empty => false;

_NonEmptyAVLNode<A> _unsafeGetNonEmpty() => this;
}

class _EmptyAVLNode<A> extends _AVLNode<A> {
Expand Down Expand Up @@ -236,6 +267,8 @@ class _EmptyAVLNode<A> extends _AVLNode<A> {
@override int get hashCode => 0;

bool get empty => true;

_NonEmptyAVLNode<A> _unsafeGetNonEmpty() => null;
}

_AVLNode<A> emptyAVLNode<A>() => new _EmptyAVLNode();
Expand All @@ -255,7 +288,7 @@ final Foldable<AVLTree> AVLTreeFo = new FoldableOpsFoldable<AVLTree>();
class _AVLTreeIterable<A> extends Iterable<A> {
final AVLTree<A> _tree;
_AVLTreeIterable(this._tree);
@override Iterator<A> get iterator => _tree._root.empty ? new _AVLTreeIterator(null) : new _AVLTreeIterator(cast(_tree._root));
@override Iterator<A> get iterator => new _AVLTreeIterator(_tree._root._unsafeGetNonEmpty());
}

class _AVLTreeIterator<A> extends Iterator<A> {
Expand Down Expand Up @@ -285,7 +318,7 @@ class _AVLTreeIterator<A> extends Iterator<A> {

bool _descend() {
if (!_currentNode._right.empty) {
_currentNode = cast(_currentNode._right);
_currentNode = _currentNode._right._unsafeGetNonEmpty();
_descendLeft();
return true;
} else {
Expand All @@ -304,7 +337,7 @@ class _AVLTreeIterator<A> extends Iterator<A> {
var currentLeft = current._left;
while(true) {
if (!currentLeft.empty) {
final _NonEmptyAVLNode<A> cl = cast(currentLeft);
final _NonEmptyAVLNode<A> cl = currentLeft._unsafeGetNonEmpty();
_path = cons(current, _path);
current = cl;
currentLeft = cl._left;
Expand Down
65 changes: 53 additions & 12 deletions lib/src/either.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
part of dartz;

// Workaround for https://github.com/dart-lang/sdk/issues/29949
abstract class Either<L, R> extends TraversableOps<Either/*<L, dynamic>*/, R> with FunctorOps<Either/*<L, dynamic>*/, R>, ApplicativeOps<Either/*<L, dynamic>*/, R>, MonadOps<Either/*<L, dynamic>*/, R>, TraversableMonadOps<Either/*<L, dynamic>*/, R> {
abstract class Either<L, R> implements TraversableMonadOps<Either<L, dynamic>, R> {
const Either();

B fold<B>(B ifLeft(L l), B ifRight(R r));

Either<L, R> orElse(Either<L, R> other()) => fold((_) => other(), (_) => this);
Expand All @@ -13,14 +14,11 @@ abstract class Either<L, R> extends TraversableOps<Either/*<L, dynamic>*/, R> wi
bool isRight() => fold((_) => false, (_) => true);
Either<R, L> swap() => fold(right, left);

@override Either<L, R2> pure<R2>(R2 r2) => right(r2);
@override Either<L, R2> map<R2>(R2 f(R r)) => fold(left, (R r) => right(f(r)));
@override Either<L, R2> bind<R2>(Either<L, R2> f(R r)) => fold(left, f);
@override Either<L, R2> flatMap<R2>(Either<L, R2> f(R r)) => fold(left, f);
@override Either<L, R2> andThen<R2>(Either<L, R2> next) => fold(left, (_) => next);

@override G traverse<G>(Applicative<G> gApplicative, G f(R r)) => fold((_) => gApplicative.pure(this), (R r) => gApplicative.map(f(r), right));

IList<Either<L, R2>> traverseIList<R2>(IList<R2> f(R r)) => fold((l) => cons(left(l), nil()), (R r) => f(r).map(right));

IVector<Either<L, R2>> traverseIVector<R2>(IVector<R2> f(R r)) => fold((l) => emptyVector<Either<L, R2>>().appendElement(left(l)), (R r) => f(r).map(right));
Expand Down Expand Up @@ -66,17 +64,60 @@ abstract class Either<L, R> extends TraversableOps<Either/*<L, dynamic>*/, R> wi

@override String toString() => fold((l) => 'Left($l)', (r) => 'Right($r)');

@override B foldMap<B>(Monoid<B> bMonoid, B f(R r)) => fold((_) => bMonoid.zero(), f);

@override Either<L, B> mapWithIndex<B>(B f(int i, R r)) => map((r) => f(0, r));

@override Either<L, Tuple2<int, R>> 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;

@override R concatenate(Monoid<R> mi) => getOrElse(mi.zero);

@override Option<R> concatenateO(Semigroup<R> si) => toOption();

@override B foldLeft<B>(B z, B f(B previous, R r)) => fold((_) => z, (a) => f(z, a));

@override B foldLeftWithIndex<B>(B z, B f(B previous, int i, R r)) => fold((_) => z, (a) => f(z, 0, a));

@override Option<B> foldMapO<B>(Semigroup<B> si, B f(R r)) => map(f).toOption();

@override B foldRight<B>(B z, B f(R r, B previous)) => fold((_) => z, (a) => f(a, z));

@override B foldRightWithIndex<B>(B z, B f(int i, R r, B previous))=> fold((_) => z, (a) => f(0, a, z));

@override R intercalate(Monoid<R> mi, R r) => fold((_) => mi.zero(), id);

@override int length() => fold((_) => 0, (_) => 1);

@override Option<R> maximum(Order<R> or) => toOption();

@override Option<R> minimum(Order<R> or) => toOption();

@override Either<L, B> replace<B>(B replacement) => map((_) => replacement);

Either<L, R> reverse() => this;

@override Either<L, Tuple2<B, R>> strengthL<B>(B b) => map((a) => tuple2(b, a));

@override Either<L, Tuple2<R, B>> strengthR<B>(B b) => map((a) => tuple2(a, b));

@override Either<L, B> ap<B>(Either<L, Function1<R, B>> ff) => ff.bind((f) => map(f));

// PURISTS BEWARE: side effecty stuff below -- proceed with caution!

Iterable<R> toIterable() => fold((_) => cast(_emptyIterable), (r) => new _SingletonIterable(r));
Iterable<R> toIterable() => fold((_) => const Iterable.empty(), (r) => new _SingletonIterable(r));
Iterator<R> iterator() => toIterable().iterator;

void forEach(void sideEffect(R r)) => fold((_) => null, sideEffect);
}

class Left<L, R> extends Either<L, R> {
final L _l;
Left(this._l);
const Left(this._l);
L get value => _l;
@override B fold<B>(B ifLeft(L l), B ifRight(R r)) => ifLeft(_l);
@override bool operator ==(other) => other is Left && other._l == _l;
Expand All @@ -85,7 +126,7 @@ class Left<L, R> extends Either<L, R> {

class Right<L, R> extends Either<L, R> {
final R _r;
Right(this._r);
const Right(this._r);
R get value => _r;
@override B fold<B>(B ifLeft(L l), B ifRight(R r)) => ifRight(_r);
@override bool operator ==(other) => other is Right && other._r == _r;
Expand All @@ -108,10 +149,10 @@ class EitherMonad<L> extends MonadOpsMonad<Either<L, dynamic>> {
}

final EitherMonad EitherM = new EitherMonad();
EitherMonad<L> eitherM<L>() => cast(EitherM);
EitherMonad<L> eitherM<L>() => new EitherMonad();
final Traversable<Either> EitherTr = new TraversableOpsTraversable<Either>();
Traversable<Either<L, R>> eitherTr<L, R>() => cast(EitherTr);

Traversable<Either<L, R>> eitherTr<L, R>() => new TraversableOpsTraversable();
/*
class EitherTMonad<M> extends Functor<M> with Applicative<M>, Monad<M> {
Monad _stackedM;
EitherTMonad(this._stackedM);
Expand All @@ -122,4 +163,4 @@ class EitherTMonad<M> extends Functor<M> with Applicative<M>, Monad<M> {
}

Monad eitherTMonad(Monad mmonad) => new EitherTMonad(mmonad);

*/
Loading