func-fave
v1.31.0
Published
This is a library that allows use of functional programming in JavaScript by utilizing specialized functions.
Downloads
64
Maintainers
Readme
func-fave
Func-fave is an npm package that allows easy use of functional programming in Javascript by utilizing specialized functions. These specialized functions are called Functionals. The purpose of these functionals are to replace the traditional control-flow statements found in Javascript.
Terminology:
conditional
: A conditional is an expression,
functional, or value that holds or returns
the value of true
or false
.
example:
const { FIF } = require( 'func-fave' );
conditionalExamples = {
'example_one': (1 >= 2), // false
'example_two': FIF( this.example_one ), // false
'example_three': false
}
functional
: A functional is a function
that returns a value and can replace or
expand on a traditional control-flow
statement in usage.
example:
/*
The following are functionals
that func-fave provides.
*/
/* Control-flow */
const { FIF } = require( 'func-fave' ); // if
const { FDEC } = require( 'func-fave' ); // declarative if
const { FIM } = require( 'func-fave' ); // imperative func
const { FFOR } = require( 'func-fave' ); // functional for-loop
/* Operators */
const { FEQ } = require( 'func-fave' ); // equal to
const { FGR } = require( 'func-fave' ); // greator than
const { FLR } = require( 'func-fave' ); // less than
anonymous | anon
: An anonymous is a function that takes no
parameters and acts as a callback function. The return
value of the anonymous is returned by the higher
order function in which it is used.
truthy
: describes any value, expression, or return value that is expressed as true.
falsey
: describes any value, expression, or return value that is expressed as false.
callable
: A callable is any function or functional that can be called, regardless of parameter usage.
functor
: In functional programming, a functor is a type that implements a map function. This map function applies a function to every element within a context (such as an array or another type of container) and returns a new context with the transformed elements. In JavaScript, arrays are functors because they have a .map() method that fulfills this purpose.
recursion
: Recursion is a technique that is unique to functions. A recursive function is one that calls itself in order to break down a problem into smaller, more manageable parts.
state
: a collection of information held in a dynamic data-structure.
Installation
npm install func-fave
Basic Control-Flow
FIF()
description:
FIF()
is a functional that replaces the
traditional if
statement. It allows you to
pass three parameters. First it accepts a
conditional expression
as a parameter. This
expression must equate to true
or false
.
The second and third parameters it accepts
may be either a value or callback function.
const { isCallable } = require( 'func-fave' );
function FIF(
condition,
onTrue = () => 1,
onFalse = () => 0
) {
if ( isCallable(condition) ) {
if ( condition() ) {
if (isCallable(onTrue)) { return onTrue() }
return onTrue
}
if (isCallable(onFalse)) { return onFalse() }
return onFalse
}
if ( condition ) {
if (isCallable(onTrue)) { return onTrue() }
return onTrue
}
if (isCallable(onFalse)) { return onFalse() }
return onFalse
}
usage:
const { FIF } = require( 'func-fave' );
const user = 'houdini';
const password = 'abracadabra';
const successfulLogin = `Login success!\n Welcome ${user} ...`
const failedLogin = `Login failed!\n password '${password}' is incorrect ...`
return FIF(
( user === 'houdini' || password === 'abracadabra' )
,
() => {
console.log(successfulLogin)
},
() => {
console.log(failedLogin)
}
)
FDEC()
description:
FDEC()
is a functional that utilizes the
power of the FIF()
functional to use the runtime
declaratively. It makes decisions based on truthy
and falsey conditionals
. In this case, conditionals
and FIF()
conditionals are on and the same.
const { FIF, isCallable } = require( 'func-fave' );
function FDEC(
conditional,
onSuccess,
onFailure,
params = []
) {
return FIF(
( isCallable(conditional) )
,
() => {
return FIF(
(
FIF(
( params.len > 0 )
,
() => conditional(...params),
() => conditional()
)
)
,
() => onSuccess(),
() => onFailure()
)
}
,
() => {
return FIF(
( conditional )
,
() => onSuccess(),
() => onFailure()
)
},
)
}
usage:
const { FDEC, FIF } = require( 'func-fave' );
const userDataBase = {
'houdini': 'abracadabra',
'moriarty': 'ilovesherlock',
'dr.who': 'done_it',
}
const user = Object.keys(userDataBase)[1];
const password = userDataBase[user];
const userNotFound = `Login failed!\n user '${user}' not found ...`
const invalidPassword = `Login failed!\n password '${password}' is incorrect ...`
const successfulLogin = `Login success!\n Welcome ${user} ...`
return FDEC(
(
FIF(
(
Object.keys(userDataBase)
.indexOf(user) > -1
)
) // returns truthy or falsey based on conditional expression
)
,
() => {
return FIF(
( password === userDataBase[user] )
,
() => {
console.log(successfulLogin)
return 1
},
() => {
console.log(invalidPassword)
return 0
}
)
},
() => {
console.log(userNotFound)
return 0
}
)
FIM()
description:
The FIM()
functional's utility is based in its ability to return the value its callback function. It is a tool used to define a callback, operate on parameters passed to said callback, and return the value of the callback itself in the same runtime. This is extremely useful in Functional Programming. This is also a component that allows the FFOR()
function to run smoothly.
const { FIF, isCallable } = require( 'func-fave' );
function FIM(
func,
params = []
) {
return FIF(
( isCallable(func) )
,
() => func(...params),
() => 0
)
}
usage:
const { FIM, FIF, FEQ, createNode } = require( 'func-fave' );
const key = 'find-the-secret-node';
const passkey_attempt = 'find-the-secret';
const Node1 = createNode();
const Node2 = createNode();
Node1.set({
edge1: 'do',
edge2: 're',
edge3: 'me'
})
Node2.set({
edge1: 'fa',
edge2: 'so',
edge3: 'la',
secretNode: createNode(),
setNode: (_secretNode) => {
_secretNode.set({
edge1: 'ti',
edge2: 'do',
secretMsg: 'congrats on finding me!!'
})
}
})
return FIF(
(
FEQ(
(passkey_attempt + '-node'),
key
)
)
,
FIM(
(_NODE) => {
const {
edge1, edge2, edge3,
secretNode, setNode
} = _NODE;
setNode(secretNode);
console.log(secretNode.secretMsg);
return {
body: _NODE,
endpoints: [edge1, edge2, edge3, secretNode]
};
},
[Node2]
)
,
FIM(
(_NODE) => {
const { edge1, edge2, edge3 } = _NODE;
console.log(edge1, edge2, edge3);
return {
body: _NODE,
endpoints: [edge1, edge2, edge3]
};
},
[Node1]
)
)
Operators
FEQ()
description:
The FEQ()
functional tests an equality expression. If the expression is truthy
, it returns the first operand that is passed. If the expression is falsey
, it returns a falsey value.
const { FIF } = require( '../fif.cjs' );
function FEQ(
operandOne,
operandTwo,
) {
return FIF(
( operandOne === operandTwo )
,
() => operandOne,
() => 0
)
}
FGR()
description:
The FGR()
functional tests an inequality expression. If the expression is truthy
, it returns the largest operand that is passed. If the expression is truthy, and the treatConditionally
boolean is toggled, it returns a truthy value. If the expression is falsey
, it returns the largest operand that is passed. If the expression is falsey, and the treatConditionally
boolean is toggled, it returns a falsey value. The passed operands can be callable
.
const { FIF, FIM, FEQ, isCallable } = require( 'func-fave' );
function FGR(
assertGreater,
assertLesser,
treatConditionally = 1
) {
FIM(
() => {
FIF( ( isCallable(assertGreater) ),
() => {
assertGreater = assertGreater()
}
)
FIF( ( isCallable(assertLesser) ),
() => {
assertLesser = assertLesser()
}
)
}
)
const condition = ( assertGreater > assertLesser );
assertGreater = FIF(
(typeof assertGreater !== 'number')
,
() => {
return FIF(
(
FEQ(typeof assertGreater, 'null') || FEQ(typeof assertGreater, 'undefined')
)
,
() => 0,
() => assertGreater.length
);
},
() => assertGreater
);
assertLesser = FIF(
(typeof assertLesser !== 'number')
,
() => {
return FIF(
(
FEQ(typeof assertLesser, 'null') || FEQ(typeof assertLesser, 'undefined')
)
,
() => 0,
() => assertLesser.length
);
},
() => assertLesser
);
return FIF(
( treatConditionally )
,
() => {
return FIF(
( condition )
,
() => 1,
() => 0
)
}
,
() => {
return FIF(
( condition ),
() => assertGreater,
() => assertLesser
)
}
);
}
FLR()
description:
The FLR()
functional tests an inequality expression. If the expression is truthy
, it returns the smallest operand that is passed. If the expression is truthy, and the treatConditionally
boolean is toggled, it returns a truthy value. If the expression is falsey
, it returns the smallest operand that is passed. If the expression is falsey, and the treatConditionally
boolean is toggled, it returns a falsey value. The passed operands can be callable
.
const { FIF, FEQ } = require( 'func-fave' );
function FLR(
assertLesser,
assertGreater,
treatConditionally = 1
) {
FIM(
() => {
FIF( ( isCallable(assertLesser) ),
() => {
assertLesser = assertLesser()
}
)
FIF( ( isCallable(assertGreater) ),
() => {
assertGreater = assertGreater()
}
)
}
)
const condition = ( assertLesser < assertGreater );
assertLesser = FIF(
(typeof assertLesser !== 'number')
,
() => {
return FIF(
(FEQ(typeof assertLesser, 'null') || FEQ(typeof assertLesser, 'undefined'))
,
() => 0,
() => assertLesser.length
);
},
() => assertLesser
);
assertGreater = FIF(
(typeof assertGreater !== 'number')
,
() => {
return FIF(
(FEQ(typeof assertGreater, 'null') || FEQ(typeof assertGreater, 'undefined'))
,
() => 0,
() => assertGreater.length
);
},
() => assertGreater
);
return FIF(
( treatConditionally )
,
() => {
return FIF(
( condition )
,
() => 1,
() => 0
)
},
() => {
return FIF(
( condition )
,
() => assertLesser,
() => assertGreater
)
}
);
}
Iterators
FFOR()
description:
The FFOR()
conditional is a functor
similarly to the traditional
.map()
method for Javascript arrays. However, the FFOR()
conditional
has multiple key differences:
It supports the functionality of the
continue
,break
keywords that are found in traditional syntax.end.set(true)
: breaks recursive iteration, and returns result.cont.set(true)
: continues to next iteration cycle, ignoring current cycle result.
It supports backtracking, which is not found in any current iterative tools.
back.set(true)
: implements backtracking per one iteration cycle.
It iterates over iterable values by using
recursion
.
Upon calling the FFOR()
functional, a Node is initiated and set with values.
The following values may be used in your custom function that is passed into FFOR()
:
index
: References the index of the element of the current iteration cycle.elem
: References the element of the iteration cycle.jump
: References the interval of iteration per cycle.self
: References the original iterable value passed intoFFOR()
.history
: The return value that is provided at the end of recursion.back
: A toggle that enables backtracking.end
: A toggle that enables breaking.cont
: A toggle that enables continuation.
iterable
, the first parameter passed into FFOR()
allows users to define the
iterable data structure that you wish to operate the functor
on.
actionFn
, the second parameter passed into FFOR()
allows users to
operate on an iterable
with an arbitrary function.
setNode
, the third parameter passed into FFOR()
is an object that
describes the starting point and interval of iteration. Omission of this parameter
sets default values:
setNode.startAt
: every iteration starts at the-1
index, as this index increments before every iteration cycle. After incrementing, and before the first iteration cycle, this value increments to0
, and is used as the first index.setNode.jump
: this allows the user to set the jump interval before the functor operation.
const {
FIF, FDEC,
createIterator,
createNode,
createEdge
} = require( 'func-fave' );
function FFOR(
iterable,
actionFn,
setNode = {
'startAt': -1,
'jump': 1
}
) {
const node = createNode();
node.set({
index: createEdge(setNode.startAt),
elem: createEdge(),
jump: createEdge(setNode.jump),
self: createEdge(iterable),
history: createEdge([]),
back: createEdge(false),
end: createEdge(false),
cont: createEdge(false)
});
const {
index, elem, jump,
self, history,
back, end, cont
} = node.get();
const iterator = createIterator(
index,
elem,
jump,
self,
history,
back,
end,
cont
)
return FDEC(
(
FIF(
(end.get())
,
() => 1,
() => 0
)
),
() => {
/* backtrack to index where end.set(true) was called */
/* remove last iteration from history */
iterator.backtracking();
/* end iteration loop */
return node.get().history.get();
},
() => {
return FIF(
(back.get())
,
() => {
back.set(false);
return iterator.backtracking(
actionFn,
node
)
},
() => {
return iterator.iterating(
actionFn,
node
)
}
)
}
)
}
NOTE
: When using back.set(true)
to implement backtracking, be sure to remove the last index of the return value of FFOR()
. This last index will contain a circular object reference. If you have no use for this circular reference, implement the following:
const { FFOR } = require( 'func-fave' );
const recursiveResult = FFOR(
/* ... */
).slice(0, -1)
/* the '.slice()' method provides a clean way to remove the last index. */
usage:
const { FFOR, FIF } = require('func-fave')
const cFBT = { //-- cFBT = checkForBackTrack
'a': 0, 'd': 0, 'j': 0, 'o': 0,
'r': 0, 'n': 0, 'e': 0, 'd': 0,
'_': 0,
}
const cFBT_limit = 50;
const startTime = performance.now();
const recursiveResult = FFOR(
("adjorned_1_3_5_8"),
(Node) => {
const {
index, elem,
jump, self,
back, end,
cont,
} = Node.get();
return FIF(
( cFBT[elem.get().toString()] < cFBT_limit ),
() => {
cFBT[elem.get().toString()]++
back.set(true)
//end.set(true)
//cont.set(true)
return {"elem": elem.get()}
},
() => { return {"index": index.get()} }
)
},
).slice(0, -1)
const endTime = performance.now();
console.log(` time elapsed (ms): ${Math.floor(endTime - startTime)} `)
console.log(`\nFFOR() return value: `)
console.log(recursiveResult)
console.log(`\ncheck for backtracking: (limit --> ${cFBT_limit}) `)
console.log(cFBT)
createIterator()
description:
The createIterator()
function initiates and returns a new instance of the
Functional Iterator
class. This class is what allows the FFOR()
functor to
iterate, backtrack, break, and continue. The functional iterator, abbrev FIT
,
recursively iterates over a specified iterable. The State Managers
maintain the
state of each iteration cycle over the specified iterable.
class FunctionalIterator {
/* meat and potatoes... */
}
function createIterator(
Index,
Iterand,
Interval,
Body,
History,
Back,
End,
Cont
) {
return new FunctionalIterator(
Index,
Iterand,
Interval,
Body,
History,
Back,
End,
Cont
)
}
State Managers
createEdge()
description:
The createEdge()
function initiates and returns a class that contains and records changes to the value it holds.
const { FIM } = require( 'func-fave' );
const {
createStack,
createIdentifier,
ID
} = require( 'private-func-fave' ); // private meaning unreachable
class Edge {
constructor(value) {
this.id = ID.generate('edge')
this.value = value;
this.origin = value;
this.history = createStack([value]);
this.folds = [];
}
get = () => this.value;
set = (value) => {
this.value = value;
this.history.add(value);
}
reset = () => {
this.value = this.history[0];
this.origin = this.history[0];
this.history = [this.value];
this.folds = [];
}
getHistory = () => this.history;
getOrigin = () => this.origin;
getLength = () => this.history.length;
getFolds = () => this.folds;
setFolds = (value) => { this.folds = value; }
resetFolds = () => { this.folds = []; }
pushFolds = (value) => { this.folds.push([value]) }
foldWith = (counterEdge) => {
return FIM(
() => {
const generateFolds = (self, entity) => {
return {
origin: self,
value: [self.getHistory(), entity.getHistory()],
heads: [ self.get(), entity.get() ],
tails: [ self.getHistory()[0], entity.getHistory()[0] ]
}
}
counterEdge.pushFolds(generateFolds(counterEdge, this))
const foldedEdges = generateFolds(this, counterEdge)
this.pushFolds(foldedEdges)
return foldedEdges;
}
)
}
unfold = (fold, counterFold) => {
const fold_arr = [fold, counterFold];
for (let fold of fold_arr) { fold.origin.resetFolds() }
}
}
function createEdge(value) {
return new Edge(value);
}
createNode()
description:
The createNode()
function initiates and returns a class
that contains and records any state
, which is a combination
of Edges. Just as any State is a collection of information,
any Node State is a collection of Edges.
In theory, a 'State Space' is a collection of Node States. These levels of abstraction can keep going upwards if there is information to be recorded and changed, and there are efficient and appropriate use cases for these abstractions.
const {
createIdentifier,
ID
} = require( 'private-func-fave' ); // private meaning unreachable
class Node {
constructor(body) {
this.body = body;
this.id = ID.generate('node');
}
get = () => this.body;
set = (value) => this.body = value;
}
function createNode(body = []) {
return new Node(body);
}