副作用を避ける
副作用の定義(ここが一番混乱した...)
副作用とは...
主作用(引数を受け取り値を返す)以外の、論理的状態(ローカル環境以外の状態変数の値)を変化させる作用
つまり、その関数のローカル変数以外の状態・値を変化させること
code: sample.py
# 副作用のある関数
A = 1
def add(a, b):
"""ブロック外の値を変更してることから、この関数は副作用があると言える"""
global A
result = A = a + b
return result
注意.icon ネットの定義が結構分かれてたが、自分はこの定義が一番しっくりきたonigiri.w2.icon
正しい定義は置いといて、この定義を持って今後のプログラミングを考えていこうと思う。
そして、今回棄却した副作用の別の意味を純粋関数の定義として扱っていく。
別の意味 = 同じ引数を与えたら、同じ結果が返ってくる。かつ、副作用が無い。
副作用は原則として避ける
副作用を避けるべき理由
1. 影響範囲が広がる(結合度が強くなる)ことで、変更しにくいコードになる
2. コードリーディング負荷の高いコードになってしまう
引数と返り値だけわかる状態であれば、コード内部を読みにいかなくても済むが...
副作用があるコードだと、内部までちゃんと読みにいかないといけなくなる
「1」「2」を加味すると、変更負荷が高くなってしまうので、原則として副作用のあるコードは避ける
とはいえ...、システム内から全ての副作用を排除できるわけではない
データベースに書き込みする
オブジェクトの中身を変更することもあるやろし(ここは値オブジェクトとかで避けれる可能性もあるけど)
そういった状態変化は付き纏うので、副作用を全排除することはできない
でも副作用は避けたい。じゃあどうするか。
以下あたりのルールを守って実装していくと良さげ
1. 原則、副作用無し関数を実装できるよう頑張る
2. それでも副作用が必要なら、副作用だけの処理を切り出してあげる
3. 切り出す際は、できる限り副作用無しでも実現できるものを別実装に変えることが大事
4. API処理、DB書き込みなどの横断的に副作用が起きそうな実装は、それぞれ一箇所にまとめてあげる
5. とにかくあちこちから副作用を発生させないことが大事。「ここが副作用の起きる箇所だ」と自明である状況を作り出す
参考:副作用を最小限に抑えるために必要なこと - かとじゅんの技術日誌
おまけ: 純粋関数であれば尚よし
純粋関数は、返り値が引数以外に依存してない関数。
なので一番、返り値を予測しやすく、想定外の事態が起きない関数でめちゃくちゃ安全。
純粋関数が一番、結合度が低い。最高。
とは言え、副作用のある関数も必要なので、バランスだよね。って感じ。
参考
副作用 (プログラム) - Wikipedia
副作用を最小限に抑えるために必要なこと - かとじゅんの技術日誌
プログラミングで副作用と状態ってなに? | 民主主義に乾杯
関数型プログラミングについて教えてください。
関数型プログラミングのそれほど怖くないガイド | YLDブログ
プログラミングにおける副作用とは何ですか? | | | | サラバナン M | | | テックオタク | 中くらい
プログラミング言語 - 「副作用」とは? - ソフトウェア エンジニアリング スタック交換
副作用 - 開発者コミュニティ 👩‍💻👨‍💻
純粋関数 - Qiita
純粋関数の割合を最大化しよう|保守性の高いソフトウェア開発のTips集
プログラミングで言う「副作用」とは何ですか?副作用があるのは良くないとかHaskellなどの関数型言語は「IOモナド (IO monad) 」で副作用なしにプログラムできると聞きますがそれは何ですか? - Quora
副作用 (プログラム) - Wikipedia
副作用 – プログラミング用語解説|Unity高校&ゲームスクールのG学院
関数型はプログラミングスタイル
切り取り線.icon
副作用の定義が人によってバラバラ過ぎて混乱
1. 関数が、返り値以外の箇所に影響を与える(変更を加える)
論派
副作用 (プログラム) - Wikipedia
副作用を最小限に抑えるために必要なこと - かとじゅんの技術日誌
プログラミングで副作用と状態ってなに? | 民主主義に乾杯
プログラミング言語 - 「副作用」とは? - ソフトウェア エンジニアリング スタック交換
純粋関数 - Qiita
純粋関数の割合を最大化しよう|保守性の高いソフトウェア開発のTips集
副作用 – プログラミング用語解説|Unity高校&ゲームスクールのG学院
関数型はプログラミングスタイル
議論.icon
プログラミングにおいて、式の評価による作用には、主たる作用とそれ以外の副作用(side effect)とがある。式は、評価値を得ること(※関数では「引数を受け取り値を返す」と表現する)が主たる作用とされ、それ以外のコンピュータの論理的状態(ローカル環境以外の状態変数の値)を変化させる作用を副作用という。
この定義の真偽は置いておいて、3つの論説の中で一番わかりやすくてシンプルだわonigiri.w2.icon
返り値が主作用に対して、それ以外の更新は副作用だと。判断基準が明確
数学的関数は本来計算結果を返すことが目的ですが、その過程で周囲の状態を変化させてしまうことを指します。
関数は引数を取って何かを返すだけonigiri.w2.icon
2. 関数が、利用者が意図しないこと(目的の作用以外のこと)を起こす
論派
プログラミングで言う「副作用」とは何ですか?副作用があるのは良くないとかHaskellなどの関数型言語は「IOモナド (IO monad) 」で副作用なしにプログラムできると聞きますがそれは何ですか? - Quora
議論.icon
主張としては、本来想定してる作用以外のことが起きることを副作用と言うとのこと。
なので返り値とかそうじゃ無い変化とかは関係ない。
外部の変数を変更しようとも、それが想定したものであるなら副作用ではないと言うとのこと。
なるほど、副作用が意図的でないもの的な捉え方をしてるんやなこれはonigiri.w2.icon
薬の副作用から説明してるな。それと同じやと。
3. 関数が、外部と相互作用(参照・更新)してる
論派
関数型プログラミングについて教えてください。
関数型プログラミングのそれほど怖くないガイド | YLDブログ
副作用 - 開発者コミュニティ 👩‍💻👨‍💻
議論.icon
副作用は参照透過性と対立するものだと考えるといい
グローバル変数はなるべく排除しますよね。スコープが広くなると、可能な状態が増えて、結果バグも増えるからです。
じゃあ、空間的だけでなく時間的にも、可能な状態を減らせばバグが減る。これが参照透過、あるいは「不変」の考え方です。
参照透過性ってなんだ...調べるか...onigiri.w2.icon
不変の話も出てきたな。一旦放置。
まず、「副作用」という言葉で混乱しやすいのですが、「メインの動作」に対する副作用、という意味ではありません。
メインの動作って何を指してるんだろうなonigiri.w2.icon
Wikipediaの言ってる「返り値」に直結する操作のことかな
まずは、対義語になる「純粋な関数」というものを考えます。これは「同じ引数を与えれば常に同じ結果を返す」というものです。つまり、そのような状態を破る
- 関数の実行を超えて生き残る変数に値を読み書きする
- 他の純粋でない関数を呼ぶ
- 入出力を行う
などの行為がすべて副作用です。
ここも気になるのがさ、純粋な関数の定義を見ると、条件の中に「副作用が無いこと」て説明があるのよonigiri.w2.icon
そうなると、どうも対義語っていう説明がしっくりこないのよな 
副作用とは、関数が何かを行うためにそのパラメーターの外部にあるものに依存するか、変更する場合です。たとえば、関数自体の引数、データベース、ファイル、またはコンソールの外部で変数を読み書きする関数は、副作用があると説明できます。
副作用を純粋関数の対比として考えてるonigiri.w2.icon
関数が非ローカル状態を変更する場合、またはその出力が決定論的でない場合、関数は副作用を持つと言われます。
スコープ外のものを使用または変更する場合、それらを副作用と呼びます。
ここでもスコープ外からの読み取りも含めて副作用と言ってるなonigiri.w2.icon
純粋関数と副作用の違いってなんだろう
関数型はプログラミングスタイル