fcf v0.1.0
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 athencallthen(fn: function)- add a callback to a previous condition and set thevaluepropertyvalue- value returned by the first matchingthencallrun(...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) // helloSwitchFlow - 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 thecaseis matchedcase(fn: function|any)- add a new case that must be followed by athencallthen(fn: function)- add a callback to a previouscasecall and can set thevaluepropertyvalue- value returned by the first matchingthencallrun(...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) // helloWhileFlow - 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 adofunction will returnfalsethe 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) // trueTODO
- Provide async functions support
- Add more control flow methods