fcf
v0.1.0
Published
Monadic Functional Control Flow Micro-Library for Javascript
Downloads
6
Readme
FCF
What is FCF?
FCF is a Monadic Functional Control Flow Micro-Library for Javascript written in Typescript.
It aims at providing a functional and semantic alternative to some native Javascript control flow statements like if
, switch
and while
.
Native Imperative Flow Statements
Keywords like if
or switch
are imperative statements that normally must be combined to functions to give them a semantic meaning for example:
// BAD imperative (non semantic)
if (document.visibilityState === 'visible') {
// do stuff with visible document
} else if(document.visibilityState === 'hidden') {
// do stuff with hidden document
}
// a bit better... with a semantic functions
const isDocumentVisible = () => document.visibilityState === 'visible'
const isDocumentHidden = () => document.visibilityState === 'hidden'
// notice that here that we didn't store the value of the conditions
// for that we need to introduce new variables
if (isDocumentVisible()) {
// do stuff with visible document
} else if (isDocumentHidden()) {
// do stuff with hidden document
}
Functional Control Flow
With FCF we can write your program flow in a more semantic way retaining the value of its conditional statement:
import fcf from 'fcf'
const checkIfApplicationIsActive = fcf
.if(value => value === 'visible')
.then(() => {
// do stuff with visible document
return 'active'
})
.elseIf(value => value === 'hidden')
.then(() => {
// do stuff with hidden document
return 'standby'
})
// check the condition
const {value} = checkIfApplicationIsActive.run(document.visibilityState)
// the value returned by the first `then` call matched ('active'|'standby')
console.log(value)
Notice that FCF is strictly typed so you can rewrite the example above in typescript in this way:
import fcf from 'fcf'
const checkIfApplicationIsActive = fcf
// with `[string]` you can define the type of arguments provided to the 'run' function
// `string` is the value retained by the fcf.if object
.if<[string], string>(value => value === 'visible')
.then(() => {
// do stuff with visible document
return 'active'
})
// `value` here is of type `string`
.elseIf(value => value === 'hidden')
.then(() => {
// do stuff with hidden document
return 'standby'
})
// check the condition
const {value} = checkIfApplicationIsActive.run(document.visibilityState)
// will be of type "string"
console.log(value)
Another big advantage of FCF is that its API is composable:
const greetUserByRole = fcf
.switch(user => user.role)
.case('power')
.then(() => {
console.log('Good morning power user')
})
.case('base')
.then(() => {
console.log('hello base user')
})
.default(() => {
console.log('hello base user')
})
const checkUser = fcf
.if(user => user.isLogged)
.then(user => greetUserByRole.run(user))
.else(() => {
console.log('you are not logged in')
})
checkUser.run({
role: 'power',
isLogged: true
})
checkUser.run({
role: 'base',
isLogged: true
})
checkUser.run({
role: 'power',
isLogged: false
})
IfFlow - fcf.if
fcf.if
provides an alternative to the native Javascript if
statement.
fcf
.if(true)
.then(() => {
console.log('hello')
})
.run()
Any fcf.if
object has the following properties
else(fn: function)
- provide a fallback method if none of the conditions are matchedelseIf(fn: function|any)
- add a new condition that must be followed by athen
callthen(fn: function)
- add a callback to a previous condition and set thevalue
propertyvalue
- value returned by the first matchingthen
callrun(...args: any[])
- run the condition flow passing eventually arguments to it
simple
The simplest fcf.if
might look like this:
fcf
.if(true)
.then(() => {
console.log('hello')
})
.run()
if-else
The else
method works like for normal if
statements
fcf
.if(false)
.then(() => {
console.log('you will never get here')
})
.else(() => {
console.log('hello')
})
.run()
if-else-if
With the elseIf
method you can add new conditions
fcf
.if(true)
.then(() => {
console.log('you will never get here')
})
.elseIf(false)
.then(() => {
console.log('hello')
})
.run()
functional conditions
The fcf.if
and fcf.if.elseIf
methods accept also functions as argument.
fcf
.if(() => true)
.then(() => {
console.log('you will never get here')
})
.elseIf(() => false)
.then(() => {
console.log('hello')
})
.run()
functional conditions with arguments
The fcf.if.run
method allows you to pass arguments into your ifFlow chain
fcf
.if(greeting => greeting === 'goodbye')
.then(() => {
console.log('goodbye')
})
.elseIf(greeting => greeting === 'hello')
.then(() => {
console.log('hello')
})
.run('hello')
value property
The fcf.if
objects will retain the value returned by the first then
call matched
const {value} = fcf
.if(greeting => greeting === 'goodbye')
.then(() => {
return 'goodbye'
})
.elseIf(greeting => greeting === 'hello')
.then(() => {
return 'hello'
})
.run('hello')
console.log(value) // hello
SwitchFlow - fcf.switch
fcf.switch
provides an alternative to the native Javascript switch
statement.
It normalizes also the default switch
behavior avoiding the need of break
statements: the first case
matched will avoid the evaluation of the others
fcf
.switch(greeting => greeting)
.case('hello')
.then(() => {
console.log('hello')
})
.case('goodbye')
.then(() => {
console.log('goodbye')
})
.default(() => {
console.log('¯\\_(ツ)_/¯ ')
})
.run('goodbye')
Any fcf.switch
object has the following properties
default(fn: function)
- provide a fallback method if none of thecase
is matchedcase(fn: function|any)
- add a new case that must be followed by athen
callthen(fn: function)
- add a callback to a previouscase
call and can set thevalue
propertyvalue
- value returned by the first matchingthen
callrun(...args: any[])
- run the condition flow passing eventually arguments to it
simple
The simplest fcf.switch
might look like this:
fcf
.switch(greeting => greeting)
.case('hello')
.then(() => {
console.log('hello')
})
.case('goodbye')
.then(() => {
console.log('goodbye')
})
.default(() => {
console.log('¯\\_(ツ)_/¯ ')
})
.run('goodbye')
switch-default
The default
method works like for normal switch
statements: if no case
is matched the default
method will be called.
fcf
.switch(greeting => greeting)
.case('goodbye')
.then(() => {
console.log('you will never get here')
})
.default(greeting => {
console.log(greeting)
})
.run('hello')
functional cases
The fcf.switch
and fcf.switch.case
methods accept also functions as argument.
fcf
.switch(greeting => greeting)
.case(() => 'goodbye')
.then(() => {
console.log('goodbye')
})
.case(() => 'hello')
.then(() => {
console.log('hello')
})
.run('hello')
functional cases with arguments
The fcf.switch.run
method allows you to pass arguments into your switchFlow chain
fcf
.switch(greeting => greeting)
.case(greeting => greeting === 'goodbye')
.then(() => {
console.log('goodbye')
})
.case(greeting => greeting === 'hello')
.then(() => {
console.log('hello')
})
.run('hello')
value property
The fcf.switch
objects will retain the value returned by the first then
call matched
const {value} = fcf
.switch(greeting => greeting)
.case(greeting => greeting === 'goodbye')
.then(() => {
return 'goodbye'
})
.case(greeting => greeting === 'hello')
.then(() => {
return 'hello'
})
.run('hello')
console.log(value) // hello
WhileFlow - fcf.while
fcf.while
provides an alternative to the native Javascript while
statement.
It normalizes its behavior in browsers and node using requestAnimationFrame
or setImmediate
to run loops
fcf
.while(true)
.do(() => {
console.log('hello')
})
.run()
Any fcf.while
object has the following properties
do(fn: function)
- add a callback that will be called forever when the whileFlow is running. If ado
function will returnfalse
the while flow will be stoppedbreak(fn?: function)
- if called, it will stop the while flow. It accepts eventually a callback to call when the flow will be stoppedvalue
- value returned by the initial control functionrun(...args: any[])
- run the while flow passing eventually arguments to it
simple
The simplest fcf.while
might look like this:
fcf
.while(true)
.do(() => {
console.log('hello')
})
.run()
break
The break
allows to stop the while flow
const loggerFlow = fcf
.while(true)
.do(() => {
console.log('hello')
})
.run()
// stop the while loop after 1 second
setTimeout(() => {
loggerFlow.break()
}, 1000)
functional control function
The fcf.while
method accept also functions as argument.
fcf
// it will log until the document.visibilityState === 'visible'
// otherwise it will be stopped
.while(() => document.visibilityState === 'visible')
.do(() => {
console.log('hello')
})
.run()
stop the while flow from a do function
The fcf.while.do
can stop the while flow returning false
fcf
.while(true)
// it will log only once and then stop the while flow
.do(() => {
console.log('hello')
return false
})
.run()
functional with arguments
The fcf.while.run
method allows you to pass arguments into your whileFlow chain
const greetUser = fcf
.switch(user => user.role)
.case('power')
.then(() => {
console.log('Good morning power user')
})
.then('base')
.then(() => {
console.log('hello base user')
})
.default(() => {
console.log('hello base user')
})
fcf
.while(true)
.do(user => greetUser.run(user))
.run(user)
value property
The fcf.while
objects will retain the value returned by its initial control function
const {value} = fcf
.while(true)
.do(() => {
console.log('hello')
})
.run('hello')
console.log(value) // true
TODO
- Provide async functions support
- Add more control flow methods