Core
Utility
- deepInspect
- inspectArray
- inspectFunction
- inspectObject
- inspectString
- lengthOf
- log
- minusOneToUndefined
- passThrough
- spy
- typeOf
Arity
Monads
- Maybe
- maybe
- mergeMaybes
- maybeToEither
- maybeToSyncEffect
- maybeToAsyncEffect
- Either
- either
- mergeEithers
- validateEithers
- eitherToMaybe
- eitherToSyncEffect
- eitherToAsyncEffect
- Case
- SyncEffect
- syncEffectToMaybe
- syncEffectToEither
- syncEffectToAsyncEffect
- AsyncEffect
- mergeAsyncEffects
List
- entriesOf
- everyOf
- filter
- filterMap
- find
- findIndex
- groupBy
- headOf
- initOf
- join
- keysOf
- lastOf
- randomOf
- reduce
- reduceRight
- slice
- someOf
- sort
- sortAlphabetically
- sortAlphabeticallyZA
- sortNumerically
- sortNumerically21
- tailOf
String
- endsWith
- firstLetterOf
- lastLetterOf
- lowerCaseOf
- repeat
- replace
- search
- split
- startsWith
- substr
- testRegEx
- trim
- upperCaseOf
Conditional
- and
- ifElse
- isArray
- isAtLeast
- isAtMost
- isBetween
- isBoolean
- isDeepEqual
- isElement
- isEmpty
- isEqual
- isFalse
- isFunction
- isGreaterThan
- isInRange
- isJust
- isLength
- isLessThan
- isNotArray
- isNotBoolean
- isNotDeepEqual
- isNotElement
- isNotEmpty
- isNotEqual
- isNotFunction
- isNothing
- isNotLength
- isNotNull
- isNotNumber
- isNotObject
- isNotString
- isNotTypeOf
- isNotUndefined
- isNotZero
- isNull
- isNumber
- isObject
- isString
- isTrue
- isTypeOf
- isUndefined
- isZero
- or
- unless
- when
Core Functions
composecompose(fns: function)(anything: any): any
pure function compose :: [(a -> b)] -> a -> b
compose executes functions in reverse order to pipe.
compose(f,g)(x) is equivalent to f(g(x)).
1import {compose} from '@7urtle/lambda';23const addA = a => a + 'A';4const addB = a => a + 'B';5const addAB = value => compose(addA, addB)(value);67addAB('Order: ');8// => Order: BA
concatconcat(a: any)(b: any): any
pure function concat :: a -> a|boolean
concat can be called both as a curried unary function or as a standard binary function.
1import {concat} from '@7urtle/lambda';23concat('cd')('ab'); // => 'abcd'4concat([3, 4])([1,2]); // => [1, 2, 3, 4]5concat({here: {here: 'there'}})({hi: 'hello'}); // => {hi: 'hello', here: {here: 'there'}}6concat('cd')(1); // => undefined78// concat can be called both as a curried unary function or as a standard binary function9concat('cd')('ab') === concat('cd', 'ab');
failfail(error: string|Error): null
function fail :: a -> b -> number
1import { fail } from '@7urtle/lambda';23fail('I am an error.'); // => throws 'I am an error.'4fail(new Error('something happend :(')); // => throws Error('something happened :('))
flatMapflatMap(fn: function)(functor: functor): functor
function flatMap :: (a -> Functor) -> Functor -> Functor
You should use flatMap when you want to work with functors using functions and functional composition rather than calling flatMaps.
The function can be called both as a unary flatMap(fn)(functor) and binary flatMap(fn, functor).
1import {flatMap, map, Maybe} from '@7urtle/lambda';23const maybePlus2 = number => Maybe.of(number + 2);45// the function maybePlus2 is applied to the value of the functor6flatMap(maybePlus2)(Maybe.of(3)); // => Just(5)7map(maybePlus2)(Maybe.of(3)); // => Just(Just(5))89// use of flatMap equals the use of flatMap on the functor10flatMap(maybePlus2)(Maybe.of(3)).value === Maybe.of(3).flatMap(maybePlus2).value;1112// flatMap can be called both as a curried unary function or as a standard binary function13flatMap(maybePlus2)(Maybe.of(3)).value === flatMap(maybePlus2, Maybe.of(3)).value;
identityidentity(anything: any): any
pure function identity :: a -> a
1import {identity} from '@7urtle/lambda';23identity('anything');4// => anything
includesincludes(a: any)(b: any): any
pure function includes :: a -> b -> boolean
includes can be called both as a curried unary function or as a standard binary function.
1import {includes} from '@7urtle/lambda';23includes('rt')('7urtle'); // => true4includes(1)([1, 2, 3]) // => true5includes('turtle')([1, 2, 3]) // => false67// includes can be called both as a curried unary function or as a standard binary function8includes('rt')('7urtle') === includes('rt', '7urtle');
indexOfindexOf(a: any)(b: any): any
pure function indexOf :: a -> b -> number
indexOf can be called both as a curried unary function or as a standard binary function.
1import {indexOf} from '@7urtle/lambda';23indexOf('7')('7urtle'); // => 04indexOf(7)('7urtle'); // => 05indexOf(2)([1, 2, 3]); // => 16indexOf(4)([1, 2, 3]); // => undefined78// indexOf can be called both as a curried unary function or as a standard binary function9indexOf('7')('7urtle') === indexOf('7', '7urtle');
lastIndexOflastIndexOf(a: any)(b: any): any
pure function lastIndexOf :: a -> b -> number
lastIndexOf can be called both as a curried unary function or as a standard binary function.
1import {lastIndexOf} from '@7urtle/lambda';23lastIndexOf('urtle')('7urtle'); // => 14lastIndexOf(2)([1, 2, 3, 2]); // => 35lastIndexOf('8')('7urtle'); // => undefined67// lastIndexOf can be called both as a curried unary function or as a standard binary function8lastIndexOf('7')('7urtle') === lastIndexOf('7', '7urtle');
liftA2liftA2(fn: function)(ap1: functor)(ap2: functor): functor
pure function liftA2 (a -> b -> c) -> Applicative a -> Applicative b -> Applicative c
The function can be called both as a unary liftA2(fn)(functor)(functor) and ternary liftA2(fn, functor, functor).
1import {liftA2, Maybe} from '@7urtle/lambda';23const add = a => b => a + b;45// function add which expects two inputs is applied to the values of two applicative functors Maybe6// the result is a Maybe functor with the internal value 57liftA2(add)(Maybe.of(2))(Maybe.of(3)); // => Just(5)89// an example of applying a function over a Maybe of undefined value to demonstrate continued safety of functors10liftA2(add)(Maybe.of(1))(Maybe.of(undefined)).isNothing(); // => true1112// liftA2 can be called both as a curried unary function or as a standard ternary function13liftA2(add)(Maybe.of(2))(Maybe.of(3)).value === liftA2(add, Maybe.of(2), Maybe.of(3)).value;
liftA3liftA3(fn: function)(ap1: functor)(ap2: functor)(ap3: functor): functor
pure function liftA3 (a -> b -> c -> d) -> Applicative a -> Applicative b -> Applicative c -> Applicative d
The function can be called both as a unary liftA3(fn)(functor)(functor)(functor) and quaternary liftA2(fn, functor, functor, functor).
1import {liftA3, Maybe} from '@7urtle/lambda';23const add = a => b => c => a + b + c;45// function add which expects three inputs is applied to the values of three applicative functors Maybe6// the result is a Maybe functor with the internal value 97liftA3(add)(Maybe.of(2))(Maybe.of(3))(Maybe.of(4)); // => Just(9)89// an example of applying a function over a Maybe of undefined value to demonstrate continued safety of functors10liftA3(add)(Maybe.of(1))(Maybe.of(2))(Maybe.of(undefined)).isNothing(); // => true1112// liftA3 can be called both as a curried unary function or as a standard quaternary function13liftA3(add)(Maybe.of(2))(Maybe.of(3))(Maybe.of(4)).value === liftA3(add, Maybe.of(2), Maybe.of(3), Maybe.of(4)).value;
mapmap(mapper: function)(anything: any): any
function map :: (a -> b) -> a -> b
In case of monads, you should use map when you want to work with functors using functions and functional composition rather than calling Functor.map.
If you need to both filter and map over an array, consider using the filterMap function.
map can be called both as a curried unary function or as a standard binary function.
1import {map, Maybe, upperCaseOf} from '@7urtle/lambda';23const mapper = a => a + 'm';4const list = ['a', 'b', 'c'];56// the function mapper is applied to each member of the array7map(mapper)(list); // => ['am', 'bm', 'cm']89// the function upperCaseOf is applied to the value of the functor10map(upperCaseOf)(Maybe.of('something')); // => Just('SOMETHING')1112// the function upperCaseOf is applied to the input string13map(upperCaseOf)('turtle'); // => 'TURTLE'1415// use of map equals the use of map on the functor16map(upperCaseOf)(Maybe.of('something')).value === Maybe.of('something').map(upperCaseOf).value;1718// map can be called both as a curried unary function or as a standard binary function19map(upperCaseOf)(Maybe.of('something')).value === map(upperCaseOf, Maybe.of('something')).value;
memomemo(fn: function): function
pure function memo :: (a -> b) -> (a -> b)
1import {memo} from '@7urtle/lambda';23const addTwo = a => a + 2;4const memoAddTwo = memo(addTwo);5const memoAddThree = memo(a => a + 3);67memoAddTwo(1); // => 38memoAddThree(1); // => 4910let count = 0;11const increaseCount = () => ++count;1213increaseCount(); // 114increaseCount(); // 21516const memoIncreaseCount = memo(increaseCount);1718memoIncreaseCount(); // 319memoIncreaseCount(); // 320memoIncreaseCount(); // 3
memoizememoize(memory: object)(fn: function)(anything: any): any
function memoize :: object -> (a -> b) -> a -> b
The function can be called both as a curried unary function or as a standard ternary function.
1import {memoize} from '@7urtle/lambda';23const addTwo = a => a + 2;4let memory = {};56memoize(memory)(addTwo)(1); // => 37memoize(memory)(addTwo)(1); // => 38memory[1]; // => 3910// lastIndexOf can be called both as a curried unary function or as a standard ternary function11memoize(memory)(addTwo)(1) === memoize(memory, addTwo, 1);
mergemerge(sources: array|object): array|object
pure function merge :: [a] -> [b]
1import {merge} from '@7urtle/lambda';23const obj1 = { a: 'a', c: ['a'] };4const obj2 = { b: a => a, d: ['a', 'b'] };5const obj3 = { a: 'c', c: ['c'] };67merge(obj1, obj2, obj3));8// => {"a": "c", "b": a => a, "c": ["a", "c"], "d": ["a", "b"]}910const list1 = ['a', 'b'];11const list2 = [1, 2];1213merge(list1,list2);14// => ['a', 'b', 1, 2]1516merge(list1, obj1)17// => {"0": "a", "1": "b", "a": "a", "c": ["a"]}
pipepipe(fns: function)(anything: any): any
pure function pipe :: [(a -> b)] -> a -> b
pipe executes functions in reverse order to compose.
pipe(f,g)(x) is equivalent to g(f(x)).
1import {pipe} from '@7urtle/lambda';23const addA = a => a + 'A';4const addB = a => a + 'B';5const addAB = value => pipe(addA, addB)(value);67addAB('Order: ');8// => Order: AB
Utility Functions
deepInspectdeepInspect(a: any): string
pure function deepInspect :: a -> string
1import {deepInspect} from '@7urtle/lambda';23function namedFunction() {4return null;5}67deepInspect({a: 'b'}); // => "{a: 'b'}"8deepInspect(namedFunction); // => 'namedFunction'9deepInspect([1, 'a']); // => "[1, 'a']"10deepInspect('my string'); // => "'my string'"11deepInspect(undefined); // => 'undefined'
inspectArrayinspectArray(a: array): string
pure function inspectArray :: [a] -> string
1import {inspectArray} from '@7urtle/lambda';23function namedFunction() {4return null;5}67inspectArray([1, 'a']); // => "[1, 'a']"8inspectArray([namedFunction, 'a']); // => "[namedFunction, 'a']"
inspectFunctioninspectFunction(fn: function): string
pure function inspectFunction :: (a -> b) -> string
1import {inspectFunction} from '@7urtle/lambda';23function namedFunction() {4return null;5}67inspectFunction(namedFunction); // => 'namedFunction'8inspectFunction(() => 'b');9// => `function () {10// => return 'b';11// => }`
inspectObjectinspectObject(a: object): string
pure function inspectObject :: a -> string
1import {inspectObject} from '@7urtle/lambda';23inspectObject({a: 'b'}); // => "{a: 'b'}"
inspectStringinspectString(a: any): string
pure function inspectString :: a -> string
1import {inspectString} from '@7urtle/lambda';23inspectString('my string'); // => "'my string'"
lengthOflengthOf(a: string|array): number
pure function lengthOf :: (string|array) -> number
1import {lengthOf} from '@7urtle/lambda';23lengthOf('7turtle'); // => 74lengthOf([1,2,3]); // => 35lengthOf({}); // => undefined
loglog(anything: any): any
function log :: a -> a
1import {log} from '@7urtle/lambda';23log('anything'); // => 'anything'
minusOneToUndefinedminusOneToUndefined(anything: any): any|boolean
pure function minusOneToUndefined :: a -> a|boolean
Because some functions return -1 as error state, this function is created to change it into a more consistent undefined output.
1import {log} from '@7urtle/lambda';23minusOneToUndefined(-1); // => undefined4minusOneToUndefined(0); // => 05minusOneToUndefined('7urtle'); // => '7urtle'
passThroughpassThrough(fn: function)(anything: any): boolean
pure function passThrough :: function -> a -> a
passThrough can be called both as a curried unary function or as a standard binary function.
1import {passThrough} from '@7urtle/lambda';23passThrough(() => 'b')('a'); // => 'a'45// isTypeOf can be called both as a curried unary function or as a standard binary function6passThrough(() => 'b')('a') === passThrough(() => 'b', 'a');
spyspy(anything: any): any
function spy :: a -> a
1import {spy} from '@7urtle/lambda';23spy([1, 'a']); // => "[1, 'a']"
typeOftypeOf(a: any): string
pure function typeOf :: a -> string
1import {typeOf} from '@7urtle/lambda';23typeOf('7turtle'); // => 'string'
Arity Function
currycurry(fn: function): function
pure function curry :: (a -> b) -> a -> b
1import {curry} from '@7urtle/lambda';23const fn = curry((a, b) => a + b);4fn('a')('b') === fn('a', 'b'); // => true
narynary(fn: function): function
pure function nary :: (a -> b) -> a -> b
1import {nary} from '@7urtle/lambda';23const fn = nary(a => b => a + b);4fn('a')('b') === fn('a', 'b'); // => true
Applicative Functor Monads
Maybe
Applicative Functor Monad
Maybe is one of the simplest and well known monads. Maybe is also quite similar to our monad Either.Maybe expects a value as its input. It is Nothing if the value is null, undefined, or empty. It returns Just for all other cases.
Maybe is called Maybe because it maybe holds a value. You want to use Maybe for situations when you don't know whether there is going to be an input. For example for your API endpoint, it makes it very obvious that you service may not receive a value by mistake and forces the consumer of Maybe to safely deal with it.
In other languages, Maybe monad can also be called Option monad or Nullable monad.
1import {maybe, Maybe, Just, Nothing, upperCaseOf, liftA2, flatMap, compose, startsWith} from '@7urtle/lambda';23// in the example we randomly give Maybe a value or undefined. Maybe.of() outputs an instance of Maybe.4const myMaybe = Maybe.of(Math.random() > 0.5 ? 'random success' : undefined);56// you can use Just and Nothing directly7Just('7urtle') === Maybe.of('7urtle'); // => true8Just('7urte') === Maybe.Just('7urtle'); // => true9Nothing === Maybe.of(undefined); // => true10Nothing === Maybe.Nothing; // => true1112// you could access the actual value like this13myMaybe.value; // => 'random success' or undefined1415// you can also inspect it by16myMaybe.inspect(); // => "Just('random success')" or "Nothing"1718// you can check if the value is Nothing19myMaybe.isNothing(); // => true or false20Maybe.of('abc').isNothing(); // => false21Maybe.of([]).isNothing(); // => true22Just('7urtle').isNothing(); // => false23Nothing.isNothing(); // => true2425// you can check if the value is Just26myMaybe.isJust(); // => true or false27Maybe.of(123).isJust(); // => true28Maybe.of(null).isJust(); // => false29Just('7urtle').isJust(); // => true30Nothing.isJust(); // => false3132// as a functor the value inside is safely mappable (map doesn't execute over Nothing)33myMaybe.map(value => upperCaseOf(value));34myMaybe.inspect(); // => "Just('RANDOM SUCCESS')" or "Nothing"3536// as a monad Maybe can be safely flat mapped with other Maybes (flatMap doesn't execute over Nothing)37Maybe.of(3).flatMap(a => Maybe.of(a + 2)).inspect(); // => 'Just(5)'38Maybe.of(3).flatMap(a => Maybe.of(null)).inspect(); // => 'Nothing'39Maybe.of(3).flatMap(a => a + 2); // => 54041// as an applicative functor you can apply Maybes to each other especially using liftA2 or liftA342const add = a => b => a + b;43liftA2(add)(Maybe.of(2))(Maybe.of(3)); // => Just(5)44Maybe.of(1).map(add).ap(Maybe.of(2)).inspect(); // => 'Just(3)'45Maybe.of(1).map(add).ap(Maybe.of(null)).inspect(); // => 'Nothing'46Maybe.of(add).ap(Maybe.of(1)).ap(Maybe.of(2)).inspect(); // => 'Just(3)'4748// as an example you can use Maybe to help you work with DOM like this49Maybe.of(document.querySelector('#iexist')).map(a => a.offsetTop); // => Just(1240)50Maybe.of(document.querySelector('#idontexist')).map(a => a.offsetTop); // => Nothing51maybe52(() => 'error: the object doesnt exist')53(offsetTop => 'offset from top is ' + offsetTop)54(Maybe.of(document?.querySelector('#iexist')?.offsetTop));5556// to read API request you can use Maybe this way57const getQuery = body =>58flatMap59(a => Maybe.of(a.queryText))60(Maybe.of(body.queryResult));6162// you can use Maybe, Just, and Nothing as output of your functions63const maybeGetEnvironmentVariable = key => Maybe.of(process?.env?[key]);64const maybeDIDKeyFromEnvironment =65compose(66flatMap(did => startsWith('did:key')(did) ? Just(did) : Nothing),67maybeGetEnvironmentVariable68);
maybemaybe(onNothing: functioon)(onJust: function)(functorMaybe: Maybe): any
pure function maybe :: (a -> b) -> (c -> d) -> Maybe -> e
maybe can be called both as a curried unary function or as a standard ternary function.
1import {maybe, Maybe} from '@7urtle/lambda';23maybe(() => 'error')(value => value)(Maybe.of('abc')); // => 'abc'4maybe(() => 'error')(value => value)(Maybe.of(undefined)); // => 'error'5maybe(() => 'error')(() => 'not error)(Maybe.of(undefined)) === Maybe.of(undefined).isNothing() ? 'error' ? 'not error';67// maybe can be called both as a curried unary function or as a standard ternary function8maybe(() => 'error')(value => value)(Maybe.of('abc')) === maybe('error', value => value, Maybe.of('abc'));
maybeToAsyncEffectmaybeToAsyncEffect(maybeMonad: Maybe): AsyncEffect
pure function maybeToAsyncEffect :: Maybe -> AsyncEffect
1import { maybeToAsyncEffect, Maybe } from '@7urtle/lambda';23maybeToAsyncEffect(Maybe.of('7urtle')); // resolves to '7urtle'4maybeToAsyncEffect(Maybe.of(undefined)); // rejects 'Maybe is Nothing.'
maybeToEithermaybeToEither(maybeMonad: Maybe): Either
pure function maybeToEither :: Maybe -> Either
1import { maybeToEither, Maybe } from '@7urtle/lambda';23maybeToEither(Maybe.of('7urtle')); // => Success('7urtle')4maybeToEither(Maybe.of(undefined)); // => Failure('Maybe is Nothing.')
maybeToSyncEffectmaybeToSyncEffect(maybeMonad: Maybe): SyncEffect
pure function maybeToSyncEffect :: Maybe -> SyncEffect
1import { maybeToSyncEffect, Maybe } from '@7urtle/lambda';23maybeToSyncEffect(Maybe.of('7urtle')).trigger(); // => '7urtle'4maybeToSyncEffect(Maybe.of(undefined)).trigger(); // throws 'Maybe is Nothing.'
mergeMaybesmergeMaybes(maybes: Maybe): Maybe
pure function mergeMaybes :: ([Maybe]) -> Maybe
1import { mergeMaybes, Nothing, Just, Maybe } from '@7urtle/lambda';23mergeMaybes(Maybe.of('abc'), Just('def')); // => Just(['abc', 'def'])4mergeMaybes(Maybe.of('abc'), Nothing); // => Nothing5mergeMaybes(Nothing, Maybe.of('def')); // => Nothing6mergeMaybes(Nothing, Nothing); // => Nothing
Either
Applicative Functor Monad
Either is an excellent monad for handling error states and it is fairly similar to our monad Maybe. Either.Failure represents an error state and Either.Success represents a success state.Either.of expects a value as its input. Either.of is the same as Either.Success. You can initiate Either in its error state by Either.Failure.
You can also initiate it using Either.try which expects a function as an input. It is Failure if an error or exception is thrown. It is Success if there are no errors or exceptions.
Either is called Either because it allows you to branch based on an error state. You want to use Either for situations when you don't know whether there might be an error. It makes the very visible that an error can occur and it forces the consumer to handle the situation.
1import {either, Either, Success, Failure, upperCaseOf, liftA2} from '@7urtle/lambda';23// in the example we randomly give Either a value or throw an error. Either.try() outputs an instance of Either.4const myEither = Either.try(() => Math.random() > 0.5 ? 'random success' : throw 'random failure');56// you can also return Either.Failure or Either.Success based on a function logic7const myFunction = Math.random() > 0.5 ? Either.Success('random success') : Either.Failure('random failure');89// Success and Failure can be called directly10Success('7urtle') === Either.Success('7urtle'); // => true11Success('7urtle') === Either.of('7urtle'); // => true12Failure('error') === Either.Failure('error'); // => true1314// you could access the actual value like this15myEither.value; // => 'random success' or 'random failure'1617// you can also inspect it by18myEither.inspect(); // => "Success('random success')" or Failure('random failure')1920// Either.of and Either.Success both represent success states21Either.of('some value').inspect() === Success('some value').inspect(); // => true2223// you can check if the value is Failure24myEither.isFailure(); // => true or false25Either.of('abc').isFailure(); // => false26Success('anything').isFailure(); // => false27Failure('anything').isFailure(); // => true28Either.try(() => {throw 'error'}).isFailure(); // => true2930// you can check if the value is Success31myEither.isSuccess(); // => true or false32Either.of('abc').isSuccess(); // => true33Success('anything').isSuccess(); // => true34Failure('anything').isSuccess(); // => false35Either.try(() => {throw 'error'}).isSuccess(); // => false3637// as a functor the value inside is safely mappable (map doesn't execute over Failure)38myEither.map(value => upperCaseOf(value));39myEither.inspect(); // => "Success('RANDOM SUCCESS')" or "Failure('random failure')"4041// as a monad Either can be safely flat mapped with other Eithers (flatMap doesn't execute over Failure)42Either.of(3).flatMap(a => Either.of(a + 2)).inspect(); // => 'Success(5)'43Failure(3).flatMap(a => Either.of(null)).inspect(); // => 'Failure(3)'44Either.of(3).flatMap(a => a + 2); // => 54546// you can use catchMap if you want to map over Failure47Failure('error').catchMap(a => a + 's'); // => Failure('errors')48Success('7urtle').catchMap(a => a + 's'); // => Success('7urtle')4950// you can use bimap to map over both Success and Failure with different functions51Failure('error').bimap(a + ' is left')(a => a + ' is right'); // => Failure('error is left')52Succcess('7urtle').bimap(a + ' is left')(a => a + ' is right'); // => Success('7urtle is right')5354// orOf(a) replaces Failure with Success55Failure('error').orOf('7urtles'); // => Success('7urtle')56Success('7urtle').orOf('tortoise'); // => Success('7urtle')5758// orElse(a -> Either) replaces Failure with the output of orElse function59Failure('error').orElse(() => Success('7urtle')); // => Success('7urtle')60Success('7urtle').orElse(() => Success('tortoise')); // => Success('7urtle')6162// orTry(a -> b) replaces original Fairlure with Either.try63Failure('error').orTry(() => { throw 'i am an error'; }); // => Failure('i am an error')64Failure('error').orTry(() => '7urtle'); // => Success('7urtle')65Success('7urtle').orTry(() => { throw 'i am an error'; }); // => Success('7urtle')6667// as an applicative functor you can apply Eithers to each other especially using liftA2 or liftA368const add = a => b => a + b;69liftA2(add)(Either.of(2))(Either.of(3)); // => Success(5)70Either.of(1).map(add).ap(Either.of(2)).inspect(); // => 'Success(3)'71Failure(1).map(add).ap(Either.of(2)).inspect(); // => 'Failure(1)'72Either.of(add).ap(Either.of(1)).ap(Either.of(2)).inspect(); // => 'Success(3)'
eithereither(onFailure: function)(onSuccess: function)(functorEither: Either): any
pure function either :: (a -> b) -> (b -> c) -> Either
either can be called both as a curried unary function or as a standard ternary function.
1import {either, Either} from '@7urtle/lambda';23either(a => 'error ' + a)(a => 'success ' + a)(Either.of('abc')); // => 'success abc'4either(a => 'error ' + a)(a => 'success ' + a)(Either.Failure('failure')); // => 'error failure'5either(a => 'error ' + a)(a => 'success ' + a)(Either.try(() => throw 'failure')); // => 'error failure'67// either can be called both as a curried unary function or as a standard ternary function8either(a => 'error ' + a)(a => 'success ' + a)(Either.of('abc')) === either(a => 'error ' + a, a => 'success ' + a, Either.of('abc'));
eitherToAsyncEffecteitherToAsyncEffect(eitherMonad: Either): AsyncEffect
pure function eitherToAsyncEffect :: Either -> AsyncEffect
1import { eitherToAsyncEffect, Either } from '@7urtle/lambda';23eitherToAsyncEffect(Either.Success('7urtle')); // resolves to '7urtle'4eitherToAsyncEffect(Either.Failure('I am an error.')); // rejects 'I am an error.'
eitherToMaybeeitherToMaybe(eitherMonad: Either): Maybe
pure function eitherToMaybe :: Either -> Maybe
1import { eitherToMaybe, Either } from '@7urtle/lambda';23eitherToMaybe(Either.Success('7urtle')); // => Just('7urtle')4eitherToMaybe(Either.Success(undefined)); // => Nothing5eitherToMaybe(Either.Failure('I am an error.')); // => Nothing6eitherToMaybe(Either.Failure('I am an error.')).value; // => 'I am an error.'
eitherToSyncEffecteitherToSyncEffect(eitherMonad: Either): SyncEffect
pure function eitherToSyncEffect :: Either -> SyncEffect
1import { eitherToSyncEffect, Either } from '@7urtle/lambda';23eitherToSyncEffect(Either.Success('7urtle')).trigger(); // => '7urtle'4eitherToSyncEffect(Either.Failure('I am an error.')).trigger(); // throws 'I am an error.'
mergeEithersmergeEithers(eithers: Either): Either
pure function mergeEithers :: ([Either]) -> Either
1import { mergeEithers, Either } from '@7urtle/lambda';23mergeEithers(Either.of('abc'), Either.of('def')); // => Success(['abc', 'def'])4mergeEithers(Either.of('abc'), Either.Failure('def')); // => Failure(['def'])5mergeEithers(Either.Failure('abc'), Either.of('def')); // => Failure(['abc'])6mergeEithers(Either.Failure('abc'), Either.Failure('def')); // => Failure(['abc', 'def'])
validateEithersvalidateEithers(fns: functions)(input: any): Either
pure function validateEithers :: ([a -> b]) -> a -> Either
1import { validateEithers, Either, isAtLeast, lengthOf } from '@7urtle/lambda';23// mergeEithers is great to be used for validations4const isPasswordLongEnough = password =>5isAtLeast(6)(lengthOf(password))6? Either.Success(password)7: Either.Failure('Password must have more than 6 characters.');89const isPasswordStrongEnough = password =>10/[\W]/.test(password)11? Either.Success(password)12: Either.Failure('Password must contain special characters.');1314const validatePassword = validateEithers(isPasswordLongEnough, isPasswordStrongEnough);1516validatePassword('LongPa$$word'); // => Success('LongPa$$word')17validatePassword('Pa$$'); // => Failure(['Password must have more than 6 characters.'])18validatePassword('LongPassword'); // => Failure(['Password must contain special characters.'])19validatePassword('Pass'); // => Failure(['Password must have more than 6 characters.', 'Password must contain special characters.'])
Case
Applicative Functor Monad
Case is a monad that helps you with conditional processing.Case expects an array of key-value pairs as its input. Case.match then matches against a key to provide its value.
Case is internally build on a JavaScript map and turns it into an applicative functor monad.
1import {Case, upperCaseOf, liftA2} from '@7urtle/lambda';23// in the example we define Case using key-value pairs. Case.of() outputs an instance of Case.4const myCase = Case.of([[1, 'one'], ['key', 'value'], ['_', 'fallback']]);56// you reach a value by matching keys using Case.match7myCase.match(1); // => 'one'8myCase.match('key'); // => 'value'9myCase.match('nope'); // => 'fallback'1011// if no fallback is defined and no key is matched, we return undefined12Case.of([]).match('nope'); // => undefined1314// you can also inspect it by15myCase.inspect(); // => 'Case(...1617// as a functor the result is safely mappable (map doesn't execute over undefined matches)18myCase.map(value => upperCaseOf(value)).match('key'); // => 'VALUE'19Case.of([]).map(upperCaseOf).match('key'); // => undefined2021// as a monad Case can be safely flat mapped with other Cases (flatMap doesn't execute over undefined)22Case.of([[1, 'I am']]).flatMap(a => Case.of([[1, a + ' a turtle']]).match(1); // => 'I am a turtle'23Case.of([[1, 'I am']]).flatMap(a => Case.of([])).match(1); // => undefined2425// as an applicative functor you can apply Cases to each other especially using liftA2 or liftA326const add = a => b => a + b;27liftA2(add)(Case.of([[1, 1]]))(Case.of([[1, 2]])).match(1); // => 328Case.of([[1, add]]).ap(Case.of([[1, 'I am']])).ap(Case.of([[1, ' a turtle']])).match(1); // => 'I am a turtle'29Case.of([[1, add]]).ap(Case.of([])).ap(Case.of([[1, 'I am']])).match(1); // => undefined
SyncEffect
Applicative Functor Monad
SyncEffect is a monad that allows you to safely work with synchronous side effects in JavaScript.SyncEffect expects as its input a function.
SyncEffect is evaluated lazily and nothing is executed until a trigger function is called. It does not have any inner error/exception handling mechanism for the effects of the trigger. Consider using the monads Maybe and Either for managing the results of the trigger.
In other languages and framework, SyncEffect can be also called the IO monad.
1import {SyncEffect, log, upperCaseOf, liftA2, Either, isNull} from '@7urtle/lambda';23// we create SyncEffect that expects a number from 0 to 14// and based on that, it returns a value or throws an error5const throwError = () => {throw 'random failure'};6const dangerousFunction = value => value > 0.5 ? 'random success' : throwError();7const mySyncEffect = SyncEffect.of(dangerousFunction);89// when you are ready, you can call trigger to trigger the side effect10// nothing is executed until the trigger is called11mySyncEffect.trigger(Math.random());12// => returns 'random success' or throws 'random failure' depending on Math.random() value1314// you can inspect SyncEffect by15mySyncEffect.inspect(); // => "SyncEffect(function...1617// as a functor the value inside is safely mappable18// map doesn't execute in case of an error and nothing executes until a trigger is called19mySyncEffect20.map(value => upperCaseOf(value))21.trigger(Math.random());22// => returns 'RANDOM SUCCESS' or throws 'random failure' depending on Math.random() value2324// as a monad SyncEffect can be safely flat mapped with other SyncEffects25// flatMap doesn't execute in case of an error and nothing executes until a trigger is called26SyncEffect.of(() => '7turtle').flatMap(a => SyncEffect.of(() => a + 's')).trigger();27// => '7urtles'28SyncEffect.of(() => {throw 'error'}).flatMap(a => SyncEffect.of(() => a + 's')).trigger();29// => throws 'error'3031// as an applicative functor you can apply SyncEffects to each other especially using liftA2 or liftA332const add = a => b => a + b;33liftA2(add)(SyncEffect.of(() => 1)(SyncEffect.of(() => 2)).trigger(); // => 334SyncEffect.of(() => add).ap(SyncEffect.of(() => 1)).ap(SyncEffect.of(() => 2)).trigger(); // => 33536// in practice you can use SyncEffect to work for example with DOM37const DOMSyncEffect = SyncEffect.of(targetID => document.querySelector(targetID));38const TopOffsetSyncEffect = DOMSyncEffect.map(a => a.offsetTop);39const ClientHeightSyncEffect = DOMSyncEffect.map(a => a.clientHeight);4041TopOffsetSyncEffect.trigger('article'); // 128042Either.try(ClientHeightSyncEffect.trigger('#dontexist')); // Failure('Uncaught TypeError: Cannot read property 'offsetTop' of null')
syncEffectToAsyncEffectsyncEffectToAsyncEffect(syncEffectMonad: SyncEffect): AsyncEffect
pure function syncEffectToAsyncEffect :: SyncEffect -> AsyncEffect
1import { syncEffectToAsyncEffect, SyncEffect } from '@7urtle/lambda';23syncEffectToAsyncEffect(SyncEffect.of(() => '7urtle')); // resolves to '7urtle'4syncEffectToAsyncEffect(SyncEffect.of(() => { throw 'I am an error.'; })); // rejects 'I am an error.'
syncEffectToEithersyncEffectToEither(syncEffectMonad: SyncEffect): Either
pure function syncEffectToEither :: SyncEffect -> Either
1import { syncEffectToEither, SyncEffect } from '@7urtle/lambda';23syncEffectToEither(SyncEffect.of(() => '7urtle')); // => Success('7urtle')4syncEffectToEither(SyncEffect.of(() => { throw 'I am an error.'; })); // => Failure('I am an error.')
syncEffectToMaybesyncEffectToMaybe(syncEffectMonad: SyncEffect): Maybe
pure function syncEffectToMaybe :: SyncEffect -> Maybe
1import { syncEffectToMaybe, SyncEffect } from '@7urtle/lambda';23eitherToMaybe(SyncEffect.of(() => '7urtle')); // => Just('7urtle')4eitherToMaybe(SyncEffect.of(() => undefined)); // => Nothing5eitherToMaybe(SyncEffect.of(() => { throw 'I am an error.'; }))); // => Nothing
AsyncEffect
Applicative Functor Monad
AsyncEffect is a monad that allows you to safely work with asynchronous side effects in JavaScript.AsyncEffect expects as its input a function that takes two inputs of a reject function, and a resolve function. Reject function is called on failure and resolve function is called on success. It is similar to using JavaScript Promise and AsyncEffect can be directly created from a Promise turning it into a monad.
AsyncEffect is evaluated lazily and nothing is executed until a trigger function is called.
AsyncEffect can also be called Future monad in other libraries or languages.
1import {AsyncEffect, log, upperCaseOf, liftA2, liftA3} from '@7urtle/lambda';23// we create AsyncEffect that expects a number from 0 to 14// and based on that, it resolve or rejects 10 milliseconds after it is triggered5const myAsyncEffect = AsyncEffect6.of(reject => resolve =>7setTimeout(() => Math.random() > 0.5 ? resolve('random success') : reject('random failure'), 10)8);910// we could also create AsyncEffect from a function returning JavaScript Promise11const myPromise = () => new Promise((resolve, reject) =>12setTimeout(() => Math.random() > 0.5 ? resolve('random success') : reject('random failure'), 10)13);14const promiseAsyncEffect = AsyncEffect.ofPromise(myPromise);1516// you can inspect AsyncEffect by17myAsyncEffect.inspect(); // => "AsyncEffect(function...1819// when you are ready, you can call trigger to trigger the side effect20// nothing is executed until the trigger is called21myAsyncEffect22.trigger23(error => log(error))24(result => log(result));25// => logs 'random success' or 'random failure' depending on Math.random() value2627// you can also turn AsyncEffect into a JavaScript Promise28myAsyncEffect29.promise()30.then(result => log(result), error => log(error));31// => logs 'random success' or 'random failure' depending on Math.random() value3233// thrown exceptions lead AsyncEffect to reject34AsyncEffect35.of(() => {36throw 'error';37})38.trigger(log)(log);39// => logs 'error'4041// as a functor the value inside is safely mappable42// map doesn't execute in case of an error and nothing executes until a trigger is called43myAsyncEffect44.map(value => upperCaseOf(value))45.trigger(log)(log);46// => logs 'RANDOM SUCCESS' or 'random failure' depending on Math.random() value4748// as a monad AsyncEffect can be safely flat mapped with other AsyncEffects49// flatMap doesn't execute in case of an error and nothing executes until a trigger is called50AsyncEffect51.of(reject => resolve => resolve('7urtle'))52.flatMap(a => AsyncEffect.of(reject => resolve => resolve(a + 's')))53.trigger(log)(log);54// => logs '7urtles'5556// as an applicative functor you can apply AsyncEffects to each other especially using liftA2 or liftA357const add = a => b => a + b;58const AS1 = AsyncEffect.of(reject => resolve => resolve(1));59const AS2 = AsyncEffect.of(reject => resolve => resolve(2));60liftA2(add)(AS1)(AS2); // => resolve(3)6162const ASFail = AsyncEffect.of(() => {throw 'error'});63liftA3(add)(ASFail)(AS1)(AS2); // => reject('error')6465// AsyncEffect.of as well as AsyncEffect.trigger accept both curried and binary functions66AsyncEffect.of((reject, resolve) => resolve('7urtle')).trigger(log, log); // logs '7urtle'6768// as an example you can use AsyncEffect to help you work with axios or fs6970// axios example71import axios from 'axios';72const getFromURL = url => AsyncEffect.ofPromise(() => axios.get(url));7374getFromURL('/my/ajax/url')75.trigger76(error => log(error))77(result => log(result.data));7879// reading file example80import fs from 'fs';81const readFile => input =>82AsyncEffect83.of(reject => resolve =>84fs.readFile(input, (err, data) =>85err ? reject(err) : resolve(data)86)87);8889readFile('./file.txt')90.trigger91(error => log(error))92(result => log(result));;
mergeAsyncEffectsmergeAsyncEffects(asyncEffects: AsyncEffect): AsyncEffect
pure function mergeAsyncEffects :: ([AsyncEffect]) -> AsyncEffect
1import { mergeAsyncEffects, AsyncEffect } from '@7urtle/lambda';23const resolvingOne = AsyncEffect.of(_ => resolve => resolve('Resolving One'));4const resolvingTwo = AsyncEffect.of(_ => resolve => resolve('Resolving Two'));56mergeAsyncEffects(resolvingOne, resolvingTwo)7.trigger(console.log)(console.log);8// => logs ['Resolving One', 'Resolving Two']910const rejectingOne = AsyncEffect.of(reject => _ => reject('Rejecting One'));11const rejectingTwo = AsyncEffect.of(reject => _ => reject('Rejecting Two'));1213mergeAsyncEffects(resolvingOne, rejectingOne, rejectingTwo, resolvingTwo)14.trigger(console.log)(console.log);15// => logs 'Rejecting One'
List Functions
entriesOfentriesOf(list: Object|array): array
pure function entriesOf :: object -> [[string, a]]
1import {entriesOf} from '@7urtle/lambda';23entriesOf([2, 3, 4]); // => [['0', 2], ['1', 3], ['2', 4]]4entriesOf({1: 2, 2: 3}); // => [['1', 2],['2', 3]]
everyOfeveryOf(checker: function)(list: array): boolean
pure function everyOf :: (a -> boolean) -> [a] -> boolean
everyOf can be called both as a curried unary function or as a standard binary function.
1import {everyOf} from '@7urtle/lambda';23everyOf(a => a > 1)([2, 3, 4]); // => true4everyOf(a => a > 5)([2, 3, 4]); // => false56// everyOf can be called both as a curried unary function or as a standard binary function7everyOf(a => a > 1)([2, 3, 4]) === everyOf(a => a > 1, [2, 3, 4]);
filterfilter(checker: function)(list: array): any
pure function filter :: (a -> boolean) -> [a] -> [b]
If you need to both filter and map over an array, consider using the filterMap function.
filter can be called both as a curried unary function or as a standard binary function.
1import {filter} from '@7urtle/lambda';23const list = [0, 1, 2, 3]45filter(a => a > 1)(list); // => [2, 3]67// filter can be called both as a curried unary function or as a standard binary function8filter(a => a > 1)(list) === filter(a => a > 1, list);
filterMapfilterMap(checker: function)(mapper: function)(list: array): any
pure function filterMap :: (a -> boolean) -> (a -> b) -> [a] -> [b]
Only one pass through the array is executed unlike the use of map(mapper)(filter(checker)(list)).
filterMap can be called both as a curried unary function or as a standard ternary function.
1import {filterMap} from '@7urtle/lambda';23const list = [0, 1, 2, 3]4const mapper = a => a + 1;5const checker = a => a > 1;67filterMap(checker)(mapper)(list); // => [3, 4]8filterMap(a => a > 1)(a => a + 1)([0, 1, 2, 3]); // => [3, 4]910const mapOverLargerThanOne = filterMap(checker);11mapOverLargerThanOne(mapper)(list); // => [3, 4]1213// filterMap can be called both as a curried unary function or as a standard ternary function14filterMap(a => a > 1)(a => a + 1)(list) === filterMap(a => a > 1, a => a + 1, list);
findfind(checker: function)(list: array): any
pure function find :: (a -> boolean) -> [a] -> [b]
find can be called both as a curried unary function or as a standard binary function.
1import {find} from '@7urtle/lambda';23const list = [0, 1, 2, 3]45find(a => a > 1)(list); // => 26find(a => a > 3)(list); // => undefined78// find can be called both as a curried unary function or as a standard binary function9find(a => a > 1)(list) === find(a => a > 1, list);
findIndexfindIndex(checker: function)(list: array): any
pure function findIndex :: (a -> boolean) -> [a] -> [b]
findIndex can be called both as a curried unary function or as a standard binary function.
1import {findIndex} from '@7urtle/lambda';23const list = [2, 3, 4];45findIndex(a => a > 2)(list); // => 16findIndex(a => a > 4)(list); // => undefined78// findIndex can be called both as a curried unary function or as a standard binary function9findIndex(a => a > 1)(list) === findIndex(a => a > 1, list);
groupBygroupBy(fn: function)(list: array): object
pure function groupBy :: (a -> b) -> [a] -> {b: a}
groupBy can be called both as a curried unary function or as a standard binary function.
1import {groupBy} from '@7urtle/lambda';23groupBy(a => a.length)(['one', 'two', 'three']);4// => {"3": ["one", "two"], "5": ["three"]}56groupBy(a => a % 2)([1, 2, 3]);7// => {"0": [2], "1": [1, 3]}89// groupBy can be called both as a curried unary function or as a standard binary function10groupBy(a => a.length)(['one', 'two', 'three']) === groupBy(a => a.length, ['one', 'two', 'three'])
headOfheadOf(list: array): any
pure function headOf :: [a] -> a
1import {headOf} from '@7urtle/lambda';23headOf([3, 4, 1, 8]); // => 34headOf([8]); // => 85headOf([]); // => undefined
initOfinitOf(list: array): array
pure function initOf :: [a] -> []
1import {initOf} from '@7urtle/lambda';23initOf([3, 4, 1, 8]); // => [3, 4, 1]4initOf([8]); // => []5initOf([]); // => []
joinjoin(separator: string)(list: array): any
pure function join :: string -> [a] -> string
join can be called both as a curried unary function or as a standard binary function.
1import {join} from '@7urtle/lambda';23const list = [2, 3, 4];45join('')(list); // => '234'6join(' and ')(list); // => '2 and 3 and 4'7join()(list); // => '2,3,4'89// join can be called both as a curried unary function or as a standard binary function10join('')(list) === join('', list);
keysOfkeysOf(list: Object|array): array
pure function keysOf :: object -> [string]
1import {keysOf} from '@7urtle/lambda';23keysOf([2, 3, 4]); // => ['0', '1', '2']4keysOf({1: 2, 2: 3}); // => ['1', '2']
lastOflastOf(list: array): any
pure function lastOf :: [a] -> a
1import {lastOf} from '@7urtle/lambda';23lastOf([3, 4, 1, 8]); // => 84lastOf([3]); // => 35lastOf([]); // => undefined
randomOfrandomOf(list: array): any
pure function randomOf :: [a] -> a
1import {randomOf} from '@7urtle/lambda';23randomOf([3, 4, 1, 8]); // => 84randomOf([3]); // => 35randomOf([]); // => undefined
reducereduce(initial: any)(reducer: function)(list: array): any
pure function reduce :: a -> ((a, b) -> a) -> [b] -> a
reduce executes functions in reverse order to reduceRight.
reduce can be called both as a curried unary function or as a standard ternary function.
1import {reduce} from '@7urtle/lambda';23const reducer = (accumulator, currentValue) => accumulator + currentValue;4const list = ['a', 'b', 'c'];56reduce('start')(reducer)(list); // => startabc78// reduce can be called both as a curried unary function or as a standard ternary function9reduce('start')(reducer)(list) === reduce('start', reducer, list);
reduceRightreduceRight(initial: any)(reducer: function)(list: array): any
pure function reduceRight :: a -> ((a, b) -> a) -> [b] -> a
reduceRight executes functions in reverse order to reduce.
reduceRight can be called both as a curried unary function or as a standard ternary function.
1import {reduceRight} from '@7urtle/lambda';23const reducer = (accumulator, currentValue) => accumulator + currentValue;4const list = ['a', 'b', 'c'];56reduceRight('start')(reducer)(list); // => startcba78// reduceRight can be called both as a curried unary function or as a standard ternary function9reduceRight('start')(reducer)(list) === reduceRight('start', reducer, list);
sliceslice(end: number)(start: number)(list: array): array
pure function slice :: number -> number -> [a] -> [a]
slice can be called both as a curried unary function or as a standard ternary function.
1import {slice} from '@7urtle/lambda';23slice(2)(1)([1, 2, 3, 4, 5]); // => [2]4slice(2)(0)([1, 2, 3, 4, 5]); // => [1, 2]5slice(8)(7)([1, 2, 3, 4, 5]); // => []67// slice can be called both as a curried unary function or as a standard ternary function8slice(2)(1)([1, 2, 3, 4, 5]) === slice(2, 1, [1, 2, 3, 4, 5]);
someOfsomeOf(checker: function)(list: array): boolean
pure function some :: (a -> boolean) -> [a] -> boolean
some can be called both as a curried unary function or as a standard binary function.
1import {some} from '@7urtle/lambda';23someOf(a => a > 1)([2, 3, 4]); // => true4someOf(a => a > 5)([2, 3, 4]); // => false56// some can be called both as a curried unary function or as a standard binary function7someOf(a => a > 1)([2, 3, 4]) === someOf(a => a > 1, [2, 3, 4]);
sortsort(compare: function)(list: array): array
pure function sort :: (a -> number) -> [a] -> [a]
sort can be called both as a curried unary function or as a standard binary function.
1import {sort} from '@7urtle/lambda';23sort((a, b) => a < b ? -1 : a > b ? 1 : 0)(['a', 'd', 'c', 'd']); // => ['a', 'c', 'd', 'd']4sort((a, b) => a - b)([5, 3, 6]); // => [3, 5, 6]56// sort can be called both as a curried unary function or as a standard binary function7sort((a, b) => a - b)([5, 3, 6]) === sort((a, b) => a - b, [5, 3, 6]);
sortAlphabeticallysortAlphabetically(list: array): array
pure function sortAlphabetically :: [string] -> [string]
1import {sortAlphabetically} from '@7urtle/lambda';23sortAlphabetically(['petra', 'Martin', 'Petra']); // => ['Martin', 'petra', 'Petra']
sortAlphabeticallyZAsortAlphabeticallyZA(list: array): array
pure function sortAlphabeticallyZA :: [string] -> [string]
1import {sortAlphabeticallyZA} from '@7urtle/lambda';23sortAlphabeticallyZA(['petra', 'Martin', 'Petra']); // => ['petra', 'Petra', 'Martin']
sortNumericallysortNumerically(list: array): array
pure function sortNumerically :: [number] -> [number]
1import {sortNumerically} from '@7urtle/lambda';23sortNumerically([3, 4, 1, 3]); // => [1, 3, 3, 4]
sortNumerically21sortNumerically21(list: array): array
pure function sortNumerically21 :: [number] -> [number]
1import {sortNumerically21} from '@7urtle/lambda';23sortNumerically21([3, 4, 1, 3]); // => [4, 3, 3, 1]
tailOftailOf(list: array): array
pure function tailOf :: [a] -> []
1import {tailOf} from '@7urtle/lambda';23tailOf([3, 4, 1, 8]); // => [4, 1, 8]4tailOf([8]); // => []5tailOf([]); // => []
String Functions
endsWithendsWith(substring: string)(string: string): boolean
pure function endsWith :: string -> string -> boolean
endsWith can be called both as a curried unary function or as a standard binary function.
1import {endsWith} from '@7urtle/lambda';23endsWith('7e')('7urtle'); // => true4endsWith('urtle')('7urtle'); // => true5endsWith('urtls')('7urtle'); // => false67// endsWith can be called both as a curried unary function or as a standard binary function8endsWith('e')('7urtle') === endsWith('e', '7urtle');
firstLetterOffirstLetterOf(string: string): string
pure function firstLetterOf :: string -> string
1import {firstLetterOf} from '@7urtle/lambda';23firstLetterOf('7urtle'); // => '7'
lastLetterOflastLetterOf(string: string): string
pure function lastLetterOf :: string -> string
1import {lastLetterOf} from '@7urtle/lambda';23lastLetterOf('7urtle'); // => 'e'
lowerCaseOflowerCaseOf(string: string): string
pure function lowerCaseOf :: string -> string
1import {lowerCaseOf} from '@7urtle/lambda';23lowerCaseOf('PeTrA'); // => 'petra'4lowerCaseOf('PŘÍŠERNĚ ŽLUŤOUČKÝ KŮŇ ÚPĚL ĎÁBELSKÉ ÓDY'); // => 'příšerně žluťoučký kůň úpěl ďábelské ódy'
repeatrepeat(count: number)(string: string): string
pure function repeat :: number -> string -> string
repeat can be called both as a curried unary function or as a standard binary function.
1import {repeat} from '@7urtle/lambda';23repeat(2)('7urtle'); // => '7urtle7urtle'4repeat(0)('7urtle'); // => ''56// repeat can be called both as a curried unary function or as a standard binary function7repeat(2)('7urtle') === repeat(2, '7urtle');
replacereplace(replacement: string)(substring: string)(string: string): string
pure function replace :: string -> string -> string -> string
replace can be called both as a curried unary function or as a standard ternary function.
1import {replace} from '@7urtle/lambda';23replace('8')('7')('7urtle'); // => '8urtle'4replace('7')('')('7urtle'); // => '77urtle'5replace('')('7')('7urtle'); // => 'urtle'67// replace can be called both as a curried unary function or as a standard ternary function8replace('8')('7')('7urtle') === replace('8', '7', '7urtle');
searchsearch(substring: string|regex)(string: string): number
pure function search :: string/regex -> string -> number
search can be called both as a curried unary function or as a standard binary function.
1import {search} from '@7urtle/lambda';23search('7')('7urtle'); 04search('e')('7urtle'); // => 55search('rt')('7urtle'); // => 26search(/URT/i)('7urtle'); // => 17search('8')('7urtle'); => undefined89// search can be called both as a curried unary function or as a standard binary function10search('7')('7urtle') === search('7', '7urtle');
splitsplit(substring: string)(string: string): array
pure function split :: string -> string -> array
split can be called both as a curried unary function or as a standard binary function.
1import {split} from '@7urtle/lambda';23split(' ')('7urtles are awesome'); // => ['7urtles', 'are', 'awesome']4split('/')('7urtles are awesome'); // => ['7urtles are awesome']56// split can be called both as a curried unary function or as a standard binary function7split(' ')('7urtles are awesome') === split(' ', '7urtles are awesome');
startsWithstartsWith(substring: string)(string: string): boolean
pure function startsWith :: string -> string -> boolean
startsWith can be called both as a curried unary function or as a standard binary function.
1import {startsWith} from '@7urtle/lambda';23startsWith('7')('7urtle'); // => true4startsWith('7urtl')('7urtle'); // => true5startsWith('8urtl')('7urtle'); // => false67// startsWith can be called both as a curried unary function or as a standard binary function8startsWith('7')('7urtle') === startsWith('7', '7urtle');
substrsubstr(limit: number)(start: number)(string: string): string
pure function substr :: number -> number -> string -> string
substr can be called both as a curried unary function or as a standard ternary function.
1import {substr} from '@7urtle/lambda';23substr(3)(1)('7urtle'); // => 'urt'4substr(1)(0)('7urtle'); // => '7'5substr(1)(-1)('7urtle'); // => 'e'67// substr can be called both as a curried unary function or as a standard ternary function8substr(3)(1)('7urtle') === substr(3, 1, '7urtle');
testRegExtestRegEx(regex: regex)(string: string): boolean
pure function testRegEx :: regex -> string -> boolean
testRegEx can be called both as a curried unary function or as a standard binary function.
1import {testRegEx} from '@7urtle/lambda';23testRegEx(/[a-z]/)('7urtle'); // => true4testRegEx(/[0-9]/)('1'); // => true5testRegEx(/[0-9]/)('abc'); // => false67// testRegEx can be called both as a curried unary function or as a standard binary function8testRegEx(/[a-z]/)('7urtle') === testRegEx(/[a-z]/, '7urtle');
trimtrim(string: string): string
pure function trim :: string -> string
1import {trim} from '@7urtle/lambda';23trim(' a \n '); // => 'a'
upperCaseOfupperCaseOf(string: string): string
pure function upperCaseOf :: string -> string
1import {upperCaseOf} from '@7urtle/lambda';23upperCaseOf('PeTrA'); // => 'PETRA'4upperCaseOf('příšerně žluťoučký kůň úpěl ďábelské ódy'); // => 'PŘÍŠERNĚ ŽLUŤOUČKÝ KŮŇ ÚPĚL ĎÁBELSKÉ ÓDY'
Conditional Functions
andand(predicates: function)(anything: any): any
pure function and :: [(a -> boolean)] -> a -> boolean
The boolean functions may be entered in any order.
and can be used together with or to encapsulate a predicate in a single function.
1import {and, isGreaterThan, isLessThan} from '@7urtle/lambda';23const isEven = number => number % 2 === 0;45const isSingleEvenDigit = and(isEven, isGreaterThan(-10), isLessThan(10));6isSingleEvenDigit(8)7// => true
ifElseifElse(predicate: function)(whenTrueFn: function)(whenFalseFn: function)(anything: any): any
pure function ifElse :: (a -> Boolean) -> (a -> a) -> (a -> a) a -> a
The function can be called both as a unary ifElse(predicate)(whenTrueFn)(whenFalseFn)(anything) and quaternary ifElse(predicate, whenTrueFn, whenFalseFn, anything).
1import {ifElse} from '@7urtle/lambda';23const predicate = a => a > 1;4const whenTrueFn = a => a / 2;5const whenFalseFn = a => a * 2;67ifElse(predicate)(whenTrueFn)(whenFalseFn)(4); // => 28ifElse(predicate)(whenTrueFn)(whenFalseFn)(1); // => 2910ifElse(predicate)(whenTrueFn)(whenFalseFn)(1) === ifElse(predicate, whenTrueFn, whenFalseFn, 1); // => true
isArrayisArray(a: any): boolean
pure function isArray :: a -> boolean
1import {isArray} from '@7urtle/lambda';23isArray([]); // => true4isArray({}); // => false
isAtLeastisAtLeast(a: number)(b: number): boolean
pure function isAtLeast :: a -> b -> boolean
isAtLeast can be called both as a curried unary function or as a standard binary function.
1import {isAtLeast} from '@7urtle/lambda';23isAtLeast(1)(2); // => true4isAtLeast(2)(2); // => true5isAtLeast(3)(2); // => false67// isAtLeast can be called both as a curried unary function or as a standard binary function8isAtLeast(1)(2) === isAtLeast(1, 2);
isAtMostisAtMost(a: number)(b: number): boolean
pure function isAtMost :: a -> b -> boolean
isAtMost can be called both as a curried unary function or as a standard binary function.
1import {isAtMost} from '@7urtle/lambda';23isAtMost(1)(2); // => false4isAtMost(2)(2); // => true5isAtMost(3)(2); // => true67// isAtLeast can be called both as a curried unary function or as a standard binary function8isAtMost(3)(2) === isAtMost(31, 2);
isBetweenisBetween(a: number)(b: number)(c: number): boolean
pure function isBetween :: a -> b -> c -> boolean
isBetween can be called both as a curried unary function or as a standard ternary function.
1import {isBetween} from '@7urtle/lambda';23isBetween(1)(3)(2); // => true4isBetween(3)(1)(2); // => true5isBetween(1)(3)(3); // => false6isBetween(1)(3)(4); // => false78// isBetween can be called both as a curried unary function or as a standard ternary function9isBetween(1)(3)(2) === isBetween(1, 3, 2);
isBooleanisBoolean(a: any): boolean
pure function isBoolean :: a -> boolean
1import {isBoolean} from '@7urtle/lambda';23isBoolean(false); // => true4isBoolean(1); // => false
isDeepEqualisDeepEqual(a: any)(b: any): boolean
pure function isDeepEqual :: a -> b -> boolean
isDeepEqual can be called both as a curried unary function or as a standard binary function.
1import {isDeepEqual} from '@7urtle/lambda';23isDeepEqual('something')('something'); // => true4isDeepEqual('something')('something else'); // => false5isDeepEqual(['a'])(['a']); // => true6isDeepEqual({a : 'something'})({a : 'something'}); // => true7isDeepEqual([])([]); // => true8isDeepEqual([])([]); // => true910// isDeepEqual can be called both as a curried unary function or as a standard binary function11isDeepEqual('something')('something') === isDeepEqual('something', 'something');
isElementisElement(anything: any): boolean
pure function isElement :: a -> boolean
1import {isElement} from '@7urtle/lambda';23isElement(document.createElement('span')); // => true
isEmptyisEmpty(anything: string|array|object): boolean
pure function isEmpty :: (string|array|Element) -> boolean
1import {isEmpty} from '@7urtle/lambda';23isEmpty(''); // => true4isEmpty([]); // => true5isEmpty({}); // => true6isEmpty('abc'); // => false7isEmpty(document.getElementByID('image')); // => false
isEqualisEqual(a: any)(b: any): boolean
pure function isEqual :: a -> b -> boolean
isEqual can be called both as a curried unary function or as a standard binary function.
1import {isEqual} from '@7urtle/lambda';23isEqual('something')('something'); // => true4isEqual('something')('something else'); // => false5isEqual(['a'])(['a']); // => false6isEqual({a : 'something'})({a : 'something'}); // => false7isEqual([])([]); // => false8isEqual([])([]); // => false910// isEqual can be called both as a curried unary function or as a standard binary function11isEqual('something')('something') === isEqual('something', 'something');
isFalseisFalse(anything: any): boolean
pure function isFalse :: a -> Boolean
1import {isFalse} from '@7urtle/lambda';23isFalse(true); // => false4isFalse(false); // => true
isFunctionisFunction(a: any): boolean
pure function isFunction :: a -> boolean
1import {isFunction} from '@7urtle/lambda';23isFunction(() => null); // => true4isFunction(1); // => false
isGreaterThanisGreaterThan(a: number)(b: number): boolean
pure function isGreaterThan :: a -> b -> boolean
isGreaterThan can be called both as a curried unary function or as a standard binary function.
1import {isGreaterThan} from '@7urtle/lambda';23isGreaterThan(1)(2); // => true4isGreaterThan(3)(2); // => false56// isGreaterThan can be called both as a curried unary function or as a standard binary function7isGreaterThan(1)(2) === isGreaterThan(1, 2);
isInRangeisInRange(a: number)(b: number)(c: number): boolean
pure function isInRange :: a -> b -> c -> boolean
isInRange can be called both as a curried unary function or as a standard ternary function.
1import {isInRange} from '@7urtle/lambda';23isInRange(1)(3)(2); // => true4isInRange(3)(1)(2); // => true5isInRange(1)(3)(3); // => true6isInRange(1)(3)(4); // => false78// isInRange can be called both as a curried unary function or as a standard ternary function9isInRange(1)(3)(2) === isInRange(1, 3, 2);
isJustisJust(anything: any): boolean
pure function isJust :: a -> boolean
1import { isJust, Maybe, Just, Nothing } from '@7urtle/lambda';23isJust(null); // => false4isJust(undefined); // => false5isJust(''); // => false6isJust([]); // => false7isJust({}); // => false8isJust(Nothing); // => false9isJust('7urtle'); // => true10isJust(Maybe.of('7urtle')); // => true11isJust(Just('7urtle')); // => true
isLengthisLength(a: string|array)(b: number): boolean
pure function isLength :: (string|array) -> b -> boolean
1import {isLength} from '@7urtle/lambda';23isLength(3)('abc'); // => true4isLength(3)([1,2,3]); // => true5isLength(3)('abc'); // => false
isLessThanisLessThan(a: number)(b: number): boolean
pure function isLessThan :: a -> b -> boolean
isLessThan can be called both as a curried unary function or as a standard binary function.
1import {isLessThan} from '@7urtle/lambda';23isLessThan(1)(2); // => false4isLessThan(3)(2); // => true56// isLessThan can be called both as a curried unary function or as a standard binary function7isLessThan(3)(2) === isLessThan(3, 2);
isNotArrayisNotArray(a: any): boolean
pure function isNotArray :: a -> boolean
1import {isNotArray} from '@7urtle/lambda';23isNotArray([]); // => false4isNotArray({}); // => true
isNotBooleanisNotBoolean(a: any): boolean
pure function isNotBoolean :: a -> boolean
1import {isNotBoolean} from '@7urtle/lambda';23isNotBoolean(false); // => false4isNotBoolean(1); // => true
isNotDeepEqualisNotDeepEqual(a: any)(b: any): boolean
pure function isNotDeepEqual :: a -> b -> boolean
isNotDeepEqual can be called both as a curried unary function or as a standard binary function.
1import {isNotDeepEqual} from '@7urtle/lambda';23isNotDeepEqual('something')('something'); // => false4isNotDeepEqual('something')('something else'); // => true5isNotDeepEqual(['a', 'b'])(['a']); // => true6isNotDeepEqual({a : 'something', b: c => c})({a : 'something'}); // => true7isNotDeepEqual([])([]); // => false8isNotDeepEqual([])([]); // => false910// isNotDeepEqual can be called both as a curried unary function or as a standard binary function11isNotDeepEqual('something')('something else') === isNotDeepEqual('something', 'something else');
isNotElementisNotElement(anything: any): boolean
pure function isNotElement :: a -> boolean
1import {isNotElement} from '@7urtle/lambda';23isNotElement(document.createElement('span')); // => false
isNotEmptyisNotEmpty(anything: string|array|object): boolean
pure function isNotEmpty :: (string|array|Element) -> boolean
1import {isNotEmpty} from '@7urtle/lambda';23isNotEmpty(''); // => false4isNotEmpty([]); // => false5isNotEmpty('abc'); // => true6isNotEmpty({}); => true7isNotEmpty(document.getElementByID('image')); // => true
isNotEqualisNotEqual(a: any)(b: any): boolean
pure function isNotEqual :: a -> b -> boolean
isEqual can be called both as a curried unary function or as a standard binary function.
1import {isNotEqual} from '@7urtle/lambda';23isNotEqual('something')('something'); // => false4isNotEqual('something')('something else'); // => true5isNotEqual(['a'])(['a']); // => true6isNotEqual({a : 'something'})({a : 'something'}); // => true7isNotEqual([])([]); // => true8isNotEqual([])([]); // => true910// isNotEqual can be called both as a curried unary function or as a standard binary function11isNotEqual('something')('something else') === isNotEqual('something', 'something else');
isNotFunctionisNotFunction(a: any): boolean
pure function isNotFunction :: a -> boolean
1import {isNotFunction} from '@7urtle/lambda';23isNotFunction(() => null); // => false4isNotFunction(1); // => true
isNothingisNothing(anything: any): boolean
pure function isNothing :: a -> boolean
1import { isNothing, Maybe, Just, Nothing } from '@7urtle/lambda';23isNothing(null); // => true4isNothing(undefined); // => true5isNothing(''); // => true6isNothing([]); // => true7isNothing({}); // => true8isNothing(Maybe.of('')); // => true9isNothing(Nothing); // => true10isNothing('7urtle'); // => false11isNothing(Maybe.of('7urtle')); // => false12isNothing(Just('7urtle')); // => false
isNotLengthisNotLength(a: string|array)(b: number): boolean
pure function isNotLength :: (string|array) -> b -> boolean
The function can be called both as a unary isNotLength(a)(b) and binary isNotLength(a, b).
1import {isNotLength} from '@7urtle/lambda';23isNotLength(3)('abc'); // => false4isNotLength(3)([1,2,3]); // => false5isNotLength(3)('abce'); // => true67isNotLength(3)('abcd') === isNotLength(3, 'abcd'); // => true
isNotNullisNotNull(a: any): boolean
pure function isNotNull :: a -> boolean
1import {isNotNull} from '@7urtle/lambda';23isNotNull(null); // => false4isNotNull(1); // => true
isNotNumberisNotNumber(a: any): boolean
pure function isNotNumber :: a -> boolean
1import {isNotNumber} from '@7urtle/lambda';23isNotNumber(1); // => false4isNotNumber('string'); // => true
isNotObjectisNotObject(a: any): boolean
pure function isNotObject :: a -> boolean
1import {isNotObject} from '@7urtle/lambda';23isNotObject({}); // => false4isNotObject([]); // => false5isNotObject(null); // => false6isNotObject(1); // => true
isNotStringisNotString(a: any): boolean
pure function isNotString :: a -> boolean
1import {isNotString} from '@7urtle/lambda';23isNotString('string'); // => false4isNotString(1); // => true
isNotTypeOfisNotTypeOf(a: any)(b: any): boolean
pure function isNotTypeOf :: a -> b -> boolean
isNotTypeOf can be called both as a curried unary function or as a standard binary function.
1import {isNotTypeOf} from '@7urtle/lambda';23isNotTypeOf('number')(1); // => false4isNotTypeOf('string')(1); // => true56// isNotTypeOf can be called both as a curried unary function or as a standard binary function7isNotTypeOf('string')(1) === isNotTypeOf('string', 1);
isNotUndefinedisNotUndefined(a: any): boolean
pure function isNotUndefined :: a -> boolean
1import {isNotUndefined} from '@7urtle/lambda';23isNotUndefined(undefined); // => false4isNotUndefined(1); // => true
isNotZeroisNotZero(a: number): boolean
pure function isNotZero :: a -> boolean
1import {isZero} from '@7urtle/lambda';23isZero(0); // => false4isZero(1); // => true
isNullisNull(a: any): boolean
pure function isNull :: a -> boolean
1import {isNull} from '@7urtle/lambda';23isNull(null); // => true4isNull(1); // => false
isNumberisNumber(a: any): boolean
pure function isNumber :: a -> boolean
1import {isNumber} from '@7urtle/lambda';23isNumber(1); // => true4isNumber('string'); // => false
isObjectisObject(a: any): boolean
pure function isObject :: a -> boolean
1import {isObject} from '@7urtle/lambda';23isObject({}); // => true4isObject([]); // => true5isObject(null); // => true6isObject(1); // => false
isStringisString(a: any): boolean
pure function isString :: a -> boolean
1import {isString} from '@7urtle/lambda';23isString('string'); // => true4isString(1); // => false
isTrueisTrue(anything: any): boolean
pure function isTrue :: a -> boolean
1import {isTrue} from '@7urtle/lambda';23isTrue(true); // => true4isTrue(false); // => false
isTypeOfisTypeOf(a: any)(b: any): boolean
pure function isTypeOf :: a -> b -> boolean
isTypeOf can be called both as a curried unary function or as a standard binary function.
1import {isTypeOf} from '@7urtle/lambda';23isTypeOf('number')(1); // => true4isTypeOf('string')(1); // => false56// isTypeOf can be called both as a curried unary function or as a standard binary function7isTypeOf('number')(1) === isTypeOf('number', 1);
isUndefinedisUndefined(a: any): boolean
pure function isUndefined :: a -> boolean
1import {isUndefined} from '@7urtle/lambda';23isUndefined(undefined); // => true4isUndefined(1); // => false
isZeroisZero(a: number): boolean
pure function isZero :: a -> boolean
1import {isZero} from '@7urtle/lambda';23isZero(0); // => true4isZero(1); // => false
oror(predicates: function)(anything: any): any
pure function or :: [(a -> boolean)] -> a -> boolean
The boolean functions may be entered in any order.
or can be used together with and to encapsulate a predicate in a single function.
1import {or} from '@7urtle/lambda';23const isDivisibleBy = divisor => number => number % divisor === 0;4const isFizzBuzzNumber = or(isDivisibleBy(3), isDivisibleBy(5));56isFizzBuzzNumber(15)7// => true
unlessunless(predicate: function)(whenFalseFn: function)(anything: any): any
pure function unless :: (a -> Boolean) -> (a -> a) -> a -> a
The function can be called both as a unary unless(predicate)(whenFalseFn)(anything) and ternary unless(predicate, whenFalseFn, anything).
1import {unless} from '@7urtle/lambda';23const predicate = a => a > 1;4const whenFalseFn = a => a * 2;56when(predicate)(whenFalseFn)(4); // => 47when(predicate)(whenFalseFn)(1); // => 289when(predicate)(whenFalseFn)(1) === when(predicate, whenFalseFn, 1); // => true
whenwhen(predicate: function)(whenTrueFn: function)(anything: any): any
pure function when :: (a -> Boolean) -> (a -> a) -> a -> a
The function can be called both as a unary when(predicate)(whenTrueFn)(anything) and ternary when(predicate, whenTrueFn, anything).
1import {when} from '@7urtle/lambda';23const predicate = a => a > 1;4const whenTrueFn = a => a * 2;56when(predicate)(whenTrueFn)(2); // => 47when(predicate)(whenTrueFn)(1); // => 189when(predicate)(whenTrueFn)(2) === when(predicate, whenTrueFn, 2); // => true