Last active
December 3, 2022 04:25
-
-
Save Gavinok/180ef95840b1eb6dce4cb70088aa84a2 to your computer and use it in GitHub Desktop.
failed attempt at monad examples in 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 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