├── .clang_complete ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── Vagrantfile ├── bench ├── CMakeLists.txt └── bench_lambda.cpp ├── build.sh ├── demo ├── CMakeLists.txt ├── euler.cpp └── problem.hpp ├── lib ├── lambda.hpp └── lambda │ ├── core.hpp │ ├── core │ ├── apply.hpp │ ├── bind.hpp │ ├── compose.hpp │ ├── curried.hpp │ ├── curry.hpp │ ├── display.hpp │ ├── dollar.hpp │ ├── flip.hpp │ ├── function_traits.hpp │ ├── invoke.hpp │ ├── macros.hpp │ ├── maybe.hpp │ ├── monad.hpp │ ├── show.hpp │ └── type_traits.hpp │ ├── stream.hpp │ └── stream │ ├── chain.hpp │ ├── collect.hpp │ ├── drop.hpp │ ├── filter.hpp │ ├── fold.hpp │ ├── generator.hpp │ ├── ints.hpp │ ├── map.hpp │ ├── pipe.hpp │ ├── stream.hpp │ └── take.hpp └── test ├── CMakeLists.txt ├── test_bind.cpp ├── test_curry.cpp ├── test_dollar.cpp ├── test_lambda.cpp ├── test_maybe.cpp ├── test_show.cpp └── test_stream.cpp /.clang_complete: -------------------------------------------------------------------------------- 1 | -Ilib 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | build/ 3 | .vagrant 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/googletest"] 2 | path = vendor/googletest 3 | url = https://github.com/google/googletest.git 4 | [submodule "vendor/benchmark"] 5 | path = vendor/benchmark 6 | url = https://github.com/google/benchmark.git 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # travis-ci configuration 4 | # - reference: https://github.com/boostorg/hana/blob/master/.travis.yml 5 | # 6 | 7 | language: c++ 8 | sudo: false 9 | 10 | env: 11 | matrix: # Workaround for https://github.com/travis-ci/travis-ci/issues/4681 12 | - TRAVIS_EMPTY_JOB_WORKAROUND=true 13 | 14 | addons: 15 | apt: 16 | packages: 17 | - g++-6 18 | sources: &sources 19 | - ubuntu-toolchain-r-test 20 | 21 | matrix: 22 | exclude: 23 | - env: TRAVIS_EMPTY_JOB_WORKAROUND=true 24 | 25 | include: 26 | - os: linux 27 | env: GCC_VERSION=6 28 | compiler: gcc 29 | 30 | before_install: 31 | - export CHECKOUT_PATH=${PWD}; 32 | 33 | - mkdir -p cmake && cd cmake 34 | - curl -sSL https://cmake.org/files/v3.5/cmake-3.5.2-Linux-x86_64.tar.gz | tar -xzC . 35 | - export PATH=${PWD}/cmake-3.5.2-Linux-x86_64/bin:${PATH} 36 | 37 | - export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}" 38 | 39 | install: 40 | - cd $CHECKOUT_PATH 41 | 42 | - mkdir -p build && cd build 43 | - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE 44 | 45 | script: 46 | - make verify 47 | - make perf 48 | 49 | notifications: 50 | email: false 51 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 2.8) 3 | project(lambda) 4 | 5 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -O3") 7 | 8 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/demo EXCLUDE_FROM_ALL) 9 | 10 | set(GTEST_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/vendor/googletest/googletest/include 11 | ${CMAKE_CURRENT_SOURCE_DIR}/vendor/googletest/googlemock/include 12 | ${CMAKE_CURRENT_SOURCE_DIR}/vendor/benchmark/include) 13 | 14 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/vendor/googletest/googlemock EXCLUDE_FROM_ALL) 15 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/vendor/benchmark EXCLUDE_FROM_ALL) 16 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/test) 17 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/bench) 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tom Schroeder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # λ 2 | 3 | `lambda` is a functional library for C++ 4 | 5 | [![Build Status](https://travis-ci.org/jtomschroeder/lambda.svg?branch=master)](https://travis-ci.org/jtomschroeder/lambda) 6 | [![](https://tokei.rs/b1/github/jtomschroeder/lambda)](https://github.com/jtomschroeder/lambda) 7 | 8 | ### Usage 9 | ```cpp 10 | using namespace lambda; 11 | 12 | let multipleOf = curry(flip([](auto x, auto y) { return x % y == 0; })); 13 | let even = multipleOf(2); 14 | 15 | // ... 16 | 17 | using namespace lambda::streams; 18 | 19 | // Find the sum of all the multiples of 3 or 5 below 1000. 20 | ints(0, 1000) | filter(multipleOf(3) || multipleOf(5)) | sum; 21 | ``` 22 | 23 | ### About 24 | 25 | `lambda` brings the power of __currying__ and other functional paradigms to C++, in a simple library. The goal: making functional programming in C++ *straight-forward* and *intuitive*. 26 | 27 | __Features__ 28 | - currying 29 | - function composition 30 | - partial application (simplified functional `bind`) 31 | - streams 32 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure(2) do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://atlas.hashicorp.com/search. 15 | config.vm.box = "ubuntu/precise64" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | # config.vm.network "forwarded_port", guest: 80, host: 8080 26 | 27 | # Create a private network, which allows host-only access to the machine 28 | # using a specific IP. 29 | # config.vm.network "private_network", ip: "192.168.33.10" 30 | 31 | # Create a public network, which generally matched to bridged network. 32 | # Bridged networks make the machine appear as another physical device on 33 | # your network. 34 | # config.vm.network "public_network" 35 | 36 | # Share an additional folder to the guest VM. The first argument is 37 | # the path on the host to the actual folder. The second argument is 38 | # the path on the guest to mount the folder. And the optional third 39 | # argument is a set of non-required options. 40 | # config.vm.synced_folder "../data", "/vagrant_data" 41 | 42 | # Provider-specific configuration so you can fine-tune various 43 | # backing providers for Vagrant. These expose provider-specific options. 44 | # Example for VirtualBox: 45 | # 46 | # config.vm.provider "virtualbox" do |vb| 47 | # # Display the VirtualBox GUI when booting the machine 48 | # vb.gui = true 49 | # 50 | # # Customize the amount of memory on the VM: 51 | # vb.memory = "1024" 52 | # end 53 | # 54 | # View the documentation for the provider you are using for more 55 | # information on available options. 56 | 57 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 58 | # such as FTP and Heroku are also available. See the documentation at 59 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 60 | # config.push.define "atlas" do |push| 61 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 62 | # end 63 | 64 | # Enable provisioning with a shell script. Additional provisioners such as 65 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 66 | # documentation for more information about their specific syntax and use. 67 | # config.vm.provision "shell", inline: <<-SHELL 68 | # sudo apt-get update 69 | # sudo apt-get install -y apache2 70 | # SHELL 71 | end 72 | -------------------------------------------------------------------------------- /bench/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | include_directories(${GTEST_INCLUDE_DIRS} 5 | ${CMAKE_CURRENT_SOURCE_DIR}/../lib) 6 | 7 | set(BENCHMARKS ${CMAKE_CURRENT_SOURCE_DIR}/bench_lambda.cpp) 8 | 9 | add_executable(bench_lambda ${BENCHMARKS}) 10 | 11 | target_link_libraries(bench_lambda benchmark) 12 | 13 | add_custom_target(perf 14 | COMMAND ${CMAKE_CURRENT_BINARY_DIR}/bench_lambda 15 | COMMENT "Build & Run Benchmarks" 16 | USES_TERMINAL 17 | DEPENDS bench_lambda) 18 | -------------------------------------------------------------------------------- /bench/bench_lambda.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | using namespace lambda; 8 | using namespace lambda::streams; 9 | 10 | class LambdaPerf : public benchmark::Fixture {}; 11 | 12 | BENCHMARK_F(LambdaPerf, Streams)(benchmark::State &state) { 13 | while (state.KeepRunning()) { 14 | let solution = ints(0, 1000) | filter(multipleOf(3) || multipleOf(5)) | sum; 15 | benchmark::DoNotOptimize(solution); 16 | } 17 | } 18 | 19 | BENCHMARK_MAIN(); 20 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir -p build && cd build 4 | 5 | if [ ! -f Makefile ]; then 6 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .. 7 | fi 8 | 9 | make $@ 10 | -------------------------------------------------------------------------------- /demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../lib 5 | ${CMAKE_CURRENT_SOURCE_DIR}/../vendor/range/include) 6 | 7 | set(SRC ${CMAKE_CURRENT_SOURCE_DIR}/euler.cpp) 8 | 9 | add_executable(demo_lambda ${SRC}) 10 | 11 | target_link_libraries(demo_lambda) 12 | 13 | add_custom_target(demo 14 | COMMAND ${CMAKE_CURRENT_BINARY_DIR}/demo_lambda 15 | COMMENT "Run demo!" 16 | USES_TERMINAL 17 | DEPENDS demo_lambda) 18 | -------------------------------------------------------------------------------- /demo/euler.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "problem.hpp" 5 | #include "lambda.hpp" 6 | 7 | using namespace lambda; 8 | using namespace lambda::streams; 9 | 10 | // Find the sum of all the multiples of 3 or 5 below 1000. 11 | PROBLEM(E1) { 12 | let solution = ints(0, 1000) | filter(multipleOf(3) || multipleOf(5)) | sum; 13 | SOLUTION(solution, 233168); 14 | } 15 | 16 | // By considering the terms in the Fibonacci sequence whose values do not exceed 17 | // four million, find the sum of the even-valued terms. 18 | PROBLEM(E2) { 19 | let fibonacci = generator([] { 20 | static uint64_t x = 0, y = 1; 21 | auto tmp = x; 22 | x += y; 23 | std::swap(x, y); 24 | return tmp; 25 | }); 26 | 27 | let solution = fibonacci | filter(even) | take_while(less(4e6)) | sum; 28 | SOLUTION(solution, 4613732); 29 | } 30 | 31 | uint64_t smallest_factor(uint64_t n) { 32 | auto factors = closed_ints(2ull, n) | filter([n](auto i) { return n % i == 0; }); 33 | return *factors.next(); 34 | } 35 | 36 | uint64_t largest_prime_factor(uint64_t n) { 37 | const auto p = smallest_factor(n); 38 | return (p == n) ? n : largest_prime_factor(n / p); 39 | } 40 | 41 | // What is the largest prime factor of the number 600851475143? 42 | PROBLEM(E3) { 43 | let solution = largest_prime_factor(600851475143); 44 | SOLUTION(solution, 6857); 45 | } 46 | 47 | // Find the largest palindrome made from the product of two 3-digit numbers. 48 | PROBLEM(E4) { 49 | // list comprehension of product of two 3-digit numbers 50 | // auto products = ints(100, 1000) >>= 51 | // [](int x) { return ints(x, 1000) >>= [=](int y) { return yield(x * y); }; }; 52 | 53 | // list comprehension of product of two 3-digit numbers 54 | let products = stream([]() -> Maybe { 55 | static int x = 100, y = 100; 56 | if (x >= 1000) { 57 | return none; 58 | } 59 | 60 | if (y >= 1000) { 61 | y = ++x; 62 | } 63 | 64 | return x * y++; 65 | }); 66 | 67 | const auto palindrome = [](auto n) { 68 | const auto str = std::to_string(n); 69 | 70 | auto front = str.begin(); 71 | auto back = str.rbegin(); 72 | for (; front + (str.size() / 2) != str.end(); ++front, ++back) { 73 | if (*front != *back) { 74 | return false; 75 | } 76 | } 77 | 78 | return true; 79 | }; 80 | 81 | let max = [](auto x, auto y) { return std::max(x, y); }; 82 | 83 | let solution = products | filter(palindrome) | fold(0, max); 84 | SOLUTION(solution, 906609); 85 | } 86 | 87 | // What is the smallest positive number that is evenly divisible by all of the 88 | // numbers from 1 to 20? 89 | PROBLEM(E5) { 90 | let solution = closed_ints(1ull, 20ull) | fold(1, lcm); 91 | SOLUTION(solution, 232792560); 92 | } 93 | 94 | // Find the difference between the sum of the squares of the first one hundred 95 | // natural numbers and the square of the sum. 96 | PROBLEM(E6) { 97 | let n = 100; 98 | let solution = square(closed_ints(1, n) | sum) - (closed_ints(1, n) | map(square) | sum); 99 | 100 | SOLUTION(solution, 25164150); 101 | } 102 | 103 | let prime = [](auto x) { 104 | if (x == 2) { 105 | return true; 106 | } 107 | 108 | if (x < 2 || even(x)) { 109 | return false; 110 | } 111 | 112 | using I = std::remove_reference_t>; 113 | for (I i = 3, end = sqrt(x); i <= end; i += 2) { 114 | if (multipleOf(i, x)) { 115 | return false; 116 | } 117 | } 118 | 119 | return true; 120 | }; 121 | 122 | // What is the 10,001st prime number? 123 | PROBLEM(E7) { 124 | auto solution = ints(1) | filter(prime) | drop(10000); 125 | SOLUTION(solution.next(), some(104743)); 126 | } 127 | 128 | PROBLEM_MAIN() 129 | -------------------------------------------------------------------------------- /demo/problem.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "lambda.hpp" 7 | 8 | struct Problem { 9 | static std::vector> problems; 10 | explicit Problem(std::function p) { problems.push_back(p); } 11 | }; 12 | 13 | #define CONCAT_IMPL(X, Y) X##Y 14 | #define CONCAT(X, Y) CONCAT_IMPL(X, Y) 15 | 16 | #define PROBLEM(E) \ 17 | void E(); \ 18 | Problem CONCAT(P, CONCAT(E, __COUNTER__)){E}; \ 19 | void E() 20 | 21 | namespace problem { 22 | template 23 | void solution(std::string function, const T &t, const U &u) { 24 | lambda::display << function << ": " << t << " == " << u << " " << (t == u ? "👍" : "👎") << "\n"; 25 | } 26 | } 27 | 28 | #define SOLUTION(X, Y) problem::solution(__FUNCTION__, (X), (Y)) 29 | 30 | #define PROBLEM_MAIN() \ 31 | std::vector> Problem::problems; \ 32 | int main() { \ 33 | for (auto problem : Problem::problems) { \ 34 | problem(); \ 35 | } \ 36 | return 0; \ 37 | } 38 | -------------------------------------------------------------------------------- /lib/lambda.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "lambda/core.hpp" 5 | -------------------------------------------------------------------------------- /lib/lambda/core.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "core/type_traits.hpp" 5 | 6 | #include "core/apply.hpp" 7 | #include "core/invoke.hpp" 8 | 9 | #include "core/bind.hpp" 10 | #include "core/compose.hpp" 11 | #include "core/curry.hpp" 12 | #include "core/flip.hpp" 13 | 14 | #include "core/macros.hpp" 15 | #include "core/maybe.hpp" 16 | 17 | #include "core/display.hpp" 18 | #include "core/monad.hpp" 19 | #include "core/show.hpp" 20 | 21 | #include "core/curried.hpp" 22 | 23 | #include "core/dollar.hpp" 24 | -------------------------------------------------------------------------------- /lib/lambda/core/apply.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "invoke.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace lambda { 10 | 11 | namespace detail { 12 | template 13 | constexpr auto apply(F &&f, Tuple &&t, std::index_sequence /*unused*/) { 14 | return invoke(std::forward(f), std::get(std::forward(t))...); 15 | } 16 | } 17 | 18 | template 19 | constexpr auto apply(F &&f, Tuple &&t) { 20 | return detail::apply(std::forward(f), std::forward(t), 21 | std::make_index_sequence>{}>{}); 22 | } 23 | 24 | } /* lambda */ 25 | -------------------------------------------------------------------------------- /lib/lambda/core/bind.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "invoke.hpp" 5 | 6 | namespace lambda { 7 | 8 | template 9 | auto bind(Fn fn, Ts &&... ts) { 10 | return 11 | [fn, ts...](auto &&... ps) { return invoke(fn, ts..., std::forward(ps)...); }; 12 | } 13 | 14 | } /* lambda */ 15 | -------------------------------------------------------------------------------- /lib/lambda/core/compose.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "type_traits.hpp" 5 | 6 | namespace lambda { 7 | 8 | // Note: (library fundamentals TS) 9 | 10 | template 11 | auto compose(F f, G g) { 12 | return [f, g](auto &&... ps) { return f(g(std::forward(ps)...)); }; 13 | } 14 | 15 | } /* lambda */ 16 | -------------------------------------------------------------------------------- /lib/lambda/core/curried.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "curry.hpp" 5 | 6 | namespace lambda { 7 | 8 | // Arithmetic operations 9 | 10 | let plus = curry(std::plus<>{}); 11 | let minus = curry(std::minus<>{}); 12 | let multiplies = curry(std::multiplies<>{}); 13 | let divides = curry(std::divides<>{}); 14 | let modulus = curry(std::modulus<>{}); 15 | let negate = curry(std::negate<>{}); 16 | 17 | // Comparisons 18 | 19 | let less = curry(flip(std::less<>{})); 20 | 21 | // Miscellaneous 22 | 23 | let multipleOf = curry(flip([](auto x, auto y) { return x % y == 0; })); 24 | let even = multipleOf(2); 25 | 26 | } /* lambda */ 27 | -------------------------------------------------------------------------------- /lib/lambda/core/curry.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "macros.hpp" 5 | #include "type_traits.hpp" 6 | 7 | #include 8 | #include 9 | 10 | namespace lambda { 11 | 12 | template 13 | struct Curried { 14 | private: 15 | F f; 16 | std::tuple::type...> args; 17 | 18 | template )> 19 | auto dispatch(Ts &&... ts) const { 20 | return f(std::forward(ts)...); 21 | } 22 | 23 | template )> 24 | auto dispatch(Ts &&... ts) const { 25 | return Curried(f, std::forward(ts)...); 26 | } 27 | 28 | template 29 | auto call(std::index_sequence /*unused*/, Ts &&... ts) const { 30 | return dispatch(std::get(args)..., std::forward(ts)...); 31 | } 32 | 33 | public: 34 | explicit Curried(F f, BoundArgs &&... args) 35 | : f(std::move(f)), args(std::forward(args)...) {} 36 | 37 | template 38 | auto operator()(Ts &&... ts) const { 39 | return call(std::make_index_sequence(), std::forward(ts)...); 40 | } 41 | 42 | template 43 | auto operator||(G g) const { 44 | return [this, g](auto &&... ps) { 45 | return this->operator()(std::forward(ps)...) || 46 | g(std::forward(ps)...); 47 | }; 48 | } 49 | }; 50 | 51 | template () && 52 | !std::is_member_object_pointer())> 53 | auto curry(F &&f, Ts &&... ts) { 54 | return Curried(f, std::forward(ts)...); 55 | } 56 | 57 | template 58 | auto curry(R (C::*fn)(Args...)) { 59 | return Curried>( 60 | [fn](C *c, Args &&... ps) { return (c->*fn)(std::forward(ps)...); }); 61 | } 62 | 63 | template 64 | auto curry(R (C::*fn)(Args...) const) { 65 | return Curried>( 66 | [fn](const C *c, Args &&... ps) { return (c->*fn)(std::forward(ps)...); }); 67 | } 68 | 69 | } /* lambda */ 70 | -------------------------------------------------------------------------------- /lib/lambda/core/display.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "show.hpp" 5 | #include 6 | 7 | namespace lambda { 8 | 9 | class Display { 10 | public: 11 | static Display get() { 12 | static Display p; 13 | return p; 14 | } 15 | 16 | Display() = default; 17 | 18 | Display &operator<<(std::string s) { 19 | std::cout << s; 20 | return *this; 21 | } 22 | }; 23 | 24 | static auto display = Display::get(); 25 | 26 | template 27 | Display &operator<<(Display &display, const T value) { 28 | display << show(value); 29 | return display; 30 | } 31 | 32 | } /* lambda */ 33 | -------------------------------------------------------------------------------- /lib/lambda/core/dollar.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | namespace lambda { 5 | namespace factory { 6 | 7 | template 8 | constexpr std::vector> vector(Ts &&... ts) { 9 | return {std::forward(ts)...}; 10 | } 11 | 12 | template 13 | constexpr auto array(Ts &&... ts) -> std::array, sizeof...(ts)> { 14 | return {std::forward(ts)...}; 15 | } 16 | 17 | } /* factory */ 18 | 19 | let id = [](auto &&i) { return std::forward(i); }; 20 | 21 | let gcd = curry([](auto x, auto y) { 22 | while (y != 0) { 23 | auto r = x % y; 24 | x = y; 25 | y = r; 26 | } 27 | return x; 28 | }); 29 | 30 | let lcm = curry([](auto x, auto y) { return (x * y) / gcd(x, y); }); 31 | 32 | let square = [](auto i) { return i * i; }; 33 | 34 | } /* lambda */ 35 | -------------------------------------------------------------------------------- /lib/lambda/core/flip.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace lambda { 7 | 8 | template 9 | auto flip(F f) { 10 | return [f](auto &&b, auto &&a) { 11 | return f(std::forward(a), std::forward(b)); 12 | }; 13 | } 14 | 15 | } /* lambda */ 16 | -------------------------------------------------------------------------------- /lib/lambda/core/function_traits.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace lambda { 8 | 9 | template 10 | struct function_traits; 11 | 12 | template 13 | struct function_traits : public function_traits {}; 14 | 15 | template 16 | struct function_traits { 17 | using return_t = R; 18 | 19 | static constexpr std::size_t arity = sizeof...(Args); 20 | 21 | template 22 | struct argument { 23 | static_assert(N < arity, "error: invalid parameter index."); 24 | using type = typename std::tuple_element>::type; 25 | }; 26 | 27 | template 28 | using argument_t = typename argument::type; 29 | }; 30 | 31 | // member function pointer 32 | template 33 | struct function_traits : public function_traits {}; 34 | 35 | // const member function pointer 36 | template 37 | struct function_traits : public function_traits {}; 38 | 39 | // member object pointer 40 | template 41 | struct function_traits : public function_traits {}; 42 | 43 | // function object 44 | template 45 | struct function_traits { 46 | private: 47 | using call_t = function_traits; 48 | 49 | public: 50 | using return_t = typename call_t::return_t; 51 | 52 | static constexpr std::size_t arity = call_t::arity - 1; 53 | 54 | template 55 | struct argument { 56 | static_assert(N < arity, "error: invalid parameter index."); 57 | using type = typename call_t::template argument::type; 58 | }; 59 | 60 | template 61 | using argument_t = typename argument::type; 62 | }; 63 | 64 | template 65 | struct function_traits : public function_traits {}; 66 | 67 | template 68 | struct function_traits : public function_traits {}; 69 | 70 | } /* lambda */ 71 | -------------------------------------------------------------------------------- /lib/lambda/core/invoke.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace lambda { 7 | 8 | // Note: since C++17 9 | 10 | namespace detail { 11 | template 12 | auto INVOKE(F &&f, Args &&... args) -> decltype(std::forward(f)(std::forward(args)...)) { 13 | return std::forward(f)(std::forward(args)...); 14 | } 15 | 16 | template 17 | auto INVOKE(T Base::*pmd, Derived &&ref) -> decltype(std::forward(ref).*pmd) { 18 | return std::forward(ref).*pmd; 19 | } 20 | 21 | template 22 | auto INVOKE(PMD pmd, Pointer &&ptr) -> decltype((*std::forward(ptr)).*pmd) { 23 | return (*std::forward(ptr)).*pmd; 24 | } 25 | 26 | template 27 | auto INVOKE(T Base::*pmf, Derived &&ref, Args &&... args) 28 | -> decltype((std::forward(ref).*pmf)(std::forward(args)...)) { 29 | return (std::forward(ref).*pmf)(std::forward(args)...); 30 | } 31 | 32 | template 33 | auto INVOKE(PMF pmf, Pointer &&ptr, Args &&... args) 34 | -> decltype(((*std::forward(ptr)).*pmf)(std::forward(args)...)) { 35 | return ((*std::forward(ptr)).*pmf)(std::forward(args)...); 36 | } 37 | } 38 | 39 | template 40 | auto invoke(F &&f, ArgTypes &&... args) { 41 | return detail::INVOKE(std::forward(f), std::forward(args)...); 42 | } 43 | 44 | } /* lambda */ 45 | -------------------------------------------------------------------------------- /lib/lambda/core/macros.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #define let const auto 5 | 6 | #define REQUIRE_CONCEPT(...) std::enable_if_t<(__VA_ARGS__), int> = 0 7 | -------------------------------------------------------------------------------- /lib/lambda/core/maybe.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace lambda { 7 | 8 | template 9 | using Maybe = std::experimental::optional; 10 | 11 | template 12 | constexpr auto some(T value) { 13 | return Maybe::type>(std::move(value)); 14 | } 15 | 16 | constexpr auto none = std::experimental::nullopt; 17 | 18 | } /* lambda */ 19 | -------------------------------------------------------------------------------- /lib/lambda/core/monad.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "maybe.hpp" 5 | 6 | namespace lambda { 7 | namespace monad { 8 | 9 | template 10 | auto operator>>(const Maybe &m, F &&f) { 11 | return m ? some(f(*m)) : none; 12 | } 13 | 14 | } // monad 15 | } // lambda 16 | -------------------------------------------------------------------------------- /lib/lambda/core/show.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "maybe.hpp" 9 | 10 | namespace lambda { 11 | 12 | inline std::string show(std::string s) { return s; } 13 | inline std::string show(char c) { return std::string(1, c); } 14 | 15 | template ())> 16 | std::string show(I i) { 17 | return std::to_string(i); 18 | } 19 | 20 | template 21 | std::string show(const Maybe maybe) { 22 | return maybe ? "Some(" + show(*maybe) + ")" : "None"; 23 | } 24 | 25 | template 26 | std::string show(const std::vector vec) { 27 | std::string os = "["; 28 | for (const auto &i : vec) { 29 | os += show(i); 30 | if (&i != &vec.back()) { 31 | os += ", "; 32 | } 33 | } 34 | os += "]"; 35 | return os; 36 | } 37 | 38 | // TODO! tuple 39 | // TODO! map 40 | 41 | template 42 | std::string show(const std::pair &pair) { 43 | return show(pair.first) + " +> " + show(pair.second); 44 | } 45 | 46 | template 47 | std::string show(const std::unordered_map &map) { 48 | std::string os = "{ "; 49 | 50 | size_t count = 0; 51 | for (auto it = map.begin(), end = map.end(); it != end; ++it, ++count) { 52 | os += show(*it); 53 | 54 | if (count + 1 != map.size()) { 55 | os += ", "; 56 | } 57 | } 58 | 59 | os += " }"; 60 | return os; 61 | } 62 | 63 | } /* lambda */ 64 | -------------------------------------------------------------------------------- /lib/lambda/core/type_traits.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "function_traits.hpp" 5 | #include 6 | 7 | namespace lambda { 8 | 9 | namespace detail { 10 | struct nope {}; 11 | } 12 | 13 | // 14 | // iterator has begin(T) and end(T) 15 | // TODO! T::value_type, T.begin()++ 16 | // 17 | template 18 | struct is_iterator { 19 | private: 20 | template 21 | static auto check(R &&r) -> std::pair; 22 | 23 | static detail::nope check(...); 24 | 25 | public: 26 | static constexpr bool value = 27 | !std::is_same()))>::value; 28 | }; 29 | 30 | template 31 | constexpr bool is_iterator_v = is_iterator::value; 32 | 33 | template 34 | struct is_callable { 35 | private: 36 | template 37 | static auto check(G &&g, Qs &&... qs) -> decltype(g(qs...)); 38 | 39 | static detail::nope check(...); 40 | 41 | public: 42 | using type = decltype(check(std::declval(), std::declval()...)); 43 | 44 | static constexpr bool value = 45 | !std::is_same(), std::declval()...))>::value; 47 | }; 48 | 49 | template 50 | constexpr bool is_callable_v = is_callable::value; 51 | 52 | template 53 | using is_callable_t = typename is_callable::type; 54 | 55 | } /* lambda */ 56 | -------------------------------------------------------------------------------- /lib/lambda/stream.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "core.hpp" 5 | 6 | #include "stream/generator.hpp" 7 | #include "stream/ints.hpp" 8 | #include "stream/pipe.hpp" 9 | #include "stream/stream.hpp" 10 | 11 | #include "stream/chain.hpp" 12 | #include "stream/drop.hpp" 13 | #include "stream/filter.hpp" 14 | #include "stream/map.hpp" 15 | #include "stream/take.hpp" 16 | 17 | #include "stream/collect.hpp" 18 | #include "stream/fold.hpp" 19 | 20 | // 21 | // TODO(jtomschroeder): 22 | // - max: (fold?) 23 | // - drop (change to 'skip') 24 | // - list comprehension 25 | // - stream -> iter(): useable with for-range loop 26 | // - reversable streams 27 | // - chain-able streams 28 | // - collect() 29 | // 30 | -------------------------------------------------------------------------------- /lib/lambda/stream/chain.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "stream.hpp" 5 | 6 | namespace lambda { 7 | namespace streams { 8 | 9 | template 10 | class ChainStream : public Stream { 11 | S stream; 12 | C chain; 13 | 14 | public: 15 | using Type = std::common_type_t; 16 | 17 | ChainStream(S stream, C chain) : stream(std::move(stream)), chain(std::move(chain)) {} 18 | 19 | Maybe next() { 20 | if (auto s = std::move(stream.next())) { 21 | return s; 22 | } 23 | 24 | return std::move(chain.next()); 25 | } 26 | }; 27 | 28 | template 29 | class Chain : public Pipeable { 30 | C chain; 31 | 32 | public: 33 | explicit Chain(C chain) : chain(std::move(chain)) {} 34 | 35 | template 36 | auto pipe(S stream) const { 37 | return ChainStream{stream, chain}; 38 | } 39 | }; 40 | 41 | template 42 | auto chain(S &&stream) { 43 | return Chain{std::forward(stream)}; 44 | } 45 | 46 | } /* streams */ 47 | } /* lambda */ 48 | -------------------------------------------------------------------------------- /lib/lambda/stream/collect.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "lambda.hpp" 5 | 6 | namespace lambda { 7 | namespace streams { 8 | 9 | template