twitterからながれてきたこの話題。昔のCコンパイラは、if文の条件節で代入を書いても文句を言わなかったので、このようなコードに何の警告も出なかった。
#include<stdio.h>
int main() {
int x = 0;
/* おそらく意図と違う。 x == 1 と書くべきであった
これでは常に実行されてしまう */
if ( x = 1 ) {
puts("残念");
}
}
「これをこのように書けば、コンパイルエラーになり、ある種の誤りをコンパイラに見つけさせることができる」というのが、「老害」とされる人の主張である。
/* これはコンパイルエラーになる */
if ( 1 = x ) {
puts("残念");
}
もし使っている環境が「コンパイラや処理系が前者のコードに文句を付けない」のであればこの意見も一理ある。しかし、本来この問題は機械が解決すべきである(コンパイラの警告やlintなど)。今現在、そのような環境はどのくらいあるのだろうか?
以下の言語について調べてみた。そんなに真剣に使ってない言語については、macに最初から入っていたバージョンだったりするので「他のバージョンでは違うよ」といった指摘を歓迎する。また、オプションの類いは基本的にデフォルト(何も指定しない)である。
- Python (2.7.5)
- Perl (5.16.2)
- Ruby (2.0.0p481)
- C (clang Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
- PHP (5.4.30)
- Java (1.6.0_65)
- Scheme (gauche 0.9.3.3)
- Haskell (ghc 7.6.3)
- Erlang (17)
構文エラーになって実行できない。
i = 0
#syntax error
if i = 1 :
print "hoge"
構文エラーになって実行できない。
use strict;
use warnings;
my $i = 0;
#Found = in conditional, should be == at hoge.pl line 7.
if ( $i = 1 ) {
print "hoge\n";
}
実行時警告になる。
i = 0
#hoge.rb:4: warning: found = in conditional, should be ==
if i = 1
puts "hoge"
end
(clangでは)コンパイル時警告になる(警告があったらコンパイル失敗する設定になっていればコンパイル失敗であるが、デフォルトはそうではない)
#include<stdio.h>
int main(){
int i = 0;
/* warning: using the result of an assignment as a condition without parentheses [-Wparentheses] */
if ( i = 1 ){
puts("hoge");
}
}
さて、ここまで見てきた言語であれば、コンパイラや処理系が誤りを見つけてくれる。警告を無視したりしているのでなければ我々はこの種の誤りを見つけることができる。 もし「老害」に先のようなイディオムを強制されたとしても、「もし間違っても警告が出ます。警告を無視するのやめましょう。警告潰ししていきましょう」と前向きな提案に変えることができる。
貫禄のPHPは、なんの警告もつけないし、黙って実行する。いいね?
そもそも君はテンプレートエンジンを宝島か何かと思っているのかね?PHPはかつてクラウドにあり全webを支配した恐怖の帝国だったのだ。
<?php
$i = 0;
if ( $i = 1 ) {
echo "abort";
}
javaにはこの問題はないと思った?残念。javaは条件節に真理値がないと怒るが、真理値でありさえすれば代入が書いてあっても文句を付けない。
public class hoge {
public static void main(String[] args){
boolean b = false;
if ( b = true ){
System.out.println("abort");
}
}
}
ここから先の言語達は、そもそも副作用がないとか、変数を再束縛できないとか、代入と比較が間違えるような構文ではないという理由でこの種の誤りは起きない
再代入と比較を間違うような構文ではない。
(define x 0)
(print (if (eq? x 1)
"hoge"
"fuga"))
副作用がないし、パターンマッチがあるのでこんな用途で if など書かない
hoge :: Integer -> String
hoge 1 = "hoge"
hoge _ = "fuga"
main :: IO ()
main = print $ hoge 0
再束縛がないし、パターンマッチがあるのでこんな用途で if など書かない。
#!/usr/bin/env escript
main(_) ->
io:fwrite(hoge(0)).
hoge(1) -> hoge;
hoge(_) -> fuga.
- Python、Perl、Ruby、C(clang)では、「老害」の意見には反発しよう
- 機械が見つけてくれるか、そもそも書けない。
- Java、PHP、および昔のCなどの環境では、残念なことに「老害」の意見には一理ある。
- 世代間闘争に帰着するよりも、lintなどの静的解析を提案しよう。
- このようなコーディングスタイルを勧めてくるくせに静的解析を拒否する人は本当に老害である。退職の準備をしよう。
JavaScript