Skip to content

Instantly share code, notes, and snippets.

@zoedsoupe
Created October 29, 2024 16:49
Show Gist options
  • Save zoedsoupe/f5cb4f67b8b4d1e81fa1768d7dc56063 to your computer and use it in GitHub Desktop.
Save zoedsoupe/f5cb4f67b8b4d1e81fa1768d7dc56063 to your computer and use it in GitHub Desktop.
Implementação de mônadas úteis em TypeScript
// Definição da Either Monad
type Either<L, R> = Left<L> | Right<R>;
type Left<L> = { kind: 'left'; value: L };
type Right<R> = { kind: 'right'; value: R };
function left<L, R>(value: L): Either<L, R> {
return { kind: 'left', value };
}
function right<L, R>(value: R): Either<L, R> {
return { kind: 'right', value };
}
// Função bind para encadear computações via Either
function bind<L, R, R2>(
either: Either<L, R>,
func: (value: R) => Either<L, R2>
): Either<L, R2> {
return either.kind === 'right' ? func(either.value) : either;
}
// Exemplo de uso da Either
function parseNumber(str: string): Either<string, number> {
const num = Number(str);
return isNaN(num) ? left('Not a number') : right(num);
}
function reciprocal(num: number): Either<string, number> {
return num === 0 ? left('Division by zero') : right(1 / num);
}
const result = bind(parseNumber('5'), reciprocal);
// Resultado: right(0.2)
// Definição da State Monad
type State<S, A> = (state: S) => [A, S];
function stateOf<S, A>(value: A): State<S, A> {
return (state) => [value, state];
}
// Encadear computações via State
function bind<S, A, B>(
stateFunc: State<S, A>,
func: (value: A) => State<S, B>
): State<S, B> {
return (state) => {
const [a, newState] = stateFunc(state);
return func(a)(newState);
};
}
// Exemplo de uso da State! bem parecida com React `useState`, quem diria né?
function increment(): State<number, void> {
return (state) => [undefined, state + 1];
}
function getState<S>(): State<S, S> {
return (state) => [state, state];
}
const program = bind(increment(), () =>
bind(increment(), () => getState<number>())
);
const [result, finalState] = program(0);
// finalState: 2
// Definição da Writer Monad
type Writer<W, A> = { value: A; log: W[] };
function writerOf<W, A>(value: A): Writer<W, A> {
return { value, log: [] };
}
// Encadear operaçõe via Writer
function bind<W, A, B>(
writer: Writer<W, A>,
func: (value: A) => Writer<W, B>
): Writer<W, B> {
const result = func(writer.value);
return { value: result.value, log: writer.log.concat(result.log) };
}
function tell<W>(message: W): Writer<W, void> {
return { value: undefined, log: [message] };
}
function compute(x: number): Writer<string, number> {
return bind(tell(`Starting with ${x}`), () => {
const result = x * 2;
return bind(tell(`Doubled to ${result}`), () => writerOf(result));
});
}
const result = compute(5);
// result.value: 10
// result.log: ['Starting with 5', 'Doubled to 10']
// Definição da Option Monad
type Option<A> = Some<A> | None;
type Some<A> = { kind: 'some'; value: A };
type None = { kind: 'none' };
function some<A>(value: A): Option<A> {
return { kind: 'some', value };
}
const none: Option<never> = { kind: 'none' };
// Encadear computações via Option
function bind<A, B>(
option: Option<A>,
func: (value: A) => Option<B>
): Option<B> {
return option.kind === 'some' ? func(option.value) : none;
}
// Exemplos de uso
function safeDivide(a: number, b: number): Option<number> {
return b === 0 ? none : some(a / b);
}
const result = bind(some(10), (x) => safeDivide(x, 2));
// result: some(5)
// Promise já é nativo do JS/TS, então a operação de bind é meio redundante
// As Promises já são uma mônada nativa em JavaScript/TypeScript
function bind<A, B>(
promise: Promise<A>,
func: (value: A) => Promise<B>
): Promise<B> {
return promise.then(func);
}
// Exemplo de uso
function fetchData(url: string): Promise<string> {
return fetch(url).then((res) => res.text());
}
const result = bind(fetchData('https://example.com'), (data) => {
console.log(data);
return Promise.resolve('Done');
});
// Imprime o conteúdo e retorna 'Done'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment