public function train(Dataset $dataset) : void
    {
        if ($this->type === self::CLASSIFIER or $this->type === self::REGRESSOR) {
            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([
                'experts' => $this->experts,
                'influences' => $this->influences,
                'workers' => $this->workers,
            ]));
        }

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

            $coroutines = [];

            foreach ($this->experts as $index => $estimator) {
                $task = new CallableTask(
                    [$this, '_train'],
                    [$estimator, $dataset]
                );

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

                    if ($this->logger) {
                        $this->logger->info(Params::stringify([
                            $index => $estimator,
                        ]) . ' finished training');
                    }

                    return $estimator;
                });
            }

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

        if ($this->type === self::CLASSIFIER and $dataset instanceof Labeled) {
            $this->classes = $dataset->possibleOutcomes();
        }

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