読者です 読者をやめる 読者になる 読者になる

関数とは、手続きを書く場所を移動させるためにあるのではない

プログラム言語における関数とは、手続きを書く場所を移動させるためにあるのではない。単純で明確な意味を持つ言葉を定義すること。言葉を組み合わせて無限の意味を表すのが、まさに「言語」の本質。そのためには、関数のI/Oを最小化すること。便利かもしれないと誤解して、無駄に制御引数を増やした関数は、まるでシェルコマンドのようで、言語の価値を低下させかねない。

課題:「野球観戦。7回までは試合結果を観戦、7回以降は試合結果を録画」を言語で表現してみる。

試合=gameに、観戦と録画を切り替えるモードフラグを持たせ、回の範囲を指定できるように考えた。

score = 0
def game(watch_or_record, start, end):
  global score
  for r in xrange(start, end+1):
    score += play(r)
  if watch_or_record:
    watch(score)
  else:
    record(score)

game(True, 1, 6)
game(False, 7, 9)

これじゃ手続きを書く場所を変えているだけ。最後の2行、引数順序を忘れると意味が読めなくなるし、1と9という整数の範囲を間違えると、野球じゃなくなる。

こんな関数は展開したほうがマシだ。

score = 0
for r in xrange(1, 7):
  score += play(r)
watch(score)
for r in xrange(7, 10):
  score += play(r)
record(score)

ね。

いっぽう、野球の回の「まで」と「以降」、それと試合することを意味的に分けてみる。

def before(c):
  return xrange(1, c)

def after(c):
  return xrange(c, 9+1)

score = 0
def game(round):
  global score
  for r in round:
    score += play(r)
  return score

watch(game(before(7)))
record(game(after(7)))

before/afterの引数は1つで、意味を見失うことはないし、また、watch/recordをクライアントコードに任せることで、観戦/録画以外の選択肢も与えられる。クライアントコードが管理する変数は 7 だけになる。

さて、いつも使っているAPI

game(True, 1, 6)
game(False, 7, 9)

みたいなのがどのぐらいあるだろう?