Last active
July 7, 2022 09:40
-
-
Save Lucifier129/aab621635ac9d986b1efed980a45bcd0 to your computer and use it in GitHub Desktop.
pull-stream via codata
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function pipe<A, B>(a: A, f: (a: A) => B): B; | |
function pipe<A, B, C>(a: A, f: (a: A) => B, g: (b: B) => C): C; | |
function pipe<A, B, C, D>( | |
a: A, | |
f: (a: A) => B, | |
g: (b: B) => C, | |
h: (c: C) => D | |
): D; | |
function pipe<A, B, C, D, E>( | |
a: A, | |
f: (a: A) => B, | |
g: (b: B) => C, | |
h: (c: C) => D, | |
i: (d: D) => E | |
): E; | |
function pipe<A, B, C, D, E, F>( | |
a: A, | |
f: (a: A) => B, | |
g: (b: B) => C, | |
h: (c: C) => D, | |
i: (d: D) => E, | |
j: (e: E) => F | |
): F; | |
function pipe<A, B, C, D, E, F, G>( | |
a: A, | |
f: (a: A) => B, | |
g: (b: B) => C, | |
h: (c: C) => D, | |
i: (d: D) => E, | |
j: (e: E) => F, | |
k: (f: F) => G | |
): G; | |
function pipe<A, B, C, D, E, F, G, H>( | |
a: A, | |
f: (a: A) => B, | |
g: (b: B) => C, | |
h: (c: C) => D, | |
i: (d: D) => E, | |
j: (e: E) => F, | |
k: (f: F) => G, | |
l: (g: G) => H | |
): H; | |
function pipe<A, B, C, D, E, F, G, H, I>( | |
a: A, | |
f: (a: A) => B, | |
g: (b: B) => C, | |
h: (c: C) => D, | |
i: (d: D) => E, | |
j: (e: E) => F, | |
k: (f: F) => G, | |
l: (g: G) => H, | |
m: (h: H) => I | |
): I; | |
function pipe(a: any, ...fns: any[]): any { | |
return fns.reduce((a, f) => f(a), a); | |
} | |
type Stream<T> = null | { | |
head(): T; | |
tail(): Stream<T>; | |
}; | |
const countUp = (init: number, step = 1): Stream<number> => { | |
return { | |
head: () => init, | |
tail: () => countUp(init + step), | |
}; | |
}; | |
const filter = (predicate: (value: number) => boolean) => { | |
return (stream: Stream<number>): Stream<number> => { | |
if (stream === null) { | |
return null; | |
} | |
const head = stream.head(); | |
if (predicate(head)) { | |
return { | |
head: () => head, | |
tail: () => pipe(stream.tail(), filter(predicate)), | |
}; | |
} | |
return pipe(stream.tail(), filter(predicate)); | |
}; | |
}; | |
const takeUntil = <T>(predicate: (value: T) => boolean) => { | |
return (stream: Stream<T>): Stream<T> => { | |
if (stream === null) { | |
return null; | |
} | |
const head = stream.head(); | |
if (predicate(head)) { | |
return null; | |
} | |
return { | |
head: () => head, | |
tail: () => pipe(stream.tail(), takeUntil(predicate)), | |
}; | |
}; | |
}; | |
const map = <T, U>(mapper: (value: T) => U) => { | |
return (stream: Stream<T>): Stream<U> => { | |
if (stream === null) { | |
return null; | |
} | |
return { | |
head: () => mapper(stream.head()), | |
tail: () => pipe(stream.tail(), map(mapper)), | |
}; | |
}; | |
}; | |
const take = <T>(total: number) => { | |
const consume = (stream: Stream<T>, count = 0): Stream<T> => { | |
if (stream === null) { | |
return null; | |
} | |
if (count >= total) { | |
return null; | |
} | |
return { | |
head: () => stream.head(), | |
tail: () => consume(stream.tail(), count + 1), | |
}; | |
}; | |
return (stream: Stream<T>) => consume(stream, 0); | |
}; | |
const reverse = <T>(source: Stream<T>, target: Stream<T> = null): Stream<T> => { | |
if (source === null) { | |
return target; | |
} | |
if (target === null) { | |
return reverse(source.tail(), { | |
head: () => source.head(), | |
tail: () => null, | |
}); | |
} | |
return reverse(source.tail(), { | |
head: () => source.head(), | |
tail: () => target, | |
}); | |
}; | |
const range = (start: number, end: number, step = 1): Stream<number> => { | |
return pipe( | |
countUp(start, step), | |
takeUntil((value) => value > end) | |
); | |
}; | |
const forEach = <T>(stream: Stream<T>, callback: (value: T) => void): void => { | |
if (stream === null) { | |
return; | |
} | |
callback(stream.head()); | |
forEach(stream.tail(), callback); | |
}; | |
const sift = (stream: Stream<number>): Stream<number> => { | |
if (stream === null) { | |
return null; | |
} | |
const head = stream.head(); | |
return { | |
head: () => head, | |
tail: () => pipe(stream.tail(), filter((n) => n % head !== 0), sift), | |
}; | |
}; | |
let numbers = countUp(2); | |
const primes = pipe(numbers, sift); | |
const test = pipe( | |
primes, | |
take(20), | |
) | |
console.log(`primes:`); | |
forEach(test, (value) => console.log(value)); | |
numbers = pipe( | |
range(0, 10), | |
map((value) => value * 2), | |
reverse | |
); | |
console.log(`reverse:`); | |
forEach(numbers, console.log); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment