3 ポイント 投稿者 GN⁺ 2025-11-01 | 1件のコメント | WhatsAppで共有
  • John Carmack可変変数(mutable variable) の使用に関する個人的な見解を共有
  • Pythonを使ううちに「単一代入(single assignment)」の原則をおろそかにしがちになったとして、自分自身で警戒する必要があると言及
  • ループ内の反復計算を除いては、変数の再代入や更新を避けるべきだと強調
  • 中間計算の段階がすべて残っていれば デバッグ時に役立ち、コードブロックを移動する際に 意図せず以前の値が使われる問題を防げる
  • C/C++では、ほぼすべての変数を初期化時点でconstとして宣言するのが良い習慣だと説明
  • 最後に「mutableがキーワードであってほしい」と述べ、不変性がデフォルトになることを望むと強調

1件のコメント

 
GN⁺ 2025-11-01
Hacker Newsの意見
  • Clojureを2年間使った結果、不変性がもたらす明快さを他の開発者に説明するのは本当に難しいと感じた
    状態変化によって効果を生み出す考え方に慣れた人は、実際に体験してみるまで理解しにくい

    • 変数の変更は暗黙の順序依存性を生み出す
      たとえば x = 7; x = x + 3; x = x / 2 のように書くと、順序を入れ替えてもエラーにはならないが結果は変わる
      一方で x1x2 のように新しい変数を使えば、順序が誤っている場合にエラーが発生して問題が明確になる
      結局、単一代入(single assignment)はこうした依存関係を明示的に表現する方法だ
    • 私もSchemeで似た経験をした
      関数型言語を使ったことのない同僚に、関数中心の考え方がどれほどテストしやすく、すっきりしているかを説明しても、なかなか伝わらなかった
      Pythonは関数型スタイルを読みやすく書きにくく、JSのほうがむしろ良いと感じる
      結局、好奇心の強い開発者だけがClojureのような言語を試すことになる
    • 不変データと純粋関数を基本にすると、関数は完全にブラックボックスとして扱える
      関数は外部状態を知らなくてよく、外部も関数内部を知らなくてよい
      プログラム全体の状態を知らなくても、特定の関数だけを独立してテストしたりデバッグしたりできる
    • 不変性と可変性を単純に対立させるのは複雑さから目を背けることだ
      Haskellコミュニティが最終的には型システムの中で可変性を再発明しようとしているのを見ると興味深い
      核心は副作用を最小限のコストで制御することだ
    • Clojureは私が学んだ言語の中で最も影響力の大きい言語だった
      Haskellは型システムのせいで参入障壁が高く、F#はあまりに妥協的で、結局C#構文でコーディングすることになる
      Clojureのhomoiconicityと強力なデータ構造のおかげで、「値として働く」という概念が初めてはっきり腑に落ちた
      仕事で使うことはないだろうが、関数型言語やLispの経験がない人にはぜひ勧めたい
  • 変数は基本的に不変で、すべてが式(expression)であってほしい
    だが現実には、Clojure開発者として
    Pythonの侵攻
    に苦しめられている

    • 私もPython開発者だが、個人プロジェクトでしかClojureを使っていない
      今はTypeScriptの侵攻に苦しんでいるので共感できる
    • Rustを学んで、言語が純粋関数型でなくても、すべてを式にできるのだと気づいた
      このやり方は変更のスコープを制限するのに本当に役立つ
    • ClojureはいつでもPythonより速い、それは慰めになる
    • あなたは単にClojureを使っている人であって、「Clojureプログラマー」と自分を規定する必要はない
      言語間の部族戦争に巻き込まれる必要はない
      生産性向上の時代には境界は無意味だ
      Don’t Call Yourself a Programmer という記事を勧める
  • 変数の再代入は最小限にしようとしているが、しばしば変数シャドーイングを使う
    result = result.process() のようなパターンは簡潔なので好んでいる

    • 抽象的な例だからそう見えるだけかもしれないが、多くの場合は各段階に明確な名前を付けられる
    • こうしたパターンはセキュリティバグを引き起こすことがある
      たとえば process()検証関数なら、どの時点で処理された値なのか不明確になりうる
      したがって名前によって状態を明確に区別したほうがよい
    • 関数型スタイルでは、こうした中間変数なしに関数チェイニングで解決できる
      例: result = x |> foo |> bar |> baz または (-> x foo bar baz)
    • result.process()って、いったいどのresultで、どのprocessなんだ?」
      後でコードを読む人は混乱する
    • すでに結果(result)なのに、もう一度処理(process)するというのは論理的に不自然
  • 「変数(variable)」という用語そのものがいつも引っかかる
    不変なのに、なぜ variable と呼ぶのか?

    • 変数は実行中には変わらなくても、呼び出しごとに値が異なりうる
      Rustでは mut を明示して初めて変更可能になる
      一方Cでは定数を作るのにプリプロセッサを使う必要があり、混乱を招く
    • 関数の引数 x は呼び出しごとに異なる値を受け取るので、それ自体が変わる値
      再代入がなくても変数と呼べる
    • 数学でも変数は特定のオブジェクトではなく、任意の値を指す記号だ
      プログラミングはこの概念をそのまま持ち込んでいる
    • 結局、変数は実行ごとに値が異なりうるから variable なのだ
      定数(constant)はすべての実行で同じ値を持つ
      Variable (mathematics) を参照
    • 「変数」は時間とともに変わるというより、文脈によって異なるという意味で使われている
  • IDEが変数の変更有無を視覚的に表示してくれるといい
    たとえば、変更された変数を少しだけ目立たせるような形で

    • IntelliJでは再代入された変数に下線が付き、マウスを載せると “Reassigned local variable” というヒントが表示される
      できるだけ final を多く使うと、コードは読みやすく保守しやすい
    • ただ、自動推論よりも明示的な opt-inのほうがよいと思う
      IDEが警告を出し、本当に必要な場合だけ変更を許可するのがよい
      Rich Hickeyのset vs listの話のように、意味を明確に表現する構造を選ぶべきだ
    • Swiftではコンパイラが変数の変更有無を検出し、不要な変更なら定数に変えるよう提案してくれる
    • JetBrainsのIDEには変数の読み取り/書き込み位置を探す機能がすでにある
    • こういう機能の**リンター(linter)**を作るなら、名前は “mutalator” が合いそうだ
  • 以前、スレッドセーフ性のために不変性を厳格に適用したプロジェクトに関わったことがある
    そのおかげでコードは読みやすくなり、何が変わりうるのか追跡しやすくなった
    それ以来、不変性の熱烈なファンになった

    • それならぜひRustを使ってみることを勧めたい
  • 大規模なHaskellコードベースで働いた後にCへ戻ると、不変性がデフォルトであってほしいと思う
    const では足りない

    • Cでは実際には直接的な変更ではなく、値の再代入しかできない
      変更するにはポインタを使う必要があり、C++では関数呼び出しだけでも引数を変えられるので不透明だ
    • Rustのデフォルトが十分に安全な不変性を提供しているのか気になる
  • 「ほとんどすべての変数を const で宣言するのがよい」という意見に共感する
    Rustに言及すべきだろう

  • 不変がデフォルトで、特定のブロック内でだけmutableを許可する構文を想像してみる
    たとえばPythonの with ブロックのように

    with mutable(x, items):
        x = 3
        items.append(4)
    

    ブロックを抜けると再び不変に戻るイメージだ

    • これは実質的にmutable borrowの概念だ
      Rustのborrow checkerを見ると、この概念がどれほど複雑かわかる
    • borrowチェックがなければ、ブロックの外でも参照が残って変更されうる
  • State is the enemy」という言葉がある
    状態が増えるほど、テストすべき条件は指数関数的に増えていく
    不変性はこうした状態爆発を防ぐ方法だ