- Read
- Top Stories
- Write
- Listen
- Learn
Web Development Data Science - What is it?
- Best 50 Sites to Learn it
- Data Engineering
- Data Science Course 2020 ๐
- Deep Learning A-Z ๐
- Deep Learning vs. Machine Learning
- Love
- ML Essentials
- PG Program in Artificial Intelligence and Machine Learning ๐
- Optimize Your CV
- Python for Machine Learning ๐
- Statistics for Data Science and Business Analysis๐
- Zero to H...

Languages - Advertise
- About
- Tech Companies
- Experts ๐

Function Type Signatures in Javascript by@lunasunkaiser

May 13th 2017 9,081 reads

When a Javascript Developer starts to explore the deepest secrets of the Functional Programming, he often finds these weird *arrow notations with type* written above the functions and feels *โWhat the hell is this?โ*. After all, heโs a master of dynamic typed Javascript, free from the boundaries of types.

These type notations are a meta language called **Type Signatures** that tells a lot about a Pure Function and are important in functional programming *much more than we might first expect*.

*Letโs see what these Type Signatures are and why we should use them in our code.*

A Type Signature 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.

These type signatures are highly expressive statements written above Pure Functions and are used to deduce itโs working.

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

These statements serves a greater purpose by **Formalizing** the functional expression in Type Inferring Algorithms*(commonly used by Haskell)*, but for now weโll use them to document our Javascript code better and deriving **Free Theorems **out of them.

And if you find any Pure Function documented with these Type Signatures, the power to decode them will give you an ahead-of-time insight on the workings of that Function.

Weโll be defining Type Signatures as comments above our functions. You can also use *Flow* to infer types when using functions. To get started with Flow you can follow this,

// length :: String โ Number

const length = s => s.length;

So the above function takes a string and returns a number. If we look closely we can see

1. The function name is written first followed by `::`.

2. The input type of the function is written before the arrow.

3. The return type of the function is written after the arrow or at last.

Remember, only the types of the input and output is written so that it can be read as *โA function length from string to numberโ.*

The above length function can also be written as

// length :: [Number] โ Number

const length = arr => arr.length

And itโs normal for a function to have a multiple type signatures as long it is practical, if a function is too flexible with its parameter types then we should use ** HM-arbitrary variables**, weโll discuss them in a sec.

In Javascript, we can have functions with many parameters, unlike other FP languages, and itโs a good practice to curry those functions to take only one parameter at a time. But, if we still want to use multiple parameters in our functions we can do this.

// join :: (String, [String]) โ String

const join = (separator, arr) => arr.join(separator)

*Itโs not functional programming if we donโt have functions working on functions.*

// addOneToAll :: ((Number โ Number),[Number]) โ [Number]

const addOneToAll = (addOne = x=>x+1 , arr) => arr.map(addOne)

When a function is passed as parameter, we wrap itโs signature in a parentheses to present a more meaningful overall Type Signature.

The above function is a โ*map*โ function, not every time this function works on defined data types, basically, it can work on any type of array. So to describe these kinds of a function we need something else.

*Functions like identity, map, filter and reduce accepts arguments that are too flexible to be defined by a specific type so we use classic Hindley-Milner variables **a** and **b**ย .*

// identity :: a โ a

const identity = a => a

Since identity will always be giving us the same output type for the same input type. Therefore, we used `a โ a`

to represent itโs signature.

And also our length function can be written as

// length :: [a] โ Number

const length = arr => arr.length

Similarly,

// head :: [a] โ a

const head = arr => arr[0]

*Type Signatures of the Purest of the Pure functions*โจ

For a function taking multiple arguments, its always a good choice to curry them so that they can be composed, later in our code. Also, itโs not a good practice to use arbitrary HMโs variables with functions with multiple arguments.

If you are curious about ** Why we should curry our Functions** please go through this,

// map :: (a โ b) โ [a] โ [b]

const map = fn => arr => arr.map(fn)

A standard map function will have the above type signature. But, many times map can also be defined by this type signature

map :: [a] โ [b]

Sometimes we know the type of the array returned by map, like in this case.

// allToString :: [a] โ [String]

const allToString = arr => arr.map(toString)

Letโs look at standard filter and reduce

// filter :: (a โ bool) โ [a] โ [a]

const filter = fn => arr => arr.filter(fn)

// reduce ::`(b`

โ`a`

โ`b)`

โ`b`

โ`[a]`

โ`b`

const reduce = fn => init => arr => arr.reduce(fn, init)

Clearly, the type signature of a reduce function is a little complex, well if we can understand how to write the type signature of reduce function we can write the type signature for almost any function.

Okay, so the first argument of reduce is a ** reducing function** that takes

`b`

and `a`

to give `b`

this means that this function will be reducing everything to type `b`

ย , so the final value obtained from `b`

type. And since every single value from the list of type `a`

will be fed to this `a`

type, which it is. Therefore, the Type Signature of Another use of these signatures is to produce *Free Theorems.* These theorems are highly useful when we are dealing with the **Compositions of Pure Functions **as they helps us in optimising and refactoring of our code.

// Type signature of head says

// head :: [a] โ a

compose(map(fn), head) == compose(head, fn)

This is our first free theorem, solely derived from the Type signature of head and map functions, which states

If we `map`

a function `fn`

on every element and then take the `head`

of the resultant array then that will be equivalent to applying the function `fn`

on `head`

of the array.

Letโs prove this Theorem

compose(map(fn), head) == compose(head, fn)

--Converting to Type Signatures--

[a] โ [b] โ b == [a] โ a โ b

-- Removing Intermediates --

[a] โ b == [a] โ b

Since the overall Type Signatures of both the functions are same, we can conclude that both composition will return same result for same input.

The above derivation is simplified as it requires Lambda Calculas for the actual derivation of free theorems which is not in the scope of this post.

You can always go through Wadlerโs Paper on Free Theorems, if you want to dig deeper.

Please note that the Compose function used here is actually opposite of idiomatic Compose. More info here.

The power to decipher and use Type Signatures is useful not only in Javascript but also in other FP Languages, so if we need to borrow any Pure function to Javascript we can just refer to itโs Type Signature and weโll know just where to put that function in our code.

Thanks for reading

Written with ๐

Join Hacker Noon

Create your free account to unlock your custom reading experience.