QA@IT

if-elseでelse側のインデントを2にすると、ifブロック全体にパイプライン演算子が適用されないのはなぜ?

1476 PV
let toUpper (s: string) = s.ToUpper()
if true then
  "a"
else
  "b"
|> toUpper

このコードは、"A"ではなく"a"を表示します。
空白の数を4にすると、"A"が表示されるようになりますが、この挙動は仕様でしょうか?
それとも、バグでしょうか?

回答

正常な動作です。

以下の「インデント規則の例外」に
http://msdn.microsoft.com/ja-jp/library/dd233191.aspx

前の行よりもインデントが少ない場合のコンテキストの終了に関するもう 1 つの例外は、+ や |> などの挿入演算子に関するものです。 挿入演算子で始まる行は、コンテキストの終了をトリガーすることなく、通常の位置より (1 + oplength) 列前から開始できます (oplength は演算子の文字数)。 このようにすると、演算子の後の最初のトークンの位置が前の行に揃えられます。

と、あります。

たとえば

  1
+ 1

は インデントがそろっていないが継続した行とみなされるということですね。

ですので、

> if true then
  1
+ 1
  else
  2
+ 2
;;
val it : int = 2

また、(条件をfalseにして)

> if false then
  1
+ 1
  else
  2
+ 2
;;
val it : int = 4

となります。 + 1 は thenブロック内の継続した行であり、 + 2 は elseブロック内の継続した行です。

そして、この例外は |> にも適用され、演算子の長さ + 1 文字(つまり3文字)早く始めても継続した行として見られるということです。

ですから

> if false then
  "a"
  else
  "b"
|> toUpper
;;

|> toUpper は 前の行より2文字手前から始めていますが、
|> は3文字までは手前から書き始めても継続行とみなされるので)
これはelseブロックの行です。

同様に

> if false then
   "a"
   else
   "b"
|> toUpper
;;

も、 3文字手前なので、 |> toUpper は elseブロックです。

インデントを4にすると、結果的に |> toUpper は 4文字手前から始まることになるので
コンテキストの終了が発生して別の行になります。

ということで、|> の前の行が、先頭からのスペースの数を4つ以上になるようにするか

> if true
    then
      "a"
    else
      "b"
|> toUpper
;;
val it : string = "A"
> // 極端な例
;;
> if true then
 "a"
 else
        "b"
|> toUpper
;;
val it : string = "A"

thenブロックにも toUpperを書けば、 a もtoUpperされます。

> if true then
  "a"
|> toUpper
  else
  "b"
|> toUpper
;;
val it : string = "A"
編集 履歴 (3)

以下のサイトにて
http://fsharpforfunandprofit.com/posts/fsharp-syntax/

Infix operators such as "+", "|>" and ">>" are allowed to be outside the line by their length plus one space:

//character columns
//34567890123456789
let x =  1   // defines a new line at col 10
       + 2   // "+" allowed to be outside the line
       + 3

let f g h =   g   // defines a new line at col 15
           >> h   // ">>" allowed to be outside the line

と書かれているのを見つけました。
何か関係ありますかね?

追記

let toUpper (s: string) = s.ToUpper()
if false then
  "a"
else
  "b"
|> toUpper

とすると、"B" と出力されるので、パイプラインの行が2インデントとして扱われてるっぽい気がします。

編集 履歴 (2)
ウォッチ

この質問への回答やコメントをメールでお知らせします。