人気ブログランキング | 話題のタグを見る

RWH の読み方(21) 第3章

Real World Haskell 第3章 Defining Types, Streamlining Functions

The Offside Rule and Whitespace in an Expression

Haskell の仕様で唯一不満なところが、コードのインデントのルールであるこのオフサイド・ルールだ。ルールが複雑で、なかなか頭に入らないし、長大なインデントを全部 whitespace で記述しないといけない事が多い。

あとで本文の説明に従って調べていくが、最初に言っておきたいことは、Haskell のコードのインデントに tab は使えないということだ。ファイルの行の左端からの whitespace の個数が意味を持ってしまうため、インデントに tab は使えない。長いインデントでも全て whitespace で記述しないといけない。

また、本文にはないが、オフサイド・ルールの大原則として、右にインデントしている行は上の行につながっていると考えるというのがある。

Haskell の関数の定義は基本的に1行の a single statement だ。しかしそのまま横に伸ばしていくと後で読んだり編集したりするのが大変だ。そこで、関数の定義の先頭から whitespace 1個以上右にインデントしている場合は1行が連続していると考えるのだ。次の例はインデントがバラバラだが、ひとつの行としてつながっていると考えることができる。

foo = "this is an "
       ++ "example of " ++
  "folding of the line."

実行例

*Main> foo
"this is an example of folding of the line."

第2の大原則は、関数の定義の先頭に whitespace がある場合、折り返しの部分はそれより左からはじめることはできないということだ。これがオフサイド・ルールという名前の由来のような気がする。ちなみにオフサイド・ルールは Haskell に特有のものというわけではなく、Python や他のプログラム言語でも採用されている。

第3の大原則は右でもない左でもない同じ位置でインデントしている行同士は、同じブロックの異なる文であるということ。つまり、 { statement 1; statement 2 } というブロックは、

    statement 1
    statement 2

と書くことができるということだ。これで、インデントが右の場合、左の場合、同列の場合のルールができたのでインデントを網羅しているといえる。

それではいよいよ本文の説明を読んでみよう。本文の最初にはトップレベルの関数の定義がインデントしているときは、それに続く関数の定義は必ずそのインデントに揃えなければならないと書いてある。RWH 本文の例は次のようなものだ。

--  file:  ch03/GoodIndent.hs
--  This  is  the  leftmost  column.

    --  It's  fine  for  top-level  declarations  to  start  in  any  column...
    firstGoodIndentation  =  1

    --  ...provided  all  subsequent  declarations  do,  too!
    secondGoodIndentation  =  2

これは上で述べた大原則の2にあたる。また、上の大原則3からも頷ける。トップレベルの関数の定義は全体で一つのブロックをなすと考えることもできるからだ。

RWH の本文では次に let expression と where clause で使われるオフサイド・ルールについて述べている。let キーワード や while キーワードの後にはブロックが来るが、オフサイドルールを使うと、ブロックの {} や ; を省略することができる。たとえば、

foo = let { a = 1; b = 2; c = 3 } in a + b + c

は、

foo =
  let  a = 1
       b = 2
       c = 3
  in a + b + c

と書くこともできる。let の後のブロックの item の先頭のインデントは同じ位置に揃えなければならない。また、let と in は同じ位置でインデントしている必要がある。where の場合は次のようになる。

foo =
    a + b + c
  where
    a = 1
    b = 2
    c = 3

where のあとにくるブロックを、where キーワードの直後ではなく改行とインデントをして記述しているがこういう書き方は let の場合も可能だ。

A Note About Tabs Versus Spaces

このサブサブセクションでは、インデントに Tab を使うための注意が書かれている。慣れてなければ whitespace だけを使うように勧めている。

The Offside Rule is Not Mandatory

このサブサブセクションでは let や where の後のブロックは { } と ; を使って書くことができるが、Haskell では余り使われないと書いてある。

Haskell のコード例には、オフサイド・ルールのために非常に長いインデントのあるものが多い。しかし、長いインデントを whitespace だけで記述するのは面倒だ。

幸いなことに、冒頭に述べた3原則を利用すると、Haskell の場合も Ruby 流に2個のスペースだけでインデントを表現できる。このブログでも紹介した。最近の Haskell の解説書を読むと、本場でもこのような2個のスペースによるインデントで記述しているものが増えてきている。

最初からオフサイド・ルールを意識して長いインデントを作ってしまうよりは、2個のスペースのインデントで記述して、コンパイルが通らなかったら再考するというやり方でもうまくいくようだ。
by tnomura9 | 2012-03-17 07:09 | Haskell | Comments(0)
<< RWH の読み方(22) 第3章 RWH の読み方(20) 第3章 >>