DEV Community

loading...
Cover image for Documenting JS functions as haskell :P

Documenting JS functions as haskell :P

Damian Cipolat
Hi, I'm from Argentina. I specialize in Nodejs and microservice technologies over AWS. Thanks for reading me XD
・3 min read

Documenting functions in javascript

The idea of this repository is to continue with the last 2 projects that I raised on functional programming in js.

But why?

For some time I have been dedicated to javascript and I see that there is still no standard on how developers should document what data should be passed as parameters. Although JS is not a typed language, somehow we have to specify what data is sent in each parameter.

Currently: Currently this is one of my functions, I have started working with this format.

/*
  Receive currency code and return money simbol.
  Params
    currency : string
  Return
    string
*/
const getSymbol = (currency)=>{

  switch(currency){
    case 'ARS':
      return '$';
    case 'USD':
      return 'U$S';
    default:
      return '$';
  }

}

New format: I'm moving to this new format.

/*
  Receive currency code and return money simbol.
  getSymbol:: string → string
*/
const getSymbol = (currency)=>{...}

Solution - Type Signatures

These type notations are a meta language called Type Signatures, defines the inputs and outputs for the function, sometimes including the number of arguments, the types of arguments and order of arguments contained by a function.

Type Signatures are based on Hindley-Milner Type system as a standard type system which is also followed by ML-influenced languages, including Haskell.

Some examples:

// length :: String → Number
const length = s => s.length;

// length :: [Number] → Number
const length = arr => arr.length;

// join :: (String, [String]) → String
const join = (separator, arr) => arr.join(separator)

Code examples:

The sections are divided in One parameter / Multiple parameters / High order functions.

One parameter:

Examples using 1 parameter as input and one flat return data, f(x) = y.

  • STRING - f(String) = Number:
//length :: String → Number
const length = (a)=>a.length;
  • NUMBER - f(Number) = Number:
//increase :: Number → Number
const increase = value => value+10;
  • BOOLEAN - f(Bool) = Bool.
//inverse :: Bool → Bool
const inverse = value => !value;
  • ARRAY - f([x]) = Number.
//length :: [a] → Number
const length = list => list.length;

//length :: [string] → Number
const length = list => list.length;

//length :: [Number] → Number
const length = list => list.length;
  • DATE - f(date) = Bool.
//expire :: Date → Bool
const expire = expireDate => new Date()<=expireDate;
  • FUNCTION - f(g([a])) = [b].
//map :: (a → b) → [a] → [b]
const map = fn => arr => arr.map(fn)
  • ANY - f(*) = b.
//isNull :: * → Bool.
const isNull = obj => !!obj;
  • OBJECT - f(object) = String.

Generic format

//map :: object → string
const toJson = obj => JSON.stringify(obj);

Custom format

//map :: {name:String, age: Number} → string
const toJson = people => JSON.stringify(people);

Array of objects

//map :: [{name:String, age: Number}] → [string]
const encode = people => people.map(p=>btoa(p));

Multiple parameters:

Examples using 2 parameters as input and one flat return data, f(x,y) = z.

  • STRING:
// join :: (String, [String]) → String
const join = (separator, arr) => arr.join(separator)
  • Number - f(x,y) ) = z.
// sum :: (Number, Number) → Number
const sum = (x,y) => x+y;
  • Array - f([a],b) = c.
//concat :: [*],string → string
const concat = (list,char) => list.join(char).

High order parameters:

When a function is passed as parameter, we wrap it’s signature in a parentheses to present a more meaningful overall Type Signature. f(g(x)) = y

// addOneToAll :: ((Number → Number),[Number]) → [Number]
const addOneToAll = (addOne = x=>x+1 , arr) => arr.map(addOne)

Using in real world JS

Examples converting functions, comments signature.

Example 1

Before, code example.

/*
  Receive two arrays with field, analyze both parameters and return the situation.
  Params
    flowFields   : {fields:[],onboarding_vu:string,document_attached:string}
    clientFields : {fields:[],onboarding_vu:string,document_attached:string}
  Return
    [string] : required fields
*/
const underAge = birth => !birth || (birth && moment().diff(birth, 'years')) < 18;

After, code example using the meta lenguage notation.

/*
  Receive two arrays with field, analyze both parameters and return the situation.
  underAge :: date → bool
*/
const underAge = birth => !birth || (birth && moment().diff(birth, 'years')) < 18;

Example 2

Before, code example.

/*
  GET a file from S3 bucket.
  Params 
    S3 : aws s3 instance,
    params : {Bucket:'xxxx',Key:'xxxx'}
  Return
    promise
*/
const getFile = (s3,params) => s3.getObject(params).promise();

After, code example using the meta lenguage notation.

/*
  GET a file from S3 bucket.
  getFile :: object, {Bucket:string, Key:string} → promise
*/
const getFile = (s3,params) => s3.getObject(params).promise();

Readings:

Discussion (0)