Effect TS
モナドのdo記法とRustの Option, Result と Effect TS を比較する。
Maybe と Either
- 無効値をとり得る値:
Maybe a,Option<T> - 失敗するかもしれない値:
Either a b,Result<T, E>
safeDiv :: Int -> Int -> Maybe Int
safeDiv m 0 = Nothing
safeDiv m n = Just (m `div` n)
tryDiv :: Int -> Int -> Either () Int
tryDiv m 0 = Left ()
tryDiv m n = Right (m `div` n)fn safe_div(m: i64, n: i64) -> Option<i64> {
match n {
0 => None,
_ => Some(m / n),
}
}
fn try_div(m: i64, n: i64) -> Result<i64, ()> {
match n {
0 => Err(()),
_ => Ok(m / n),
}
}Monadとdo記法
以下のように書き直してネストを減らすことができる。
divs1: ネストしている処理divs2:bind(>>=)1 ・{Option,Result}::and_then()を用いるdivs3: do記法・?を用いる
divs1 :: Maybe Int
divs1 = case safeDiv 42 2 of
Nothing -> Nothing
Just a -> case safeDiv a 0
divs2 :: Maybe Int
divs2 =
safeDiv 42 2 >>= (\a ->
safeDiv a 0
)
divs3 :: Maybe Int
divs3 = do
a <- safeDiv 42 2
safeDiv a 0fn divs1() -> Option<i64> {
match safe_div(42, 2) {
None => None,
Some(a) => safe_div(a, 0),
}
}
fn divs2() -> Option<i64> {
safe_div(42, 2)
.and_then(|a| safe_div(a, 0))
}
fn divs3() -> Option<i64> {
let a = safe_div(42, 2)?;
safe_div(a, 0)
}Monad Transformer
MaybeとEitherをそのまま混ぜることはできない- → Monad Transformerでモナドを他のモナドに持ち上げて解決するらしい(ミリしら)
- Rustにはモナドはない2 が、
OptionとResultは相互に変換できる
fn divs3() -> Option<i64> {
let a = safe_div(42, 2)?;
try_div(a, 0).ok() // Result -> Option
}
fn divs4() -> Result<i64, ()> {
let a = try_div(42, 2)?;
safe_div(a, 0).ok_or(()) // Option -> Result
}effect systemとは(雑)
- Monadの拡張
- 実行すると値が得られる
実装
- OCaml 5.0
- Haskellのextensible-effects
- PureScriptの Run
- Freeモナド で実装されるらしい
- Scalaの Cats Effect, atonos-eff, ZIO, Kyo → Algebraic Effect
- Koka
- Effect TS
Effect TS
- fp-tsの後継
- Effect Monad を中心としたライブラリ
- ScalaのZIOの移植らしい
Effect<number, Error, never>number: 成功時の型Error: 失敗時の型never: 制約
import { Effect, Either, Option } from "npm:effect";
const safeDiv = (m: number, n: number) =>
n === 0 ? Option.none() : Option.some(m / n);
const tryDiv = (m: number, n: number) =>
n === 0 ? Either.left("Division by zero") : Either.right(m / n);
// program: Effect.Effect<number, string | NoSuchElementException, never>
const program = Effect.gen(function* () {
const a = yield* safeDiv(42, 2);
const b = yield* tryDiv(a, 0);
return b;
});
// { _id: "Exit", _tag: "Failure", cause: { _id: "Cause", _tag: "Fail", failure: "Division by zero" } }
const res = Effect.runSyncExit(program);TODO
- 代数的エフェクト(algebraic effect)の実装(Extensible Effectsとか)
- Effect TS と effect-system がある言語の比較
参考文献
- Step by Stepで学ぶ、ADT(代数的データ型)、モナドからEffect-TSまで
- Maybe と IO を一緒に使いたくなったら
- はじめての Monad, Monad Transformer, Extensible Effects
Footnotes
-
EitherやMaybeはMonadのインスタンスなので(>>=) :: Monad m => m a -> (a -> m b) -> m bが使える ↩ -
Rustにeffect-systemを入れるとどうなるかみたいな記事 Extending Rust’s Effect System がある ↩