npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@fluss/core

v0.36.0

Published

Core functions and structures for functional programming.

Downloads

144

Readme

@fluss/core

code style: prettier

Library for functional coding in the modern environment.

Design goals

  • Get the most from TypeScript's inference power.
  • The implementation of each function should be as minimal as possible.
  • Respect the immutability.
  • All functions must be safe as much as possible.
  • Do not override native methods, if function will make same work and produce result same as native method.
  • Each function is maximally independent module (I try my best, though there can be some dependencies).

@fluss/core's advantages

  • TypeScript included out the box
  • Small size
  • Separated modules. You can import only needed functions into your code.

Install

npm i @fluss/core

Import

import { curry } from '@fluss/core';
// or
import { curry } from '@fluss/core/curry';

API

Library embraces a lot of ES modules. It doesn't support CommonJS. If you need old module system, transform code with any tool (Babel etc.).

pipe

Compose functions from left to right. Can handle asynchronous functions along with synchronous ones.

const fn /*: (str: string) => string */ = pipe(
  (str) => str + 2,
  (str: string) => str + 3,
);
const result /*: '123' */ = fn('1');

const composed /* Promise<number> */ = pipe(
  async (str: string) => str + 2,
  parseInt,
);
const asyncResult /*: Promise<number> */ = composed('1');

identity

Returns own argument back to the calling place.

const value = 5;

const result /*: 5 */ = identity(value);

once

Execute fn only once. And then after function if it is provided.

const doOnlyOnce = once(() => {
  /* Initialize something. */
});

flip

Reverses function's parameters.

const fn = (s: string, n: number) => Number(s) + n;

const flipped /*: (args_0: number, args_1: string) => number */ = flip(fn);

// ...

flipped(1, '2'); // -> 3

curry

Create curried version of function with optional partial application. If function accepts variadic arguments (...rest), then you can apparently define function's arity.

const fn /*: Curried<(arg_0: string, arg_1: string) => string, 2> */ = curry(
  (str1: string, str2: string) => str1 + str2 + 3,
);

There is a special value _ that you can use with curried function to preserve place for an argument for the next function execution.

// _anotherFn_ will accept first parameter from original _fn_ function.
const anotherFn /*: Curried<(arg_0: string) => string, 1> */ = fn(_, '2');

binary

Creates function for binary operation. For unknown operator it returns tuple with left and right operands.

const sum = [1, 2, 3, 4, 5, 6].reduce(binary('+'), 0);

isJust

Checks if value is not null and undefined.

const y /*: boolean */ = isJust(null);
const y1 /*: boolean */ = isJust(false);
const y2 /*: boolean */ = isJust(0);

isError

Checks if value is Error or its extended classes.

const y /*: false */ = isError(null);
const y1 /*: true */ = isError(new Error('message'));
const y2 /*: true */ = isError(new TypeError('message'), TypeError);
const y3 /*: false */ = isError(new Error('message'), TypeError);

isPromise

Checks if value is Promise.

const y /*: false */ = isPromise(false);
const y1 /*: true */ = isPromise(Promise.resolve(9));

isFunction

Check if value is a function.

const f: unknown = () => 2;

if (isFunction<() => number>(f)) {
  // `f` will be type of () => number here.
}

throttle

Makes function be executed once per frames count. If frames argument is equal to 0 or less, then, if present, requestAnimationFrame is used. Otherwise, setTimeout function is in use.

const cpuHeavyFunction = throttle(() => {
  /* Do some heavy stuff. */
}, 4);

document.addEventListener('scroll', cpuHeavyFunction);

consequent

Executes function while it is not in process. It can handle asynchronous functions.

const consequentFunction = consequent((...args) => {
  /* Some work here */
});

consequentFunction(); // Start doing the job.
consequentFunction(); // If previous invocation is not completed then this is ignored.

debounce

Delays function invocation for frames from last invocation of debounced function. If interval between invocations will be less than frames time, then original function won't be executed.

const debouncedFunction = debounce((event: ScrollEvent) => {
  /* Some work here */
}, 2);

// It starts job when you finish scrolling.
window.addEventListener('scroll', debouncedFunction);

delay

Delays function invocation by some frames. If frames equals to zero or less, then requestAnimationFrame function is used.

delay(() => {
  /* Some work here. */
}); // Will use `requestAnimationFrame` in browser.
const stamp = delay(() => {
  /* Some another work here. */
}, 2); // Will use `setTimeout`.

stamp.canceled; // -> false
stamp.result; // -> Promise<T> holds result if delayed function.
stamp.cancel(); // -> cancels delay.

memoize

Wraps function and cache all execution results. Allows to customize key for cache. By default, it is first function's argument. Cache readable object is visible to outside.

const fn = (num: number) => Math.random() * num;
const memoizedFn = memoize(fn);

const result1 = memoizedFn(1); // Function is executed
const result2 = memoizedFn(1); // Value from cache will be returned
const result3 = memoizedFn(4); // Function is executed

// Allows manually clear cache.
memoizedFn.cache.clear();

tap

Performs side effect on value while returning it as is. It does not wait for side effect callback to be finished.

const result /*: 5 */ = tap(console.log)(5);

awaitedTap

Performs side effect on value while returning it as is. It waits for side effect callback to be finished.

const result /*: 5 */ = awaitedTap(prepareListenerForTheValue)(5);

when

Replaces conditional flow (ternary operator and if/else).

const multiplyIf = when((num: number) => num > 10)((num) => num * 3, identity);

const result /*: number */ = multiplyIf(9); // Will be returned as is.
const result2 /*: number */ = multiplyIf(11); // Will be multiplied.

isOption

Checks if value is instance of Option monad.

isOption(8); // false
isOption(Some(8)); // true

Some

Creates the Option monad instance with the Just state.

Some(2); // Option<number>

None

Option with the Nothing state.

const a /*: None */ = None;

Option

Monad that gets rid of null and undefined. Its methods work only if inner value is not nothing(null and undefined) and its state is Just, otherwise they aren't invoked (except extract and fill). Wraps nullable value and allows to work with it without checking on null and undefined.

isResult

Checks if value is instance of Result monad.

isResult(8); // false
isResult(Ok(8)); // true

Ok

Wraps a value with the Result monad with the Right state.

// We are sure that 8 is not "left" value.
Ok(8); // Result<number, never>

Err

Creates the Result monad instance with the Left state.

Err(new Error('Error is occurred!')); // Result<never, Error>

tryExecute

Runs function and return a result wrapped in the Result monad.

const result /*: Either<Error, string> */ = tryExecute(() => {
  if (someVariable > 3) {
    return someVariable; // the Ok will be returned.
  } else {
    throw new Error('The variable is less than 3.'); // the Err will be returned.
  }
});

Result

Monad that can contain success value or failure value. Allow handle errors in functional way.

Task

Defines the Task monad or copies fork function from another Task or Promise.

function getSomeDataFromInternet(): Promise<string> {
  /* useful code */
}

const dataTask = Task(getSomeDataFromInternet()).map(JSON.parse);

// Runs the task and returns the Promise with the Result.
// The returned Promise never throws, so you don't have wrap it with try/catch.
const data = await dataTask.run();

Succeed

Wraps value to process as Task.

const data = {
  /* some data */
};

const dataTask = Succeed(data)
  .map(JSON.stringify)
  .chain(Task(sendOverInternet));

// somewhere in the code
dataTask.run();

Fail

Create a failed Task.

const dataTask = Fail(someError);

// somewhere in code
dataTask.run();

isTask

Check if a value is instance of the Task.

const dataTask = Succeed(8);

isTask(dataTask); // true

Task

Monad that allow to perform some actions asynchronously and deferred in time (in opposite Promise that starts doing job immediately after definition).

Difference between Task and Promise.

List

Create the List from values, array-like objects or iterables.

const numbers /*: List<number> */ = List(1, new Set([2]), [3]);

iterate

Create the List from function that returns iterator.

const numbers /*: List<number> */ = iterate(function* () {
  yield 1;
  yield 2;
  yield 3;
});

isList

Checks if value is instance of the List.

const result /*: boolean */ = isList(List());

List

Monad that represents lazy Array. It can decrease computation step comparably to Array. Actual execution of List's methods starts when one of terminating method (method that do not return a new List instance) is called.

Stream

Creates a live empty stream.

const y /*: Stream<number, number> */ = Stream((value: number) => Math.pow(value, 2));

y.forEach((value) => (document.body.innerHTML = value));

// Somewhere in the code
y.send(2); // document.body.innerHTML will set to equal to 4

Stream

Structure that makes operations with values over time in live mode.

Idle

Queues a data returned by fn to be evaluated at interpreter's idle period.

const value /*: Idle<boolean> */ = Idle(() => 1).map((num) => num > 7);
// somewhere in the code
const evaluated /*: boolean */ = value.extract();

Idle

Monad that allow to defer data initialization.

reviver

Add recognition of Idle, Option, List and Result data structures for JSON.parse.

const obj = JSON.parse('{"type":"__$Option","value":1}', reviver);
// obj will be instance of Option type.

Word from author

Have fun ✌️