diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6e6519becdede37024477578eec9023bfa60ee40..32afcc27e8a64c825f749874c366d98210234f15 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,10 +12,6 @@ stages:
       - vendor/
     expire_in: 1 day
 
-install.php81:
-  extends: .install
-  image: grifart/php8.1-with-all-modules-and-various-tools
-
 install.php82:
   extends: .install
   image: grifart/php8.2-with-all-modules-and-various-tools
@@ -24,11 +20,15 @@ install.php83:
   extends: .install
   image: grifart/php8.3-with-all-modules-and-various-tools
 
+install.php84:
+  extends: .install
+  image: grifart/php8.4-with-all-modules-and-various-tools
+
 .tests:
   stage: test
   interruptible: true
   services:
-    - postgres:14-alpine
+    - postgres:16-alpine
   variables:
     POSTGRES_DB: tables
     POSTGRES_USER: postgres
@@ -39,12 +39,6 @@ install.php83:
   script:
     - composer run test
 
-tests.php81:
-  extends: .tests
-  image: grifart/php8.1-with-all-modules-and-various-tools
-  dependencies:
-    - install.php81
-
 tests.php82:
   extends: .tests
   image: grifart/php8.2-with-all-modules-and-various-tools
@@ -57,11 +51,17 @@ tests.php83:
   dependencies:
     - install.php83
 
+tests.php84:
+  extends: .tests
+  image: grifart/php8.4-with-all-modules-and-various-tools
+  dependencies:
+    - install.php84
+
 phpstan:
   stage: test
-  image: grifart/php8.3-with-all-modules-and-various-tools
+  image: grifart/php8.4-with-all-modules-and-various-tools
   interruptible: true
   dependencies:
-    - install.php83
+    - install.php84
   script:
     - composer run phpstan
diff --git a/composer.json b/composer.json
index 6f141889c4538810117757663bfffbeed3b08d89..43beb4f5e568f93bc6088038a15385c0cf1e0261 100644
--- a/composer.json
+++ b/composer.json
@@ -22,22 +22,22 @@
 		"test": "tester -C tests"
 	},
 	"require": {
-		"php": "^8.1",
+		"php": "^8.2",
 		"dibi/dibi": "^4.0.2||^5.0",
 		"grifart/scaffolder": "^0.6.3",
-		"lstrojny/functional-php": "^1.17",
+		"jiripudil/phun": "^0.1.0",
 		"nette/utils": "^3.0.1||^4.0"
 	},
 	"require-dev": {
-		"brick/date-time": "^0.4.1",
-		"brick/math": "^0.11.0",
-		"grifart/phpstan-oneline": "^0.4.0",
-		"nette/bootstrap": "^3.1",
-		"nette/di": "^3.0",
-		"nette/tester": "^2.3",
-		"nikic/php-parser": "^4.13",
-		"phpstan/phpstan": "^1.0",
-		"ramsey/uuid": "^4.2"
+		"brick/date-time": "^0.7.0",
+		"brick/math": "^0.12.0",
+		"grifart/phpstan-oneline": "^0.5.0",
+		"nette/bootstrap": "^3.2",
+		"nette/di": "^3.2",
+		"nette/tester": "^2.5",
+		"nikic/php-parser": "^5.4",
+		"phpstan/phpstan": "^2.0",
+		"ramsey/uuid": "^4.7"
 	},
 	"suggest": {
 		"brick/date-time": "if you want to use date-time types",
diff --git a/phpstan.neon b/phpstan.neon
index 885431b029a9dc2a97b1be4c8982c9548773c29c..d974faf007f4dd310c1c31cf741672b331da7c01 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -11,12 +11,3 @@ parameters:
 
 	excludePaths:
 		- tests/DI/temp/*
-
-	ignoreErrors:
-		# null() / notNull() currently require type parameter so that it can be used in the return annotation.
-		# A better solution would be to use a type projection in the return type, but that's not currently supported in PHPStan.
-		# more info: https://gitlab.grifart.cz/grifart/tables/-/merge_requests/38#note_90571, https://github.com/phpstan/phpstan/issues/3290
-		-
-			message: "#^Template type ValueType of function Grifart\\\\Tables\\\\Conditions\\\\(null|notNull)\\(\\) is not referenced in a parameter\\.$#"
-			count: 2
-			path: src/Conditions/functions.php
diff --git a/src/Conditions/Composite.php b/src/Conditions/Composite.php
index 146bb63b8857216373359cb201f3625b60d07a6d..1d6eeb09ca1e8e4d405aab92364dbd2952b2646c 100644
--- a/src/Conditions/Composite.php
+++ b/src/Conditions/Composite.php
@@ -5,7 +5,7 @@ declare(strict_types=1);
 namespace Grifart\Tables\Conditions;
 
 use Dibi\Expression as DibiExpression;
-use function Functional\map;
+use function Phun\map;
 
 final class Composite implements Condition
 {
diff --git a/src/Conditions/IsIn.php b/src/Conditions/IsIn.php
index 50c2db44f6a12299ab6022bf9246bab703957b04..b9bb5caf6e9f0332221a254fe84dd3ce58bbce8b 100644
--- a/src/Conditions/IsIn.php
+++ b/src/Conditions/IsIn.php
@@ -6,8 +6,8 @@ namespace Grifart\Tables\Conditions;
 
 use Dibi\Expression as DibiExpression;
 use Grifart\Tables\Expression;
-use function Functional\map;
 use function Grifart\Tables\Types\mapToDatabase;
+use function Phun\map;
 
 /**
  * @template ValueType
diff --git a/src/Conditions/IsNotIn.php b/src/Conditions/IsNotIn.php
index c9bb3f6a61644054000fc485609be6a81e07f698..f2bdd34886894baade269acb4e7f5b31cdd60309 100644
--- a/src/Conditions/IsNotIn.php
+++ b/src/Conditions/IsNotIn.php
@@ -6,8 +6,8 @@ namespace Grifart\Tables\Conditions;
 
 use Dibi\Expression as DibiExpression;
 use Grifart\Tables\Expression;
-use function Functional\map;
 use function Grifart\Tables\Types\mapToDatabase;
+use function Phun\map;
 
 /**
  * @template ValueType
diff --git a/src/Conditions/IsNotNull.php b/src/Conditions/IsNotNull.php
index 9496096261df3249c8271c85b1b4563b634a4d52..c1aac9d3f93a720eaa6a9ca2cc81e2bdd0083fcb 100644
--- a/src/Conditions/IsNotNull.php
+++ b/src/Conditions/IsNotNull.php
@@ -10,7 +10,7 @@ use Grifart\Tables\Expression;
 final class IsNotNull implements Condition
 {
 	/**
-	 * @param Expression<mixed> $expression
+	 * @param Expression<*> $expression
 	 */
 	public function __construct(
 		private Expression $expression,
diff --git a/src/Conditions/IsNull.php b/src/Conditions/IsNull.php
index ed08268608f1ece9fdf26abea1d7a4f6d7b46a4f..e75992fd389f10e952d71332a48b2fd2e5d7a8dc 100644
--- a/src/Conditions/IsNull.php
+++ b/src/Conditions/IsNull.php
@@ -10,7 +10,7 @@ use Grifart\Tables\Expression;
 final class IsNull implements Condition
 {
 	/**
-	 * @param Expression<mixed> $expression
+	 * @param Expression<*> $expression
 	 */
 	public function __construct(
 		private Expression $expression,
diff --git a/src/Conditions/functions.php b/src/Conditions/functions.php
index 57f445b3bbcba4d213fa02abf1c1b0ff16b7e9b8..8cfa7143ee25a52726abb12bff5d523b27a2144b 100644
--- a/src/Conditions/functions.php
+++ b/src/Conditions/functions.php
@@ -94,8 +94,7 @@ function notIn(array $values): \Closure {
 }
 
 /**
- * @template ValueType
- * @return \Closure(Expression<ValueType>): IsNull
+ * @return \Closure(Expression<*>): IsNull
  */
 function null(): \Closure
 {
@@ -103,8 +102,7 @@ function null(): \Closure
 }
 
 /**
- * @template ValueType
- * @return \Closure(Expression<ValueType>): IsNotNull
+ * @return \Closure(Expression<*>): IsNotNull
  */
 function notNull(): \Closure
 {
diff --git a/src/Database/Identifier.php b/src/Database/Identifier.php
index 5cef9a3a0c54b48cca43e1585be0343f664ee488..b7b1aea8b64a6740736a18d9893633eaccb212c4 100644
--- a/src/Database/Identifier.php
+++ b/src/Database/Identifier.php
@@ -5,7 +5,7 @@ namespace Grifart\Tables\Database;
 
 use Dibi\Expression;
 use Dibi\Literal;
-use function Functional\map;
+use function Phun\map;
 
 final class Identifier
 {
diff --git a/src/Scaffolding/PostgresReflector.php b/src/Scaffolding/PostgresReflector.php
index 3c4ce66e5135ebb98bca3d7ed4c977be350e5501..5565d53c44d258084f5943806679b84ed6a97103 100644
--- a/src/Scaffolding/PostgresReflector.php
+++ b/src/Scaffolding/PostgresReflector.php
@@ -7,8 +7,8 @@ namespace Grifart\Tables\Scaffolding;
 use Dibi\Connection;
 use Grifart\Tables\ColumnMetadata;
 use Grifart\Tables\MissingPrimaryIndex;
-use function Functional\map;
-use function Functional\reindex;
+use function Phun\map;
+use function Phun\reindex;
 
 final class PostgresReflector
 {
diff --git a/src/Scaffolding/PrimaryKeyImplementation.php b/src/Scaffolding/PrimaryKeyImplementation.php
index 7f2a8b3a79ce137af0fe6d2249cb8669360b83de..56c68dee0a0307a837ca3938fb1c3a390b29f4a7 100644
--- a/src/Scaffolding/PrimaryKeyImplementation.php
+++ b/src/Scaffolding/PrimaryKeyImplementation.php
@@ -13,7 +13,7 @@ use Grifart\Tables\Conditions\Condition;
 use Grifart\Tables\PrimaryKey;
 use Grifart\Tables\Table;
 use Nette\PhpGenerator\Literal;
-use function Functional\map;
+use function Phun\map;
 
 final class PrimaryKeyImplementation implements Capability
 {
diff --git a/src/Scaffolding/TableImplementation.php b/src/Scaffolding/TableImplementation.php
index bc5bd15662e28641ec93c347f65f714e95223faa..89192cdff0b8f406dd374a83675325f2f37984e3 100644
--- a/src/Scaffolding/TableImplementation.php
+++ b/src/Scaffolding/TableImplementation.php
@@ -27,8 +27,8 @@ use Grifart\Tables\Type;
 use Grifart\Tables\TypeResolver;
 use Nette\PhpGenerator as Code;
 use Nette\Utils\Paginator;
-use function Functional\map;
 use function Grifart\ClassScaffolder\Definition\Types\resolve;
+use function Phun\map;
 use function usort;
 
 final class TableImplementation implements Capability
diff --git a/src/Scaffolding/TablesDefinitions.php b/src/Scaffolding/TablesDefinitions.php
index ebd350c92d95a6430db960ffd392703a04598f4d..0a5b6290434a0d50cf22c54745cd9bfb8e8e03d0 100644
--- a/src/Scaffolding/TablesDefinitions.php
+++ b/src/Scaffolding/TablesDefinitions.php
@@ -11,14 +11,14 @@ use Grifart\Tables\Database\Identifier;
 use Grifart\Tables\Row;
 use Grifart\Tables\Type;
 use Grifart\Tables\TypeResolver;
-use function array_keys;
-use function Functional\map;
 use function Grifart\ClassScaffolder\Capabilities\constructorWithPromotedProperties;
 use function Grifart\ClassScaffolder\Capabilities\getters;
 use function Grifart\ClassScaffolder\Capabilities\implementedInterface;
 use function Grifart\ClassScaffolder\Capabilities\namedConstructor;
 use function Grifart\ClassScaffolder\Capabilities\privatizedConstructor;
 use function Grifart\ClassScaffolder\Definition\Types\nullable;
+use function Phun\map;
+use function Phun\mapWithKeys;
 
 final class TablesDefinitions
 {
@@ -41,17 +41,17 @@ final class TablesDefinitions
 			throw new \LogicException('No columns found for given configuration. Does referenced table exist?');
 		}
 
-		$columnResolvedTypes = map(
+		$columnResolvedTypes = mapWithKeys(
 			$columnMetadata,
-			function (ColumnMetadata $column) use ($schema, $table): Type {
+			function ($_, ColumnMetadata $column) use ($schema, $table): Type {
 				$location = new Identifier($schema, $table, $column->getName());
 				return $this->typeResolver->resolveType($column->getType(), $location);
 			},
 		);
 
-		$columnPhpTypes = map(
+		$columnPhpTypes = mapWithKeys(
 			$columnMetadata,
-			static function (ColumnMetadata $column) use ($columnResolvedTypes): PhpType {
+			static function ($_, ColumnMetadata $column) use ($columnResolvedTypes): PhpType {
 				$type = $columnResolvedTypes[$column->getName()];
 				$phpType = $type->getPhpType();
 				return $column->isNullable() ? nullable($phpType) : $phpType;
@@ -78,7 +78,7 @@ final class TablesDefinitions
 			->with(new ModificationsImplementation($tableClassName, $primaryKeyClassName, $columnMetadata));
 
 		$primaryKeyColumnNames = $this->pgReflector->retrievePrimaryKeyColumns($schema, $table);
-		$primaryKeyFields = map($primaryKeyColumnNames, static fn(string $name) => $columnPhpTypes[$name]);
+		$primaryKeyFields = mapWithKeys($primaryKeyColumnNames, static fn($_, string $name) => $columnPhpTypes[$name]);
 		$primaryKeyClass = (new ClassDefinition($primaryKeyClassName))
 			->withFields($primaryKeyFields)
 			->with(
diff --git a/src/TableManager.php b/src/TableManager.php
index 48ee27da7deadea313c97bdd00f06bbe198108ee..acbf929754ff0b27455643c39eb45fe1efca894c 100644
--- a/src/TableManager.php
+++ b/src/TableManager.php
@@ -11,7 +11,8 @@ use Grifart\Tables\Conditions\Condition;
 use Grifart\Tables\OrderBy\OrderBy;
 use Grifart\Tables\OrderBy\OrderByDirection;
 use Nette\Utils\Paginator;
-use function Functional\map;
+use function Phun\map;
+use function Phun\mapWithKeys;
 
 // todo: error handling
 // todo: mapping of exceptions
@@ -37,9 +38,9 @@ final class TableManager
 			$this->connection->query(
 				'INSERT',
 				'INTO %n.%n', $table::getSchema(), $table::getTableName(),
-				map(
+				mapWithKeys(
 					$changes->getModifications(),
-					static fn(mixed $value, string $columnName) => $value !== null ? $table->getTypeOf($columnName)->toDatabase($value) : null,
+					static fn(string $columnName, mixed $value) => $value !== null ? $table->getTypeOf($columnName)->toDatabase($value) : null,
 				),
 			);
 		} catch (UniqueConstraintViolationException $e) {
@@ -60,7 +61,6 @@ final class TableManager
 		$rows = $this->findBy($table, $primaryKey->getCondition($table));
 		if (\count($rows) === 1) {
 			$row = \reset($rows);
-			\assert($row instanceof Row, 'It cannot return false as there must be one element in array');
 			return $row;
 		}
 		\assert(\count($rows) === 0);
@@ -110,15 +110,15 @@ final class TableManager
 
 		$dibiRows = $result->fetchAll();
 
-		/** @var Row $rowClass */
+		/** @var class-string<Row> $rowClass */
 		$rowClass = $table::getRowClass();
 		$modelRows = [];
 		foreach ($dibiRows as $dibiRow) {
 			\assert($dibiRow instanceof \Dibi\Row);
 			$modelRows[] = $rowClass::reconstitute(
-				map(
+				mapWithKeys(
 					$dibiRow->toArray(),
-					static fn(mixed $value, string $columnName) => $value !== null ? $table->getTypeOf($columnName)->fromDatabase($value) : null,
+					static fn(string $columnName, mixed $value) => $value !== null ? $table->getTypeOf($columnName)->fromDatabase($value) : null,
 				),
 			);
 		}
@@ -150,9 +150,9 @@ final class TableManager
 		$this->connection->query(
 			'UPDATE %n.%n', $table::getSchema(), $table::getTableName(),
 			'SET %a',
-			map(
+			mapWithKeys(
 				$changes->getModifications(),
-				static fn(mixed $value, string $columnName) => $value !== null ? $table->getTypeOf($columnName)->toDatabase($value) : null,
+				static fn(string $columnName, mixed $value) => $value !== null ? $table->getTypeOf($columnName)->toDatabase($value) : null,
 			),
 			'WHERE %ex', $primaryKey->getCondition($table)->toSql()->getValues(),
 		);
diff --git a/src/Types/ArrayType.php b/src/Types/ArrayType.php
index a5cc3dbc9c12651044a8a6464544d6e09855f45c..524863480be546d4ff17e0a098461dfbf7b5bd41 100644
--- a/src/Types/ArrayType.php
+++ b/src/Types/ArrayType.php
@@ -9,8 +9,8 @@ use Dibi\Literal;
 use Grifart\ClassScaffolder\Definition\Types\Type as PhpType;
 use Grifart\Tables\Database\ArrayType as DatabaseArrayType;
 use Grifart\Tables\Type;
-use function Functional\map;
 use function Grifart\ClassScaffolder\Definition\Types\listOf;
+use function Phun\map;
 
 /**
  * @template ItemType
diff --git a/src/Types/CompositeType.php b/src/Types/CompositeType.php
index eaf60c01478eb408ebacc8fef298afe22076a695..64c42f6c34bf87e7762dad4eb3eb40d576d1fe37 100644
--- a/src/Types/CompositeType.php
+++ b/src/Types/CompositeType.php
@@ -8,7 +8,8 @@ use Dibi\Expression;
 use Dibi\Literal;
 use Grifart\Tables\Database\DatabaseType;
 use Grifart\Tables\Type;
-use function Functional\map;
+use function Phun\map;
+use function Phun\mapWithKeys;
 
 /**
  * @template T
@@ -44,9 +45,9 @@ abstract class CompositeType implements Type
 	{
 		$args = [
 			new Literal('ROW('),
-			...map(
+			...mapWithKeys(
 				$value,
-				fn(mixed $item, int $index) => $item !== null ? $this->types[$index]->toDatabase($item) : new Literal('NULL'),
+				fn(int $index, mixed $item) => $item !== null ? $this->types[$index]->toDatabase($item) : new Literal('NULL'),
 			),
 			new Literal(')::'),
 			$this->getDatabaseType()->toSql(),
@@ -66,9 +67,9 @@ abstract class CompositeType implements Type
 		$result = $this->parseComposite($value);
 
 		\assert(\count($result) === \count($this->types));
-		return map(
+		return mapWithKeys(
 			$result,
-			fn($item, $index) => $item !== null ? $this->types[$index]->fromDatabase($item) : null,
+			fn($index, $item) => $item !== null ? $this->types[$index]->fromDatabase($item) : null,
 		);
 	}
 
diff --git a/src/functions.php b/src/functions.php
index 891dea6c03e97d38c6d2686617f2cbb2503a19f7..5500ccff9f027cce068afa4a01e4913842b58bbe 100644
--- a/src/functions.php
+++ b/src/functions.php
@@ -5,7 +5,7 @@ declare(strict_types=1);
 namespace Grifart\Tables;
 
 use Dibi\Expression as DibiExpression;
-use function Functional\map;
+use function Phun\map;
 
 /**
  * @template ValueType