https://github.com/joellefkowitz/funky
Functional and ergonomic transformations.
https://github.com/joellefkowitz/funky
applicative functional functor instance monad
Last synced: 3 months ago
JSON representation
Functional and ergonomic transformations.
- Host: GitHub
- URL: https://github.com/joellefkowitz/funky
- Owner: JoelLefkowitz
- License: mit
- Created: 2024-05-01T16:59:09.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2025-02-23T14:45:08.000Z (8 months ago)
- Last Synced: 2025-05-21T21:40:17.547Z (5 months ago)
- Topics: applicative, functional, functor, instance, monad
- Language: C++
- Homepage:
- Size: 341 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Funky
Functional and ergonomic transformations.

## Installing
```bash
conan install funky
```You can also download the [sources](https://download-directory.github.io?url=https://github.com/JoelLefkowitz/funky/tree/master/src).
## Motivation
Rather than this:
```cpp
#include
#includestd::vector sequence({1, 2, 3});
std::vector mapped;std::transform(
sequence.begin(),
sequence.end(),
std::back_inserter(mapped),
[](auto x) { return x + 1; }
);
```Let's write this:
```cpp
#include
#includestd::vector sequence({1, 2, 3});
auto mapped = map>(
[](auto x) { return x + 1; },
sequence
);
```Now `mapped` will still be:
```cpp
std::vector({2, 3, 4});
```And this function doesn't mutate the input and is much easier to read.
> Most structures are small enough that immutability makes transformations easier to read and thread safe without loss of performance.
The C++ templating system is smart enough to evaluate the types involved in these kinds of transformations for us. This means we can avoid using std::function or interfaces to declare the types involved. This also means we don't need to define concepts for `Functor` or `Foldable` to use `map` and `fold`.
Our `map` function makes an empty container and still uses `.begin()` and `.end()` internally to map the results. This makes it just as generic as `std::transform` and suitable for transforming any iterable structure. This is reflected in its type signature:
```cpp
// map ≔ (A → B) → [ A ] → [ B ]
```If we do need to mutable a structure we can use the functions in `mutable.tpp`:
```cpp
#include
#includeauto sequence = std::vector({1, 2, 3, 2});
remove(sequence, 2);
```Now `sequence` will be updated in place:
```cpp
std::vector({1, 3});
```## Documentation
Documentation and more detailed examples are hosted on [Github Pages](https://joellefkowitz.github.io/funky).
## Usage
This package makes use of templates. The definitions and implementations are split between `.hpp` and `.tpp` files. If you are calling the functions implemented in them then include the `.tpp` file instead.
## Overview
### Callables
Available in: `funky/generics/callables.hpp`
#### compose
```cpp
// T → U → auto
auto compose(const T &f, const U &g);// T → U → auto
auto compose(const T &f, const U &...gs);
```#### pipe
```cpp
// T → U → auto
auto pipe(const T &f, const U &...gs);
```### Iterables
Available in: `funky/generics/iterables.hpp`
#### convert
```cpp
// [ A ] → [ B ]
FB convert(const FA &source);
```#### reverse
```cpp
// [ A ] → [ A ]
FA reverse(const FA &vec);
```#### map
```cpp
// (char → char) → std::string → std::string
std::string map(const T &mapper, const std::string &source);// (A → B → C) → std::map → std::vector
std::vector map(const T &mapper, const std::map &source);// (A → B) → [ A ] → [ B ]
FB map(const T &mapper, const FA &source);// (A → size_t → B) → [ A ] → [ B ]
FB map(const T &mapper, const FA &source);
```#### foreach
```cpp
// (A → void) → [ A ] → void
void foreach (const T &effect, const FA &source);// (A → size_t → void) → [ A ] → void
void foreach (const T &effect, const FA &source);
```#### filter
```cpp
// (A → bool) → [ A ] → [ A ]
FA filter(const T &condition, const FA &source);
```#### fold
```cpp
// (B → A → B) → B → [ A ] → B
B fold(const T &folder, const B &initial, const FA &source);
```#### concat
```cpp
// [ A ] → A → [ A ]
FA concat(const FA &vec, const A &x);// [ A ] → [ A ] → [ A ]
FA concat(const FA &vec, const FA &x);
```#### flatten
```cpp
// std::vector<[ A ]> → [ A ]
FA flatten(const std::vector &vec);
```#### slice
```cpp
// [ A ] → size_t → size_t → [ A ]
FA slice(const FA &vec, const size_t start, const size_t end);
```#### slice_first
```cpp
// [ A ] → size_t → [ A ]
FA slice_first(const FA &vec, size_t width);
```#### slice_last
```cpp
// [ A ] → size_t → [ A ]
FA slice_last(const FA &vec, size_t width);
```#### drop_first
```cpp
// [ A ] → size_t → [ A ]
FA drop_first(const FA &vec, size_t width);
```#### drop_last
```cpp
// [ A ] → size_t → [ A ]
FA drop_last(const FA &vec, size_t width);
```#### aperture
```cpp
// [ A ] → size_t → std::vector<[ A ]>
std::vector aperture(const FA &vec, size_t width);
```### Sets
Available in: `funky/generics/sets.hpp`
#### contains
```cpp
// [ A ] → A → bool
bool contains(const FA &vec, const A &x);
```#### is_unique
```cpp
// [ A ] → bool
bool is_unique(const FA &vec);
```#### unique
```cpp
// [ A ] → [ A ]
FA unique(const FA &vec);
```#### intersects
```cpp
// [ A ] → [ A ] → bool
bool intersects(const FA &source, const FA &target);
```#### intersection
```cpp
// [ A ] → [ A ] → [ A ]
FA intersection(const FA &source, const FA &target);
```#### difference
```cpp
// [ A ] → [ A ] → [ A ]
FA difference(const FA &source, const FA &target);
```#### index
```cpp
// [ A ] → A → size_t
size_t index(const FA &vec, const A &x);
```#### find
```cpp
// (A → bool) → [ A ] → size_t
size_t find(const T &condition, const FA &source);
```#### min
```cpp
// [ A ] → A → A
A min(const FA &vec, A empty);
```#### max
```cpp
// [ A ] → A → A
A max(const FA &vec, A empty);
```### Pairs
Available in: `funky/generics/pairs.hpp`
#### zip
```cpp
// [ A ] → [ B ] → std::vector>
std::vector> zip(const FA &a, const FB &b);
```#### product
```cpp
// [ A ] → [ B ] → std::vector>
std::vector> product(const FA &a, const FB &b);
```#### pairs
```cpp
// std::map → std::vector>
std::vector> pairs(const std::map &source);
```### Mutable
Available in: `funky/generics/mutable.hpp`
#### insert
```cpp
// [ A ] → [ A ] → void
void insert(const FA &source, FA &target);
```#### remove
```cpp
// [ A ] → A → void
void remove(FA &vec, const A &x);
```#### partition
```cpp
// (A → bool) → [ A ] → void
void partition(const T &filter, FA &vec);
```### Booleans
Available in: `funky/concrete/booleans.hpp`
#### all
```cpp
// [ A ] → bool
bool all(const FA &source);// (A → bool) → [ A ] → bool
bool all(const T &predicate, const FA &source);
```#### any
```cpp
// [ A ] → bool
bool any(const FA &source);// (A → bool) → [ A ] → bool
bool any(const T &predicate, const FA &source);
```#### at_least
```cpp
// size_t → [ A ] → bool
bool at_least(size_t min, const FA &source);// (A → bool) → size_t → [ A ] → bool
bool at_least(const T &predicate, size_t min, const FA &source);
```### Numbers
Available in: `funky/concrete/numbers.hpp`
#### fractional
```cpp
// double → double
double fractional(double x);
```#### round
```cpp
// double → size_t → double
double round(double x, size_t dps);
```#### sign
```cpp
// int → int
int sign(int n);
```#### order
```cpp
// double → int
int order(double n);
```#### proportion
```cpp
// double → double
double proportion(double x);
```#### normalise
```cpp
// double → double → double → double → double
double normalise(double x, double min, double max, double scale);
```#### is_factor
```cpp
// int → int → bool
bool is_factor(int divisor, int dividend);
```#### has_factor
```cpp
// int → int → bool
bool has_factor(int dividend, int divisor);
```#### ratio
```cpp
// size_t → double → double
double ratio(size_t dividend, double divisor);// double → size_t → double
double ratio(double dividend, size_t divisor);// size_t → size_t → double
double ratio(size_t dividend, size_t divisor);
```#### unit
```cpp
// double → std::string → std::string
std::string unit(double n, const std::string &metric);
```#### bounds
```cpp
// A → A → A → bool
bool bounds(A lower, A x, A higher);
```#### includes
```cpp
// A → A → A → bool
bool includes(A lower, A x, A higher);
```#### clamp
```cpp
// A → A → A → A
A clamp(A lower, A x, A higher);
```### Strings
Available in: `funky/concrete/strings.hpp`
#### starts_with
```cpp
// std::string → std::string → bool
bool starts_with(const std::string &str, const std::string &prefix);
```#### ends_with
```cpp
// std::string → std::string → bool
bool ends_with(const std::string &str, const std::string &suffix);
```#### reverse
```cpp
// std::string → std::string
std::string reverse(const std::string &str);
```#### pad
```cpp
// std::string → size_t → std::string
std::string pad(const std::string &str, size_t size);
```#### truncate
```cpp
// std::string → size_t → std::string → std::string
std::string truncate(const std::string &str,size_t limit,const std::string &ellipsis = "...");
```#### uppercase
```cpp
// std::string → std::string
std::string uppercase(const std::string &str);
```#### lowercase
```cpp
// std::string → std::string
std::string lowercase(const std::string &str);
```#### split
```cpp
// std::string → std::string → std::vector
std::vector split(const std::string &str,const std::string &delimiter);
```#### join
```cpp
// std::vector → std::string → std::string
std::string join(const std::vector &strings,const std::string &delimiter = "");
```#### chunk
```cpp
// std::string → size_t → std::vector
std::vector chunk(const std::string &str, size_t size);
```#### without_substrings
```cpp
// std::string → std::vector → std::string
std::string without_substrings(const std::string &str,const std::vector &substr);
```#### format_hex
```cpp
// int → size_t → std::string
std::string format_hex(int n, size_t length = 6UL);
```#### parse_hex
```cpp
// std::string → int
int parse_hex(const std::string &str);
```### Vectors
Available in: `funky/concrete/vectors.hpp`
#### range
```cpp
// size_t → std::vector
std::vector range(size_t limit);// A → A → A → std::vector
std::vector range(A start, A stop, A step = 1);
```#### linspace
```cpp
// A → A → size_t → std::vector
std::vector linspace(A start, A stop, size_t count);
```#### enumerate
```cpp
// std::vector → std::vector>
std::vector> enumerate(const std::vector &vec);
```## Tooling
### Dependencies
To install dependencies:
```bash
yarn install
pip install .[all]
conan install .
```### Tests
To run tests:
```bash
scons test
```### Documentation
To generate the documentation locally:
```bash
scons docs
```### Linters
To run linters:
```bash
scons lint
```### Formatters
To run formatters:
```bash
scons format
```### Publishing
The [ConanCenter](https://conan.io/center) doesn't yet allow users to publish packages independently. Package recipes are submitted to the [conan-center-index](https://github.com/conan-io/conan-center-index). A copy of this recipe is kept in this repository in the `publish` folder. This allows us to test that the recipe is compatible with new versions and makes it easier to submit updates to the conan-center-index.
To test the recipe can build the latest published tag:
```bash
conan create publish/all/conanfile.py --version $(yq -r ".versions | keys | .[0]" publish/config.yml)
```This will fetch the sources and create a locally cached version of the package. This version can also be published to a local remote for testing:
```bash
conan upload / -r
```### Toolchains
Scripts are defined in the `scripts` folder and can be invoked with `toolchains`:
To generate header guards:
```bash
npx toolchains guards
```## Contributing
Please read this repository's [Code of Conduct](CODE_OF_CONDUCT.md) which outlines our collaboration standards and the [Changelog](CHANGELOG.md) for details on breaking changes that have been made.
This repository adheres to semantic versioning standards. For more information on semantic versioning visit [SemVer](https://semver.org).
Bump2version is used to version and tag changes. For example:
```bash
bump2version patch
```### Contributors
- [Joel Lefkowitz](https://github.com/joellefkowitz) - Initial work
## Remarks
Lots of love to the open source community!
![]()
![]()
![]()