Few weeks ago, I decided to challenge myself in learning Haskell. My motivation was to get a better grasp of functional patterns. ( such functors or monads There are a lot more here → The Patternopedia). In one-line of Haskell you can describe abstractive concepts — this is very powerful.

Haskell is different from the other languages I’m familiar with. But I eventually came up with a try to implement a functor in TS, which I called a “List Functor”. You can find the explanation on my previous post. BTW check out the following impressive project fp-ts.

So how learning a pure functional lang improves the way you think in Typescript?

Anything can be typed, Let’s rethink of our functions:

A short time learning Haskell and you’ll start seeing types everywhere. I immediately started to look for type patterns on any TS function I wrote.

Let’s explore the following Haskell snippet:

add :: Integer -> Integer -> Integer --function declaration

add x y = x + y --function definition

The add function declaration has a type of: Integer and another Integer outputs the some of both Integers.

In TS it will be:

type Add = (a: number, b: number) => number; // type signature

export const add: Add = (num1, num2) => num1 + num2;

add('foo', 1); // type error

add(2, 1); // boom

You can visit the following TS playground examples of documenting the shape of functions!

Thinking of types as variables gets us flexibility

In Haskell, a type variable is like a Generic (defined by a lowercase letter).

Following snippet represent the first item of a tuple:

first :: (a, b) -> a

The variable ‘a’ could be of any type.

In typescript it will probably look like:

type first = <T, X> ([a, b]: [T, X]) => T

const fst: first = ([a, b]) => a;

console.log(fst(['foo', 666]));

If we were to describe a more complex function, such as the map() function, it will look like:

map(transformFn: <T, V> (arg: T) => V ): MyFunctorContext<T> {

const newItems = [];



for (const item of this.items) {

newItems.push(transformFn(item));

}



return new MyFunctorContext(newItems);

}

Zooming into the transformFn type description:

<T, V> (arg: T) => V

The input of the transform function could be of any type (T). And so as the transformed output (V) would be of any type but of a different type of the input!

Type Constraints:

Let’s focus on a certain behaviour of a type. If we’ll have a certain behaviour included on a set of types we could achieve a generic function, (polymorphic function) that could operate on them.

Saying that a certain behaviour (or an attribute) required by a type is actually adding type “constraint”.

Here’s an example of adding a type constraint in TS: (using the “extends” keyword).

interface Instrument {

play: () => void;

} type PlayFn = <I extends Instrument>(instruments: I[]) => void; class Guitar implements Instrument {

play() {}

}



class Drums implements Instrument {

play() {}

}



const playPart:PlayFn = (instrumets) => {

instrumets.forEach(instrument => instrument.play());

};



playPart([new Guitar(), new Drums()]);

We can see that in TS a constraint could be elegantly added to a one line representation of a function type:

type PlayFn = <I extends Instrument>(instruments: I[]) => void;

In Haskell’s we refer to constraints by using Type Classes , which are often compared to interfaces.

Follows is a one line Haskell example of using type variable constraints:

read :: (Read a) => String -> a

Read is part of the Haskell lang standard, it provides operations for parsing character strings to obtain the values they may represent.

The function read is of type: [(Read a) => String -> a], and it could accept any type (‘a’) that implements the Read behaviour (as a constraint).

Rethinking of functional patterns:

If you’re into thinking of patterns, you’ll really appreciate the following source:

Functor is a functional representation of different Types which can be mapped over.

The Haskell’s Functor Typeclass implementation is:

class Functor f where

fmap :: (a -> b) -> f a -> f b

I implemented a “List functor”, it contains the ‘map’ function but it’s limited to a List of any type, List is a functor implementation.

Another example of Functor implementation which is not a List could be the Maybe Functor:

instance Functor Maybe where

fmap f (Just x) = Just (f x)

fmap f Nothing = Nothing

The Maybe “box” can either return one value of something or return nothing.

Thats all for this post,

Thanks for reading!

If you liked this post you may like:

Cheers!

Liron.