純粋関数型言語

すべての関数が純粋(副作用なし)であることを型システムで強制する関数型言語。

プログラマ向けの一言

副作用を書くのに型の許可が要る言語。 console.log すら自由に呼べない。その代わり、関数のシグネチャを見るだけで「この関数は何をするか」が完全にわかる。

純粋 vs 非純粋

// --- 非純粋な関数型(OCaml, Scala, JS/TS)---
// 好きなところで副作用を起こせる
function greet(name: string): string {
  console.log("Side effect!");   // 自由に書ける
  return `Hello, ${name}`;
}

// --- 純粋関数型(Haskell)の考え方を TS で表現 ---
// 副作用を起こすには「IO という箱」に入れないといけない
type IO<A> = { run: () => A }; // 実行するまで副作用は起きない

function pureGreet(name: string): string {
  // ここでは console.log を呼べない(型が許さない)
  return `Hello, ${name}`;
}

function greetWithLog(name: string): IO<string> {
  return {
    run: () => {
      console.log("Side effect!");  // IO の中でだけ許される
      return `Hello, ${name}`;
    }
  };
}
// greetWithLog("world")      ← この時点では何も起きない
// greetWithLog("world").run() ← 実行して初めて副作用が発生

参照透過性

// 純粋関数型の核心: 参照透過性 (referential transparency)
// 「式をその結果で置き換えても、プログラムの意味が変わらない」

const x = double(3); // 6
// double(3) を 6 に置き換えてもプログラムの意味は同じ → 参照透過

let counter = 0;
const increment = () => ++counter;
const y = increment(); // 1
// increment() を 1 に置き換えると counter が増えない → 参照透過でない

代表: Haskell

-- Haskell では型が副作用を明示する
double :: Int -> Int           -- 純粋。副作用なし。
double x = x * 2

greet :: String -> IO String   -- IO がついている = 副作用あり
greet name = do
  putStrLn "Side effect!"
  return ("Hello, " ++ name)

押さえどころ(カード化候補)

関連

Graph