LoginSignup
2
0
この記事誰得? 私しか得しないニッチな技術で記事投稿!

PHPのFiberを使用してコールバックをTraversableに変換する

Posted at

PHP の Fibers で何か面白いことができないかという試みです。

function subscribe(callable $onNext): void
{
    for ($n = 0; $n < 10; ++$n) {
        $onNext($n);
    }
}

foreach (fromObservable(subscribe(...)) as $n) {
    echo $n, PHP_EOL;
}

上記コードのようにコールバックで値が供給される構造を FiberTraversable に変換することで、foreach でループすることができます。

Traversable 自体が汎用的なパーツなので、map のような汎用ユーティリティがある前提下でさまざまな応用が考えられます。

/**
 * @template TKey
 * @template TSource
 * @template TResult
 * @param Traversable<TKey,TSource> $source
 * @param callable(TSource,TKey):TResult
 * @return Traversable<TKey,TResult>
 */
function map(Traversable $source, callable $selector): Traversable;

map(fromObservable(subscribe(...)), fn($n) => $n * $n);

https://www.tehplayground.com/Idh8iJE8SAsx0p5j

<?php

/**
 * @template TSource
 * @param callable(callable(TSource):void):void $observable
 * @return Traversable<TSource>
 */
function fromObservable(callable $observable): Traversable
{
    $fiber = new Fiber(static function () use ($observable) {
        $observable(static function ($element) {
            Fiber::suspend($element);
        });
    });

    return new class ($fiber) implements Iterator {
        private int $_key = 0;
        /** @var ?TSource */
        private mixed $_current;

        public function __construct(private readonly Fiber $fiber)
        {
        }

        public function current(): mixed
        {
            return $this->_current;
        }

        public function key(): mixed
        {
            return $this->_key;
        }

        public function next(): void
        {
            $this->_current = $this->fiber->resume();
            ++$this->_key;
        }

        public function rewind(): void
        {
            $this->_current = $this->fiber->start();
        }

        public function valid(): bool
        {
            return $this->_current !== null;
        }
    };
}

/**
 * @param callable(int):void $onNext
 */
function subscribe(callable $onNext): void
{
    for ($n = 0; $n < 10; ++$n) {
        $onNext($n);
    }
}

foreach (fromObservable(subscribe(...)) as $n) {
    echo $n, PHP_EOL;
}
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0