', curryN( 2, function ( $cacheKeyFn, $fn ) { return function () use ( $cacheKeyFn, $fn ) { static $cache = []; $args = func_get_args(); $key = call_user_func_array( $cacheKeyFn, $args ); if ( array_key_exists( $key, $cache ) ) { return $cache[ $key ]; } $result = call_user_func_array( $fn, $args ); $cache[ $key ] = $result; return $result; }; } ) ); self::macro( 'memorize', self::memorizeWith( gatherArgs( pipe( Fns::map( 'json_encode'), Lst::join('|') ) ) ) ); self::macro( 'once', curryN( 1, function ( $fn ) { return function () use ( $fn ) { static $result = []; if ( array_key_exists( 'data', $result ) ) { return $result['data']; } $result['data'] = call_user_func_array( $fn, func_get_args() ); return $result['data']; }; } ) ); self::macro( 'withNamedLock', curryN( 3, function ( $name, $returnFn, $fn ) { static $inProgress = []; return function () use ( &$inProgress, $name, $returnFn, $fn ) { $args = func_get_args(); if ( Obj::prop( $name, $inProgress ) ) { return call_user_func_array( $returnFn, $args ); } $inProgress[ $name ] = true; $result = call_user_func_array( $fn, $args ); $inProgress[ $name ] = false; return $result; }; } ) ); self::macro( 'withoutRecursion', curryN( 2, function ( $returnFn, $fn ) { return function () use ( $returnFn, $fn ) { static $inProgress = false; $args = func_get_args(); if ( $inProgress ) { return call_user_func_array( $returnFn, $args ); } $inProgress = true; $result = call_user_func_array( $fn, $args ); $inProgress = false; return $result; }; } ) ); self::macro( 'liftA2', curryN( 3, function ( $fn, $monadA, $monadB ) { return $monadA->map( $fn )->ap( $monadB ); } ) ); self::macro( 'liftA3', curryN( 4, function ( $fn, $monadA, $monadB, $monadC ) { return $monadA->map( $fn )->ap( $monadB )->ap( $monadC ); } ) ); self::macro( 'liftN', function ( $n, $fn ) { $liftedFn = curryN( $n, function () use ( $n, $fn ) { $args = func_get_args(); $result = $args[0]->map( curryN( $n, $fn ) ); return Fns::reduce( function ( $result, $monad ) { return $result->ap( $monad ); }, $result, Lst::drop( 1, $args ) ); } ); return call_user_func_array( $liftedFn, Lst::drop( 2, func_get_args() ) ); } ); self::macro( 'until', curryN( 3, function ( $predicate, array $fns, ...$args ) { foreach ( $fns as $fn ) { $result = $fn( ...$args ); if ( $predicate( $result ) ) { return $result; } } return null; } ) ); } /** * @return \Closure */ public static function noop() { return function () { }; } /** * Curried function that transforms a Maybe into an Either. * * @param mixed|null $or * @param Maybe|null $maybe * * @return callable|Either */ public static function maybeToEither( $or = null, $maybe = null ) { $toEither = function ( $or, Maybe $maybe ) { return self::isJust( $maybe ) ? Either::right( $maybe->getOrElse( null ) ) : Either::left( $or ); }; return call_user_func_array( curryN( 2, $toEither ), func_get_args() ); } } Fns::init();