Skip to content

Instantly share code, notes, and snippets.

@Gavinok
Last active December 3, 2022 04:25
Show Gist options
  • Save Gavinok/180ef95840b1eb6dce4cb70088aa84a2 to your computer and use it in GitHub Desktop.
Save Gavinok/180ef95840b1eb6dce4cb70088aa84a2 to your computer and use it in GitHub Desktop.
failed attempt at monad examples in typescript
interface Monad<Type> {
m: Type;
}
interface MonadInterface<Type1, Type2> {
bind: (a: Monad<Type1>, z: (a: Type1) => Monad<Type2>) => Monad<Type2>;
sequence: (a: Monad<Type1>, b: Monad<Type2>) => Monad<Type2>;
pure: (a: Type1) => Monad<Type1>;
}
// Since I can't actually implement this interface lets just use it as a template
type Maybe<Type> = Just<Type> | Nothing;
class Just<T> {
readonly _tag = "Just";
constructor(readonly value: T) {}
}
class Nothing {
readonly _tag = "Nothing";
constructor() {}
}
// Now lets write the functions though
const bind = <Type1, Type2>(
a: Maybe<Type1>,
z: (a: Type1) => Maybe<Type2>
): Maybe<Type2> => (a._tag == "Nothing" ? a : z(a.value));
const sequence = <Type1, Type2>(
a: Maybe<Type1>,
b: Maybe<Type2>
): Maybe<Type2> => bind(a, (_) => b);
const pure = <Type1>(a: Type1): Maybe<Type1> => new Just(a);
// Example
const safeDivide = (numerator: number, denominator: number) =>
denominator === 0 ? new Nothing() : new Just(numerator / denominator);
const inc = (a: Maybe<number>) => bind(a, (preinc: number) => pure(preinc + 1));
// Here we will divide and increment the number
const d = safeDivide(1, 2);
const incremented: Maybe<number> = inc(d);
console.log("incremented is ", incremented);
// We can substitute the internals of the maybe
const replaced = sequence(incremented, new Just(2));
console.log("replaced is ", replaced);
// Now instead of throwing an exception on the case of 0 we will return Nothing
const badDiv: Maybe<number> = safeDivide(1, 0);
const notIncremented: Maybe<number> = inc(badDiv);
console.log("not incremented is", notIncremented);
// Now since sequnecing is a way to substitute each element in a monad
// with the contents of another monad this may not work as you would
// expect
// in this case we still return a Nothing
const notReplaced = sequence(inc(badDiv), new Just(2));
console.log("not replaced is", notIncremented);
class IO<T> {
readonly _tag = "IO";
constructor(readonly value: T) {}
}
// Now lets write the functions though
const bindIO = <Type1, Type2>(
a: IO<Type1>,
z: (a: Type1) => IO<Type2>
): IO<Type2> => z(a.value);
class Unit {}
const sequenceIO = <Type1, Type2>(a: IO<Type1>, b: IO<Type2>): IO<Type2> =>
bindIO(a, (_) => b);
const pureIO = <Type1>(a: Type1): IO<Type1> => new IO(a);
const putStrLn = (s: string): IO<Unit> => {
// note that this is the only thing that is actually requiring procedural code
console.log(s);
return new IO(Unit);
};
const getLine = (): IO<string> => new IO("Result of reading stdin"); // imagine I actually did the IO
const main = (): IO<Unit> =>
sequenceIO(
bindIO(getLine(), (x) => putStrLn("This was the " + x)),
putStrLn("End of Input")
);
main();
console.log(bind(new Nothing(), (a) => pure(1)));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment