Skip to main content

Pattern Matching

·535 words·3 mins
cyancaesar
Author
cyancaesar

Algebraic Data Type
#

As the name suggests, it is a composite type formed by a combination of other types.

The term Algebraic is the method of composing type needs to be equivalence in terms of Algebraic Structure that heavily influenced by Category Theory.

There are two classes to create a composite types

Product Types
#

In simple explanation, its a structure that allows you to store multiple types inside it. Its like tuples and structs.

Where two or more type can be merged in a container that holds fields for each type.

type Plot = [number, number]
struct Plot {
	int x;
	int y;
}

The total number of combinations to form this type is the cardinality of number set times the cardinality of another number set.

Sum Types
#

Also called disjoint union, tagged or co-product.

It is type construct that offers you a choice to to choose among a list of types called variant, where the composite type is one of the constituent types.

Its like enum and tagged union in C++.

Can be achieved in TypeScript using union.

type Student<S> = {
	tag: 'Student',
	val: S
}

type Employee<E> = {
	tag: 'Employee',
	val: E
}

type Person<S, E> = Student<S> | Employee<E>

So algebraic data type gives us operations to construct our desired types, mainly the operations are:

  • Product operation
  • Sum operation

Pattern Matching
#

Pattern matching is a mechanism that searches for a value against a pattern. If the pattern is found, extract the value from it.

It is used to destructure the composed type to its one of its constitutes.

It is a core feature to any functional programming languages. Mainly used to distinguish the composed type that formed using sum type.

Pattern matching:

  • Reduced code complexity, no need to use if-else block
  • Makes the code readable. You can grasp how the constitutes of the composed type is processed inside a function and provides a branch for each case.
  • Makes the code modular, where you can use the matching function everywhere it needed.

Match Option Type
#

Option type is a composed type of some and none, we add on each of the composable types a tag or a kind to differentiate both types.


// Match function signature
type Match = <A, B>(onNone: () => B, onSome: (a: A) => B) 
	=> (a: Option<A>) 
	=> B

const match: Match = (onNone, onSome) => x => isNone(x) ? onNone() : onSome(x.value)

// Implement match function
// When none is matched, return -1
// When some is matched, unwrap some and return the value
const matched = match(
	() => -1, 
	val => val
	)(maybeNum) // Pass Option type

console.log(matched)

Match Either Type
#

type Match = <L, R, M>(
	onLeft: (left: Left<L>) => M, 
	onRight: (right: Right<R>) => M
	) 
	=> (e: Either<L, R>) 
	=> M

// Type guard
const isLeft = <L, R>(x: Either<L, R>): x is Left<L> => x.kind === 'Left'

const match: Match = (onLeft, onRight) => e => isLeft(e) ? onLeft(e.value) : onRight(e.value)

// Either value to be matched
const value = right(5)

const matched = match(
	l => `Left, do fallback`,
	r => `Right, do something with ${r.value}`
	)(value) // Pass Either type

console.log(matched)