diff --git a/lib/dfunc.dart b/lib/dfunc.dart
index 8973813..c75d9d5 100644
--- a/lib/dfunc.dart
+++ b/lib/dfunc.dart
@@ -23,6 +23,7 @@ export 'src/map.dart';
export 'src/map_by.dart';
export 'src/map_indexed.dart';
export 'src/maybe.dart';
+export 'src/memoize.dart';
export 'src/optional.dart';
export 'src/pipe.dart';
export 'src/scope.dart';
diff --git a/lib/src/memoize.dart b/lib/src/memoize.dart
new file mode 100644
index 0000000..52bf71f
--- /dev/null
+++ b/lib/src/memoize.dart
@@ -0,0 +1,47 @@
+/// {@template dfunc.memoize}
+/// The `memoize` function is used to cache the results of expensive function
+/// calls and return the cached result when the same inputs are provided again.
+///
+/// Usage:
+///
+/// ```dart
+/// final memoized = memoize(expensiveFunction);
+/// final result = memoized(arg);
+/// ```
+/// {@endtemplate}
+Memoize memoize(
+ A Function(B lastArgument) f, {
+ int? capacity,
+}) =>
+ Memoize(f, capacity: capacity);
+
+/// {@macro dfunc.memoize}
+class Memoize {
+ /// {@macro dfunc.memoize}
+ Memoize(this._f, {int? capacity}) : _capacity = capacity;
+
+ final Map _cache = {};
+
+ final A Function(B lastArgument) _f;
+ final int? _capacity;
+
+ /// Returns the cached value for the argument [b] if it exists, otherwise
+ /// computes the value using the function [_f] and caches it.
+ A call(B b) {
+ if (_cache.containsKey(b)) return _cache[b] as A;
+
+ final value = _f(b);
+ _cache[b] = value;
+
+ // ignore: avoid-non-null-assertion, checked for null
+ if (_capacity != null && _cache.length > _capacity!) {
+ _cache.remove(_cache.keys.first);
+ }
+
+ return value;
+ }
+
+ /// Clears the memoization cache, subsequent call with the same argument will
+ /// recompute the value.
+ void reset() => _cache.clear();
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index a7d613f..f6c7fc1 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -8,5 +8,5 @@ environment:
dev_dependencies:
melos: ^3.0.1
- mews_pedantic: ^0.23.0
+ mews_pedantic: ^0.26.0
test: ^1.16.2
diff --git a/test/intersperse_test.dart b/test/intersperse_test.dart
index f7d0c0c..c55cea8 100644
--- a/test/intersperse_test.dart
+++ b/test/intersperse_test.dart
@@ -1,3 +1,5 @@
+// ignore_for_file: avoid-unnecessary-collections, avoid-duplicate-collection-elements
+
import 'package:dfunc/dfunc.dart';
import 'package:test/test.dart';
diff --git a/test/memoize_test.dart b/test/memoize_test.dart
new file mode 100644
index 0000000..f250f01
--- /dev/null
+++ b/test/memoize_test.dart
@@ -0,0 +1,81 @@
+// ignore_for_file: avoid-duplicate-test-assertions, avoid-duplicate-collection-elements
+
+import 'package:dfunc/dfunc.dart';
+import 'package:test/test.dart';
+
+void main() {
+ test('memoize', () {
+ int counter = 0;
+
+ final memoized = memoize((int arg) {
+ counter++;
+
+ return arg * 2;
+ });
+
+ expect(memoized(1), 2);
+ expect(counter, 1);
+
+ // Doesn't call the function again.
+ expect(memoized(1), 2);
+ expect(counter, 1);
+
+ // Calls the function for a different argument.
+ expect(memoized(2), 4);
+ expect(counter, 2);
+
+ // Doesn't call the function again.
+ expect(memoized(1), 2);
+ expect(counter, 2);
+ });
+
+ test('memoize with limited capacity', () {
+ int counter = 0;
+
+ final memoized = memoize(capacity: 1, (int arg) {
+ counter++;
+
+ return arg * 2;
+ });
+
+ expect(memoized(1), 2);
+ expect(counter, 1);
+
+ // Doesn't call the function again.
+ expect(memoized(1), 2);
+ expect(counter, 1);
+
+ // Calls the function for a different argument.
+ expect(memoized(2), 4);
+ expect(counter, 2);
+
+ // Calls the function again if capacity exceeded.
+ expect(memoized(1), 2);
+ expect(counter, 3);
+ });
+
+ test('memoize async', () async {
+ int counter = 0;
+
+ final memoized = memoize((int arg) async {
+ await Future.delayed(Duration.zero);
+ counter++;
+
+ return arg * 2;
+ });
+
+ expect(await memoized(1), 2);
+ expect(counter, 1);
+
+ // Doesn't call the function again.
+ expect(await memoized(1), 2);
+ expect(counter, 1);
+
+ // Calls the function one time for a different argument.
+ expect(
+ await Future.wait([memoized(2), memoized(2), memoized(2)]),
+ [4, 4, 4],
+ );
+ expect(counter, 2);
+ });
+}