folly::gen, or Comprehensions->Folly
authorTom Jackson <tjackson@fb.com>
Fri, 22 Jun 2012 06:27:23 +0000 (23:27 -0700)
committerJordan DeLong <jdelong@fb.com>
Mon, 29 Oct 2012 23:32:26 +0000 (16:32 -0700)
Summary:
Moving Comprehensions library into Folly and refactoring its interface to be much more modular and composable. There are now two core abstractions:

# Generators: Standalone classes supporting ##apply()## and optionally ##foreach()##. These all inherit from ##GenImpl<T, Self>##.
# Operators: Standalone classes which, when composed with a generator, produce a new generator. These all inherit from ##OperatorImpl<Self>##.

These generators may be composed with ##operator|## overloads which only match ##const GenImpl<T, Self>&## on the left like ##gen | op##.  Additionally, generator may be consumed inline with ##gen | lambda## like ##gen | [](int x) { cout << x << endl; };##.

With this design, new operators may be added very simply without modifying the core library and templates are instantiated only exactly as needed.

Example:

```lang=cpp
auto sum = seq(1, 10) | filter(isPrime) | sum;
seq(1, 10) | [](int i) {
cout << i << endl;
};
```

Test Plan: Unit tests

Reviewed By: andrei.alexandrescu@fb.com

FB internal diff: D542215

folly/experimental/Gen-inl.h [new file with mode: 0644]
folly/experimental/Gen.h [new file with mode: 0644]
folly/experimental/test/GenBenchmark.cpp [new file with mode: 0644]
folly/experimental/test/GenTest.cpp [new file with mode: 0644]

diff --git a/folly/experimental/Gen-inl.h b/folly/experimental/Gen-inl.h
new file mode 100644 (file)
index 0000000..c213f13
--- /dev/null
@@ -0,0 +1,1296 @@
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace folly { namespace gen {
+
+/**
+ * IsCompatibleSignature - Trait type for testing whether a given Functor
+ * matches an expected signature.
+ *
+ * Usage:
+ *   IsCompatibleSignature<FunctorType, bool(int, float)>::value
+ */
+template<class Candidate, class Expected>
+class IsCompatibleSignature {
+  static constexpr bool value = false;
+};
+
+template<class Candidate,
+         class ExpectedReturn,
+         class... ArgTypes>
+class IsCompatibleSignature<Candidate, ExpectedReturn(ArgTypes...)> {
+  template<class F,
+           class ActualReturn =
+             decltype(std::declval<F>()(std::declval<ArgTypes>()...)),
+           bool good = std::is_same<ExpectedReturn, ActualReturn>::value>
+  static constexpr bool testArgs(int* p) {
+    return good;
+  }
+
+  template<class F>
+  static constexpr bool testArgs(...) {
+    return false;
+  }
+public:
+  static constexpr bool value = testArgs<Candidate>(nullptr);
+};
+
+/**
+ * ArgumentReference - For determining ideal argument type to receive a value.
+ */
+template<class T>
+struct ArgumentReference :
+  public std::conditional<std::is_reference<T>::value,
+                          T, // T& -> T&, T&& -> T&&, const T& -> const T&
+                          typename std::conditional<
+                            std::is_const<T>::value,
+                            T&, // const int -> const int&
+                            T&& // int -> int&&
+                          >::type> {};
+
+/**
+ * FBounded - Helper type for the curiously recurring template pattern, used
+ * heavily here to enable inlining and obviate virtual functions
+ */
+template<class Self>
+struct FBounded {
+  const Self& self() const {
+    return *static_cast<const Self*>(this);
+  }
+
+  Self& self() {
+    return *static_cast<Self*>(this);
+  }
+};
+
+/**
+ * Operator - Core abstraction of an operation which may be applied to a
+ * generator. All operators implement a method compose(), which takes a
+ * generator and produces an output generator.
+ */
+template<class Self>
+class Operator : public FBounded<Self> {
+ public:
+  /**
+   * compose() - Must be implemented by child class to compose a new Generator
+   * out of a given generator. This function left intentionally unimplemented.
+   */
+  template<class Source,
+           class Value,
+           class ResultGen = void>
+  ResultGen compose(const GenImpl<Value, Source>& source) const;
+ protected:
+  Operator() = default;
+  Operator(const Operator&) = default;
+  Operator(Operator&&) = default;
+};
+
+/**
+ * GenImpl - Core abstraction of a generator, an object which produces values by
+ * passing them to a given handler lambda. All generator implementations must
+ * implement apply(). foreach() may also be implemented to special case the
+ * condition where the entire sequence is consumed.
+ */
+template<class Value,
+         class Self>
+class GenImpl : public FBounded<Self> {
+ protected:
+  // To prevent slicing
+  GenImpl() = default;
+  GenImpl(const GenImpl&) = default;
+  GenImpl(GenImpl&&) = default;
+
+ public:
+  typedef Value ValueType;
+  typedef typename std::decay<Value>::type StorageType;
+
+  /**
+   * apply() - Send all values produced by this generator to given
+   * handler until it returns false. Returns true if the false iff the handler
+   * returned false.
+   */
+  template<class Handler>
+  bool apply(Handler&& handler) const;
+
+  /**
+   * foreach() - Send all values produced by this generator to given lambda.
+   */
+  template<class Body>
+  void foreach(Body&& body) const {
+    this->self().apply([&](Value value) {
+        body(std::forward<Value>(value));
+        return true;
+      });
+  }
+
+  template<class Next,
+           class Chain = detail::Chain<Value, Self, Next>,
+           class ValueNext>
+  Chain operator+(const GenImpl<ValueNext, Next>& next) const {
+    static_assert(
+      std::is_same<Value, ValueNext>::value,
+      "Generators may ony be combined if Values are the exact same type.");
+    return Chain(*this, next);
+  }
+};
+
+/**
+ * operator|() which enables foreach-like usage:
+ *   gen | [](Value v) -> void {...};
+ */
+template<class Value,
+         class Gen,
+         class Handler>
+typename std::enable_if<
+  IsCompatibleSignature<Handler, void(Value)>::value>::type
+operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) {
+  gen.self().foreach(std::forward<Handler>(handler));
+}
+
+/**
+ * operator|() which enables foreach-like usage with 'break' support:
+ *   gen | [](Value v) -> bool { return shouldContinue(); };
+ */
+template<class Value,
+         class Gen,
+         class Handler>
+typename std::enable_if<
+  IsCompatibleSignature<Handler, bool(Value)>::value>::type
+operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) {
+  gen.self().apply(std::forward<Handler>(handler));
+}
+
+/**
+ * operator|() for composing generators with operators, similar to boosts' range
+ * adaptors:
+ *   gen | map(square) | sum
+ */
+template<class Value,
+         class Gen,
+         class Op>
+auto operator|(const GenImpl<Value, Gen>& gen, const Operator<Op>& op) ->
+decltype(op.self().compose(gen)) {
+  return op.self().compose(gen);
+}
+
+namespace detail {
+
+/*
+ * ReferencedSource - Generate values from an STL-like container using
+ * iterators from .begin() until .end(). Value type defaults to the type of
+ * *container->begin(). For std::vector<int>, this would be int&. Note that the
+ * value here is a reference, so the values in the vector will be passed by
+ * reference to downstream operators.
+ *
+ * This type is primarily used through the 'from' helper method, like:
+ *
+ *   string& longestName = from(names)
+ *                       | maxBy([](string& s) { return s.size() });
+ */
+template<class Container,
+         class Value>
+class ReferencedSource :
+    public GenImpl<Value, ReferencedSource<Container, Value>> {
+  Container* const container_;
+public:
+  explicit ReferencedSource(Container* container)
+    : container_(container) {}
+
+  template<class Body>
+  void foreach(Body&& body) const {
+    for (auto& value : *container_) {
+      body(std::forward<Value>(value));
+    }
+  }
+
+  template<class Handler>
+  bool apply(Handler&& handler) const {
+    for (auto& value : *container_) {
+      if (!handler(std::forward<Value>(value))) {
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
+/**
+ * CopiedSource - For producing values from eagerly from a sequence of values
+ * whose storage is owned by this class. Useful for preparing a generator for
+ * use after a source collection will no longer be available, or for when the
+ * values are specified literally with an initializer list.
+ *
+ * This type is primarily used through the 'fromCopy' function, like:
+ *
+ *   auto sourceCopy = fromCopy(makeAVector());
+ *   auto sum = sourceCopy | sum;
+ *   auto max = sourceCopy | max;
+ *
+ * Though it is also used for the initializer_list specialization of from().
+ */
+template<class StorageType,
+         class Container>
+class CopiedSource :
+  public GenImpl<const StorageType&,
+                 CopiedSource<StorageType, Container>> {
+  static_assert(
+    !std::is_reference<StorageType>::value, "StorageType must be decayed");
+ public:
+  // Generator objects are often copied during normal construction as they are
+  // encapsulated by downstream generators. It would be bad if this caused
+  // a copy of the entire container each time, and since we're only exposing a
+  // const reference to the value, it's safe to share it between multiple
+  // generators.
+  static_assert(
+    !std::is_reference<Container>::value,
+    "Can't copy into a reference");
+  const std::shared_ptr<const Container> copy_;
+public:
+  typedef Container ContainerType;
+
+  template<class SourceContainer>
+  explicit CopiedSource(const SourceContainer& container)
+    : copy_(new Container(begin(container), end(container))) {}
+
+  explicit CopiedSource(Container&& container) :
+    copy_(new Container(std::move(container))) {}
+
+  // To enable re-use of cached results.
+  CopiedSource(const CopiedSource<StorageType, Container>& source)
+    : copy_(source.copy_) {}
+
+  template<class Body>
+  void foreach(Body&& body) const {
+    for (const auto& value : *copy_) {
+      body(value);
+    }
+  }
+
+  template<class Handler>
+  bool apply(Handler&& handler) const {
+    // The collection may be reused by others, we can't allow it to be changed.
+    for (const auto& value : *copy_) {
+      if (!handler(value)) {
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
+/**
+ * Sequence - For generating values from beginning value, incremented along the
+ * way with the ++ and += operators. Iteration may continue indefinitely by
+ * setting the 'endless' template parameter to true. If set to false, iteration
+ * will stop when value reaches 'end', either inclusively or exclusively,
+ * depending on the template parameter 'endInclusive'. Value type specified
+ * explicitly.
+ *
+ * This type is primarily used through the 'seq' and 'range' function, like:
+ *
+ *   int total = seq(1, 10) | sum;
+ *   auto indexes = range(0, 10);
+ */
+template<class Value,
+         bool endless,
+         bool endInclusive>
+class Sequence : public GenImpl<const Value&,
+                                Sequence<Value, endless, endInclusive>> {
+  static_assert(!std::is_reference<Value>::value &&
+                !std::is_const<Value>::value, "Value mustn't be const or ref.");
+  Value bounds_[endless ? 1 : 2];
+public:
+  explicit Sequence(const Value& begin)
+      : bounds_{begin} {
+    static_assert(endless, "Must supply 'end'");
+  }
+
+  explicit Sequence(const Value& begin, const Value& end)
+    : bounds_{begin, end} {}
+
+  template<class Handler>
+  bool apply(Handler&& handler) const {
+    Value value = bounds_[0];
+    for (;endless || value < bounds_[1]; ++value) {
+      const Value& arg = value;
+      if (!handler(arg)) {
+        return false;
+      }
+    }
+    if (endInclusive && value == bounds_[1]) {
+      const Value& arg = value;
+      if (!handler(arg)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  template<class Body>
+  void foreach(Body&& body) const {
+    Value value = bounds_[0];
+    for (;endless || value < bounds_[1]; ++value) {
+      const Value& arg = value;
+      body(arg);
+    }
+    if (endInclusive && value == bounds_[1]) {
+      const Value& arg = value;
+      body(arg);
+    }
+  }
+};
+
+/**
+ * Chain - For concatenating the values produced by two Generators.
+ *
+ * This type is primarily used through using '+' to combine generators, like:
+ *
+ *   auto nums = seq(1, 10) + seq(20, 30);
+ *   int total = nums | sum;
+ */
+template<class Value, class First, class Second>
+class Chain : public GenImpl<Value,
+                             Chain<Value, First, Second>> {
+  const First first_;
+  const Second second_;
+public:
+  explicit Chain(const GenImpl<Value, First>& first,
+                 const GenImpl<Value, Second>& second)
+      : first_(first.self())
+      , second_(second.self()) {}
+
+  template<class Handler>
+  bool apply(Handler&& handler) const {
+    return first_.apply(std::forward<Handler>(handler))
+        && second_.apply(std::forward<Handler>(handler));
+  }
+
+  template<class Body>
+  void foreach(Body&& body) const {
+    first_.foreach(std::forward<Body>(body));
+    second_.foreach(std::forward<Body>(body));
+  }
+};
+
+/**
+ * Yield - For producing values from a user-defined generator by way of a
+ * 'yield' function.
+ **/
+template<class Value, class Source>
+class Yield : public GenImpl<Value, Yield<Value, Source>> {
+  const Source source_;
+ public:
+  explicit Yield(const Source& source)
+    : source_(source) {
+  }
+
+  template<class Handler>
+  bool apply(Handler&& handler) const {
+    struct Break {};
+    auto body = [&](Value value) {
+      if (!handler(std::forward<Value>(value))) {
+        throw Break();
+      }
+    };
+    try {
+      source_(body);
+      return true;
+    } catch (Break&) {
+      return false;
+    }
+  }
+
+  template<class Body>
+  void foreach(Body&& body) const {
+    source_(std::forward<Body>(body));
+  }
+};
+
+
+/*
+ * Operators
+ */
+
+/**
+ * Map - For producing a sequence of values by passing each value from a source
+ * collection through a predicate.
+ *
+ * This type is usually used through the 'map' or 'mapped' helper function:
+ *
+ *   auto squares = seq(1, 10) | map(square) | asVector;
+ */
+template<class Predicate>
+class Map : public Operator<Map<Predicate>> {
+  const Predicate predicate_;
+ public:
+  explicit Map(const Predicate& predicate = Predicate())
+    : predicate_(predicate)
+  { }
+
+  template<class Value,
+           class Source,
+           class Result = typename ArgumentReference<
+                            typename std::result_of<Predicate(Value)>::type
+                          >::type>
+  class Generator :
+      public GenImpl<Result, Generator<Value, Source, Result>> {
+    const Source source_;
+    const Predicate pred_;
+  public:
+    explicit Generator(const Source& source, const Predicate& pred)
+      : source_(source), pred_(pred) {}
+
+    template<class Body>
+    void foreach(Body&& body) const {
+      source_.foreach([&](Value value) {
+        body(pred_(std::forward<Value>(value)));
+      });
+    }
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      return source_.apply([&](Value value) {
+        return handler(pred_(std::forward<Value>(value)));
+      });
+    }
+  };
+
+  template<class Source,
+           class Value,
+           class Gen = Generator<Value, Source>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self(), predicate_);
+  }
+};
+
+
+/**
+ * Filter - For filtering values from a source sequence by a predicate.
+ *
+ * This type is usually used through the 'filter' helper function, like:
+ *
+ *   auto nonEmpty = from(strings)
+ *                 | filter([](const string& str) -> bool {
+ *                     return !str.empty();
+ *                   });
+ */
+template<class Predicate>
+class Filter : public Operator<Filter<Predicate>> {
+  const Predicate predicate_;
+ public:
+  explicit Filter(const Predicate& predicate)
+    : predicate_(predicate)
+  { }
+
+  template<class Value,
+           class Source>
+  class Generator : public GenImpl<Value, Generator<Value, Source>> {
+    const Source source_;
+    const Predicate pred_;
+  public:
+    explicit Generator(const Source& source, const Predicate& pred)
+      : source_(source), pred_(pred) {}
+
+    template<class Body>
+    void foreach(Body&& body) const {
+      source_.foreach([&](Value value) {
+        if (pred_(std::forward<Value>(value))) {
+          body(std::forward<Value>(value));
+        }
+      });
+    }
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      return source_.apply([&](Value value) -> bool {
+        if (pred_(std::forward<Value>(value))) {
+          return handler(std::forward<Value>(value));
+        }
+        return true;
+      });
+    }
+  };
+
+  template<class Source,
+           class Value,
+           class Gen = Generator<Value, Source>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self(), predicate_);
+  }
+};
+
+/**
+ * Until - For producing values from a source until a predicate is satisfied.
+ *
+ * This type is usually used through the 'until' helper function, like:
+ *
+ *   auto best = from(sortedItems)
+ *             | until([](Item& item) { return item.score > 100; })
+ *             | asVector;
+ */
+template<class Predicate>
+class Until : public Operator<Until<Predicate>> {
+  const Predicate predicate_;
+ public:
+  explicit Until(const Predicate& predicate)
+    : predicate_(predicate)
+  { }
+
+  template<class Value,
+           class Source,
+           class Result = typename std::result_of<Predicate(Value)>::type>
+  class Generator :
+      public GenImpl<Result, Generator<Value, Source, Result>> {
+    const Source source_;
+    const Predicate pred_;
+  public:
+    explicit Generator(const Source& source, const Predicate& pred)
+      : source_(source), pred_(pred) {}
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      return source_.apply([&](Value value) -> bool {
+        return !pred_(std::forward<Value>(value))
+            && handler(std::forward<Value>(value));
+      });
+    }
+  };
+
+  template<class Source,
+           class Value,
+           class Gen = Generator<Value, Source>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self(), predicate_);
+  }
+};
+
+/**
+ * Take - For producing up to N values from a source.
+ *
+ * This type is usually used through the 'take' helper function, like:
+ *
+ *   auto best = from(docs)
+ *             | orderByDescending(scoreDoc)
+ *             | take(10);
+ */
+class Take : public Operator<Take> {
+  const size_t count_;
+public:
+  explicit Take(size_t count)
+    : count_(count) {}
+
+  template<class Value,
+           class Source>
+  class Generator :
+      public GenImpl<Value, Generator<Value, Source>> {
+    const Source source_;
+    const size_t count_;
+  public:
+    explicit Generator(const Source& source, size_t count)
+      : source_(source) , count_(count) {}
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      if (count_ == 0) { return false; }
+      size_t n = count_;
+      return source_.apply([&](Value value) -> bool {
+          if (!handler(std::forward<Value>(value))) {
+            return false;
+          }
+          return --n;
+        });
+    }
+  };
+
+  template<class Source,
+           class Value,
+           class Gen = Generator<Value, Source>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self(), count_);
+  }
+};
+
+/**
+ * Skip - For skipping N items from the beginning of a source generator.
+ *
+ * This type is usually used through the 'skip' helper function, like:
+ *
+ *   auto page = from(results)
+ *             | skip(pageSize * startPage)
+ *             | take(10);
+ */
+class Skip : public Operator<Skip> {
+  const size_t count_;
+public:
+  explicit Skip(size_t count)
+    : count_(count) {}
+
+  template<class Value,
+           class Source>
+  class Generator :
+      public GenImpl<Value, Generator<Value, Source>> {
+    const Source source_;
+    const size_t count_;
+  public:
+    explicit Generator(const Source& source, size_t count)
+      : source_(source) , count_(count) {}
+
+    template<class Body>
+    void foreach(Body&& body) const {
+      if (count_ == 0) {
+        source_.foreach(body);
+        return;
+      }
+      size_t n = 0;
+      source_.foreach([&](Value value) {
+          if (n < count_) {
+            ++n;
+          } else {
+            body(std::forward<Value>(value));
+          }
+        });
+    }
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      if (count_ == 0) {
+        return source_.apply(handler);
+      }
+      size_t n = 0;
+      return source_.apply([&](Value value) -> bool {
+          if (n < count_) {
+            ++n;
+            return true;
+          }
+          return handler(std::forward<Value>(value));
+        });
+    }
+  };
+
+  template<class Source,
+           class Value,
+           class Gen = Generator<Value, Source>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self(), count_);
+  }
+};
+
+/**
+ * Order - For ordering a sequence of values from a source by key.
+ * The key is extracted by the given selector functor, and this key is then
+ * compared using the specified comparator.
+ *
+ * This type is usually used through the 'order' helper function, like:
+ *
+ *   auto closest = from(places)
+ *                | orderBy([](Place& p) {
+ *                    return -distance(p.location, here);
+ *                  })
+ *                | take(10);
+ */
+template<class Selector, class Comparer>
+class Order : public Operator<Order<Selector, Comparer>> {
+  const Selector selector_;
+  const Comparer comparer_;
+ public:
+  Order(const Selector& selector = Selector(),
+        const Comparer& comparer = Comparer())
+    : selector_(selector) , comparer_(comparer) {}
+
+  template<class Value,
+           class Source,
+           class StorageType = typename std::decay<Value>::type,
+           class Result = typename std::result_of<Selector(Value)>::type>
+  class Generator :
+    public GenImpl<StorageType&&,
+                   Generator<Value, Source, StorageType, Result>> {
+    const Source source_;
+    const Selector selector_;
+    const Comparer comparer_;
+
+    typedef std::vector<StorageType> VectorType;
+
+    VectorType asVector() const {
+      auto comparer = [&](const StorageType& a, const StorageType& b) {
+        return comparer_(selector_(a), selector_(b));
+      };
+      auto vals = source_ | as<VectorType>();
+      std::sort(vals.begin(), vals.end(), comparer);
+      return std::move(vals);
+    }
+   public:
+    Generator(const Source& source,
+              const Selector& selector,
+              const Comparer& comparer)
+      : source_(source) , selector_(selector) , comparer_(comparer) {}
+
+    VectorType operator|(const Collect<VectorType>&) const {
+      return asVector();
+    }
+
+    VectorType operator|(const CollectTemplate<std::vector>&) const {
+      return asVector();
+    }
+
+    template<class Body>
+    void foreach(Body&& body) const {
+      for (auto& value : asVector()) {
+        body(std::move(value));
+      }
+    }
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      auto comparer = [&](const StorageType& a, const StorageType& b) {
+        // swapped for minHeap
+        return comparer_(selector_(b), selector_(a));
+      };
+      auto heap = source_ | as<VectorType>();
+      std::make_heap(heap.begin(), heap.end(), comparer);
+      while (!heap.empty()) {
+        std::pop_heap(heap.begin(), heap.end(), comparer);
+        if (!handler(std::move(heap.back()))) {
+          return false;
+        }
+        heap.pop_back();
+      }
+      return true;
+    }
+  };
+
+  template<class Source,
+           class Value,
+           class Gen = Generator<Value, Source>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self(), selector_, comparer_);
+  }
+};
+
+/*
+ * Sinks
+ */
+
+/**
+ * FoldLeft - Left-associative functional fold. For producing an aggregate value
+ * from a seed and a folder function. Useful for custom aggregators on a
+ * sequence.
+ *
+ * This type is primarily used through the 'foldl' helper method, like:
+ *
+ *   double movingAverage = from(values)
+ *                        | foldl(0.0, [](double avg, double sample) {
+ *                            return sample * 0.1 + avg * 0.9;
+ *                          });
+ */
+template<class Seed,
+         class Fold>
+class FoldLeft : public Operator<FoldLeft<Seed, Fold>> {
+  const Seed seed_;
+  const Fold fold_;
+ public:
+  FoldLeft(const Seed& seed, const Fold& fold)
+    : seed_(seed)
+    , fold_(fold)
+  {}
+
+  template<class Source,
+           class Value>
+  Seed compose(const GenImpl<Value, Source>& source) const {
+    Seed accum = seed_;
+    source | [&](Value v) {
+      accum = fold_(std::move(accum), std::forward<Value>(v));
+    };
+    return accum;
+  }
+};
+
+/**
+ * First - For finding the first value in a sequence.
+ *
+ * This type is primarily used through the 'first' static value, like:
+ *
+ *   int firstThreeDigitPrime = seq(100) | filter(isPrime) | first;
+ */
+class First : public Operator<First> {
+ public:
+  template<class Source,
+           class Value,
+           class StorageType = typename std::decay<Value>::type>
+  StorageType compose(const GenImpl<Value, Source>& source) const {
+    static_assert(std::is_same<StorageType, int>::value, "wtf");
+    Optional<StorageType> accum;
+    source | [&](Value v) -> bool {
+      accum = std::forward<Value>(v);
+      return false;
+    };
+    if (!accum.hasValue()) {
+      throw EmptySequence();
+    }
+    return std::move(accum.value());
+  }
+};
+
+
+/**
+ * Any - For determining whether any values are contained in a sequence.
+ *
+ * This type is primarily used through the 'any' static value, like:
+ *
+ *   bool any20xPrimes = seq(200, 210) | filter(isPrime) | any;
+ */
+class Any : public Operator<Any> {
+ public:
+  template<class Source,
+           class Value>
+  bool compose(const GenImpl<Value, Source>& source) const {
+    bool any = false;
+    source | [&](Value v) -> bool {
+      any = true;
+      return false;
+    };
+    return any;
+  }
+};
+
+/**
+ * Reduce - Functional reduce, for recursively combining values from a source
+ * using a reducer function until there is only one item left. Useful for
+ * combining values when an empty sequence doesn't make sense.
+ *
+ * This type is primarily used through the 'reduce' helper method, like:
+ *
+ *   sring longest = from(names)
+ *                 | reduce([](string&& best, string& current) {
+ *                     return best.size() >= current.size() ? best : current;
+ *                   });
+ */
+template<class Reducer>
+class Reduce : public Operator<Reduce<Reducer>> {
+  const Reducer reducer_;
+ public:
+  Reduce(const Reducer& reducer)
+    : reducer_(reducer)
+  {}
+
+  template<class Source,
+           class Value,
+           class StorageType = typename std::decay<Value>::type>
+  StorageType compose(const GenImpl<Value, Source>& source) const {
+    Optional<StorageType> accum;
+    source | [&](Value v) {
+      if (accum.hasValue()) {
+        accum = reducer_(std::move(accum.value()), std::forward<Value>(v));
+      } else {
+        accum = std::forward<Value>(v);
+      }
+    };
+    if (!accum.hasValue()) {
+      throw EmptySequence();
+    }
+    return accum.value();
+  }
+};
+
+/**
+ * Count - for simply counting the items in a collection.
+ *
+ * This type is usually used through its singleton, 'count':
+ *
+ *   auto shortPrimes = seq(1, 100) | filter(isPrime) | count;
+ */
+class Count : public Operator<Count> {
+ public:
+  template<class Source,
+           class Value>
+  size_t compose(const GenImpl<Value, Source>& source) const {
+    return foldl(size_t(0),
+                 [](size_t accum, Value v) {
+                   return accum + 1;
+                 }).compose(source);
+  }
+};
+
+/**
+ * Sum - For simply summing up all the values from a source.
+ *
+ * This type is usually used through its singleton, 'sum':
+ *
+ *   auto gaussSum = seq(1, 100) | sum;
+ */
+class Sum : public Operator<Sum> {
+ public:
+  template<class Source,
+           class Value,
+           class StorageType = typename std::decay<Value>::type>
+  StorageType compose(const GenImpl<Value, Source>& source) const {
+    return foldl(StorageType(0),
+                 [](StorageType&& accum, Value v) {
+                   return std::move(accum) + std::forward<Value>(v);
+                 }).compose(source);
+  }
+};
+
+/**
+ * Min - For a value which minimizes a key, where the key is determined by a
+ * given selector, and compared by given comparer.
+ *
+ * This type is usually used through the singletone 'min' or through the helper
+ * functions 'minBy' and 'maxBy'.
+ *
+ *   auto oldest = from(people)
+ *               | minBy([](Person& p) {
+ *                   return p.dateOfBirth;
+ *                 });
+ */
+template<class Selector,
+         class Comparer>
+class Min : public Operator<Min<Selector, Comparer>> {
+  Selector selector_;
+  Comparer comparer_;
+ public:
+  Min(const Selector& selector = Selector(),
+      const Comparer& comparer = Comparer())
+    : selector_(selector)
+    , comparer_(comparer)
+  {}
+
+  template<class Value,
+           class Source,
+           class StorageType = typename std::decay<Value>::type,
+           class Key = typename std::decay<
+               typename std::result_of<Selector(Value)>::type
+             >::type>
+  StorageType compose(const GenImpl<Value, Source>& source) const {
+    Optional<StorageType> min;
+    Optional<Key> minKey;
+    source | [&](Value v) {
+      Key key = selector_(std::forward<Value>(v));
+      if (!minKey.hasValue() || comparer_(key, minKey.value())) {
+        minKey = key;
+        min = std::forward<Value>(v);
+      }
+    };
+    if (!min.hasValue()) {
+      throw EmptySequence();
+    }
+    return min.value();
+  }
+};
+
+/**
+ * Append - For collecting values from a source into a given output container
+ * by appending.
+ *
+ * This type is usually used through the helper function 'appendTo', like:
+ *
+ *   vector<int64_t> ids;
+ *   from(results) | map([](Person& p) { return p.id })
+ *                 | appendTo(ids);
+ */
+template<class Collection>
+class Append : public Operator<Append<Collection>> {
+  Collection* const collection_;
+ public:
+  explicit Append(Collection* collection)
+    : collection_(collection)
+  {}
+
+  template<class Value,
+           class Source>
+  Collection& compose(const GenImpl<Value, Source>& source) const {
+    source | [&](Value v) {
+      collection_->insert(collection_->end(), std::forward<Value>(v));
+    };
+    return *collection_;
+  }
+};
+
+/**
+ * Collect - For collecting values from a source in a collection of the desired
+ * type.
+ *
+ * This type is usually used through the helper function 'as', like:
+ *
+ *   std::string upper = from(stringPiece)
+ *                     | map(&toupper)
+ *                     | as<std::string>();
+ */
+template<class Collection>
+class Collect : public Operator<Collect<Collection>> {
+ public:
+  template<class Value,
+           class Source,
+           class StorageType = typename std::decay<Value>::type>
+  Collection compose(const GenImpl<Value, Source>& source) const {
+    Collection collection;
+    source | [&](Value v) {
+      collection.insert(collection.end(), std::forward<Value>(v));
+    };
+    return collection;
+  }
+};
+
+
+/**
+ * CollectTemplate - For collecting values from a source in a collection
+ * constructed using the specified template type. Given the type of values
+ * produced by the given generator, the collection type will be:
+ *   Container<Value, Allocator<Value>>
+ *
+ * The allocator defaults to std::allocator, so this may be used for the STL
+ * containers by simply using operators like 'as<set>', 'as<deque>',
+ * 'as<vector>'. 'as', here is the helper method which is the usual means of
+ * consturcting this operator.
+ *
+ * Example:
+ *
+ *   set<string> uniqueNames = from(names) | as<set>();
+ */
+template<template<class, class> class Container,
+         template<class> class Allocator>
+class CollectTemplate : public Operator<CollectTemplate<Container, Allocator>> {
+ public:
+  template<class Value,
+           class Source,
+           class StorageType = typename std::decay<Value>::type,
+           class Collection = Container<StorageType, Allocator<StorageType>>>
+  Collection compose(const GenImpl<Value, Source>& source) const {
+    Collection collection;
+    source | [&](Value v) {
+      collection.insert(collection.end(), std::forward<Value>(v));
+    };
+    return collection;
+  }
+};
+/**
+ * Concat - For flattening generators of generators.
+ *
+ * This type is usually used through the 'concat' static value, like:
+ *
+ *   auto edges =
+ *       from(nodes)
+ *     | map([](Node& x) {
+ *           return from(x.neighbors)
+ *                | map([&](Node& y) {
+ *                    return Edge(x, y);
+ *                  });
+ *         })
+ *     | concat
+ *     | as<std::set>();
+ */
+class Concat : public Operator<Concat> {
+public:
+  template<class Inner,
+           class Source,
+           class InnerValue = typename std::decay<Inner>::type::ValueType>
+  class Generator :
+      public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> {
+    const Source source_;
+  public:
+    explicit Generator(const Source& source)
+      : source_(source) {}
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      return source_.apply([&](Inner inner) -> bool {
+          return inner.apply(std::forward<Handler>(handler));
+        });
+    }
+
+    template<class Body>
+    void foreach(Body&& body) const {
+      source_.foreach([&](Inner inner) {
+          inner.foreach(std::forward<Body>(body));
+        });
+    }
+  };
+
+  template<class Value,
+           class Source,
+           class Gen = Generator<Value, Source>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self());
+  }
+};
+
+/**
+ * RangeConcat - For flattening generators of generators.
+ *
+ * This type is usually used through the 'rconcat' static value, like:
+ *
+ *   map<int, vector<int>> adjacency;
+ *   auto sinks =
+ *       from(adjacency)
+ *     | get<1>()
+ *     | rconcat()
+ *     | as<std::set>();
+ */
+class RangeConcat : public Operator<RangeConcat> {
+public:
+  template<class Source,
+           class Range,
+           class InnerValue = typename ValueTypeOfRange<Range>::RefType>
+  class Generator
+    : public GenImpl<InnerValue, Generator<Source, Range, InnerValue>> {
+    const Source source_;
+   public:
+    Generator(const Source& source)
+      : source_(source) {}
+
+    template<class Body>
+    void foreach(Body&& body) const {
+      source_.foreach([&](Range range) {
+          for (auto& value : range) {
+            body(value);
+          }
+        });
+    }
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      return source_.apply([&](Range range) {
+          for (auto& value : range) {
+            if (!handler(value)) {
+              return false;
+            }
+          }
+          return true;
+        });
+    }
+  };
+
+  template<class Value,
+           class Source,
+           class Gen = Generator<Source, Value>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self());
+  }
+};
+
+} //::detail
+
+/**
+ * Gen<T> - For wrapping template types in simple polymorphic wrapper.
+ *
+ * This type is usually used through the 'rconcat' static value, like:
+ *
+ *   map<int, vector<int>> adjacency;
+ *   auto sinks =
+ *       from(adjacency)
+ *     | get<1>()
+ *     | rconcat()
+ *     | as<std::set>();
+ **/
+template<class Value>
+class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
+  class WrapperBase {
+   public:
+    virtual ~WrapperBase() {}
+    virtual bool apply(const std::function<bool(Value)>& handler) const = 0;
+    virtual void foreach(const std::function<void(Value)>& body) const = 0;
+    virtual std::unique_ptr<const WrapperBase> clone() const = 0;
+  };
+
+  template<class Wrapped>
+  class WrapperImpl : public WrapperBase {
+    const Wrapped wrapped_;
+   public:
+    WrapperImpl(const Wrapped& wrapped)
+     : wrapped_(wrapped) {
+    }
+
+    virtual bool apply(const std::function<bool(Value)>& handler) const {
+      return wrapped_.apply(handler);
+    }
+
+    virtual void foreach(const std::function<void(Value)>& body) const {
+      wrapped_.foreach(body);
+    }
+
+    virtual std::unique_ptr<const WrapperBase> clone() const {
+      return std::unique_ptr<const WrapperBase>(new WrapperImpl(wrapped_));
+    }
+  };
+
+  std::unique_ptr<const WrapperBase> wrapper_;
+
+ public:
+  template<class SourceValue,
+           class Self>
+  /* implicit */ VirtualGen(const GenImpl<SourceValue, Self>& source)
+   : wrapper_(new WrapperImpl<Self>(source.self()))
+  { }
+
+  VirtualGen(VirtualGen&& source)
+   : wrapper_(std::move(source.wrapper_))
+  { }
+
+  VirtualGen(const VirtualGen& source)
+   : wrapper_(source.wrapper_->clone())
+  { }
+
+  VirtualGen& operator=(const VirtualGen& source) {
+    wrapper_.reset(source.wrapper_->clone());
+    return *this;
+  }
+
+  VirtualGen& operator=(VirtualGen&& source) {
+    wrapper_= std::move(source.wrapper_);
+    return *this;
+  }
+
+  bool apply(const std::function<bool(Value)>& handler) const {
+    return wrapper_->apply(handler);
+  }
+
+  void foreach(const std::function<void(Value)>& body) const {
+    wrapper_->foreach(body);
+  }
+};
+
+/**
+ * non-template operators, statically defined to avoid the need for anything but
+ * the header.
+ */
+static const detail::Sum sum;
+
+static const detail::Count count;
+
+static const detail::First first;
+
+static const detail::Any any;
+
+static const detail::Min<Identity, Less> min;
+
+static const detail::Min<Identity, Greater> max;
+
+static const detail::Order<Identity> order;
+
+static const detail::Map<Move> move;
+
+static const detail::Concat concat;
+
+static const detail::RangeConcat rconcat;
+
+inline detail::Take take(size_t count) {
+  return detail::Take(count);
+}
+
+inline detail::Skip skip(size_t count) {
+  return detail::Skip(count);
+}
+
+}} //folly::gen::detail
diff --git a/folly/experimental/Gen.h b/folly/experimental/Gen.h
new file mode 100644 (file)
index 0000000..0797637
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <algorithm>
+
+#include "folly/Range.h"
+#include "folly/Optional.h"
+
+/**
+ * Generator-based Sequence Comprehensions in C++, akin to C#'s LINQ
+ * @author Tom Jackson <tjackson@fb.com>
+ *
+ * This library makes it possible to write declarative comprehensions for
+ * processing sequences of values efficiently in C++. The operators should be
+ * familiar to those with experience in functional programming, and the
+ * performance will be virtually identical to the equivalent, boilerplate C++
+ * implementations.
+ *
+ * Generator objects may be created from either an stl-like container (anything
+ * supporting begin() and end()), from sequences of values, or from another
+ * generator (see below). To create a generator that pulls values from a vector,
+ * for example, one could write:
+ *
+ *   vector<string> names { "Jack", "Jill", "Sara", "Tom" };
+ *   auto gen = from(names);
+ *
+ * Generators are composed by building new generators out of old ones through
+ * the use of operators. These are reminicent of shell pipelines, and afford
+ * similar composition. Lambda functions are used liberally to describe how to
+ * handle individual values:
+ *
+ *   auto lengths = gen
+ *                | mapped([](const fbstring& name) { return name.size(); });
+ *
+ * Generators are lazy; they don't actually perform any work until they need to.
+ * As an example, the 'lengths' generator (above) won't actually invoke the
+ * provided lambda until values are needed:
+ *
+ *   auto lengthVector = lengths | asVector();
+ *   auto totalLength = lengths | sum;
+ *
+ * 'auto' is useful in here because the actual types of the generators objects
+ * are usually complicated and implementation-sensitive.
+ *
+ * If a simpler type is desired (for returning, as an example), VirtualGen<T>
+ * may be used to wrap the generator in a polymorphic wrapper:
+ *
+ *  VirtualGen<float> powersOfE() {
+ *    return seq(1) | mapped(&expf);
+ *  }
+ *
+ * To learn more about this library, including the use of infinite generators,
+ * see the examples in the comments, or the docs (coming soon).
+*/
+
+namespace folly { namespace gen {
+
+template<class Value, class Self>
+class GenImpl;
+
+template<class Self>
+class Operator;
+
+class EmptySequence : std::exception {
+public:
+  virtual const char* what() const noexcept {
+    return "This operation cannot be called on an empty sequence";
+  }
+};
+
+class Less {
+public:
+  template<class First,
+           class Second>
+  auto operator()(const First& first, const Second& second) const ->
+  decltype(first < second) {
+    return first < second;
+  }
+};
+
+class Greater {
+public:
+  template<class First,
+           class Second>
+  auto operator()(const First& first, const Second& second) const ->
+  decltype(first > second) {
+    return first > second;
+  }
+};
+
+template<int n>
+class Get {
+public:
+  template<class Value>
+  auto operator()(Value&& value) const ->
+  decltype(std::get<n>(std::forward<Value>(value))) {
+    return std::get<n>(std::forward<Value>(value));
+  }
+};
+
+class Move {
+public:
+  template<class Value>
+  auto operator()(Value&& value) const ->
+  decltype(std::move(std::forward<Value>(value))) {
+    return std::move(std::forward<Value>(value));
+  }
+};
+
+class Identity {
+public:
+  template<class Value>
+  auto operator()(Value&& value) const ->
+  decltype(std::forward<Value>(value)) {
+    return std::forward<Value>(value);
+  }
+};
+
+namespace detail {
+
+template<class Self>
+struct FBounded;
+
+/*
+ * Type Traits
+ */
+template<class Container>
+struct ValueTypeOfRange {
+ private:
+  static Container container_;
+ public:
+  typedef decltype(*std::begin(container_))
+    RefType;
+  typedef typename std::decay<decltype(*std::begin(container_))>::type
+    StorageType;
+};
+
+
+/*
+ * Sources
+ */
+template<class Container,
+         class Value = typename ValueTypeOfRange<Container>::RefType>
+class ReferencedSource;
+
+template<class Value,
+         class Container = std::vector<typename std::decay<Value>::type>>
+class CopiedSource;
+
+template<class Value, bool endless = false, bool endInclusive = false>
+class Sequence;
+
+template<class Value, class First, class Second>
+class Chain;
+
+template<class Value, class Source>
+class Yield;
+
+/*
+ * Operators
+ */
+template<class Predicate>
+class Map;
+
+template<class Predicate>
+class Filter;
+
+template<class Predicate>
+class Until;
+
+class Take;
+
+class Skip;
+
+template<class Selector, class Comparer = Less>
+class Order;
+
+
+/*
+ * Sinks
+ */
+template<class Seed,
+         class Fold>
+class FoldLeft;
+
+template<class Reducer>
+class Reduce;
+
+class Sum;
+
+template<class Selector,
+         class Comparer>
+class Min;
+
+template<class Container>
+class Collect;
+
+template<template<class, class> class Collection = std::vector,
+         template<class> class Allocator = std::allocator>
+class CollectTemplate;
+
+template<class Collection>
+class Append;
+
+}
+
+/**
+ * Polymorphic wrapper
+ **/
+template<class Value>
+class VirtualGen;
+
+/*
+ * Source Factories
+ */
+template<class Container,
+         class From = detail::ReferencedSource<const Container>>
+From fromConst(const Container& source) {
+  return From(&source);
+}
+
+template<class Container,
+         class From = detail::ReferencedSource<Container>>
+From from(Container& source) {
+  return From(&source);
+}
+
+template<class Container,
+         class Value =
+           typename detail::ValueTypeOfRange<Container>::StorageType,
+         class CopyOf = detail::CopiedSource<Value>>
+CopyOf fromCopy(Container&& source) {
+  return CopyOf(std::forward<Container>(source));
+}
+
+template<class Value,
+         class From = detail::CopiedSource<Value>>
+From from(std::initializer_list<Value> source) {
+  return From(source);
+}
+
+template<class Container,
+         class From = detail::CopiedSource<typename Container::value_type,
+                                         Container>>
+From from(Container&& source) {
+  return From(std::move(source));
+}
+
+template<class Value, class Gen = detail::Sequence<Value, false, false>>
+Gen range(Value begin, Value end) {
+  return Gen(begin, end);
+}
+
+template<class Value,
+         class Gen = detail::Sequence<Value, false, true>>
+Gen seq(Value first, Value last) {
+  return Gen(first, last);
+}
+
+template<class Value,
+         class Gen = detail::Sequence<Value, true>>
+Gen seq(Value begin) {
+  return Gen(begin);
+}
+
+template<class Value,
+         class Source,
+         class Yield = detail::Yield<Value, Source>>
+Yield generator(Source&& source) {
+  return Yield(std::forward<Source>(source));
+}
+
+#define GENERATOR(type, body)                   \
+  ::folly::gen::generator<type>(                \
+    [=](const std::function<void(type)>& yield) \
+    { body })
+
+/*
+ * Operator Factories
+ */
+template<class Predicate,
+         class Map = detail::Map<Predicate>>
+Map mapped(const Predicate& pred = Predicate()) {
+  return Map(pred);
+}
+
+template<class Predicate,
+         class Map = detail::Map<Predicate>>
+Map map(const Predicate& pred = Predicate()) {
+  return Map(pred);
+}
+
+template<class Predicate,
+         class Filter = detail::Filter<Predicate>>
+Filter filter(const Predicate& pred = Predicate()) {
+  return Filter(pred);
+}
+
+template<class Predicate,
+         class Until = detail::Until<Predicate>>
+Until until(const Predicate& pred = Predicate()) {
+  return Until(pred);
+}
+
+template<class Selector,
+         class Comparer = Less,
+         class Order = detail::Order<Selector, Comparer>>
+Order orderBy(const Selector& selector,
+                const Comparer& comparer = Comparer()) {
+  return Order(selector, comparer);
+}
+
+template<class Selector,
+         class Order = detail::Order<Selector, Greater>>
+Order orderByDescending(const Selector& selector) {
+  return Order(selector);
+}
+
+template<int n,
+         class Get = detail::Map<Get<n>>>
+Get get() {
+  return Get();
+}
+
+/*
+ * Sink Factories
+ */
+template<class Seed,
+         class Fold,
+         class FoldLeft = detail::FoldLeft<Seed, Fold>>
+FoldLeft foldl(const Seed& seed, const Fold& fold) {
+  return FoldLeft(seed, fold);
+}
+
+template<class Reducer,
+         class Reduce = detail::Reduce<Reducer>>
+Reduce reduce(const Reducer& reducer) {
+  return Reduce(reducer);
+}
+
+template<class Selector,
+         class Min = detail::Min<Selector, Less>>
+Min minBy(const Selector& selector = Selector()) {
+  return Min(selector);
+}
+
+template<class Selector,
+         class MaxBy = detail::Min<Selector, Greater>>
+MaxBy maxBy(const Selector& selector = Selector()) {
+  return MaxBy(selector);
+}
+
+template<class Collection,
+         class Collect = detail::Collect<Collection>>
+Collect as() {
+  return Collect();
+}
+
+template<template<class, class> class Container = std::vector,
+         template<class> class Allocator = std::allocator,
+         class Collect = detail::CollectTemplate<Container, Allocator>>
+Collect as() {
+  return Collect();
+}
+
+template<class Collection,
+         class Append = detail::Append<Collection>>
+Append appendTo(Collection& collection) {
+  return Append(&collection);
+}
+
+}} // folly::gen
+
+#include "folly/experimental/Gen-inl.h"
diff --git a/folly/experimental/test/GenBenchmark.cpp b/folly/experimental/test/GenBenchmark.cpp
new file mode 100644 (file)
index 0000000..ad9bf7f
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "folly/experimental/Gen.h"
+
+#include <glog/logging.h>
+#include <atomic>
+
+#include "folly/Benchmark.h"
+
+using namespace folly;
+using namespace folly::gen;
+using std::ostream;
+using std::pair;
+using std::set;
+using std::vector;
+using std::tuple;
+
+static std::atomic<int> testSize(1000);
+static vector<int> testVector =
+    seq(1, testSize.load())
+  | mapped([](int) { return rand(); })
+  | as<vector>();
+static vector<vector<int>> testVectorVector =
+    seq(1, 100)
+  | map([](int i) {
+      return seq(1, i) | as<vector>();
+    })
+  | as<vector>();
+
+auto square = [](int x) { return x * x; };
+auto add = [](int a, int b) { return a + b; };
+auto multiply = [](int a, int b) { return a * b; };
+
+BENCHMARK(Sum_Basic_NoGen, iters) {
+  int limit = testSize.load();
+  int s = 0;
+  while (iters--) {
+    for (int i = 0; i < limit; ++i) {
+      s += i;
+    }
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_RELATIVE(Sum_Basic_Gen, iters) {
+  int limit = testSize.load();
+  int s = 0;
+  while (iters--) {
+    s += range(0, limit) | sum;
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(Sum_Vector_NoGen, iters) {
+  int s = 0;
+  while (iters--) {
+    for (auto& i : testVector) {
+      s += i;
+    }
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_RELATIVE(Sum_Vector_Gen, iters) {
+  int s = 0;
+  while (iters--) {
+    s += from(testVector) | sum;
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(Count_Vector_NoGen, iters) {
+  int s = 0;
+  while (iters--) {
+    for (auto& i : testVector) {
+      if (i * 2 < rand()) {
+        ++s;
+      }
+    }
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_RELATIVE(Count_Vector_Gen, iters) {
+  int s = 0;
+  while (iters--) {
+    s += from(testVector)
+       | filter([](int i) {
+                  return i * 2 < rand();
+                })
+       | count;
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(Fib_Sum_NoGen, iters) {
+  int s = 0;
+  while (iters--) {
+    auto fib = [](int limit) -> vector<int> {
+      vector<int> ret;
+      int a = 0;
+      int b = 1;
+      for (int i = 0; i * 2 < limit; ++i) {
+        ret.push_back(a += b);
+        ret.push_back(b += a);
+      }
+      return ret;
+    };
+    for (auto& v : fib(testSize.load())) {
+      s += v;
+      v = s;
+    }
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_RELATIVE(Fib_Sum_Gen, iters) {
+  int s = 0;
+  while (iters--) {
+    auto fib = GENERATOR(int, {
+      int a = 0;
+      int b = 1;
+      for (;;) {
+        yield(a += b);
+        yield(b += a);
+      }
+    });
+    s += fib | take(testSize.load()) | sum;
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+struct FibYielder {
+  template<class Yield>
+  void operator()(Yield&& yield) const {
+    int a = 0;
+    int b = 1;
+    for (;;) {
+      yield(a += b);
+      yield(b += a);
+    }
+  }
+};
+
+BENCHMARK_RELATIVE(Fib_Sum_Gen_Static, iters) {
+  int s = 0;
+  while (iters--) {
+    auto fib = generator<int>(FibYielder());
+    s += fib | take(30) | sum;
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(VirtualGen_0Virtual, iters) {
+  int s = 0;
+  while (iters--) {
+    auto numbers = seq(1, 10000);
+    auto squares = numbers | map(square);
+    auto quads = squares | map(square);
+    s += quads | sum;
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_RELATIVE(VirtualGen_1Virtual, iters) {
+  int s = 0;
+  while (iters--) {
+    VirtualGen<int> numbers = seq(1, 10000);
+    auto squares = numbers | map(square);
+    auto quads = squares | map(square);
+    s += quads | sum;
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_RELATIVE(VirtualGen_2Virtual, iters) {
+  int s = 0;
+  while (iters--) {
+    VirtualGen<int> numbers = seq(1, 10000);
+    VirtualGen<int> squares = numbers | map(square);
+    auto quads = squares | map(square);
+    s += quads | sum;
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_RELATIVE(VirtualGen_3Virtual, iters) {
+  int s = 0;
+  while (iters--) {
+    VirtualGen<int> numbers = seq(1, 10000);
+    VirtualGen<int> squares = numbers | map(square);
+    VirtualGen<int> quads = squares | map(square);
+    s += quads | sum;
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_DRAW_LINE()
+
+BENCHMARK(Concat_NoGen, iters) {
+  int s = 0;
+  while (iters--) {
+    for (auto& v : testVectorVector) {
+      for (auto& i : v) {
+        s += i;
+      }
+    }
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+BENCHMARK_RELATIVE(Concat_Gen, iters) {
+  int s = 0;
+  while (iters--) {
+    s += from(testVectorVector) | rconcat | sum;
+  }
+  folly::doNotOptimizeAway(s);
+}
+
+// Results from a dual core Xeon L5520 @ 2.27GHz:
+//
+// ============================================================================
+// folly/experimental/test/GenBenchmark.cpp        relative  time/iter  iters/s
+// ============================================================================
+// Sum_Basic_NoGen                                            301.60ns    3.32M
+// Sum_Basic_Gen                                    103.20%   292.24ns    3.42M
+// ----------------------------------------------------------------------------
+// Sum_Vector_NoGen                                           200.33ns    4.99M
+// Sum_Vector_Gen                                    99.44%   201.45ns    4.96M
+// ----------------------------------------------------------------------------
+// Count_Vector_NoGen                                          19.07fs   52.43T
+// Count_Vector_Gen                                 166.67%    11.44fs   87.38T
+// ----------------------------------------------------------------------------
+// Fib_Sum_NoGen                                                4.15us  241.21K
+// Fib_Sum_Gen                                       48.75%     8.50us  117.58K
+// Fib_Sum_Gen_Static                               113.24%     3.66us  273.16K
+// ----------------------------------------------------------------------------
+// VirtualGen_0Virtual                                         10.05us   99.48K
+// VirtualGen_1Virtual                               29.63%    33.93us   29.47K
+// VirtualGen_2Virtual                               20.47%    49.09us   20.37K
+// VirtualGen_3Virtual                               15.30%    65.68us   15.23K
+// ----------------------------------------------------------------------------
+// Concat_NoGen                                                 2.34us  427.15K
+// Concat_Gen                                        90.04%     2.60us  384.59K
+// ============================================================================
+
+int main(int argc, char *argv[]) {
+  google::ParseCommandLineFlags(&argc, &argv, true);
+  runBenchmarks();
+  return 0;
+}
diff --git a/folly/experimental/test/GenTest.cpp b/folly/experimental/test/GenTest.cpp
new file mode 100644 (file)
index 0000000..bb85aba
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+#include <iostream>
+#include <set>
+#include <vector>
+#include "folly/experimental/Gen.h"
+#include "folly/FBVector.h"
+#include "folly/dynamic.h"
+
+using namespace folly::gen;
+using namespace folly;
+using std::ostream;
+using std::pair;
+using std::set;
+using std::unique_ptr;
+using std::vector;
+using std::tuple;
+using std::make_tuple;
+//using std::unordered_map;
+
+#define EXPECT_SAME(A, B) \
+  static_assert(std::is_same<A, B>::value, "Mismatched: " #A ", " #B)
+EXPECT_SAME(int&&, typename ArgumentReference<int>::type);
+EXPECT_SAME(int&, typename ArgumentReference<int&>::type);
+EXPECT_SAME(const int&, typename ArgumentReference<const int&>::type);
+EXPECT_SAME(const int&, typename ArgumentReference<const int>::type);
+
+template<typename T>
+ostream& operator<<(ostream& os, const set<T>& values) {
+  return os << from(values);
+}
+
+template<typename T>
+ostream& operator<<(ostream& os, const vector<T>& values) {
+  os << "[";
+  for (auto& value : values) {
+    if (&value != &values.front()) {
+      os << " ";
+    }
+    os << value;
+  }
+  return os << "]";
+}
+
+auto square = [](int x) { return x * x; };
+auto add = [](int a, int b) { return a + b; };
+auto multiply = [](int a, int b) { return a * b; };
+
+auto product = foldl(1, multiply);
+
+template<typename A, typename B>
+ostream& operator<<(ostream& os, const pair<A, B>& pair) {
+  return os << "(" << pair.first << ", " << pair.second << ")";
+}
+
+TEST(Gen, Count) {
+  auto gen = seq(1, 10);
+  EXPECT_EQ(10, gen | count);
+  EXPECT_EQ(5, gen | take(5) | count);
+}
+
+TEST(Gen, Sum) {
+  auto gen = seq(1, 10);
+  EXPECT_EQ((1 + 10) * 10 / 2, gen | sum);
+  EXPECT_EQ((1 + 5) * 5 / 2, gen | take(5) | sum);
+}
+
+TEST(Gen, Foreach) {
+  auto gen = seq(1, 4);
+  int accum = 0;
+  gen | [&](int x) { accum += x; };
+  EXPECT_EQ(10, accum);
+  int accum2 = 0;
+  gen | take(3) | [&](int x) { accum2 += x; };
+  EXPECT_EQ(6, accum2);
+}
+
+TEST(Gen, Map) {
+  auto expected = vector<int>{4, 9, 16};
+  auto gen = from({2, 3, 4}) | map(square);
+  EXPECT_EQ((vector<int>{4, 9, 16}), gen | as<vector>());
+  EXPECT_EQ((vector<int>{4, 9}), gen | take(2) | as<vector>());
+}
+
+TEST(Gen, Seq) {
+  // cover the fenceposts of the loop unrolling
+  for (int n = 1; n < 100; ++n) {
+    EXPECT_EQ(n, seq(1, n) | count);
+    EXPECT_EQ(n + 1, seq(1) | take(n + 1) | count);
+  }
+}
+
+TEST(Gen, Range) {
+  // cover the fenceposts of the loop unrolling
+  for (int n = 1; n < 100; ++n) {
+    EXPECT_EQ(range(0, n) | count, n);
+  }
+}
+
+TEST(Gen, FromIterators) {
+  vector<int> source {2, 3, 5, 7, 11};
+  auto gen = from(makeRange(source.begin() + 1, source.end() - 1));
+  EXPECT_EQ(3 * 5 * 7, gen | product);
+}
+
+TEST(Gen, FromMap) {
+  auto source = seq(0, 10)
+              | map([](int i) { return std::make_pair(i, i * i); })
+              | as<std::map<int, int>>();
+  auto gen = fromConst(source)
+           | map([&](const std::pair<const int, int>& p) {
+               return p.second - p.first;
+             });
+  EXPECT_EQ(330, gen | sum);
+}
+
+TEST(Gen, Filter) {
+  const auto expected = vector<int>{1, 2, 4, 5, 7, 8};
+  auto actual =
+      seq(1, 9)
+    | filter([](int x) { return x % 3; })
+    | as<vector<int>>();
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, Take) {
+  auto expected = vector<int>{1, 4, 9, 16};
+  auto actual =
+      seq(1, 1000)
+    | mapped([](int x) { return x * x; })
+    | take(4)
+    | as<vector<int>>();
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, Skip) {
+  auto gen =
+      seq(1, 1000)
+    | mapped([](int x) { return x * x; })
+    | skip(4)
+    | take(4);
+  EXPECT_EQ((vector<int>{25, 36, 49, 64}), gen | as<vector>());
+}
+
+TEST(Gen, Until) {
+  auto gen =
+      seq(1) //infinite
+    | mapped([](int x) { return x * x; })
+    | until([](int x) { return x >= 1000; });
+  EXPECT_EQ(31, gen | count);
+}
+
+
+TEST(Gen, Chain) {
+  std::vector<int> nums {2, 3, 5, 7};
+  std::map<int, int> mappings { { 3, 9}, {5, 25} };
+  auto gen = from(nums) + (from(mappings) | get<1>());
+  EXPECT_EQ(51, gen | sum);
+  EXPECT_EQ(5, gen | take(2) | sum);
+  EXPECT_EQ(26, gen | take(5) | sum);
+}
+
+TEST(Gen, Concat) {
+  std::vector<std::vector<int>> nums {{2, 3}, {5, 7}};
+  auto gen = from(nums) | rconcat;
+  EXPECT_EQ(17, gen | sum);
+  EXPECT_EQ(10, gen | take(3) | sum);
+}
+
+TEST(Gen, ConcatGen) {
+  auto gen = seq(1, 10)
+           | map([](int i) { return seq(1, i); })
+           | concat;
+  EXPECT_EQ(220, gen | sum);
+  EXPECT_EQ(10, gen | take(6) | sum);
+}
+
+TEST(Gen, ConcatAlt) {
+  std::vector<std::vector<int>> nums {{2, 3}, {5, 7}};
+  auto actual = from(nums)
+              | map([](std::vector<int>& v) { return from(v); })
+              | concat
+              | sum;
+  auto expected = 17;
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, Order) {
+  auto expected = vector<int>{0, 3, 5, 6, 7, 8, 9};
+  auto actual =
+      from({8, 6, 7, 5, 3, 0, 9})
+    | order
+    | as<vector>();
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, OrderMoved) {
+  auto expected = vector<int>{0, 9, 25, 36, 49, 64, 81};
+  auto actual =
+      from({8, 6, 7, 5, 3, 0, 9})
+    | move
+    | order
+    | map(square)
+    | as<vector>();
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, OrderTake) {
+  auto expected = vector<int>{9, 8, 7};
+  auto actual =
+      from({8, 6, 7, 5, 3, 0, 9})
+    | orderByDescending(square)
+    | take(3)
+    | as<vector>();
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, MinBy) {
+  EXPECT_EQ(7, seq(1, 10)
+             | minBy([](int i) {
+                 auto d = i - 6.8;
+                 return d * d;
+               }));
+}
+
+TEST(Gen, MaxBy) {
+  auto gen = from({"three", "eleven", "four"});
+
+  EXPECT_EQ("eleven", gen | maxBy(&strlen));
+}
+
+TEST(Gen, Append) {
+  fbstring expected = "facebook";
+  fbstring actual = "face";
+  from(StringPiece("book")) | appendTo(actual);
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, FromRValue) {
+  {
+    // AFAICT The C++ Standard does not specify what happens to the rvalue
+    // reference of a std::vector when it is used as the 'other' for an rvalue
+    // constructor.  Use fbvector because we're sure its size will be zero in
+    // this case.
+    folly::fbvector<int> v({1,2,3,4});
+    auto q1 = from(v);
+    EXPECT_EQ(v.size(), 4);  // ensure that the lvalue version was called!
+    auto expected = 1 * 2 * 3 * 4;
+    EXPECT_EQ(expected, q1 | product);
+
+    auto q2 = from(std::move(v));
+    EXPECT_EQ(v.size(), 0);  // ensure that rvalue version was called
+    EXPECT_EQ(expected, q2 | product);
+  }
+  {
+    auto expected = 7;
+    auto q = from([] {return vector<int>({3,7,5}); }());
+    EXPECT_EQ(expected, q | max);
+  }
+  {
+    for (auto size: {5, 1024, 16384, 1<<20}) {
+      auto q1 = from(vector<int>(size, 2));
+      auto q2 = from(vector<int>(size, 3));
+      // If the rvalue specialization is broken/gone, then the compiler will
+      // (disgustingly!) just store a *reference* to the temporary object,
+      // which is bad.  Try to catch this by allocating two temporary vectors
+      // of the same size, so that they'll probably use the same underlying
+      // buffer if q1's vector is destructed before q2's vector is constructed.
+      EXPECT_EQ(size * 2 + size * 3, (q1 | sum) + (q2 | sum));
+    }
+  }
+  {
+    auto q = from(set<int>{1,2,3,2,1});
+    EXPECT_EQ(q | sum, 6);
+  }
+}
+
+TEST(Gen, OrderBy) {
+  auto expected = vector<int>{5, 6, 4, 7, 3, 8, 2, 9, 1, 10};
+  auto actual =
+      seq(1, 10)
+    | orderBy([](int x) { return (5.1 - x) * (5.1 - x); })
+    | as<vector>();
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, Foldl) {
+  int expected = 2 * 3 * 4 * 5;
+  auto actual =
+      seq(2, 5)
+    | foldl(1, multiply);
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, Reduce) {
+  int expected = 2 + 3 + 4 + 5;
+  auto actual = seq(2, 5) | reduce(add);
+  EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, ReduceBad) {
+  auto gen = seq(1) | take(0);
+  try {
+    EXPECT_TRUE(true);
+    gen | reduce(add);
+    EXPECT_TRUE(false);
+  } catch (...) {
+  }
+}
+
+TEST(Gen, Moves) {
+  std::vector<unique_ptr<int>> ptrs;
+  ptrs.emplace_back(new int(1));
+  EXPECT_NE(ptrs.front().get(), nullptr);
+  auto ptrs2 = from(ptrs) | move | as<vector>();
+  EXPECT_EQ(ptrs.front().get(), nullptr);
+  EXPECT_EQ(**ptrs2.data(), 1);
+}
+
+TEST(Gen, First) {
+  auto gen =
+      seq(0)
+    | filter([](int x) { return x > 3; });
+  EXPECT_EQ(4, gen | first);
+}
+
+TEST(Gen, FromCopy) {
+  vector<int> v {3, 5};
+  auto src = from(v);
+  auto copy = fromCopy(v);
+  EXPECT_EQ(8, src | sum);
+  EXPECT_EQ(8, copy | sum);
+  v[1] = 7;
+  EXPECT_EQ(10, src | sum);
+  EXPECT_EQ(8, copy | sum);
+}
+
+TEST(Gen, Get) {
+  std::map<int, int> pairs {
+    {1, 1},
+    {2, 4},
+    {3, 9},
+    {4, 16},
+  };
+  auto pairSrc = from(pairs);
+  auto keys = pairSrc | get<0>();
+  auto values = pairSrc | get<1>();
+  EXPECT_EQ(10, keys | sum);
+  EXPECT_EQ(30, values | sum);
+  EXPECT_EQ(30, keys | map(square) | sum);
+  pairs[5] = 25;
+  EXPECT_EQ(15, keys | sum);
+  EXPECT_EQ(55, values | sum);
+
+  vector<tuple<int, int, int>> tuples {
+    make_tuple(1, 1, 1),
+    make_tuple(2, 4, 8),
+    make_tuple(3, 9, 27),
+  };
+  EXPECT_EQ(36, from(tuples) | get<2>() | sum);
+}
+
+TEST(Gen, Any) {
+  EXPECT_TRUE(seq(0) | any);
+  EXPECT_TRUE(seq(0, 1) | any);
+  EXPECT_TRUE(from({1}) | any);
+  EXPECT_FALSE(range(0, 0) | any);
+  EXPECT_FALSE(from({1}) | take(0) | any);
+}
+
+TEST(Gen, Yielders) {
+  auto gen = GENERATOR(int, {
+    for (int i = 1; i <= 5; ++i) {
+      yield(i);
+    }
+    yield(7);
+    for (int i = 3; ; ++i) {
+      yield(i * i);
+    }
+  });
+  vector<int> expected {
+    1, 2, 3, 4, 5, 7, 9, 16, 25
+  };
+  EXPECT_EQ(expected, gen | take(9) | as<vector>());
+}
+
+TEST(Gen, NestedYield) {
+  auto nums = GENERATOR(int, {
+    for (int i = 1; ; ++i) {
+      yield(i);
+    }
+  });
+  auto gen = GENERATOR(int, {
+    nums | take(10) | yield;
+    seq(1, 5) | [&](int i) {
+      yield(i);
+    };
+  });
+  EXPECT_EQ(70, gen | sum);
+}
+
+TEST(Gen, MapYielders) {
+  auto gen = seq(1, 5)
+           | map([](int n) {
+               return GENERATOR(int, {
+                 int i;
+                 for (i = 1; i < n; ++i)
+                   yield(i);
+                 for (; i >= 1; --i)
+                   yield(i);
+               });
+             })
+           | concat;
+  vector<int> expected {
+                1,
+             1, 2, 1,
+          1, 2, 3, 2, 1,
+       1, 2, 3, 4, 3, 2, 1,
+    1, 2, 3, 4, 5, 4, 3, 2, 1,
+  };
+  EXPECT_EQ(expected, gen | as<vector>());
+}
+
+TEST(Gen, VirtualGen) {
+  VirtualGen<int> v(seq(1, 10));
+  EXPECT_EQ(55, v | sum);
+  v = v | map(square);
+  EXPECT_EQ(385, v | sum);
+  v = v | take(5);
+  EXPECT_EQ(55, v | sum);
+  EXPECT_EQ(30, v | take(4) | sum);
+}
+
+
+TEST(Gen, CustomType) {
+  struct Foo{
+    int y;
+  };
+  auto gen = from({Foo{2}, Foo{3}})
+           | map([](const Foo& f) { return f.y; });
+  EXPECT_EQ(5, gen | sum);
+}
+
+TEST(Gen, NoNeedlessCopies) {
+  auto gen = seq(1, 5)
+           | map([](int x) { return unique_ptr<int>(new int(x)); })
+           | map([](unique_ptr<int> p) { return p; })
+           | map([](unique_ptr<int>&& p) { return std::move(p); })
+           | map([](const unique_ptr<int>& p) { return *p; });
+  EXPECT_EQ(15, gen | sum);
+  EXPECT_EQ(6, gen | take(3) | sum);
+}
+
+TEST(Gen, FromArray) {
+  int source[] = {2, 3, 5, 7};
+  auto gen = from(source);
+  EXPECT_EQ(2 * 3 * 5 * 7, gen | product);
+}
+
+TEST(Gen, FromStdArray) {
+  std::array<int,4> source {{2, 3, 5, 7}};
+  auto gen = from(source);
+  EXPECT_EQ(2 * 3 * 5 * 7, gen | product);
+}
+
+TEST(Gen, StringConcat) {
+  auto gen = seq(1, 10)
+           | map([](int n) { return folly::to<fbstring>(n); })
+           | rconcat;
+  EXPECT_EQ("12345678910", gen | as<fbstring>());
+}
+
+struct CopyCounter {
+  static int alive;
+  int copies;
+  int moves;
+
+  CopyCounter() : copies(0), moves(0) {
+    ++alive;
+  }
+
+  CopyCounter(CopyCounter&& source) {
+    *this = std::move(source);
+    ++alive;
+  }
+
+  CopyCounter(const CopyCounter& source) {
+    *this = source;
+    ++alive;
+  }
+
+  ~CopyCounter() {
+    --alive;
+  }
+
+  CopyCounter& operator=(const CopyCounter& source) {
+    this->copies = source.copies + 1;
+    this->moves = source.moves;
+    return *this;
+  }
+
+  CopyCounter& operator=(CopyCounter&& source) {
+    this->copies = source.copies;
+    this->moves = source.moves + 1;
+    return *this;
+  }
+};
+
+int CopyCounter::alive = 0;
+
+TEST(Gen, CopyCount) {
+  vector<CopyCounter> originals;
+  originals.emplace_back();
+  EXPECT_EQ(1, originals.size());
+  EXPECT_EQ(0, originals.back().copies);
+
+  vector<CopyCounter> copies = from(originals) | as<vector>();
+  EXPECT_EQ(1, copies.back().copies);
+  EXPECT_EQ(0, copies.back().moves);
+
+  vector<CopyCounter> moves = from(originals) | move | as<vector>();
+  EXPECT_EQ(0, moves.back().copies);
+  EXPECT_EQ(1, moves.back().moves);
+}
+
+// test dynamics with various layers of nested arrays.
+TEST(Gen, Dynamic) {
+  dynamic array1 = {1, 2};
+  EXPECT_EQ(dynamic(3), from(array1) | sum);
+  dynamic array2 = {{1}, {1, 2}};
+  EXPECT_EQ(dynamic(4), from(array2) | rconcat | sum);
+  dynamic array3 = {{{1}}, {{1}, {1, 2}}};
+  EXPECT_EQ(dynamic(5), from(array3) | rconcat | rconcat | sum);
+}
+
+int main(int argc, char *argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+  return RUN_ALL_TESTS();
+}