From 4b4f1e347f41f672846ad96fa4118b777dfb5771 Mon Sep 17 00:00:00 2001 From: widmogrod Date: Tue, 19 Dec 2017 21:30:52 +0100 Subject: [PATCH] Fix concat behaviour --- src/Functional/functions.php | 63 ++++++------------------------------------ src/Functional/listt.php | 54 ++++++++++++++++++++++++++++-------- src/Primitive/Listt.php | 11 ++++---- test/Functional/ConcatTest.php | 52 +++++++++++++++++++--------------- 4 files changed, 86 insertions(+), 94 deletions(-) diff --git a/src/Functional/functions.php b/src/Functional/functions.php index be29b80..e7b5fe1 100644 --- a/src/Functional/functions.php +++ b/src/Functional/functions.php @@ -69,7 +69,7 @@ function toFoldable($value) { return $value instanceof Foldable ? $value - : Listt::of(toNativeTraversable($value)); + : fromIterable($value); } /** @@ -90,54 +90,9 @@ function toTraversable($value) { return $value instanceof Traversable ? $value - : Listt::of(toNativeTraversable($value)); + : fromIterable($value); } -/** - * @var callable - */ -const concat = 'Widmogrod\Functional\concat'; - -/** - * concat :: Foldable t => t [a] -> [a] - * - * - * concat([[1, 2], [3, 4]]) == [1, 2, 3, 4] - * - * - * The concatenation of all the elements of a container of lists. - * - * @param Foldable $foldable - * - * @return array - */ -function concat(Foldable $foldable) -{ - return reduce(function ($agg, $value) { - return reduce(function ($agg, $v) { - $agg[] = $v; - - return $agg; - }, $agg, toFoldable($value)); - }, [], $foldable); -} - -/** - * @var callable - */ -const toList = 'Widmogrod\Functional\toList'; - -/** - * toList :: Traversable t -> t a -> [a] - * - * @param Foldable $traversable - * - * @return mixed - */ -function toList(Foldable $traversable) -{ - return reduce(push_, [], $traversable); -} /** * Curry function @@ -154,7 +109,7 @@ function curryN($numberOfArguments, callable $function, array $args = []) $argsLeft = $numberOfArguments - func_num_args(); return $argsLeft <= 0 - ? call_user_func_array($function, push_($args, $argsNext)) + ? $function(...push_($args, $argsNext)) : curryN($argsLeft, $function, push_($args, $argsNext)); }; } @@ -348,9 +303,7 @@ function foldr(callable $callable, $accumulator = null, Foldable $foldable = nul return reduce( flip($callable), $accumulator, - reduce(function ($accumulator, $value) { - return concatM(Listt::of([$value]), $accumulator); - }, Listt::of([]), $foldable) + reduce(flip(prepend), Listt::mempty(), $foldable) ); })(...func_get_args()); } @@ -694,7 +647,7 @@ const traverse = 'Widmogrod\Functional\traverse'; * Map each element of a structure to an action, evaluate these actions from left to right, and collect the results * * @param callable $transformation (a -> f b) - * @param Traversable $t t a + * @param Traversable $t t a * * @return Applicative f (t b) */ @@ -728,7 +681,7 @@ function sequence(Monad ...$monads) /** * filterM :: Monad m => (a -> m Bool) -> [a] -> m [a] * - * @param callable $f (a -> m Bool) + * @param callable $f (a -> m Bool) * @param array|Traversable $collection [a] * * @return Monad m [a] @@ -768,8 +721,8 @@ function filterM(callable $f, $collection) /** * foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a * - * @param callable $f (a -> b -> m a) - * @param mixed $initial a + * @param callable $f (a -> b -> m a) + * @param mixed $initial a * @param array|\Traversable $collection [b] * * @return mixed m a diff --git a/src/Functional/listt.php b/src/Functional/listt.php index 2e6da3e..928231d 100644 --- a/src/Functional/listt.php +++ b/src/Functional/listt.php @@ -2,6 +2,7 @@ namespace Widmogrod\Functional; +use Widmogrod\FantasyLand\Foldable; use Widmogrod\Primitive\Listt; function fromIterable(iterable $i): Listt @@ -9,15 +10,38 @@ function fromIterable(iterable $i): Listt return Listt::of(array_map(identity, $i)); } -///** -// * concat :: Foldable t => t [a] -> [a] -// * -// * The concatenation of all the elements of a container of lists. -// */ -//function concat(Foldable $t) -//{ -// // TODO -//} +/** + * @var callable + */ +const concat = 'Widmogrod\Functional\concat'; + +/** + * concat :: Foldable t => t [a] -> [a] + * + * + * concat(fromIterable([fromIterable([1, 2]), fromIterable([3, 4])])) == fromIterable([1, 2, 3, 4]) + * + * + * concat :: Foldable t => t [a] -> [a] + * concat xs = build (\c n -> foldr (\x y -> foldr c y x) n xs) + * + * build :: forall a. (forall b. (a -> b -> b) -> b -> b) -> [a] + * build g = g (:) [] + * + * foldr :: (a -> b -> b) -> b -> [a] -> b + * + * The concatenation of all the elements of a container of lists. + * + * @param Foldable $xs + * + * @return Listt + */ +function concat(Foldable $xs) +{ + return foldr(function ($x, Listt $y) { + return foldr(prepend, $y, $x); + }, Listt::mempty(), $xs); +} ///** // * map f xs is the list obtained by applying f to each element of xs, i.e., @@ -53,7 +77,7 @@ const prepend = 'Widmogrod\Functional\prepend'; */ function prepend($x, Listt $xs = null) { - return curryN(2, function ($x, Listt $xs) { + return curryN(2, function ($x, Listt $xs): Listt { return append(fromIterable([$x]), $xs); })(...func_get_args()); } @@ -69,10 +93,16 @@ const append = 'Widmogrod\Functional\append'; * [x1, ..., xm] ++ [y1, ...] == [x1, ..., xm, y1, ...] * * If the first list is not finite, the result is the first list. + * + * @param Listt $a + * @param Listt|null $b + * @return Listt|callable */ -function append(Listt $a, Listt $b): Listt +function append(Listt $a, Listt $b = null) { - return $a->concat($b); + return curryN(2, function (Listt $a, Listt $b): Listt { + return $a->concat($b); + })(...func_get_args()); } ///** diff --git a/src/Primitive/Listt.php b/src/Primitive/Listt.php index d044510..3d17e29 100644 --- a/src/Primitive/Listt.php +++ b/src/Primitive/Listt.php @@ -23,9 +23,10 @@ class Listt implements */ public function __construct($value) { - $this->value = f\isNativeTraversable($value) - ? $value - : [$value]; + $givenType = is_object($value) ? get_class($value) : gettype($value); + assert(is_iterable($value), "Not iterable value given $givenType"); + + $this->value = $value; } /** @@ -60,7 +61,7 @@ class Listt implements public function bind(callable $transformation) { // xs >>= f = concat (map f xs) - return self::of(f\concat(f\map($transformation, $this))); + return f\concat(f\map($transformation, $this)); } /** @@ -140,7 +141,7 @@ class Listt implements $accumulator[] = $item; return $accumulator; - }, $this->extract())); + }, $this->value)); } throw new TypeMismatchError($value, self::class); diff --git a/test/Functional/ConcatTest.php b/test/Functional/ConcatTest.php index b572339..6680252 100644 --- a/test/Functional/ConcatTest.php +++ b/test/Functional/ConcatTest.php @@ -19,35 +19,43 @@ class ConcatTest extends \PHPUnit_Framework_TestCase public function provideData() { return [ - 'list' => [ - '$array' => f\toFoldable(['foo', 1]), - '$expected' => ['foo', 1], - ], 'list of lists' => [ - '$array' => f\toFoldable([['a', 1], ['b', 2]]), - '$expected' => ['a', 1, 'b', 2], + '$array' => f\fromIterable([ + f\fromIterable(['a', 1, 3]), + f\fromIterable(['b', 2, 4]) + ]), + '$expected' => f\fromIterable(['a', 1, 3, 'b', 2, 4]), ], 'list of lists of lists' => [ - '$array' => f\toFoldable([ - [ - ['a', 1], - ['b', 2] - ], - [ - ['c', 3] - ], + '$array' => f\fromIterable([ + f\fromIterable([ + f\fromIterable(['a', 1]), + f\fromIterable(['b', 2]) + ]), + f\fromIterable([ + f\fromIterable(['c', 3]) + ]), + ]), + '$expected' => f\fromIterable([ + f\fromIterable(['a', 1]), + f\fromIterable(['b', 2]), + f\fromIterable(['c', 3]) ]), - '$expected' => [['a', 1], ['b', 2], ['c', 3]], ], 'list of lists of lists with some noregulatives' => [ - '$array' => f\toFoldable([ - [ - ['a', 1], - ['b', 2] - ], - ['c', 3] + '$array' => f\fromIterable([ + f\fromIterable([ + f\fromIterable(['a', 1]), + f\fromIterable(['b', 2]), + ]), + f\fromIterable(['c', 3]) + ]), + '$expected' => f\fromIterable([ + f\fromIterable(['a', 1]), + f\fromIterable(['b', 2]), + 'c', + 3 ]), - '$expected' => [['a', 1], ['b', 2], 'c', 3], ], ]; } -- 2.11.0