Skip to content

Commit a4fad89

Browse files
v1.0.0 (#124)
2 parents 194e146 + 9e90b42 commit a4fad89

116 files changed

Lines changed: 6615 additions & 3665 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import 'package:fpdart/fpdart.dart';
2+
3+
void helloWorld(String message) {
4+
print("Hello World: $message");
5+
}
6+
7+
/// 1️⃣ Pure function (Thunk)
8+
void Function() helloWorld1(String message) => () {
9+
print("Hello World: $message");
10+
};
11+
12+
/// A thunk with no error is called [IO] in `fpdart`
13+
IO<void> helloWorld1Fpdart(String message) => IO(() {
14+
print("Hello World: $message");
15+
});
16+
17+
/// 2️⃣ Explicit error
18+
/// Understand from the return type if and how the function may fail
19+
Either<Never, void> Function() helloWorld2(String message) => () {
20+
print("Hello World: $message");
21+
return Either.of(null);
22+
};
23+
24+
/// A thunk with explicit error [Either] is called [IOEither] in `fpdart`
25+
IOEither<Never, void> helloWorld2Fpdart1(String message) => IOEither(() {
26+
print("Hello World: $message");
27+
return Either.of(null);
28+
});
29+
30+
/// ...or using the `right` constructor
31+
IOEither<Never, void> helloWorld2Fpdart2(String message) => IOEither.right(() {
32+
print("Hello World: $message");
33+
});
34+
35+
/// 3️⃣ Explicit dependency
36+
/// Provide the `print` method as a dependency instead of implicit global function
37+
38+
abstract class Console {
39+
void log(Object? object);
40+
}
41+
42+
class ConsoleImpl implements Console {
43+
@override
44+
void log(Object? object) {
45+
print(object);
46+
}
47+
}
48+
49+
Either<Never, void> Function() Function(Console) helloWorld3(String message) =>
50+
(console) => () {
51+
console.log("Hello World: $message");
52+
return Either.of(null);
53+
};
54+
55+
/// Thunk (async) + error + dependency is called [ReaderTaskEither] in `fpdart`
56+
ReaderTaskEither<Console, Never, void> helloWorld3Fpdart(String message) =>
57+
ReaderTaskEither((console) async {
58+
console.log("Hello World: $message");
59+
return Either.of(null);
60+
});
61+
62+
void main(List<String> args) {
63+
final definition = helloWorld3("Sandro");
64+
final thunk = definition(ConsoleImpl());
65+
thunk();
66+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
void helloWorld(String message) {
2+
print("Hello World: $message");
3+
}
4+
5+
void main(List<String> args) {
6+
helloWorld("Sandro");
7+
}

examples/hello_world/pubspec.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: fpdart_hello_world
2+
publish_to: none
3+
version: 0.1.0
4+
homepage: https://www.sandromaglione.com/
5+
repository: https://github.com/SandroMaglione/fpdart
6+
description: Example of Functional programming in Dart and Flutter using fpdart. Write a simple "Hello World" using fpdart.
7+
author: Maglione Sandro <lass.maglio@gmail.com>
8+
9+
environment:
10+
sdk: ">=3.0.0 <4.0.0"
11+
12+
dependencies:
13+
fpdart:
14+
path: ../../packages/fpdart
15+
16+
dev_dependencies:
17+
lint: ^2.1.2
18+
test: ^1.24.3
19+
mocktail: ^0.3.0
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import 'package:mocktail/mocktail.dart';
2+
import 'package:test/test.dart';
3+
4+
import '../bin/fpdart_hello_world.dart';
5+
6+
class ConsoleTest extends Mock implements Console {}
7+
8+
void main() {
9+
group('helloWorld3Fpdart', () {
10+
test(
11+
'should call "log" from the "Console" dependency with the correct input',
12+
() async {
13+
final console = ConsoleTest();
14+
const input = "test";
15+
16+
when(() => console.log(any)).thenReturn(null);
17+
await helloWorld3Fpdart(input).run(console);
18+
verify(() => console.log("Hello World: $input")).called(1);
19+
},
20+
);
21+
});
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import 'package:test/test.dart';
2+
3+
void main() {
4+
group('helloWorld', () {
5+
/// `'should call "print" with the correct input'`
6+
///
7+
/// This is difficult to test, since `print` is an implicit dependency 🙌
8+
///
9+
/// Furthermore, `print` will be executed at every test. Imagine having a
10+
/// request to update a production database instead of `print` (both are side-effects),
11+
/// you do not want to interact with a real database with tests ⚠️
12+
});
13+
}
Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
# Managing Imports
22

3-
Naming things is hard. Sometimes, the same name gets used for different things. In Dart, naming conflicts can be mitigated through the use of import prefixes, as well as show and hide operations. This is particularly important when using a package like `fpdart` that provides a lot of classes with common names.
3+
Naming things is hard. Sometimes, the same name gets used for different things. In Dart, naming conflicts can be mitigated through the use of [import prefixes](https://dart.dev/language/libraries#specifying-a-library-prefix), as well as [show and hide operations](https://dart.dev/language/libraries#importing-only-part-of-a-library).
44

5-
Suppose you decide to use `fpdart` with your Flutter program. You'll quickly discover that `fpdart` uses `State` as a class name, which conflicts with the `State` class in Flutter.
5+
This is particularly important when using a package like `fpdart` that provides a lot of classes with common names.
66

7-
That's problem 1.
7+
As an example, suppose you decide to use `fpdart` with your Flutter program. You'll quickly discover that `fpdart` uses `State` as a class name, which conflicts with the `State` class in Flutter.
88

9-
Now also suppose you also choose to use `fpdart`'s `Tuple2` class. That's a lot less likely to conflict with anything, but it's still possible. However, you also decide you need a `Tuple3`. `fpdart` doesn't have one. (And likely never will, thanks to the upcoming records feature.)
9+
The solution is to create an import shim that solves both of these problems. We'll call it `functional.dart`. This shim will import `fpdart`, and re-export the classes we want to use. We can rename `fpdart`'s `State` to `FpState` to avoid the conflict. We can then import `functional.dart` instead of `fpdart`.
1010

11-
However, you found one in the [tuple](https://pub.dev/packages/tuple) package, along with `Tuple4` and `Tuple5`, even though it doesn't have much more than element accessors. Close enough for your application.
11+
`functional.dart` can also hold any other functional programming utilities we want to use. It can also be used to provide or import class extensions and mapping functions between our types and the functional types.
1212

13-
But now, you decide to import `tuple` as well, and you get a naming conflict for `Tuple2`.
14-
15-
That's problem 2.
16-
17-
The solution is to create an import shim that solves both of these problems. We'll call it `functional.dart`. This shim will import `fpdart` and `tuple`, and re-export the classes we want to use. And, we can rename `fpdart`'s `State` to `FpState` to avoid the conflict. We can then import `functional.dart` instead of `fpdart` and `tuple`.
18-
19-
`functional.dart` can also hold any other functional programming utilities we want to use. It can also be used to provide or import class extensions and mapping functions between our types and the functional types. A one-stop shop for functional programming in Dart!
13+
A one-stop shop for functional programming in Dart!
Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,17 @@
11
import 'package:managing_imports/functional.dart';
22
import 'package:test/test.dart';
33

4-
void main(List<String> arguments) {
5-
// borrow the flatMap test from state_test.dart
4+
void main() {
5+
/// Borrow the `flatMap` test from `state_test.dart`
66
test('flatMap', () {
7-
final state = FpState<List<int>, int>((s) => Tuple2(s.first, s.sublist(1)));
7+
final state = FpState<List<int>, int>((s) => (s.first, s.sublist(1)));
88
final ap = state.flatMap<double>(
99
(a) => FpState(
10-
(s) => Tuple2(a / 2, s.sublist(1)),
10+
(s) => (a / 2, s.sublist(1)),
1111
),
1212
);
1313
final result = ap.run([1, 2, 3, 4, 5]);
14-
expect(result.first, 0.5);
15-
expect(result.second, [3, 4, 5]);
16-
});
17-
18-
test('Tuple2', () {
19-
const tuple = Tuple2(1, 2);
20-
// this is the item access syntax for the fpdart package
21-
expect(tuple.first, 1);
22-
expect(tuple.second, 2);
23-
});
24-
25-
test('Tuple3', () {
26-
const tuple = Tuple3(1, 2, 3);
27-
// this is the item access syntax for the tuple package
28-
expect(tuple.item1, 1);
29-
expect(tuple.item2, 2);
30-
expect(tuple.item3, 3);
14+
expect(result.$1, 0.5);
15+
expect(result.$2, [3, 4, 5]);
3116
});
3217
}

examples/managing_imports/lib/functional.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ import 'package:fpdart/fpdart.dart' as fpdart show State;
88

99
// The `fpdart` library is used to create functional programming constructs.
1010
export 'package:fpdart/fpdart.dart' hide State;
11-
// The `tuple` library is used to create tuples, which are immutable lists of
12-
// fixed length.
13-
export 'package:tuple/tuple.dart' show Tuple3, Tuple4, Tuple5, Tuple6, Tuple7;
1411

1512
/// A type alias for the `State` class from the `fpdart` library.
1613
typedef FpState<S, A> = fpdart.State<S, A>;

examples/managing_imports/pubspec.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ version: 1.0.0
44
publish_to: none
55

66
environment:
7-
sdk: ">=2.18.6 <3.0.0"
7+
sdk: ">=3.0.0 <4.0.0"
88

99
dependencies:
1010
fpdart:
1111
path: ../../packages/fpdart
1212
test:
13-
tuple:
1413
very_good_analysis:
1514

1615
dev_dependencies:

examples/open_meteo_api/lib/src/fpdart/open_meteo_api_client_fpdart.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,44 +29,44 @@ class OpenMeteoApiClientFpdart {
2929
),
3030
LocationHttpRequestFpdartFailure.new,
3131
).chainEither(
32-
(response) => Either.Do(($) {
33-
final body = $(
32+
(response) => Either.Do((_) {
33+
final body = _(
3434
_validResponseBody(response, LocationRequestFpdartFailure.new),
3535
);
3636

37-
final json = $(
37+
final json = _(
3838
Either.tryCatch(
3939
() => jsonDecode(body),
4040
(_, __) => LocationInvalidJsonDecodeFpdartFailure(body),
4141
),
4242
);
4343

44-
final data = $(
44+
final data = _(
4545
Either<OpenMeteoApiFpdartLocationFailure,
4646
Map<dynamic, dynamic>>.safeCast(
4747
json,
4848
LocationInvalidMapFpdartFailure.new,
4949
),
5050
);
5151

52-
final currentWeather = $(
52+
final currentWeather = _(
5353
data
5454
.lookup('results')
5555
.toEither(LocationKeyNotFoundFpdartFailure.new),
5656
);
5757

58-
final results = $(
58+
final results = _(
5959
Either<OpenMeteoApiFpdartLocationFailure, List<dynamic>>.safeCast(
6060
currentWeather,
6161
LocationInvalidListFpdartFailure.new,
6262
),
6363
);
6464

65-
final weather = $(
65+
final weather = _(
6666
results.head.toEither(LocationDataNotFoundFpdartFailure.new),
6767
);
6868

69-
return $(
69+
return _(
7070
Either.tryCatch(
7171
() => Location.fromJson(weather as Map<String, dynamic>),
7272
LocationFormattingFpdartFailure.new,

0 commit comments

Comments
 (0)