Skip to content

Commit

Permalink
Tuple implementation and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
SandroMaglione committed Jun 15, 2021
1 parent 3226163 commit 9addc80
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 71 deletions.
14 changes: 7 additions & 7 deletions example/src/state/state1.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ State<Stack, Unit> pushState(String value) =>
void main() {
// Without State Monad
final pop1NoState = pop(stack);
final push1NoState = push('d', pop1NoState.value2);
final pop2NoState = pop(push1NoState.value2);
final push1NoState = push('d', pop1NoState.second);
final pop2NoState = pop(push1NoState.second);
print('No State');
print(stack);
print('Pop');
print(pop1NoState.value2);
print(pop1NoState.value1);
print(pop1NoState.second);
print(pop1NoState.first);
print("Push 'd'");
print(push1NoState.value2);
print(push1NoState.second);
print('Pop');
print(pop2NoState.value2);
print(pop2NoState.value1);
print(pop2NoState.second);
print(pop2NoState.first);

// Using State Monad
print('---');
Expand Down
2 changes: 1 addition & 1 deletion lib/src/either.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'function.dart';
import 'option.dart';
import 'typeclass/typeclass.export.dart';

/// Tag the [HKT] interface for the actual [Either].
/// Tag the [HKT2] interface for the actual [Either].
abstract class _EitherHKT {}

/// Represents a value of one of two possible types, [Left] or [Right].
Expand Down
6 changes: 3 additions & 3 deletions lib/src/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class State<S, A> extends HKT2<StateHKT, S, A> with Monad2<StateHKT, S, A> {

@override
State<S, C> flatMap<C>(covariant State<S, C> Function(A a) f) =>
State((state) => f(run(state).value1).run(state));
State((state) => f(run(state).first).run(state));

@override
State<S, C> ap<C>(covariant State<S, C Function(A a)> a) =>
Expand All @@ -32,10 +32,10 @@ class State<S, A> extends HKT2<StateHKT, S, A> with Monad2<StateHKT, S, A> {
State<S, Unit> put(S state) => State((_) => Tuple2(unit, state));

/// Execute `run` and extract the value [A].
A evalState(S state) => run(state).value1;
A evalState(S state) => run(state).first;

/// Execute `run` and extract the state [S].
S execState(S state) => run(state).value2;
S execState(S state) => run(state).second;

/// Extract value [A] and state [S] by passing the original state [S].
Tuple2<A, S> run(S state) => _run(state);
Expand Down
2 changes: 1 addition & 1 deletion lib/src/task_either.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:fpdart/fpdart.dart';
import 'package:fpdart/src/task.dart';

/// Tag the [HKT] interface for the actual [Task].
/// Tag the [HKT2] interface for the actual [Task].
abstract class _TaskEitherHKT {}

/// `TaskEither<L, R>` represents an asynchronous computation that
Expand Down
158 changes: 146 additions & 12 deletions lib/src/tuple.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,156 @@
class Tuple2<T1, T2> {
final T1 value1;
final T2 value2;
const Tuple2(this.value1, this.value2);
import 'package:fpdart/fpdart.dart';

A apply<A>(A Function(T1 v1, T2 v2) f) => f(value1, value2);
/// Tag the [HKT2] interface for the actual [Tuple2].
abstract class _Tuple2HKT {}

Tuple2<TN, T2> map1<TN>(TN Function(T1 v1) f) => Tuple2(f(value1), value2);
Tuple2<T1, TN> map2<TN>(TN Function(T2 v2) f) => Tuple2(value1, f(value2));
/// `Tuple2<T1, T2>` is a type that contains a value of type `T1` and a value of type `T2`.
///
/// Used to avoid creating a custom class that contains two different types
/// or to return two values from one function.
class Tuple2<T1, T2> extends HKT2<_Tuple2HKT, T1, T2>
with
Functor2<_Tuple2HKT, T1, T2>,
Extend2<_Tuple2HKT, T1, T2>,
Foldable2<_Tuple2HKT, T1, T2> {
final T1 _value1;
final T2 _value2;

/// Build a [Tuple2] given its first and second values.
const Tuple2(this._value1, this._value2);

/// Get first value inside the [Tuple].
T1 get first => _value1;

/// Get second value inside the [Tuple].
T2 get second => _value2;

/// Return result of calling `f` given `value1` and `value2` from this [Tuple].
A apply<A>(A Function(T1 first, T2 second) f) => f(_value1, _value2);

/// Change type of first value of the [Tuple] from `T1` to `TN` using `f`.
Tuple2<TN, T2> mapFirst<TN>(TN Function(T1 first) f) =>
Tuple2(f(_value1), _value2);

/// Change type of second value of the [Tuple] from `T2` to `TN` using `f`.
Tuple2<T1, TN> mapSecond<TN>(TN Function(T2 second) f) =>
Tuple2(_value1, f(_value2));

/// Change type of second value of the [Tuple] from `T2` to `C` using `f`.
///
/// Same as `mapSecond`.
@override
String toString() => 'Tuple2($value1, $value2)';
Tuple2<T1, C> map<C>(C Function(T2 a) f) => mapSecond(f);

/// Convert the second value of the [Tuple2] from `T2` to `Z` using `f`.
@override
Tuple2<T1, Z> extend<Z>(Z Function(Tuple2<T1, T2> t) f) =>
Tuple2(_value1, f(this));

/// Wrap this [Tuple2] inside another [Tuple2].
@override
Tuple2<T1, Tuple2<T1, T2>> duplicate() => extend(identity);

/// Convert the first value of the [Tuple2] from `T1` to `Z` using `f`.
Tuple2<Z, T2> extendFirst<Z>(Z Function(Tuple2<T1, T2> t) f) =>
Tuple2(f(this), _value2);

/// Return value of type `C` by calling `f` with `b` and the second value of the [Tuple2].
///
/// Same as `foldLeft`.
@override
C foldRight<C>(C b, C Function(T2 a, C b) f) => f(_value2, b);

/// Return value of type `C` by calling `f` with `b` and the first value of the [Tuple2].
///
/// Same as `foldLeftFirst`.
C foldRightFirst<C>(C b, C Function(T1 a, C b) f) => f(_value1, b);

/// Return value of type `C` by calling `f` with `b` and the second value of the [Tuple2].
///
/// Same as `foldRight`.
@override
C foldLeft<C>(C b, C Function(C b, T2 a) f) =>
foldMap<Endo<C>>(dualEndoMonoid(), (a) => (C b) => f(b, a))(b);

/// Return value of type `C` by calling `f` with `b` and the first value of the [Tuple2].
///
/// Same as `foldRightFirst`.
C foldLeftFirst<C>(C b, C Function(C b, T1 a) f) =>
foldMapFirst<Endo<C>>(dualEndoMonoid(), (a) => (C b) => f(b, a))(b);

/// Return value of type `C` by applying `f` on `monoid`.
@override
C foldMap<C>(Monoid<C> monoid, C Function(T2 a) f) =>
foldRight(monoid.empty, (a, b) => monoid.combine(f(a), b));

/// Return value of type `C` by applying `f` on `monoid`.
C foldMapFirst<C>(Monoid<C> monoid, C Function(T1 a) f) =>
foldRightFirst(monoid.empty, (a, b) => monoid.combine(f(a), b));

/// Return value of type `C` by calling `f` with `b` and the second value of the [Tuple2].
@override
C foldRightWithIndex<C>(C c, C Function(int i, T2 b, C c) f) =>
foldRight<Tuple2<C, int>>(
Tuple2(c, length() - 1),
(a, t) => Tuple2(f(t.second, a, t.first), t.second - 1),
).first;

/// Return value of type `C` by calling `f` with `b` and the first value of the [Tuple2].
C foldRightFirstWithIndex<C>(C c, C Function(int i, T1 b, C c) f) =>
foldRightFirst<Tuple2<C, int>>(
Tuple2(c, length() - 1),
(a, t) => Tuple2(f(t.second, a, t.first), t.second - 1),
).first;

/// Return value of type `C` by calling `f` with `b` and the second value of the [Tuple2].
@override
C foldLeftWithIndex<C>(C c, C Function(int i, T2 b, C c) f) =>
foldLeft<Tuple2<C, int>>(
Tuple2(c, 0),
(t, a) => Tuple2(f(t.second, a, t.first), t.second + 1),
).first;

/// Return value of type `C` by calling `f` with `b` and the first value of the [Tuple2].
C foldLeftFirstWithIndex<C>(C c, C Function(int i, T1 b, C c) f) =>
foldLeftFirst<Tuple2<C, int>>(
Tuple2(c, 0),
(t, a) => Tuple2(f(t.second, a, t.first), t.second + 1),
).first;

/// Returns `1`.
@override
int length() => foldLeft(0, (b, _) => b + 1);

/// Return the result of calling `predicate` on the second value of the [Tuple2].
@override
bool any(bool Function(T2 a) predicate) => foldMap(boolOrMonoid(), predicate);

/// Return the result of calling `predicate` on the second value of the [Tuple2].
@override
bool all(bool Function(T2 a) predicate) =>
foldMap(boolAndMonoid(), predicate);

/// Combine the second value of [Tuple2] using `monoid`.
@override
T2 concatenate(Monoid<T2> monoid) => foldMap(monoid, identity);

/// Swap types `T1` and `T2` of [Tuple2].
Tuple2<T2, T1> swap() => Tuple2(_value2, _value1);

/// Create a copy of this [Tuple] by changing `value1` and/or `value2`.
Tuple2<T1, T2> copyWith({
T1? value1,
T2? value2,
}) =>
Tuple2(
value1 ?? this.value1,
value2 ?? this.value2,
);
Tuple2(value1 ?? _value1, value2 ?? _value2);

@override
String toString() => 'Tuple2($_value1, $_value2)';

@override
bool operator ==(Object other) =>
(other is Tuple2) && other._value1 == _value1 && other._value2 == _value2;

@override
int get hashCode => _value1.hashCode ^ _value2.hashCode;
}
16 changes: 8 additions & 8 deletions lib/src/typeclass/foldable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ abstract class Foldable<G, A> extends HKT<G, A> {
B foldRightWithIndex<B>(B b, B Function(int i, A a, B b) f) =>
foldRight<Tuple2<B, int>>(
Tuple2(b, length() - 1),
(a, t) => Tuple2(f(t.value2, a, t.value1), t.value2 - 1),
).value1;
(a, t) => Tuple2(f(t.second, a, t.first), t.second - 1),
).first;

B foldLeftWithIndex<B>(B b, B Function(int i, A a, B b) f) =>
foldLeft<Tuple2<B, int>>(
Tuple2(b, 0),
(t, a) => Tuple2(f(t.value2, a, t.value1), t.value2 + 1),
).value1;
(t, a) => Tuple2(f(t.second, a, t.first), t.second + 1),
).first;

int length() => foldLeft(0, (b, _) => b + 1);

Expand Down Expand Up @@ -58,14 +58,14 @@ abstract class Foldable2<G, A, B> extends HKT2<G, A, B> {
C foldRightWithIndex<C>(C c, C Function(int i, B b, C c) f) =>
foldRight<Tuple2<C, int>>(
Tuple2(c, length() - 1),
(a, t) => Tuple2(f(t.value2, a, t.value1), t.value2 - 1),
).value1;
(a, t) => Tuple2(f(t.second, a, t.first), t.second - 1),
).first;

C foldLeftWithIndex<C>(C c, C Function(int i, B b, C c) f) =>
foldLeft<Tuple2<C, int>>(
Tuple2(c, 0),
(t, a) => Tuple2(f(t.value2, a, t.value1), t.value2 + 1),
).value1;
(t, a) => Tuple2(f(t.second, a, t.first), t.second + 1),
).first;

int length() => foldLeft(0, (b, _) => b + 1);

Expand Down
32 changes: 16 additions & 16 deletions test/src/option_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -203,22 +203,22 @@ void main() {
test('Some (true)', () {
final option = Option.of(10);
final value = option.partition((a) => a > 5);
expect(value.value1, isA<None>());
value.value2.match((some) => expect(some, 10), () => null);
expect(value.first, isA<None>());
value.second.match((some) => expect(some, 10), () => null);
});

test('Some (false)', () {
final option = Option.of(10);
final value = option.partition((a) => a < 5);
value.value1.match((some) => expect(some, 10), () => null);
expect(value.value2, isA<None>());
value.first.match((some) => expect(some, 10), () => null);
expect(value.second, isA<None>());
});

test('None', () {
final option = Option<int>.none();
final value = option.partition((a) => a > 5);
expect(value.value1, isA<None>());
expect(value.value2, isA<None>());
expect(value.first, isA<None>());
expect(value.second, isA<None>());
});
});

Expand All @@ -227,24 +227,24 @@ void main() {
final option = Option.of(10);
final value =
option.partitionMap<String, double>((a) => Either.of(a / 2));
expect(value.value1, isA<None>());
value.value2.match((some) => expect(some, 5.0), () => null);
expect(value.first, isA<None>());
value.second.match((some) => expect(some, 5.0), () => null);
});

test('Some (left)', () {
final option = Option.of(10);
final value =
option.partitionMap<String, double>((a) => Either.left('$a'));
value.value1.match((some) => expect(some, '10'), () => null);
expect(value.value2, isA<None>());
value.first.match((some) => expect(some, '10'), () => null);
expect(value.second, isA<None>());
});

test('None', () {
final option = Option<int>.none();
final value =
option.partitionMap<String, double>((a) => Either.of(a / 2));
expect(value.value1, isA<None>());
expect(value.value2, isA<None>());
expect(value.first, isA<None>());
expect(value.second, isA<None>());
});
});

Expand Down Expand Up @@ -301,15 +301,15 @@ void main() {
group('separate', () {
test('Right', () {
final option = Option.separate<String, int>(Option.of(Either.of(10)));
expect(option.value1, isA<None>());
option.value2.match((some) => expect(some, 10), () => null);
expect(option.first, isA<None>());
option.second.match((some) => expect(some, 10), () => null);
});

test('Left', () {
final option =
Option.separate<String, int>(Option.of(Either.left('none')));
option.value1.match((some) => expect(some, 'none'), () => null);
expect(option.value2, isA<None>());
option.first.match((some) => expect(some, 'none'), () => null);
expect(option.second, isA<None>());
});
});

Expand Down
Loading

0 comments on commit 9addc80

Please sign in to comment.