TC39JavaScript新語法Pattern Matching函數式編程

TC39 新語法深度解析(八):Pattern Matching — match 表達式讓條件判斷更有表達力

整理 TC39 Pattern Matching(Stage 2)的提案語法:match 表達式、guard、陣列/物件模式。與 TypeScript discriminated union 的比較,以及 Stage 2 的当前狀態。

· 3 分鐘閱讀

這是 TC39 新語法深度解析系列的第八篇。Pattern Matching(模式匹配)是一個在函數式編程語言(Haskell、Scala、Rust、Elixir)中常見的概念,現在即將進入 JavaScript。⚠️ 注意:本文內容基於 Stage 2 提案,可能會有變更。


⚠️ 當前狀態

Pattern Matching 目前是 Stage 2,這意味著:

  • 語法可能會有變更
  • 還沒有瀏覽器原生支援
  • 需要 Babel 外掛才能試用
# 用 Babel 試用
npm install --save-dev @babel/plugin-proposal-pattern-matching

Pattern Matching 解決什麼問題?

// 過去:複雜的條件判斷
function processResponse(data) {
  if (data.type === 'success') {
    if (data.value) {
      return renderSuccess(data.value);
    } else {
      return renderEmpty();
    }
  } else if (data.type === 'error') {
    return renderError(data.message);
  } else if (data.type === 'loading') {
    return renderLoading();
  } else {
    return renderUnknown(data);
  }
}

Pattern Matching 把這個變成:

const result = match (data) {
  when ({ type: 'success', value: v }) -> renderSuccess(v)
  when ({ type: 'success' }) -> renderEmpty()
  when ({ type: 'error', message: m }) -> renderError(m)
  when ({ type: 'loading' }) -> renderLoading()
  else -> renderUnknown(data)
};

基本語法

const shape = { kind: 'circle', radius: 10 };

const area = match (shape) {
  when ({ kind: 'circle', radius: r }) -> Math.PI * r * r
  when ({ kind: 'rectangle', width: w, height: h }) -> w * h
  when ({ kind: 'triangle', base: b, height: h }) -> b * h / 2
  else -> 0
};

注意:match 是一個表達式,不是語句——它可以返回值,可以直接在賦值右側使用。


Guard:加了條件的模式

const score = { type: 'exam', value: 85, grade: 'B' };

const description = match (score) {
  when ({ type: 'exam', value: v }) v >= 90 -> 'A grade'
  when ({ type: 'exam', value: v }) v >= 80 -> 'B grade'
  when ({ type: 'exam', value: v }) -> 'Below B'
  else -> 'Not an exam'
};

when 後面的第二個表達式是 guard——必須為 true 才匹配。


與 TypeScript discriminated union 的比較

// TypeScript discriminated union
type Result =
  | { type: 'success'; data: User }
  | { type: 'error'; message: string }
  | { type: 'loading' };

function process(r: Result): string {
  switch (r.type) {  // switch 仍然是語句,不能在表達式位置用
    case 'success': return r.data.name;
    case 'error': return r.message;
    case 'loading': return '...';
  }
}

Pattern Matching 可以在表達式位置使用,而且能直接解構:

// Pattern Matching(提案語法)
function process(r) {
  return match (r) {
    when ({ type: 'success', data: { name } }) -> `Hello, ${name}!`
    when ({ type: 'error', message }) -> `Error: ${message}`
    when ({ type: 'loading' }) -> 'Loading...'
  };
}

實際應用:FSM 轉換

// 有限狀態機:燈的狀態轉換
function transition(state, event) {
  return match ([state, event]) {
    when (['off', 'turn_on']) -> 'on'
    when (['on', 'turn_off']) -> 'off'
    when (['on', 'dim']) -> 'dim'
    when (['dim', 'brighten']) -> 'on'
    else -> state  // 無效轉換,保持當前狀態
  };
}

限制與批評

提案目前有一些限制:

  • 沒有 exhaustive matching 檢查(無法像 Rust 一樣確保所有情況都被處理)
  • 只能匹配特定模式,不支援任意 boolean 表達式
  • else 是必需要的(目前提案沒有 exhaustive 檢查)

總結

Pattern Matching 是一個** Stage 2 尚未確定的提案**。如果它在未來達到 Stage 4,它會成為 JavaScript 函數式編程工具箱中的重要武器。目前:

// 試用(需要 Babel)
npm install @babel/plugin-proposal-pattern-matching

這是 TC39 新語法深度解析系列的第八篇。下一篇:Pipeline Operator — 用 |> 寫出更易讀的資料轉換鏈。


延伸閱讀


本文基於 TC39 Pattern Matching 提案(Stage 2)整理,語法可能會有變更。