Fix concat behaviour
authorwidmogrod <widmogrod@gmail.com>
Tue, 19 Dec 2017 20:30:52 +0000 (21:30 +0100)
committerwidmogrod <widmogrod@gmail.com>
Tue, 19 Dec 2017 20:30:52 +0000 (21:30 +0100)
src/Functional/functions.php
src/Functional/listt.php
src/Primitive/Listt.php
test/Functional/ConcatTest.php

index be29b80..e7b5fe1 100644 (file)
@@ -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]
- *
- * <code>
- * concat([[1, 2], [3, 4]]) == [1, 2, 3, 4]
- * </code>
- *
- * 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
index 2e6da3e..928231d 100644 (file)
@@ -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]
+ *
+ * <code>
+ * concat(fromIterable([fromIterable([1, 2]), fromIterable([3, 4])])) == fromIterable([1, 2, 3, 4])
+ * </code>
+ *
+ * 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());
 }
 
 ///**
index d044510..3d17e29 100644 (file)
@@ -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);
index b572339..6680252 100644 (file)
@@ -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],
             ],
         ];
     }