public function train(Dataset $dataset) : void
    {
        if (!$dataset instanceof Labeled) {
            throw new InvalidArgumentException('This Estimator requires a'
                . ' Labeled training set.');
        }

        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        if ($this->logger) {
            $this->logger->info('Learner init ' . Params::stringify([
                'base' => $this->base,
                'metric' => $this->metric,
                'validator' => $this->validator,
                'workers' => $this->workers,
            ]));
        }

        $results = [];

        Loop::run(function () use (&$results, $dataset) {
            $pool = new DefaultPool($this->workers);

            $coroutines = [];

            foreach ($this->combinations as $params) {
                $estimator = new $this->base(...$params);

                $task = new CallableTask(
                    [$this, 'score'],
                    [$estimator, $dataset]
                );

                $coroutines[] = call(function () use ($pool, $task, $params) {
                    $score = yield $pool->enqueue($task);

                    if ($this->logger) {
                        $constructor = array_combine($this->args, $params) ?: [];
                            
                        $this->logger->info('Test complete: ' . Params::stringify([
                            Params::shortName($this->metric) => $score,
                        ]) . ' Params: ' . Params::stringify($constructor));
                    }

                    return [$score, $params];
                });
            }

            $results = yield all($coroutines);
            
            return yield $pool->shutdown();
        });

        $bestScore = -INF;
        $bestParams = [];

        foreach ($results as [$score, $params]) {
            if ($score > $bestScore) {
                $bestScore = $score;
                $bestParams = $params;
            }
        }

        $this->best = [
            'score' => $bestScore,
            'params' => $bestParams,
        ];

        if ($this->logger) {
            $constructor = array_combine($this->args, $bestParams) ?: [];

            $this->logger->info('Best params: ' . Params::stringify($constructor));
        }

        $estimator = new $this->base(...$bestParams);

        $estimator->train($dataset);

        $this->estimator = $estimator;

        if ($this->logger) {
            $this->logger->info('Training complete');
        }
    }