IOモナド
外部の世界とのインタラクションを遅延束縛し、必要になるまで実際の副作用を発生させない code:ts
class IO<T> {
constructor(private effect: () => T) {}
// 実行する
run(): T {
return this.effect();
}
// モナドのバインド関数
bind<U>(f: (t: T) => IO<U>): IO<U> {
return new IO(() => f(this.run()).run());
}
// ヘルパー関数
static of<U>(value: U): IO<U> {
return new IO(() => value);
}
}
// 例: 副作用を持つ関数
const readFromConsole = new IO<string>(() => {
return prompt("Enter something:") || "";
});
const writeToConsole = (message: string) => new IO<void>(() => {
console.log(message);
});
// 使用例
const program = readFromConsole.bind(input => writeToConsole(You entered: ${input}));
// 実行
program.run();
code:ts
type IO<T> = ()=> T
type Of = <T>(value: T) => IO<T>
type MapIO = <T, U>(f: (t: T)=> U ) => (fa: IO<T>) => IO<U>
type Flatten = <T>(mm: IO<IO<T>>) => IO<T>
type FlatMap = <T, U>(f: (t: T)=> IO<U> ) => (fa: IO<T>) => IO<U>
const of: Of = (value) => ()=> value
const map: MapIO = (f) => (fa) => ()=> f(fa())
const flatten: Flatten = (mm) => mm()
const flatMap: FlatMap = (f) => (m) => flatten(map(f)(m))
// 実際に使ってみる
const saveStorage = (s: string): IO<void> => ()=> localStorage.setItem("key", s)
const loadStorage: IO<string> = ()=> localStorage.getItem("key") || "default"
const logger = (s: unknown):IO<void> => ()=> console.log(s)
const shout = (s: string) => ${s}!!
const repeat = (n: number) => (s: string) => Array.from({length: n}, ()=> s).join("")
/**
* 1. 値を保存するアクション
* 2. 値を取り出すアクション
* 3. アクションで取り出される値を変換 (shout)
* 4. アクションで取り出される値を変換 (repeat(5))
* 5. アクションで取り出される値を変換 (shout)
* 6. 値をロギングするアクション
*/
const io =flatMap(logger)(map(shout)(map(repeat(5))(map(shout)(flatMap(()=> loadStorage)(saveStorage("hi"))))))
// アクションの実行
io()
// output => hi!!hi!!hi!!hi!!hi!!!!