You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中Maybe Monad的chain方法正确实现方式是什么?

Correct chain Implementation for Maybe Monad in TypeScript (Aligned with Fantasy-Land)

Great to hear you're building a Monad library in TypeScript for fun—fantasy-land's standards are such a solid foundation to work with! Let's walk through the proper chain implementation for the Maybe Monad, ensuring it adheres to all three monad laws you referenced.

First, let's recap the Maybe Monad's core structure: it has two cases—Just<T> (wraps a valid value of type T) and Nothing (represents an absent/invalid value). The chain method (fantasy-land's equivalent of bind) needs to handle both cases correctly.

Step 1: Define the Maybe Type & Core Classes

Start with strongly typed classes for Just and Nothing, then unify them under a Maybe<T> union type:

type Maybe<T> = Just<T> | Nothing;

class Just<T> {
  readonly value: T;

  constructor(value: T) {
    this.value = value;
  }

  // Fantasy-land compliant chain method
  chain<U>(f: (value: T) => Maybe<U>): Maybe<U> {
    // For Just, we unwrap the value and pass it to the provided function f
    // This directly satisfies the left identity and associative laws
    return f(this.value);
  }

  // Fantasy-land of method (required for monad compliance)
  static of<T>(value: T): Maybe<T> {
    return new Just(value);
  }

  // Optional: Add fantasy-land symbol for full spec compliance
  [Symbol.for('fantasy-land/chain')]<U>(f: (value: T) => Maybe<U>): Maybe<U> {
    return this.chain(f);
  }
}

class Nothing {
  // For Nothing, chain ignores the function and returns itself
  // This preserves the "absent value" semantics through chained operations
  chain<U>(_f: (value: never) => Maybe<U>): Maybe<U> {
    return this;
  }

  // Optional: Add fantasy-land symbol
  [Symbol.for('fantasy-land/chain')]<U>(_f: (value: never) => Maybe<U>): Maybe<U> {
    return this;
  }
}

Step 2: Verify Compliance with Monad Laws

Let's confirm this implementation checks all boxes for the three laws:

1. Left Identity Law: Maybe.of(a).chain(f) ≡ f(a)

  • When Maybe.of(a) returns a Just(a), calling chain(f) directly invokes f(a)—exactly matching the right-hand side.
  • Since Maybe.of never returns Nothing, we don't have edge cases here.

2. Right Identity Law: m.chain(Maybe.of) ≡ m

  • If m is Just(a): chain(Maybe.of) calls Maybe.of(a), which returns Just(a) (the original m).
  • If m is Nothing: chain(Maybe.of) returns Nothing (the original m).

3. Associative Law: m.chain(f).chain(g) ≡ m.chain(x => f(x).chain(g))

  • If m is Just(a):
    • Left side: f(a).chain(g)
    • Right side: (x => f(x).chain(g))(a) → f(a).chain(g)
    • Both sides are identical.
  • If m is Nothing:
    • Left side: Nothing.chain(f).chain(g) → Nothing.chain(g) → Nothing
    • Right side: Nothing.chain(x => ...) → Nothing
    • Both sides are identical.

Key Notes for Type Safety

  • TypeScript's type system ensures the function passed to chain must return a Maybe<U>—this prevents invalid operations that would break monad compliance.
  • The Nothing class uses never for the function's parameter type, which correctly signals that the function will never be invoked (since Nothing has no value to pass).

This implementation stays true to the Maybe Monad's purpose (handling optional values) while fully adhering to fantasy-land's monad specifications.

内容的提问来源于stack exchange,提问作者Davide Melfi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:42:58