Last active
January 26, 2025 06:23
-
-
Save Lucifier129/d05800b93f20a8279e71acf0ca6962f6 to your computer and use it in GitHub Desktop.
some codata examples in javascript/typescript
This file contains hidden or 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
interface BtreeVisitor<T, TT> { | |
leaf: (value: T) => TT; | |
branch: (left: TT, right: TT) => TT; | |
} | |
interface Btree<T> { | |
<TT>(visitor: BtreeVisitor<T, TT>): TT; | |
} | |
const leaf = <T>(value: T): Btree<T> => { | |
return (visitor) => visitor.leaf(value); | |
}; | |
const branch = <T>(left: Btree<T>, right: Btree<T>): Btree<T> => { | |
return (visitor) => { | |
return visitor.branch(left(visitor), right(visitor)); | |
}; | |
}; | |
// reverse binary tree | |
const reverse = <T>(btree: Btree<T>): Btree<T> => { | |
return btree({ | |
leaf: (value) => leaf(value), | |
branch: (left, right) => branch(right, left), | |
}); | |
}; | |
type Data_BTree<T> = | |
| { | |
type: 'leaf'; | |
value: T; | |
} | |
| { | |
type: 'branch'; | |
left: Data_BTree<T>; | |
right: Data_BTree<T>; | |
}; | |
// data constructors | |
const dataBtreeConstructors = { | |
leaf: <T>(value: T): Data_BTree<T> => ({ | |
type: 'leaf', | |
value, | |
}), | |
branch: <T>(left: Data_BTree<T>, right: Data_BTree<T>): Data_BTree<T> => ({ | |
type: 'branch', | |
left, | |
right, | |
}), | |
}; | |
const toDataBTree = <T>(btree: Btree<T>): Data_BTree<T> => { | |
// codata to data is easy | |
return btree({ | |
leaf: dataBtreeConstructors.leaf, | |
branch: dataBtreeConstructors.branch, | |
}); | |
}; | |
const toSourceCode = <T>(btree: Btree<T>): string => { | |
return btree({ | |
leaf: (value) => `leaf(${value})`, | |
branch: (left, right) => `branch(${left}, ${right})`, | |
}); | |
}; | |
const btree = branch( | |
branch(leaf(0), branch(leaf(1), leaf(2))), | |
branch(leaf(3), leaf(4)) | |
); | |
const btreeData = toDataBTree(btree); | |
const btreeCode = toSourceCode(btree); | |
const reversedBtree = reverse(btree); | |
const reversedBtreeData = toDataBTree(reversedBtree); | |
const reversedBtreeCode = toSourceCode(reversedBtree); | |
console.log('btreeData', JSON.stringify(btreeData, null, 2)); | |
console.log('btreeCode', btreeCode); | |
console.log('reversedBtreeData', JSON.stringify(reversedBtreeData, null, 2)); | |
console.log('reversedBtreeCode', reversedBtreeCode); |
pullable & pushable streaming
interface UnaryFunction<T, TT> {
(arg: T): TT;
}
function pipe<T, A>(a: T, f1: UnaryFunction<T, A>): A;
function pipe<T, A, B>(
a: T,
f1: UnaryFunction<T, A>,
f2: UnaryFunction<A, B>
): B;
function pipe<T, A, B, C>(
a: T,
f1: UnaryFunction<T, A>,
f2: UnaryFunction<A, B>,
f3: UnaryFunction<B, C>
): C;
function pipe<T, A, B, C, D>(
a: T,
f1: UnaryFunction<T, A>,
f2: UnaryFunction<A, B>,
f3: UnaryFunction<B, C>,
f4: UnaryFunction<C, D>
): D;
function pipe<T, A, B, C, D, E>(
a: T,
f1: UnaryFunction<T, A>,
f2: UnaryFunction<A, B>,
f3: UnaryFunction<B, C>,
f4: UnaryFunction<C, D>,
f5: UnaryFunction<D, E>
): E;
function pipe(a: any, ...args: UnaryFunction<any, any>[]) {
return args.reduce((a, f) => f(a), a);
}
interface Subscriber<A> {
start: () => void;
next: (value: A) => any;
complete: () => void;
}
interface ProducerHandler {
start: () => void;
next: () => void;
complete: () => void;
}
interface Producer<A> {
(subscriber: Subscriber<A>): ProducerHandler;
}
const range = (from: number, to: number): Producer<number> => (subscriber) => {
let start = () => {
subscriber.start();
};
let next = () => {
if (from <= to) {
subscriber.next(from++);
} else {
complete();
}
};
let complete = () => {
subscriber.complete();
};
return { start, next, complete };
};
const interval = (period: number): Producer<number> => (subscriber) => {
let tid: any;
let i = 0;
let start = () => {
subscriber.start();
tid = setInterval(() => subscriber.next(i++), period);
};
let next = () => {};
let complete = () => {
clearInterval(tid);
subscriber.complete();
};
return { start, next, complete };
};
const delay = (duration: number) => <T>(producer: Producer<T>): Producer<T> => {
return (subscriber) => {
let timer = interval(duration)({
start: () => {},
next: () => {
parent.next();
},
complete: () => {},
});
let parent = producer({
...subscriber,
});
let start = () => {
timer.start();
parent.start();
};
let next = () => {};
let complete = () => {
timer.complete();
parent.complete();
};
return { start, next, complete };
};
};
const toPullable = <T>(producer: Producer<T>): Producer<T> => {
return (subsciber) => {
let count = 0;
let valueList: T[] = [];
let handler = producer({
...subsciber,
next: (value) => {
if (count) {
count -= 1;
subsciber.next(value);
} else {
valueList.push(value);
}
},
});
let next = () => {
if (valueList.length) {
let value = valueList.shift();
subsciber.next(value);
} else {
count += 1;
}
};
return { ...handler, next };
};
};
const map = <A, B>(f: UnaryFunction<A, B>) => (
producer: Producer<A>
): Producer<B> => {
return (subscriber) =>
producer({
...subscriber,
next: (value) => subscriber.next(f(value)),
});
};
const filter = <A>(f: (value: A) => boolean) => (
producer: Producer<A>
): Producer<A> => {
return (subscriber) => {
let handler = producer({
...subscriber,
next: (value) => {
if (f(value)) {
subscriber.next(value);
} else {
handler.next();
}
},
});
return handler;
};
};
const take = (count: number) => <A>(producer: Producer<A>): Producer<A> => {
return (subscriber) => {
let handler = producer({
...subscriber,
next: (value) => {
if (count-- <= 0) {
handler.complete();
} else {
subscriber.next(value);
}
},
});
return handler;
};
};
const foreach = <A>(f: (value: A) => any) => (
producer: Producer<A>
): ProducerHandler => {
let handler = producer({
start: () => {
handler.next();
},
next: (value) => {
f(value);
handler.next();
},
complete: () => {},
});
return handler;
};
let handler0 = pipe(
range(10, Number.POSITIVE_INFINITY),
filter((n) => n % 2 === 0),
map((n) => `range count: ${n}`),
take(10),
foreach(console.log)
);
handler0.start();
let handler1 = pipe(
interval(10),
filter((n) => n % 2 === 0),
map((n) => `interval count: ${n}`),
take(10),
foreach(console.log)
);
handler1.start();
let handler2 = pipe(
range(10, Number.POSITIVE_INFINITY),
delay(1000),
map((n) => `delay count: ${n}`),
foreach(console.log)
);
handler2.start();
let handler3 = pipe(interval(10), toPullable, (source) =>
source({
start: () => {},
next: (n) => {
console.log(`pullable count: ${n}`);
},
complete: () => {},
})
);
handler3.start();
setTimeout(() => {
handler3.next();
handler3.next();
handler3.next();
}, 1000);
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
streaming via codata