Helpers

assign#

crocks/helpers/assign

assign :: Object -> Object -> Object

When working with Objects, a common operation is to combine (2) of them. This can be accomplished in crocks by reaching for assign. Unlike the Object.assign that ships with JavaScript, this assign will combine your Objects into a new shallow copy of their merger. assign only takes two arguments and will overwrite keys present in the second argument with values from the first. As with most of the crocks Object based functions, assign will omit any key-value pairs that are undefined. Check out a related function named defaultProps that will only assign values that are undefined in the second argument.

binary#

crocks/helpers/binary

binary :: ((*) -> c) -> a -> b -> c

With all the different functions out there in the real world, sometimes it is nice to restrict them to a specific -arity to work with your all your wonderful compositions. When you want to restrict any function of any arity to a simple binary function. Just pass your function to binary and you will get back a curried, binary function that will only apply (2) arguments to the inner function, ignoring any others. This works very well with functions like Array.prototype.reduce where you may only care about the first 2 arguments. if you need to constrain to more than (2) arguments, then you will want to reach for nAry. binary is basically syntactic sugar for nAry(2, fn). Also related is unary, which constrains to (1) argument.

compose#

crocks/helpers/compose

compose :: ((y -> z), ..., (a -> b)) -> a -> z

While the composeB can be used to create a composition of two functions, there are times when you want to compose an entire flow together. That is where compose is useful. With compose you can create a right-to-left composition of functions. It will return you a function that represents your flow. crocks provides a means to build compostions using a left-to-right style in the form of pipe.

import compose from 'crocks/helpers/compose'
import objOf from 'crocks/helpers/objOf'
// double :: Number -> Number
const double = x =>
x + x
// doubleAndWrap :: Number -> Object
const doubleAndWrap = compose(
objOf('result'),
double
)
doubleAndWrap(25)
//=> { result: 50 }
doubleAndWrap(0)
//=> { result: 0 }

composeK#

crocks/helpers/composeK

composeK :: Chain m => ((y -> m z), ..., (a -> m b)) -> a -> m z

There are many times that, when working with the various crocks, our flows are just a series of chains. Due to some neat properties with types that provide a chain function, you can remove some boilerplate by reaching for composeK. Just pass it the functions you would normally pass to chain and it will do all the boring hook up for you. Just like compose, functions are applied right-to-left, so you can turn this:

import compose from 'crocks/helpers/compose'
import chain from 'crocks/pointfree/chain'
import getProp from 'crocks/Maybe/getProp'
import isObject from 'crocks/predicates/isObject'
import safe from 'crocks/Maybe/safe'
const data = {
do: {
re: {
mi: 'fa'
}
}
}
// fluent :: a -> Maybe b
const fluent = x =>
safe(isObject, x)
.chain(getProp('do'))
.chain(getProp('re'))
.chain(getProp('mi'))
fluent(data)
//=> Just 'fa'
// pointfree :: a -> Maybe b
const pointfree = compose(
chain(getProp('mi')),
chain(getProp('re')),
chain(getProp('do')),
safe(isObject)
)
pointfree(data)
//=> Just 'fa'

into the more abbreviated form:

import composeK from 'crocks/helpers/composeK'
import getProp from 'crocks/Maybe/getProp'
import isObject from 'crocks/predicates/isObject'
import safe from 'crocks/Maybe/safe'
const data = {
do: {
re: {
mi: 'fa'
}
}
}
// flow :: a -> Maybe b
const flow = composeK(
getProp('mi'),
getProp('re'),
getProp('do'),
safe(isObject)
)
flow(data)
//=> Just 'fa'

As demonstrated in the above example, this function more closely resembles flows that are using a more pointfree style of coding. As with the other composition functions in crocks, a pipeK function is provided for flows that make more sense expressed in a left-to-right style.

composeP#

crocks/helpers/composeP

composeP :: Promise p => ((y -> p z c), ..., (a -> p b c)) -> a -> p z c

When working with Promises, it is common place to create chains on a Promise's then function. Doing this involves a lot of boilerplate and forces you into a fluent style, whether you want to be or not. Using composeP you have the option to compose a series of Promise returning functions like you would any other function composition, in a right-to-left fashion.

Due to the nature of the then function, only the head of your composition needs to return a Promise. This will create a function that takes a value, which is passed through your chain, returning a Promise which can be extended. This is only a then chain, it does not do anything with the catch function. If you would like to provide your functions in a left-to-right manner, check out pipeP.

import composeP from 'crocks/helpers/composeP'
import Async from 'crocks/Async'
import asyncToPromise from 'crocks/Async/asyncToPromise'
import composeB from 'crocks/combinators/composeB'
const { resolveAfter } = Async
// resolveQuick :: a -> Async e a
const resolveQuick = value =>
resolveAfter(300, value)
// promiseSomething :: a -> Promise a e
const promiseSomething = composeB(
asyncToPromise, resolveQuick
)
// log :: a -> ()
const log = x =>
console.log(x)
// sayHello :: String -> String
const sayHello = to =>
`Hello ${to}`
// emphasize :: String -> String
const emphasize = to =>
`${to}!`
// promFunc :: a -> Promise a e
const promFunc = x =>
promiseSomething(x)
.then(emphasize)
.then(sayHello)
// composedPromFunc :: a -> Promise a e
const composedPromFunc =
composeP(sayHello, emphasize, promiseSomething)
promFunc('World')
.then(log)
//=> Hello World!
composedPromFunc('World')
.then(log)
//=> Hello World!

composeS#

crocks/helpers/composeS

composeS :: Semigroupoid s => (s y z, ..., s a b) -> s a z

When working with things like Arrow and Star there will come a point when you would like to compose them like you would any Function. That is where composeS comes in handy. Just pass it the Semigroupoids you want to compose and it will give you back a new Semigroupoid of the same type with all of the underlying functions composed and ready to be run. Like compose, composeS composes the functions in a right-to-left fashion. If you would like to represent your flow in a more left-to-right manner, then pipeS is provided for such things.

import Arrow from 'crocks/Arrow'
import bimap from 'crocks/pointfree/bimap'
import branch from 'crocks/Pair/branch'
import composeS from 'crocks/helpers/composeS'
import merge from 'crocks/pointfree/merge'
import mreduce from 'crocks/helpers/mreduce'
import Sum from 'crocks/Sum'
// length :: a -> Number
const length = xs =>
xs.length
// divide :: (Number, Number) -> Number
const divide = (x, y) =>
x / y
// avg :: Arrow [ Number ] Number
const avg =
Arrow(bimap(mreduce(Sum), length))
.promap(branch, merge(divide))
// double :: Arrow Number Number
const double =
Arrow(x => x * 2)
// data :: [ Number ]
const data =
[ 34, 198, 3, 43, 92 ]
composeS(double, avg)
.runWith(data)
//=> 148

curry#

crocks/helpers/curry

curry :: ((a, b, ...) -> z) -> a -> b -> ... -> z

Pass this function a function and it will return you a function that can be called in any form that you require until all arguments have been provided. For example if you pass a function: f : (a, b, c) -> d you get back a function that can be called in any combination, such as: f(x, y, z), f(x)(y)(z), f(x, y)(z), or even f(x)(y, z). That is to say, this function fulfills the role of both curry and uncurry, returning a function that can be used as a curried function, an uncurried function, or any combination of argument applications in between. The returned function has 2^(n-1) type signatures, where n is the number of parameters.

import compose from 'crocks/helpers/compose'
import curry from 'crocks/helpers/curry'
import map from 'crocks/pointfree/map'
import prop from 'crocks/maybe/prop'
// add :: (Number, Number, Number) -> Number
const add = (a, b, c) =>
a + b + c
// partial application impossible
add(1)
//=> NaN
// 1 + undefined + undefined => NaN
// crocksCurriedAdd :: Number -> Number -> Number -> Number
const crocksCurriedAdd =
curry(add)
// appliedAdd :: Number -> Number -> Number
const appliedAdd =
crocksCurriedAdd(1)
appliedAdd(1, 1)
//=> 3
crocksCurriedAdd(1)(2)(3)
crocksCurriedAdd(1, 2)(3)
crocksCurriedAdd(1)(2, 3)
crocksCurriedAdd(1, 2, 3)
//=> 6
// strictCurriedPluck :: String -> [ a ] -> Maybe b
const strictCurriedPluck =
compose(map, prop)
const crockCurriedPluck =
curry(strictCurriedPluck)
// data :: [ { a: String, b: String } ]
const data = [
{ a: 'nice' },
{ a: 'great', b: 'nice' },
{ b: 'nice' }
]
strictCurriedPluck('a')(data)
//=> [ Just "nice", Just "great", Nothing ]
crockCurriedPluck('a', data)
//=> [ Just "nice", Just "great", Nothing ]

An important caveat when using curry with functions containing optional parameters, is that the defaults are applied immediately, reducing the number of partial applications to just the number of required parameters. Adding optional parameters to functions may not be a good choice if the intention is to use them with curry, as the ability to change the defaults is lost. Parameters are applied up until the first optional parameter, at which point subsequent parameters either receive the declared default or go undefined.

import curry from 'crocks/helpers/curry'
// curriedConsolelog :: [ (Object | String) ] -> ()
const curriedConsolelog =
curry(console.log)
curriedConsolelog('Hello %s', 'World')
//=> Hello %s
curriedConsolelog('Hello', 'World')
//=> Hello

defaultProps#

crocks/helpers/defaultProps

defaultProps :: Object -> Object -> Object

Picture this, you have an Object and you want to make sure that some properties are set with a given default value. When the need for this type of operation presents itself, defaultProps can come to your aid. Just pass it an Object that defines your defaults and then the Object your want to default those props on. If a key that is present on the defaults Object is not defined on your data, then the default value will be used. Otherwise, the value from your data will be used instead. You could just apply flip to the assign function and get the same result, but having a function named defaultProps may be easier to read in code. As with most Object related functions in crocks, defaultProps will return you a shallow copy of the result and not include any undefined values in either Object.

defaultTo#

crocks/helpers/defaultTo

defaultTo :: a -> b -> a

With things like null, undefined and NaN showing up all over the place, it can be hard to keep your expected types inline without resorting to nesting in a Maybe with functions like safe. If you want to specifically guard for null, undefined and NaN and get things defaulted into the expected type, then defaultTo should work for you. Just pass it what you would like your default value to be and then the value you want guarded, and you will get back either the default or the passed value, depending on if the passed value is null, undefined or NaN. While this is JavaScript and you can return anything, it is suggested to stick to the signature and only let as through. As a b can be an a as well.

fromPairs#

crocks/helpers/fromPairs

fromPairs :: Foldable f => f (Pair String a) -> Object

As an inverse to toPairs, fromPairs takes either an Array or List of key-value Pairs and constructs an Object from it. The Pair must contain a String in the fst and any type of value in the snd. The fst will become the key for the value in the snd. All primitive values are copied into the new Object, while non-primitives are references to the original. If you provide an undefined values for the second, that Pair will not be represented in the resulting Object. Also, when if multiple keys share the same name, that last value will be moved over.

getPathOr#

crocks/helpers/getPathOr

getPathOr :: a -> [ (String | Integer) ] -> b -> a

While getPropOr is good for simple, single-level structures, there may come a time when you have to work with nested POJOs or Arrays. When you run into this situation, just pull in getPathOr, which was previously called propPathOr, and pass it a left-to-right traversal path of keys, indices or a combination of both (gross...but possible). This will kick you back a function that behaves just like getPropOr. You pass it some data, and it will attempt to resolve your provided path. If the path is valid, it will return the value. But if at any point that path "breaks" it will give you back the default value.

import getPathOr from 'crocks/helpers/getPathOr'
const data = {
foo: {
bar: 'bar',
null: null,
nan: NaN,
undef: undefined
},
arr: [ 1, 2 ]
}
// def :: [ String | Integer ] -> a -> b
const def =
getPathOr('default')
def([ 'foo', 'bar' ], data)
//=> "bar"
def([ 'baz', 'tommy' ], data)
//=> "default"
def([ 'foo', 'null' ], data)
//=> null
def([ 'foo', 'nan' ], data)
//=> NaN
def([ 'foo', 'undef' ], data)
//=> "default"
def([ 'arr', 'length' ], data)
//=> 2

getPropOr#

crocks/helpers/getPropOr

getPropOr :: a -> (String | Integer) -> b -> a

Reach for getPropOr, previously known as propOr, when you want some safety around pulling a value out of an Object or Array with a single key or index. Well, as long as you are working with non-nested data that is. Just tell getPropOr either the key or index you are interested in, and you will get back a function that will take anything and return the wrapped value if the key/index is defined. If the key/index is not defined however, you will get back the provided default value.

import getPropOr from 'crocks/helpers/getPropOr'
const data = {
foo: 'bar',
null: null,
nan: NaN,
undef: undefined
}
// def :: (String | Integer) -> a -> b
const def =
getPropOr('default')
def('foo', data)
//=> "bar"
def('null', data)
//=> null
def('nan', data)
//=> NaN
def('baz', data)
//=> "default"
def('undef', data)
//=> "default"

liftA2#

crocks/helpers/liftA2

liftA2 :: Applicative m => (a -> b -> c) -> m a -> m b -> m c

liftA3#

crocks/helpers/liftA3

liftA3 :: Applicative m => (a -> b -> c -> d) -> m a -> m b -> m c -> m d

Ever see yourself wanting to map a binary or trinary function, but map only allows unary functions? Both of these functions allow you to pass in your function as well as the number of Applicatives (containers that provide both of and ap functions) you need to get the mapping you are looking for.

liftN#

crocks/helpers/liftN

liftN :: Applicative m => Number -> ((*) -> a) -> (*m) -> m a

While liftA2 and liftA3 will handle a majority of the functions we tend to encounter, many cases arise when we want to deal with functions of a greater arity. In those cases, liftN will allow the lifting of function of any size arity.

liftN takes a Number that specifies the arity of the function to be lifted, followed by the function itself and finally the required number of Applicative instances of the same type to be applied to the target function.

In most cases, there is no need to explicitly curry the target function. This function operates in the same vein as nAry and as such manually curried functions (i.e. x => y => x + y) will need to be explicitly curried using curry to ensure proper application of the arguments.

import liftN from 'crocks/helpers/liftN'
import compose from 'crocks/helpers/compose'
import curry from 'crocks/helpers/curry'
import isNumber from 'crocks/predicates/isNumber'
import isString from 'crocks/predicates/isString'
import map from 'crocks/pointfree/map'
import safe from 'crocks/Maybe/safe'
// apply :: (((*) -> b), [ a ]) -> b
const apply = fn => xs =>
fn.apply(null, xs)
// join :: String -> String -> String
const join = x => y =>
`${x} ${y}`
// safeString :: a -> Maybe String
const safeString =
safe(isString)
// sumArgs :: (* Number) -> Number
const sumArgs = (...args) =>
args.reduce((x, y) => x + y, 0)
// max :: Applicative m => [ m Number ] -> m Number
const max =
apply(liftN(4, Math.max))
// sum :: Applicative m => [ m Number ] -> m Number
const sum =
apply(liftN(4, sumArgs))
// apJoin :: Applicative m => [ m String ] -> m String
const apJoin =
liftN(2, curry(join))
// good :: [ Number ]
const good =
[ 45, 54, 96, 99 ]
// bad :: [ String ]
const bad =
[ 'one', 'two', 'three', 'four' ]
// arMax :: [ Number ] -> [ Number ]
const arMax =
compose(max, map(Array.of))
// safeSum :: Maybe Number -> Maybe Number
const safeSum =
compose(sum, map(safe(isNumber)))
arMax(good)
//=> [ 99 ]
safeSum(good)
//=> Just 294
safeSum(bad)
//=> Nothing
apJoin(safeString('Joey'), safeString('Fella'))
//=> Just "Joey Fella"
apJoin(safeString(9), safeString('Fella'))
//=> Nothing

mapProps#

crocks/helpers/mapProps

mapProps :: { (* -> *) } -> Object -> Object

Would like to map specific keys in an Object with a specific function? Just bring in mapProps and pass it an Object with the functions you want to apply on the keys you want them associated to. When the resulting function receives an Object, it will return a new Object with the keys mapped according to the mapping functions. All keys from the original Object that do not exist in the mapping Object will still exist untouched, but the keys with mapping functions with now contain the result of applying the original value to the provided mapping function.

mapProps also allows for mapping on nested Objects for times when the shape of the original Object is know.

import mapProps from 'crocks/helpers/mapProps'
// add :: Number -> Number -> Number
const add = x => y =>
x + y
// toUpper :: String -> String
const toUpper = x =>
x.toUpperCase()
// mapping :: Object
const mapping = {
entry: toUpper,
fauna: {
unicorns: add(1),
elephants: add(-1)
},
flora: {
nariphon: add(10),
birch: add(1)
}
}
mapProps(mapping, {
entry: 'legend',
fauna: {
unicorns: 10,
zombies: 3
},
other: {
hat: 2
}
})
//=> { entry: 'LEGEND', fauna: { unicorns: 11, zombies: 3 }, other: { hat: 2} }

mapReduce#

crocks/helpers/mapReduce

mapReduce :: Foldable f => (a -> b) -> (c -> b -> c) -> c -> f a -> c

Sometimes you need the power provided by mreduceMap but you do not have a Monoid to lift into. mapReduce provides the same power, but with the flexibility of using functions to lift and combine. mapReduce takes a unary mapping function, a binary reduction function, the initial value and finally a Foldable structure of data. Once all arguments are provided, mapReduce folds the provided data, by mapping each value through your mapping function, before sending it to the second argument of your reduction function.

import mapReduce from 'crocks/helpers/mapReduce'
import isNumber from 'crocks/predicates/isNumber'
import Maybe from 'crocks/Maybe'
import Max from 'crocks/Max'
import safeLift from 'crocks/Maybe/safeLift'
const { Nothing } = Maybe
// data :: [ * ]
const data =
[ '100', null, 3, true, 1 ]
// safeMax :: [ * ] -> Maybe Max
const safeMax = mapReduce(
safeLift(isNumber, Max),
(y, x) => y.concat(x).alt(y).alt(x),
Nothing()
)
safeMax(data)
.option(Max.empty())
.valueOf()
//=> 3

mconcat#

crocks/helpers/mconcat

mconcat :: Monoid m, Foldable f => m -> f a -> m a

mreduce#

crocks/helpers/mreduce

mreduce :: Monoid m, Foldable f => m -> f a -> a

These two functions are very handy for combining an entire List or Array of values by providing a Monoid and your collection of values. The difference between the two is that mconcat returns the result inside the Monoid used to combine them. Where mreduce returns the bare value itself.

mconcatMap#

crocks/helpers/mconcatMap

mconcatMap :: Monoid m, Foldable f => m -> (b -> a) -> f b -> m a

mreduceMap#

crocks/helpers/mreduceMap

mreduceMap :: Monoid m, Foldable f => m -> (b -> a) -> f b -> a

There comes a time where the values you have in a List or an Array are not in the type that is needed for the Monoid you want to combine with. These two functions can be used to map some transforming function from a given type into the type needed for the Monoid. In essence, this function will run each value through the function before it lifts the value into the Monoid, before concat is applied. The difference between the two is that mconcatMap returns the result inside the Monoid used to combine them. Where mreduceMap returns the bare value itself.

nAry#

crocks/helpers/nAry

nAry :: Number -> ((*) -> a) -> (*) -> a

When using functions like Math.max or Object.assign that take as many arguments as you can throw at them, it makes it hard to curry them in a reasonable manner. nAry can make things a little nicer for functions like that. It can also be put to good use to limit a given function to a desired number of arguments to avoid accidentally supplying default arguments when you do not what them applied. First pass nAry the number of arguments you wish to limit the function to and then the function you wish to limit. nAry will give you back a curried function that will only apply the specified number of arguments to the inner function. Unary and binary functions are so common that crocks provides specific functions for those cases: unary and binary.

objOf#

crocks/helpers/objOf

objOf :: String -> a -> Object

If you ever find yourself in a situation where you have a key and a value and just want to combine the two into an Object, then it sounds like objOf is the function for you. Just pass it a String for the key and any type of value, and you'll get back an Object that is composed of those two. If you find yourself constantly concatenating the result of this function into another Object, you may want to use assoc instead.

omit#

crocks/helpers/omit

omit :: [ String ] -> Object -> Object

Sometimes you just want to strip Objects of unwanted properties by key. Using omit will help you get that done. Just pass it a Foldable structure with a series of Strings as keys and then pass it an Object and you will get back not only a shallow copy, but also an Object free of any of those pesky undefined values. You can think of omit as a way to black-list or reject Object properties based on key names. This function ignores inherited properties and should only be used with POJOs. If you want to filter or white-list properties rather than reject them, take a look at pick.

once#

crocks/helpers/once

once :: ((*) -> a) -> ((*) -> a)

There are times in JavaScript development where you only want to call a function once and memo-ize the first result for every subsequent call to that function. Just pass the function you want guarded to once and you will get back a function with the expected guarantees.

partial#

crocks/helpers/partial

partial :: (((*) -> c), *) -> (*) -> c

There are many times when using functions from non-functional libraries or from built-in JS functions, where it does not make sense to wrap it in a curry. You just want to partially apply some arguments to it and get back a function ready to take the rest. That is a perfect opportunity to use partial. Just pass a function as the first argument and then apply any other arguments to it. You will get back a curried function that is ready to accept the rest of the arguments.

import partial from 'crocks/helpers/partial'
import map from 'crocks/pointfree/map'
// max10 :: Number -> Number
const max10 =
partial(Math.min, 10)
// data :: [ Number ]
const data =
[ 13, 5, 13 ]
map(max10, data)
//=> [ 10, 5, 10]

pick#

crocks/helpers/pick

pick :: Foldable f => f String -> Object -> Object

When dealing with Objects, sometimes it is necessary to only let some of the key-value pairs on an object through. Think of pick as a sort of white-list or filter for Object properties. Pass it a Foldable structure of Strings that are the keys you would like to pick off of your Object. This will give you back a shallow copy of the key-value pairs you specified. This function will ignore inherited properties and should only be used with POJOs. Any undefined values will not be copied over, although null values are allowed. For black-listing properties, have a look at omit.

pipe#

crocks/helpers/pipe

pipe :: ((a -> b), ..., (y -> z)) -> a -> z

Similar to compose, pipe allows for the composition of functions, but takes its functions in a left-to-right fashion, which is the opposite of compose. Just like compose, pipe will return a new function that represents the composed flow.

import pipe from 'crocks/helpers/pipe'
import objOf from 'crocks/helpers/objOf'
// double :: Number -> Number
const double = x =>
x + x
// doubleAndWrap :: Number -> Object
const doubleAndWrap = pipe(
double,
objOf('result')
)
doubleAndWrap(25)
//=> { result: 50 }
doubleAndWrap(0)
//=> { result: 0 }

pipeK#

crocks/helpers/pipeK

pipeK :: Chain m => ((a -> m b), ..., (y -> m z)) -> a -> m z

Like composeK, you can remove much of the boilerplate when chaining together a series of functions with the signature: Chain m => a -> m b. The difference between the two functions is, while composeK is right-to-left, pipeK is the opposite, taking its functions left-to-right.

import pipeK from 'crocks/helpers/pipeK'
import curry from 'crocks/core/curry'
import List from 'crocks/List'
import Writer from 'crocks/Writer'
// OpWriter :: Writer List
const OpWriter =
Writer(List)
// addLog :: Number -> Number -> OpWriter Number
const addLog = curry(
(x, y) =>
OpWriter(`adding ${x} to ${y}`, x + y)
)
// addLog :: Number -> Number -> OpWriter Number
const scaleLog = curry(
(x, y) =>
OpWriter(`scaling ${y} by ${x}`, x * y)
)
const fluent = x =>
OpWriter.of(x)
.chain(addLog(4))
.chain(scaleLog(3))
fluent(0).log()
//=> List [ "adding 4 to 0", "scaling 4 by 3" ]
const chainPipe = pipeK(
addLog(4),
scaleLog(3)
)
chainPipe(0).log()
//=> List [ "adding 4 to 0", "scaling 4 by 3" ]

pipeP#

crocks/helpers/pipeP

pipeP :: Promise p => ((a -> p b d), ..., (y -> p z d)) -> a -> p z d

Like the composeP function, pipeP will let you remove the standard boilerplate that comes with working with Promise chains. The only difference between pipeP and composeP is that it takes its functions in a left-to-right order:

import pipeP from 'crocks/helpers/pipeP'
import Async from 'crocks/Async'
import asyncToPromise from 'crocks/Async/asyncToPromise'
import composeB from 'crocks/combinators/composeB'
const { resolveAfter } = Async
// prod :: Number -> Number -> Number
const prod = a => b =>
b * a
// resolveQuick :: a -> Async e a
const resolveQuick = value =>
resolveAfter(300, value)
// promise :: a -> Promise a e
const promise = composeB(
asyncToPromise, resolveQuick
)
// double :: Number -> Number
const double =
prod(2)
// triple :: Number -> Number
const triple =
prod(3)
// promFunc :: Number -> Promise Number e
const promFunc = x =>
promise(x)
.then(double)
.then(triple)
// promPipe :: Number -> Promise Number e
const promPipe =
pipeP(promise, double, triple)
promFunc(5)
.then(v => console.log(v))
//=> 30
promPipe(5)
.then(v => console.log(v))
//=> 30

pipeS#

crocks/helpers/pipeS

pipeS :: Semigroupoid s => (s a b, ..., s y z) -> s a z

While Stars and Arrows come in very handy at times, the only thing that could make them better is to compose them. With pipeS you can do just that with any Semigroupoid. Just like with composeS, you just pass it Semigroupoids of the same type and you will get back another Semigroupoid with them all composed together. The only difference between the two, is that pipeS composes in a left-to-right fashion, while composeS does the opposite.

import pipeS from 'crocks/helpers/pipeS'
import Maybe from 'crocks/Maybe'
import curry from 'crocks/core/curry'
import getProp from 'crocks/Maybe/getProp'
import isNumber from 'crocks/predicates/isNumber'
import safeLift from 'crocks/Maybe/safeLift'
import Star from 'crocks/Star'
// MaybeStar :: Star a (Maybe b)
const MaybeStar =
Star(Maybe)
// add :: Number -> Number -> Number
const add = curry(
(x, y) => x + y
)
// pull :: String -> MaybeStar Object Number
const pull = x =>
MaybeStar(getProp(x))
// safeAdd :: Number -> MaybeStar Number Number
const safeAdd = x =>
MaybeStar(safeLift(isNumber, add(x)))
// data :: { num: Number, string: String }
const data = {
num: 56,
string: '56'
}
// flow :: (String, Number) -> MaybeStar Object Number
const flow = (key, num) => pipeS(
pull(key),
safeAdd(num)
)
flow('num', 10)
.runWith(data)
//=> Just 66
flow('string', 100)
.runWith(data)
//=> Nothing

setPath#

crocks/helpers/setPath

setPath :: [ (String | Integer) ] -> a -> (Object | Array) -> (Object | Array)

Used to set a value on a deeply nested Object, setPath will traverse down a path and set the a the final property to the provided value. setPath returns the an Object/Array with the modification and does not alter the original Object/Array along the path.

The provided path can be a mixture of either Integers or Strings to allow for traversing through both Arrays and Objects. When an Integer zero or greater is provided it will treat that portion as an Array while Strings are used to reference through Objects. If at any point in the provided a NaN, undefined or null values is encountered, a new Object/Array will be created.

import setPath from 'crocks/helpers/setPath'
setPath([ 'account', 'name' ], 'Awesome Place', {
account: {
name: 'Great Place',
rating: 5
}
})
//=> { account: { name: 'Awesome Place', rating: 5 } }
setPath([ 'people', 2, 'age' ], 26, {
people: [
{ name: 'George', age: 22 },
{ name: 'Greta', age: 21 },
{ name: 'Ali', age: 25 }
]
})
//=> { people: [
// { name: 'George', age: 22 },
// { name: 'Greta', age: 21 },
// { name: 'Ali', age: 26 },
// ] }
setPath([ 'a', 'c' ], false, { a: { b: true } })
//=> { a: { b: true, c: false } }
setPath([ 'list', 'a' ], 'ohhh, I see.', { list: [ 'string', 'another' ] })
//=> { list: { 0: 'string', 1: 'another', a: 'ohhh, I see.' } }

setProp#

crocks/helpers/setProp

setProp :: (String | Integer) -> a -> (Object | Array) -> (Object | Array)

Used to set a given value for a specific key or index of an Object or Array. setProp, previously called assoc, takes either a String or Integer value as its first argument and a value of any type as its second. The third parameter is dependent of the type of the first argument. When a String is provided, the third argument must be an Object. Otherwise if the first argument is an Integer zero or greater, then the third must be an Array.

setProp will return a new instance of either Object or Array with the addition applied. When the value exists on the provided object, then the value will overwritten. If the value does not exist then it will be added to the resulting structure. In the case of Array, the value will be added to the provided index, leaving undefined values, resulting in a sparse Array.

import setProp from 'crocks/helpers/setProp'
setProp('a', false, { a: true })
//=> { a: false }
setProp('b', 43, { a: true })
//=> { a: true, b: 43 }
setProp(0, 'string', [ 'a' ])
//=> [ "string" ]
setProp(1, 'b', [ 'a' ])
//=> [ "a", "b" ]
setProp(2, 'c', [ 'a' ])
//=> [ "a", undefined, "c" ]

tap#

crocks/helpers/tap

tap :: (a -> b) -> a -> a

It is hard knowing what is going on inside of some of these ADTs or your wonderful function compositions. Debugging can get messy when you need to insert a side-effect into your flow for introspection purposes. With tap, you can intervene in your otherwise pristine flow and make sure that the original value is passed along to the next step of your flow. This function does not guarantee immutability for reference types (Objects, Arrays, etc), you will need to exercise some discipline here to not mutate.

tryCatch#

crocks/Result/tryCatch

tryCatch :: ((*) -> b) -> (*) -> Result e b

Typical try-catch blocks are very imperative in their usage. This tryCatch function provides a means of capturing that imperative nature in a simple declarative style. Pass it a function that could fail and it will return you another function wrapping the first function. When called, the new function will either return the result in a Result.Ok if everything was good, or an error wrapped in an Result.Err if it fails.

unary#

crocks/helpers/unary

unary :: ((*) -> b) -> a -> b

If you every need to lock down a given function to just one argument, then look no further than unary. Just pass it a function of any arity, and you will get back another function that will only apply (1) argument to given function, no matter what is passed to it. unary is just syntactic sugar around nAry in the form of nAry(1, fn) as it is such a common case. Another common case is binary which, as the name implies, only applies (2) arguments to a given function.

unit#

crocks/helpers/unit

unit :: () -> undefined

While it seems like just a simple function, unit can be used for a number of things. A common use for it is as a default noop as it is a function that does nothing and returns undefined. You can also use it in a pointed fashion to represent some special value for a given type. This pointed use is the heart and soul of the infamous Maybe type.

unsetPath#

crocks/helpers/unsetPath

unsetPath :: [ (String | Integer) ] -> a -> a

Used to remove a property or index on a deeply nested Object/Array. unsetPath, previously called dissoc, will return a new instance with the property or index removed.

The provided path can be a mixture of either Positive Integers or Strings to allow for traversing through both Arrays and Objects. When an Integer is provided it will treat that portion as an Array while Strings are used to reference through Objects.

import unsetPath from 'crocks/helpers/unsetPath'
unsetPath([ 'people', 0, 'remove' ], {
people: [
{ name: 'Tonya', remove: true },
{ name: 'Bobby' }
]
})
//=> { people: [ { name: 'Tonya' }, { name: 'Bobby' } ] }
unsetPath([ 'a', 'c', 'd' ], { a: null })
//=> { a: null }
unsetPath([ 'a', 'b' ], { a: { b: false } })
//=> { a: {} }
unsetPath([ 'a', 'b' ], { a: { c: false } })
//=> { a: { c: false } }

unsetProp#

crocks/helpers/unsetProp

unsetProp :: (String | Integer) -> a -> a

unsetProp is a binary function that takes either a property name or an index as its first argument. Which specifies what should be removed, or "unset", from the Object or Array provided as the second argument. If the value provided for the second argument is not an Object or Array, then the value provided is echoed back as the result.

The first argument must be either a non-empty String or positive Integer. A String should be provided when working with an Object, while Arrays require an Integer. unsetProp will return a new instance of either the Object or Array, sans the key or index.

import unsetProp from '/crocks/helpers/unsetProp'
unsetProp('temp', { name: 'Joey', temp: 33 })
//=> { name: 'Joey' }
unsetProp(1, [ 33, 22, 99 ])
//=> [ 33, 99 ]
unsetProp('d', { a: 'A', b: 'B' })
//=> { a: 'A', b: 'B' }
unsetProp(10, [ 'a', 'b', 'c' ])
//=> [ 'a', 'b', 'c' ]
unsetProp('silly', null)
//=> null