0.1.0 • Published 4 years ago

fcf v0.1.0

Weekly downloads
3
License
MIT
Repository
-
Last release
4 years ago

FCF

Build Status MIT License NPM version NPM downloads Coverage Status FCF Size Code Quality

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
})

Check Live the example above

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 matched
  • elseIf(fn: function|any) - add a new condition that must be followed by a then call
  • then(fn: function) - add a callback to a previous condition and set the value property
  • value - value returned by the first matching then call
  • run(...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 the case is matched
  • case(fn: function|any) - add a new case that must be followed by a then call
  • then(fn: function) - add a callback to a previous case call and can set the value property
  • value - value returned by the first matching then call
  • run(...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 a do function will return false the while flow will be stopped
  • break(fn?: function) - if called, it will stop the while flow. It accepts eventually a callback to call when the flow will be stopped
  • value - value returned by the initial control function
  • run(...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
0.1.0

4 years ago

0.0.8

4 years ago

0.0.7

4 years ago

0.0.5

4 years ago

0.0.6

4 years ago

0.0.3

4 years ago

0.0.4

4 years ago

0.0.1

4 years ago

0.0.2

4 years ago

0.0.0

4 years ago