21世紀の多くのプログラマーが最初に学ぶプログラミングパラダイムは、おそらくオブジェクト指向プログラミング(OOP)でしょう。オブジェクト指向プログラミングは、現実世界のエンティティを記述的な特性と実行可能なメソッドを持つ非常に抽象的なモデルに変換し、その動作を現実世界でシミュレートすることを可能にします。可読性、再利用性、保守性など多くの利点がある一方で、状態管理には非常に複雑なものももたらします。オブジェクトの状態を把握する難しさから、疲れ果てたプログラマーはエラーが多いコードを大量に生み出し、見つけて修正するのに数時間、あるいは数日かかるバグを生み出しています。「この望ましくない状況から抜け出す方法はあるのか?」と疑問に思うかもしれません。良いニュースがあります。あまり知られていませんが広く使われているプログラミングパラダイムである関数型プログラミング(Functional Programming、略してFP)が、状況を好転させ、私たちが引き起こすバグの数を減らす助けとなります。この記事では、関数型プログラミングとは何か、そしてなぜバグ回避に役立つのかを説明します。
関数型プログラミング:純粋な金、バグはもうない
関数型プログラミングは、以下の2つの目標を達成することを目指すプログラミングパラダイムです。
- 入力と出力が常に決定論的な関係を持つこと
- 状態変異やI/Oストリームなどの副作用を排除すること
この決定論的で副作用のないプログラミングアプローチの最終的な動機は、コードを論理的に理解しやすくし、バグを導入しにくくすることです。同じ入力が常に同じ出力を生み出せば、不快な驚きは決して起こりません。また、コードベースの副作用を排除すれば、各状態が異なる段階でどのように値が変化するかを追跡する必要もなくなります。
関数型プログラミングは、上記の目標を達成するために純粋関数を多用します。純粋関数は以下の2つの性質を持ちます。
1. 純粋関数は常に同じ引数に対して同じ値を返します
入力と出力間の決定論的な写像を持つことは明らかな利点をもたらします。純粋関数は常に期待される結果を返すことを期待できます。この確実性は、関数が予期しない結果を返すことによるバグを引き起こすことを防いでいます。
2. 純粋な関数は副作用を生じません
副作用なしルールは、各純粋関数がその範囲外で変化を引き起こしてはならないと規定しています。このルールは、非純粋関数による外部状態修正を排除するのに役立ちます。非純粋関数による修正は目立たず、私たちの注意を逃れやすいため、バグの大きな原因となっています。
では、非純関数と純関数の例を見てみましょう。
非純粋関数
num1 = 5
def add(num2: int) -> int:
グローバルNUM1
返す num1 + num2
# 最初の処刑
print(add(3)) # は8を返します
数1 = 3
# 二度目の処刑
print(add(3)) # は6を返します
上記のコード例の「add」関数は純粋ではありません。なぜなら、同じ入力に対して一貫して同じ出力を出さないからです。
積 = 0
def multiply_by_two(数:int) -> int:
グローバル製品
積 = 数 * 2
返品
# 最初の処刑
Print(multiply_by_two(3)) # 6 リターン
印刷(製品)
# 二度目の処刑
print(multiply_by_two(5)) # 10 を返す
印刷(製品)
# 三度目の処刑
Print(multiply_by_two(3)) # 6 リターン
印刷(製品)
このコード例の「multiply_by_two」関数は、同じ入力に対して同じ出力を返します。しかし、各呼び出しで「product」変数を修正するため、副作用なしルールに違反するため、純粋関数とは言えません。
純粋な関数
def add_five(num2: int) -> int:
Return num2 + 5
# 最初の処刑
print(add(3)) # は8を返します
# 二度目の処刑
print(add(3)) # は8を返します
前の「加算」関数とは異なり、ここでの「add_five」関数は純粋なもので、入力と出力間の写像が決定的だからです。
def multiply_by_two(数:int) -> int:
返す番号 * 2
# 最初の処刑
Print(multiply_by_two(3)) # 6 リターン
印刷(製品)
# 二度目の処刑
Print(multiply_by_two(3)) # 6 リターン
印刷(製品)
このコード例では、「multiply_by_two」関数は純粋です。同じ入力に対して常に同じ出力を返します。また、副作用はありません。
免責事項:
一つ注意点として、関数型プログラミングを活用するために、目的指向プログラミング(ObjectOriented Programming)などの他のプログラミングパラダイムの使用を諦める必要はありません。関数型プログラミングを他のパラダイムと組み合わせるのも自由に構いません!
概要
この記事では、関数型プログラミングとは何か、そして純粋関数をどのように活用しているかを簡単に紹介しました。また、関数型プログラミングで純粋関数を使うことで、知らず知らずのうちにバグを発生させないために役立つ理由も説明しました。この記事が関数型プログラミングのスタートに役立てば幸いです。