DEV Community

Cover image for Documenting JS functions as haskell :P
Damian Cipolat
Damian Cipolat

Posted on

Documenting JS functions as haskell :P

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:

Top comments (0)