promise 使って 順次／分岐／反復 改良版

昨日はめちゃくちゃ眠い中書いたのでとても微妙なのが出来てしまったので書き直します。

# 順次
これは昨日と同じ。

```coffeescript
promise
.then f
.then g
.then h
# ...
```

# 分岐： `p_if`
## 役割
`p_if` は、直前のコンテキストを受けて、続く処理を分離する役割を持つ。

## 仕様
`p_if` の仕様は次の通りである

```
p_if: condStmt -> thenStmt -> elseStmt -> selectionStmt
condStmt: X -> Bool|P(Bool)
thenStmt: X -> Y|P(Y)
elseStmt: X -> Y|P(Y)
selectionStmt: X -> P(Y)
```

- `p_if` は 3 つの引数を受け取る
    - condStmt は、X 型の値を受けて、真偽値を返す
    - thenStmt は、condStmt が真を返した場合に実行される。X 型の値を受けて、Y 型の値を返す。
    - elseStmt は、condStmt が偽を返した場合に実行される。
- `p_if` は 1 つの関数を返す
    - selectionStmt は、condStmt, thenStmt, elseStmt をいい感じに組み合わせた 関数を返す。

これにより、次のような記述が可能となる。

```coffeescript
promise
.then stmt1
.then stmt2
.then p_if condStmt, thenStmt, elseStmt
.then stmt3
.then stmt4
# ...
```

## 実装

```coffeescript
angular.module("structuredProgramming").factory "p_if", ["$q", ($q) ->
  return (condStmt, thenStmt, elseStmt) ->
    return (x) ->
      return $q.when condStmt x
      .then (b) ->
        return if b then thenStmt x else elseStmt x
]
```

```coffeescript
# test
describe "test for p_if", ->
  condStmt = (n) -> n % 2
  thenStmt = (n) -> n * 3 + 1
  elseStmt = (n) -> n / 2

  it "p_if true", (done) ->
    inject ["$rootScope", "$q", "p_if", ($rootScope, $q, p_if) ->
      deferred = $q.defer()
      deferred.promise
      .then p_if condStmt, thenStmt, elseStmt
      .then (n) ->
        expect(n).toBe 16
      .finally done
      deferred.resolve 5
      $rootScope.$apply()
    ]
  it "p_if false", (done) ->
    inject ["$rootScope", "$q", "p_if", ($rootScope, $q, p_if) ->
      deferred = $q.defer()
      deferred.promise
      .then p_if condStmt, thenStmt, elseStmt
      .then (n) ->
        expect(n).toBe 3
      .finally done
      deferred.resolve 6
      $rootScope.$apply()
    ]
```

# 反復： `p_while`
## 役割
`p_while` は直前のコンテキストを受けて、処理を反復する役割を持つ

## 仕様
```
p_while: condStmt -> doStmt -> iterationStmt
condStmt: X -> Bool|P(Bool)
doStmt: X -> X|P(X)
iterationStmt: X -> P(X)
```

`doStmt` が `X -> Y|P(Y)` でないのは、doStmt の返り値を再び condStmt で評価する必要があるからだ。

## 実装
```coffeescript
angular.module("structuredProgramming").factory "p_while", ["$q", ($q) ->
  return (condStmt, doStmt) ->
    iterStmt = (x) ->
      return $q.when condStmt x
      .then (b) ->
        if b
          return doStmt x
          .then iterStmt
        else
          return x
    return iterStmt
```

```coffeescript
# test
describe "test for p_while", ->
  ifCondStmt = (d) -> d[0] % 2
  ifThenStmt = (d) -> [d[0] * 3 + 1, d[1] + 1]
  ifElseStmt = (d) -> [d[0] / 2, d[1] + 1]
  condStmt = (d) -> d[0] > 1
  it "p_while", (done) ->
    inject ["$rootScope", "$q", "p_while", "p_if", ($rootScope, $q, p_while, p_if) ->
      doStmt = p_if ifCondStmt, ifThenStmt, ifElseStmt
      deferred = $q.defer()
      deferred.promise
      .then p_while condStmt, doStmt
      .then (d) ->
        expect(d[1]).toBe 16
      .finally done
      deferred.resolve [7, 0]
      $rootScope.$apply()
    ]
```

# その他
- promise の自慢は、「いつ」値を包み込むかを問わないというところだ。
- `p_if`, `p_while` は、この鉄則を破らないように作られている。
    - その証拠に、テストでは、 `p_if`, `p_while` を設置し終わった後で、`deferred.resolve` を呼び出している。
- `p_if` を応用すれば `p_switch`, `p_unless` なども書けそう。
- `p_while` を途中で抜ける `p_break` も実装したい。
- エラーのハンドリングは未実装。ただ promise には大域脱出機構も備わっているので、そんなに難しくは無いと思われる。

まぁそのうちやります。