/**
  * @dataProvider provideData
  */
 public function test_demonstrate_state_monad($expectedProducts)
 {
     $initialState = new InMemoryCache([]);
     list($result1, $outputState1) = S\runState(searchRelated('asia'), $initialState);
     $this->assertEquals($expectedProducts, $result1);
     list($result2, $outputState2) = S\runState(searchRelated('asia'), $outputState1);
     $this->assertEquals($expectedProducts, $result2);
     // After second computation, state shouldn't be modified
     // Because we have item already in cache
     $this->assertSame($outputState1, $outputState2);
 }
/**
 * doo :: State IO m => [m a] -> m a
 *
 * Haskell like "do notation" simple implementation.
 * Since "do" is reserved keyword in PHP then I use "doo".
 *
 * @param array|M\IO[] $monads
 *
 * @return M\IO
 */
function doo(array $monads)
{
    return M\IO::of(function () use($monads) {
        $result = null;
        $data = [];
        // TODO do it by foldWithKeys
        foreach ($monads as $key => $monad) {
            // TODO do it better - maybe?
            if ($monad instanceof M\IO) {
                $monad = ioState($monad);
            }
            $state = [$key, $data];
            list($result, list(, $data)) = M\State\runState($monad, $state);
        }
        return $result;
    });
}