Skip to content

Instantly share code, notes, and snippets.

@rjdestigter
Created November 15, 2018 21:51
Show Gist options
  • Save rjdestigter/1447d55e25af6728732140ba9b10ae7f to your computer and use it in GitHub Desktop.
Save rjdestigter/1447d55e25af6728732140ba9b10ae7f to your computer and use it in GitHub Desktop.
Lazy list experiment in JavScript
/**
* Type describing a "lazy" value, a.k.a a function returnting the value
*/
type Lazy<A> = () => A
/**
* Continued
*/
class Cons<A> {
constructor(readonly head: Lazy<A>, readonly tail: Lazy<List<A>>) {}
}
const a: any = null
/**
* Empty list
*/
interface Nil<A> {
type: 'NIL'
value: A
}
/**
* Singleton describing an empty list
*/
const NIL: List<any> = {
type: 'NIL',
value: a,
}
/**
* Helper function to assign an element type to an empty list
*/
const nil = <A>(): List<A> => NIL
/**
* Linked list
*/
type List<A> = Cons<A> | Nil<A>
/**
* Creates a linked list with a given head and tail
*/
const cons = <A>(head: Lazy<A>, tail: Lazy<List<A>>): List<A> => new Cons(head, tail)
/**
* Determines if a given list is an empty list
*/
const isNil = <T>(list: List<T>): list is Nil<T> => NIL === list
/**
* foldRight - Right-associative fold of a structure.
* @param f Reducer, given A, returns a lazy B.
* @param b Lazy initial value
* @param list The list to be folded
* @return The result of the final fold
* @example Summing a list using foldRight:
* `foldRight((x, y) => x + y)(0)(list)` // where "list" is of type `List<number>`
*/
const foldRight = <A, B>(f: (a: A, b: Lazy<B>) => B) => (b: Lazy<B>) => (list: List<A>): B => {
if (isNil(list)) {
return b()
}
const h = list.head()
const t = list.tail()
console.warn(`${h} [H] evaluated in foldRight`)
return f(h, () => foldRight(f)(b)(t))
}
/**
* append - Append list B to the tail of list A
* @param lista The list to append to
* @param listb The list to append to list Append
* @returns The two lists appended into one list
*/
const append = <A>(lista: List<A>) => (listb: List<A>) =>
/**
* We can express "append" in terms of "foldRight"
*/
foldRight(
/**
* On the first run the reducer takes the head of List A and the initial value, List B
* and returns a new list.
*/
(h: A, t: Lazy<List<A>>) => cons(() => h, t)
)(
// The initial value is "List B"
() => listb
)(
// The list being "folded" is "List A"
lista
)
/**
* Map a list form a - b
* @param f
*/
const map = <A, B>(f: (a: A) => B) => (list: List<A>): List<B> =>
foldRight((h: A, t: Lazy<List<B>>) => cons(() => f(h), t))(() => nil<B>())(list)
/**
* infinite - Create an infinite linked list of numbers starting at the given number.
* @param n The starting number
* @returns An infinite linked list of numbers
*/
const infinite = (n: number): List<number> => cons(() => n, () => infinite(n + 1))
/**
* take - Take n elements from the list
* @param n The number of elements to take
*/
const take = (n: number) => <A>(list: List<A>): List<A> => {
if (isNil(list)) {
return list
}
return cons(list.head, () => {
if (n - 1 >= 1) {
return take(n - 1)(list.tail())
}
return nil<A>()
})
}
/**
* filter - Filter a linked list with a given predicate function
* @param f - Filter function that given an element in the list returns `true` or `false`
*/
const filter = <A>(f: (a: A) => boolean) => (list: List<A>): List<A> => {
const empty: Lazy<List<A>> = () => nil<A>()
const filterP = (h: A, t: Lazy<List<A>>) => {
if (f(h)) {
return cons(() => h, t)
}
return t()
}
return foldRight(filterP)(empty)(list)
}
/**
* toArray - Convert a linked list to an array
* @param list The linked list to be converted
* @returns A an array of all elements in the linked list
*/
const toArray = <A>(list: List<A>): A[] => foldRight((h: A, arr: () => A[]) => [h].concat(arr()))(() => [])(list)
// Infinite list of number starting at 1
const listOfInfiniteNumbers = infinite(1)
const filterEvens = filter((n: number) => n % 2 === 0)
const mapToDoubles = map((n: number) => n * 2)
const infiniteDoubleEvens = filterEvens(mapToDoubles(listOfInfiniteNumbers))
console.log(toArray(take(10)(infiniteDoubleEvens)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment