3スペース インデントのススメ ─ 空白3つコーディングへの考察


時代は空白ベースのインデント

プログラマの世界にはコードのインデントにタブ(\t)を使う派とスペース(空白)を使う派がいます。 そしてコードシェアリングが主流の現代では環境依存を考慮すべく、スペースの利用が好まれる傾向にあります。 プログラマの間ではコード揃えの重要性も認知され始めているため、今後スペースによるインデントの必要性は更に高まっていくでしょう。

スペースで統一してコード揃えを行えば、他人の環境で表示がズレることもありません。
ちなみに純粋なタブをインデントとして用いる際のタブはハードタブと呼ばれ、逆にスペースをインデントに用いる際のそれはソフトタブと呼ばれます。
スポンサーリンク

空白派における2スペース派と4スペース派

空白派の中でも実は一般的な4スペース派と2スペース派が分かれています。これはテキストエディタ戦争におけるVim派とEmacs派の戦いに近いものを感じます。しかし私はあえてここで、4スペース派と2スペース派の間に割って入って3スペースの利用を勧めてみたいと思うのです。

4スペースの欠点

そもそも4スペースには欠点があります。 階層の深いコードを書くとテキストエディタの横幅をどうしても余計に広く使ってしまうのです。 また、インデントによってコードが間延びする分、眼球運動が余計に必要になり、読んでいて疲れてしまいます。

if (div == 0) {
    throw "error"
} else {
    return function () {
        with (arguments) {
            var c = 0
            for (var i = 0; i < length; i++) {
                c += arguments[i]
            }
            return c / div
        }
    }
}

そのためインデントに空白2つを採用し、コードをよりコンパクトに見せるという発想が生まれました。 これによりモニタの横幅をほとんど気にする必要がなくなり、また目線の移動も最小限に抑えられ、読み手の負担も軽減するという利点を獲得しました。

if (div == 0) {
  throw "error"
} else {
  return function () {
    with (arguments) {
      var c = 0
      for (var i = 0; i < length; i++) {
        c += arguments[i]
      }
      return c / div
    }
  }
}

2スペースの欠点

しかし空白2つを用いるの方法にも欠点があります。ブレース({})を多用する言語だと読みづらいのです。 PythonやCofeeScriptのようなインデントを主体とする言語であればそれなりに読みやすいのは否定しません。私自身、この手の言語では2スペースを活用することが多いです。

// オフサイドルールをベースとする擬似言語
if (div == 0)
  throw "error"
else
  return function ()
    with (arguments)
      var c = 0
      for (var i = 0; i < length; i++)
        c += arguments[i]
      return c / div

コードの階層が際立って見えます。とても構造的で読みやすいですね。

しかし先ほどのブレースを用いた言語のサンプルと比較してみてください。前回サンプルはかなり窮屈で読みづらいことに気づくはずです。どうやらブレースの終わり括弧(})が階層関係を曖昧にしているようです。else文がブレースに押し上げられ、throw文やreturn文と同一の列に並んでしまっている点にも注目してください。また、階層を認識・区別する際の基準がelse等のキーワードではなく、終わり括弧という曖昧でか細い記号である点にも注目すべきです。

なお以下のようにブレースをより細く薄くすることで、この階層構造の曖昧さがより際立つことがわかります。

if (div == 0) {
  throw "error"
} else if (div > 0) {
  return function () {
    with (arguments) {
      var c = 0
      for (var i = 0; i < length; i++) {
        c += arguments[i]
      }
      return c / div
    }
  }
} else {
  return -1
}

2文字インデントを活用する際には、終わり括弧(})をしっかりと意識する必要があります。ブレース記号のシンタックスハイライトを濃い目に設定したり、ブレースが極端に細すぎないフォントや、ブレースの凹凸が強めなフォントを利用するのもよいでしょう。

また2スペースはネストが深く複雑なコードを書くほど、階層構造が曖昧になり、階層を見失いやすくなるという問題があります(この辺の検証はまた別記事で行う予定です)

そこで3スペース

3スペースは4スペース、2スペース両者の欠点を克服し、それぞれの利点を活かすことが出来るバランスの良い解決策です。

if (div == 0) {
   throw "error"
} else {
   return function () {
      with (arguments) {
         var c = 0
         for (var i = 0; i < length; i++) {
            c += arguments[i]
         }
         return c / div
      }
   }
}

2スペース並のコンパクトさと、4スペース並の階層構造の明確化を実現することが出来ました。

スポンサーリンク

モダン言語との相性

3スペース・インデントはSwiftのif let記法との相性がとても良いようです。ソースがよりドキュメント的になり読みやすくなります。

if let ary = get() {
   
   let foo = ary.first
   let bar = ary.last
   print("\(foo) .. \(bar)")
}

if文内の変数宣言とブロック内の変数宣言が綺麗に揃っているところがとても規則的ですね。

Swift言語やGo言語,Rust言語等、丸括弧が省略可能なモダンな言語との相性はかなり良いのかもしれません。

if golang != rust {
   golang = "こんにちは世界"
}

4スペースがC言語時代のベストプラクティスだとすれば、SwiftやRustの時代にはまた別のインデント数が求められるのでは無いでしょうか。

// C/C++, Java
if (self.name != nil) {
    self.name = nil;
}

// Swift, Rust, Golang
if self.name != nil {
   self.name = nil;
}

最後に

プログラマとして、奇数を用いるというのはかなり抵抗があるかもしれません。キリの悪い数値を生理的に受け付けないという人がほとんどだと思います。ですが、ブレースベースの言語を2スペースで読む際に感じる窮屈さや歯がゆさに比べれば、それほど突飛な発想でもないように思えてきます。

人によっては2スペースでも慣れれば問題なく読めますが、4スペースから2スペースへの移行はかなり敷居が高いかと思います。そのつなぎとして3スペースを使うのはアリかもしれません。

また、職場における4スペース派と2スペース派の争いの妥協案として採用するも良いかと思います。

また個人的には書籍やサンプルコードでの利用にも最適だと感じています。3スペース・インデントは4スペースに慣れている人と2スペースに慣れている人の両者を上手くカバーできる唯一のスタイルなのです。

スポンサーリンク

まとめ

4スペース

  • コードが間延びする
  • 読んでいて疲れる

2スペース

  • 階層構造がいまいちハッキリとしない
  • ブレース主体の言語だとその問題が顕著に現れる

3スペース

  • 2スペースと4スペースの良いとこ取り
  • モダン言語との相性が良い

補足

サンプルコードでの利用について

三文字インデントをサンプルコードで利用するべきか否かについてですが、ネット上に公開するサンプルコードについては、利用者がコピー・アンド・ペースト後のコードを自身の環境に合わせて調整する手間を生んでしまうため、慎重に採用する必要がありそうです。

もっともこの問題は「二文字インデント ⇔ 四文字インデント」間でも起きる問題なので、気にするほどのことでは無いかもしれません。むしろユーザ側としては「二文字 ⇔ 四文字」間の調整よりも、「三文字 → 四文字」や「三文字 → 二文字」へ調整のほうが手間は少なくなるかもしれません。もっともマクロや一括置換を用いれば手間は変わらないのですが。

三文字幅の採用に迷うくらいなら、2016年現在最もユーザ数の多い四文字幅に合わせるのも手です。またはモバイル環境を意識して二文字幅に統一するのもよいかもしれません。ハードタブで書いてCSSのtab-sizeプロパティでタブ幅を調整する方法も考えられます。

または周りの環境や文化に合わせて使い分けるのも良いかもしれません。参考までに私は以下のような使い分けをすることが多いです。

用途文字幅インデント方法
Java───ハードタブ
C言語4文字ソフトタブ
C++2文字ソフトタブ
Ruby2文字ソフトタブ
コラム/エッセイユーザ層やコード分量に合わせるソフトタブ

字下げスタイルと判読性

「オフサイドルールは階層が際立ちとても読みやすくなる」という趣旨の説明をしましたが、これは必ずしもブレースを用いた表現よりも優れているという意味ではありません。

// オフサイドルール
if cond
  print "then"
else
  print "else"

// ブレースと字下げによる表現
if cond {
  print "then";
} else {
  print "else";
}

オフサイドルールを採用した言語であっても、ブロック内の行数が広く複雑になれば階層構造の把握は難しくなります。そのようなケースではむしろブレースを用いた字下げ表現のほうが理解しやすいコードになる場合もあります。

というのもブレースを用いたフォーマットは階層の始まりと終わりが明確になるという特徴があります。ブレースの始め括弧{と終わり括弧}が構造把握の際の指標になるのです。また{}という一貫した記号である点も重要です。

ブレースを用いたフォーマットは、「字下げ」と「括弧」という二重の情報を用いることが出来るという意味においては、多様性を兼ね備えた優れたフォーマットという見方もできます。

「字下げ」は直感性や見分けやすさ、「括弧」は具体性や論理性を生み出します。オフサイドルールではこの後者の部分が少し弱いように思います。しかしその分、字下げのメリットが生きるため、ブレースの特性を字下げの特性でカバーすることも可能になります。
オフサイドルールは右脳的、ブレースによる字下げ表現は左脳的とも言えそうです。

なお、インデント幅が極端に狭い2文字幅インデントでは、この「字下げ」の特性が抑えられてしまいます。これを補うためには「括弧」の特性に強く依存する必要があり、これは先程の「終わり括弧(})をしっかりと意識する」という説明にも繋がっていきます。

広告

関連するオススメの記事

3スペース インデントのススメ ─ 空白3つコーディングへの考察」への2件のフィードバック

  1. ピンバック: ダークサイド Objective-C コーディング規約 | MaryCore

  2. ピンバック: ヨーダ記法のススメ 〜比較対象を左辺に記述するメリット〜 | MaryCore

コメントは停止中です。