「control-structures」非同期制御構造モジュール作ってみた

#READMEはこれからだ
[npm登録した](https://www.npmjs.com/package/control-structures)。複数の引数の引き渡しをするようにしてテストして、とか適当にやっていたら、ずいぶんかかった。exception handlingが、変なハマリ方した。ブラウザから利用できるようにしようと色々やってたら、なおのことハマった。なのでREADMEはこれからだ！→書いた

原理や使ったときの見た目は、[【保存版】制御構造別非同期プログラミング完全制覇（サーバサイドJavascript・CoffeeScript）](http://qiita.com/mrpepper/items/6a538ef0b2d11fc311ac)を参照。このページにもおいおい書いてく。

#機能一覧

##simple continuation
   <dl><dt>_(firstargs,func1,func2,...)</dt>
      <dd>引数に取った非同期関数を順次実行します<dl>
      <dt>firstargs</dt>
         <dd>func1に渡す引数を配列に納めたものです
         [arg1,arg2]を渡すと、func1(func2,arg1,arg2)が呼ばれます
         この配列の要素数は可変です</dd>
      <dt>func1(next,...)</dt>
         <dd>最初に実行する関数です。次へ進むときはこの関数内でnextを呼びます。
         next(arg3,arg4) を実行すると、func2(func3,arg3,arg4)が呼ばれます
         この引数の要素数は可変です</dd>
      <dt>func2(next,...)</dt>
         <dd>以下、同様に好きな回数だけ続けられます。</dd></dl></dd></dl>

用例はこちら。直前のブロックでのnextへの引数は、直後のブロックの追加引数になります。（この仕様は、他の関数も同じです）

```coffeescript
PRINT = (str,next) -> console.log str; setTimeout(next, 200)

cs._ ['arg1','arg2']
,(next,str1,str2) ->

  PRINT '[1st block]',->
    PRINT 'str1:'+str1,->
      PRINT 'str2:'+str2,->
        next 'err'

,(next,errstr) ->

  PRINT '[2nd block]',->
    PRINT 'errstr:'+errstr,->
      next()

,(next) ->

  PRINT '[last block]',->
    next()
```

if,switch後の合流もできます。

```coffeescript
PRINT = (str,next) -> console.log str; setTimeout(next, 200)

cs._ ['arg1','arg2']
,(next,str1,str2) ->

  PRINT '[1st block]',->
    if str1 == str2
      PRINT 'str1 and str2:'+str1,->
        next()
    else
      PRINT 'str1:'+str1,->
        PRINT 'str2:'+str2,->
          next()

,(next) ->

  PRINT '[2nd block]',->
    next()
```

##Each (並行実行)
   <dl><dt>\_each(obj, applyargs_array, applyfunc, endfunc)</dt>
      <dd>ArrayまたはObjectの各要素に、並行して同じ関数を適用します<dl>
      <dt>obj</dt>
         <dd>処理するArrayまたはObject</dd>
      <dt>applyargs_array</dt>
         <dd>applyfuncに渡すその他の引数を配列にして指定します。
         [arg1,arg2]を指定すると、applyfunc(val,index,next,arg1,arg2)が呼ばれます。</dd>
      <dt>applyfunc(val,index,next,...)</dt>
         <dd>各要素に適用する関数です。objがArrayの場合、valは要素、indexは序数です。
         処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて
         endfuncに渡されます。</dd>
      <dt>applyfunc(val,index,next,...)</dt>
         <dd>各要素に適用する関数です。objがObjectの場合、keyは要素のキー、valueは要素の値です。
         処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて
         endfuncに渡されます。</dd>
      <dt>endfunc(...)</dt>
         <dd>結果を受け取る関数です。next('foo','bar')が2要素から呼ばれる場合、
         endfunc(['foo','foo'],['bar','bar'])となります。配列内の順序は、各処理の
         終了順です。弁別する必要のある場合、indexやkeyをnextの引数に加えて下さい。</dd></dl></dd></dl>

```coffeescript
PRINT = (str,next) -> console.log str; setTimeout(next, 200)

cs._each [2,3,5,7,11]
,['arg1']
,(val,i,next,str1) ->

  PRINT 'apply',->
    next(val,i)

,(array_of_args_from_next1,array_of_args_from_next2) ->
  
  for num in array_of_args_from_next1
    console.log 'value: '+num.toString()
  for num in array_of_args_from_next2
    console.log 'counter: '+num.toString()
  next()
```

##for Loop
   <dl><dt>\_for(index, f_judge, f_iter, firstarg_array, loopfunc, endfunc)</dt>
      <dd>初期値・条件・イテレータを用いて、loopfuncを繰り返します。<dl>
      <dt>index</dt>
         <dd>カウンタの初期値です。</dd>
      <dt>f_judge(n) 返値=true,false</dt>
         <dd>カウンタに対する条件です。falseが返るとループが終了します。
         ※この関数は同期関数でなければなりません。</dd>
      <dt>f_iter(n) 返値=カウンタ値</dt>
         <dd>カウンタのイテレータです。引数がカウンタの現在の値で、返値を返すと、
         カウンタに代入されます。
         ※この関数は同期関数でなければなりません。</dd>
      <dt>firstarg_array</dt>
         <dd>loopfuncへの初回の引数を、配列で示します。
         [arg1,arg2]を渡すと、loopfunc(n,next,arg1,arg2)が呼ばれます。</dd>
      <dt>loopfunc(n,\_break,\_next,...)</dt>
         <dd>繰り返し実行する内容です。nはカウンタの現在値です。
         続行する場合、\_next(...)を呼び出します。\_nextの引数は、次回のloopfuncの
         呼び出しの際、引き渡されます。たとえば、\_next(arg3,arg4)を呼び出せば、
         loopfunc(n,next,arg3,arg4)が呼ばれます。
         繰り返しを中断する場合、\_break(...)を呼び出します。すると、
         endfunc(n,...)が呼び出されます。</dd>
      <dt>endfunc(n,...)</dt>
         <dd>繰り返しの終了後に実行される関数です。nは終了時のカウンタの値です。</dd></dl></dd></dl>

```coffeescript
PRINT = (str,next) -> console.log str; setTimeout(next, 200)

cs._for 0,((n)-> n<10),((n)-> n+1)
,[]
,(n,_break,_next) ->

  PRINT 'counter: '+n.toString(),->
    _next()

,(n) ->
  
  PRINT 'last counter: '+n.toString(),->
    return
```

##for in Array or Object
   <dl><dt>\_for_in(obj, firstargs, loopfunc, endfunc)</dt>
      <dd>配列またはオブジェクトの各要素に順々に関数を適用します。<dl>
      <dt>firstargs</dt>
         <dd>初回のloopfuncの引数を配列にして示します。
         [arg1,arg2]が渡されると、loopfunc(key,val,\_break,\_next,arg1,arg2)が呼ばれます。</dd>
      <dt>loopfunc(key,val,\_break,\_next,...)</dt>
         <dd>繰り返し実行する関数です。Objectの場合、key,valが得られます
         続行する場合\_next(...)、中断する場合\_break(...)を呼び出します。
         \_nextの引数は、次回のloopfuncに渡されます。
         \_breakの引数は、endfuncに渡されます。</dd>
      <dt>endfunc(...)</dt>
         <dd>繰り返しの終了時に呼ばれます。</dd></dl></dd></dl>

```coffeescript
PRINT = (str,next) -> console.log str; setTimeout(next, 200)

cs._for_in {bro:'ani',sis:'imo',dad:'tousan',mom:'kaasan'}
,[] #first args
,(key,val,_break,_next) ->

  PRINT key+': '+val,->
    _next()

,->
  
  PRINT 'end',->
    return
```

##while Loop
   <dl><dt>\_while(firstarg_array, loopfunc, endfunc)</dt>
      <dd>繰り返しを行います。リトライループなどです。loopfuncの処理の終了時、\_breakと\_nextを呼び分ければ良いです。冒頭で分岐すれば普通のwhileのように、末尾（最奥）で分岐すればdo...whileのように振る舞います。<dl>
      <dt>firstarg_array</dt>
         <dd>初回のf_judge, loopfuncの引数を配列にして示します。
         [arg1,arg2]が渡されると、f_judge(arg1,arg2)が評価され、
         trueならば、loopfunc(\_break,\_next,arg1,arg2)が呼ばれ、
         falseならば、endfunc(arg1,arg2)が呼ばれます。</dd>
      <dt>loopfunc(\_break,\_next,...)</dt>
         <dd>繰り返し行う非同期処理です。
         繰り返しを中断するときは\_break(...)を呼ぶと、endfunc(...)が呼ばれます。
         繰り返しを続行するときは\_next(...)を呼ぶと、f_judge(...)が評価され、
         trueならば、loopfunc(\_break,\_next,arg1,arg2)が呼ばれ、
         falseならば、endfunc(arg1,arg2)が呼ばれます。</dd>
      <dt>endfunc(...)</dt>
         <dd>次の処理です。上記の通り、引数を受け取ることができます。</dd></dl></dd></dl>

```coffeescript
PRINT = (str,next) -> console.log str; setTimeout(next, 200)

cs._while [25*25, 25*88]
,(_break,_next,arg1,arg2) ->

  PRINT arg1.toString(),->
    PRINT arg2.toString(),->
      if (arg2 % arg1) == 0
        _break(arg1)
      else
        _next(arg2 % arg1,arg1)

,(arg) ->
  
  PRINT 'result: '+arg.toString(),->
    return
```

##exception handling
   <dl><dt>myexc = new exc</dt>
      <dd>例外管理オブジェクトを生成します。ここではmyexcとします。</dd>
   <dt>myexc.\_try(block_args_array,f_block,e_array,f_catch,f_finally,f_next)</dt>
      <dd>例外スコープを開始します。実処理はblock内、例外処理はf_catch内、
      終了処理はf_finally内で行い、f_nextへと進みます。
      現在のところ、f_catch,f_finally内で\_throwすると、終了処理は完了しません<dl>
      <dt>block_args_array</dt>
         <dd>blockに渡す引数を、配列にして指定します。
         [arg1,arg2]を渡すと、f_block(myexc,arg1,arg2)が呼ばれます。</dd>
      <dt>f_block(...)</dt>
         <dd>実処理を行うスコープです。このexc例外を発生しうる関数を
         呼び出すときはmyexcを渡して下さい。次へ進むときは
         myexc.\_finally(...)を呼べば、f_finally(myexc,next,...)が呼ばれます。</dd>
      <dt>e_array</dt>
         <dd>f_catchで処理する例外の種類を列挙した配列です。
         可読性のある文字列をお勧めします。</dd>
      <dt>f_catch(_e,...)</dt>
         <dd>\_throwに渡された例外の種類と、その他の\_throwに渡された引数が
         得られます。e_arrayで列挙した例外は全て処理して下さい。
         処理を終えたらmyexc.\_finally(...)を呼んで下さい。f_blockと同様です。</dd>
      <dt>f_finally(next,...)</dt>
         <dd>終了処理を簡潔に行います。処理を終えたら、next(...)を呼んで下さい。
         f_next(...)が呼ばれます。基本的にはmyexcを引数に指定して下さい。</dd>
      <dt>f_next(...)</dt>
         <dd>次の処理を行う関数です。</dd></dl></dd>
   <dt>myexc.\_throw(\_e,...)</dt>
      <dd>例外を発生させます。引数はf_catchに渡されます。</dd>
   <dt>myexc.\_finally(...)</dt>
      <dd>例外スコープのスタックをpopして、ユーザ終了処理を呼び出します。
      引数はf_finallyに渡されます。</dd></dl>

```coffeescript
PRINT = (str,next) -> console.log str; setTimeout(next, 200)

myexc = new cs.exc
myexc._try([]
,->
  #block
  PRINT 'NEST1-1',-> 
    PRINT 'NEST1-2',->
      myexc._try([]
      ,->
        #block
        PRINT 'NEST2-1',->
          PRINT 'NEST2-2',->
            if true
              myexc._throw 'err1'
            else
              PRINT 'NEST2-3',->
                myexc._finally()
      ,['err2','err3']
      ,(_e)->
        #catch
        console.log _e
        PRINT 'NEST2-CATCH',->
          myexc._finally()
      ,(fnext)->
        #finally
        PRINT 'NEST2-FINALLY',->
          fnext()
      ,->
        PRINT 'NEST1-3',->
          myexc._finally()
      )
,['err1']
,(_e)->
  #catch
  console.log _e
  PRINT 'NEST1-CATCH',->
    myexc._finally()
,(fnext)->
  #finally
  PRINT 'NEST1-FINALLY',
    fnext()
,next)
```

## y combinator
   <dl><dt>y(closure)</dt>
      <dd>closureの引数にclosure自身を渡します＝再帰関数にします</dd></dl>

無名クロージャを再帰関数にすることができます。

```coffeescript
dummy = null
index = 10
(cs.y (func) ->
  return (dummy)->
    index -= 1
    A ->
      PRINT index.toString(),->
        if index == 0
          return next()
        else
          func(dummy)
)(dummy)
```

##Todo
<dl><dt>引数チェック</dt>
         <dd>firstarg_arrayが配列でないor(null)のときの処理など
         関数を取る場合のチェック＆nullチェック（devのみ？）</dd>
      <dt>返値チェック</dt>
         <dd>イテレータや終了条件など、同期関数の返値チェック</dd>
      <dt>ループ関数に同期関数を入れられたとき</dt>
         <dd>stack overflowを避ける方法が有ればやる。さもなくば分かりやすくあぼん</dd>
      <dt>Javascript版の無意味な返値を消去</dt>
         <dd>楽勝なはず？</dd></dl>

#CoffeeScript版ソースコード
ん？引用だけだと右の目次に出ない？

```coffeescript:control-structures.coffee
((root, factory) ->
  if FORCE_CLIENTSIDE?
    root.cs = factory()
  else if (typeof define == 'function' && define.amd)
    define([], factory)
  else if (typeof exports == 'object')
    module.exports = factory()
  else
    root.cs = factory()
)(this, () ->

  ###
     y combinator
  ###
  y: (func) ->
    return ((p) ->
      return ->
        return func(p(p)).apply(this,arguments)
    )((p) ->
      return ->
        return func(p(p)).apply(this,arguments)
    )

  ###
     simple continuation
     （継続のネストが深くなりすぎるのを防ぐため、
     　または、if,switchの後、容易に合流するために用います）
     
     _(firstargs,func1,func2,...)
        引数に取った非同期関数を順次実行します
        firstargs
           func1に渡す引数を配列に納めたものです
           [arg1,arg2]を渡すと、func1(func2,arg1,arg2)が呼ばれます
           この配列の要素数は可変です
        func1(next,...)
           最初に実行する関数です。次へ進むときはこの関数内でnextを呼びます。
           next(arg3,arg4) を実行すると、func2(func3,arg3,arg4)が呼ばれます
           この引数の要素数は可変です
        func2(next,...)
           以下、同様に好きな回数だけ続けられます。
  ###
  _: ->
    args = [].slice.call(arguments)
    firstargs = args.shift()
    (@.y (func) ->
      return ->
        arg = args.shift()
        if arg?
          passargs = [].slice.call(arguments)
          passargs.unshift(func)
          arg.apply(this,passargs)
    ).apply(this,firstargs)

  ###
     Each (simultaneous)
     
     _each(obj, applyargs_array, applyfunc, endfunc)
        ArrayまたはObjectの各要素に、並行して同じ関数を適用します
        obj
           処理するArrayまたはObject
        applyargs_array
           applyfuncに渡すその他の引数を配列にして指定します。
           [arg1,arg2]を指定すると、applyfunc(val,i,next,arg1,arg2)が呼ばれます。
        applyfunc(val,i,next,...)
           各要素に適用する関数です。objがArrayの場合、valは要素、iは序数です。
           処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて
           endfuncに渡されます。
        applyfunc(val,i,next,...)
           各要素に適用する関数です。objがObjectの場合、keyは要素のキー、valueは要素の値です。
           処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて
           endfuncに渡されます。
        endfunc(...)
           結果を受け取る関数です。next('foo','bar')が2要素から呼ばれる場合、
           endfunc(['foo','foo'],['bar','bar'])となります。配列内の順序は、各処理の
           終了順です。弁別する必要のある場合、iやkeyをnextの引数に加えて下さい。
  ###
  _each: (obj, applyargs_array, applyfunc, endfunc) ->
    ((nextc) ->
      isarr = ('Array' == Object.prototype.toString.call(obj).slice(8, -1))
      num = 0
      next = nextc()
      if isarr
        for val,i in obj
          num++
          applyfunc.apply this,[val,i,->
            next.apply this,[num,endfunc].concat([].slice.call(arguments))
          ].concat(applyargs_array)
      else
        for key,val of obj
          num++
          applyfunc.apply this,[key,val,->
            next.apply this,[num,endfunc].concat([].slice.call(arguments))
          ].concat(applyargs_array)
    )(->
      count = 0
      result = []
      return (num,next) ->
        count++
        if arguments.length > 0
          args = [].slice.call(arguments).shift().shift()
          for arg,i in args
            if result.length <= i
              result.push([])
            result[i].push(arg)
        if count == num
          next.apply this,result
    )

  ###
     for Loop
     
     _for(i, f_judge, f_iter, firstarg_array, loopfunc, endfunc)
        初期値・条件・イテレータを用いて、loopfuncを繰り返します。
        i
           カウンタの初期値です。
        f_judge(n) 返値=true,false
           カウンタに対する条件です。falseが返るとループが終了します。
           ※この関数は同期関数でなければなりません。
        f_iter(n) 返値=カウンタ値
           カウンタのイテレータです。引数がカウンタの現在の値で、返値を返すと、
           カウンタに代入されます。
           ※この関数は同期関数でなければなりません。
        firstarg_array
           loopfuncへの初回の引数を、配列で示します。
           [arg1,arg2]を渡すと、loopfunc(n,break,next,arg1,arg2)が呼ばれます。
        loopfunc(n,_break,_next,...)
           繰り返し実行する内容です。nはカウンタの現在値です。
           続行する場合、_next(...)を呼び出します。_nextの引数は、次回のloopfuncの
           呼び出しの際、引き渡されます。たとえば、_next(arg3,arg4)を呼び出せば、
           loopfunc(n,next,arg3,arg4)が呼ばれます。
           繰り返しを中断する場合、_break(...)を呼び出します。すると、
           endfunc(n,...)が呼び出されます。
        endfunc(n,...)
           繰り返しの終了後に実行される関数です。nは終了時のカウンタの値です。
  ###
  _for: (i, f_judge, f_iter, firstarg_array, loopfunc, endfunc) ->
    if firstarg_array?
      firstarg_array = []
    (@.y (func) ->
      return ->
        if f_judge i
          loopfunc.apply this,[i,->
            #_break
            endfunc.apply this,[i].concat([].slice.call(arguments))
          ,->
            #_next
            i = f_iter i
            func.apply this,arguments
          ].concat([].slice.call(arguments))
        else
          endfunc.apply this,[i].concat([].slice.call(arguments))
    ).apply this,firstarg_array

  ###
     for in Array or Object
     
     _for_in(obj, firstargs, loopfunc, endfunc)
        firstargs
           初回のloopfuncの引数を配列にして示します。
           [arg1,arg2]が渡されると、loopfunc(key,val,_break,_next,arg1,arg2)が呼ばれます。
        loopfunc(key,val,_break,_next,...)
           繰り返し実行する関数です。Objectの場合、key,valが得られます
           続行する場合_next(...)、中断する場合_break(...)を呼び出します。
           _nextの引数は、次回のloopfuncに渡されます。
           _breakの引数は、endfuncに渡されます。
        endfunc(...)
           繰り返しの終了時に呼ばれます。
  ###
  _for_in: (obj, firstargs, loopfunc, endfunc) ->
    i = 0
    isarr = ('Array' == Object.prototype.toString.call(obj).slice(8, -1))
    if isarr
      indexlimit = obj.length
    else
      indexlimit = Object.keys(obj).length
    (@.y (func) ->
      return ->
        if i < indexlimit
          if isarr
            loopfunc.apply this,[obj[i],i, ->
              endfunc.apply this,arguments
            ,->
              func.apply this,arguments
            ].concat([].slice.call(arguments))
          else
            key = Object.keys(obj)[i]
            value = obj[key]
            loopfunc.apply this,[key, value, ->
              endfunc.apply this,arguments
            ,->
              func.apply this,arguments
            ].concat([].slice.call(arguments))
        else
          endfunc.apply this,arguments
    )(firstargs)

  ###
     while Loop
     
     _while(firstarg_array, loopfunc, endfunc)
        終了条件のみを指定した繰り返しを行います。リトライループなどです。
        firstarg_array
           初回のf_judge, loopfuncの引数を配列にして示します。
           [arg1,arg2]が渡されると、loopfunc(_break,_next,arg1,arg2)が呼ばれます。
        loopfunc(_break,_next,...)
           繰り返し行う非同期処理です。
           繰り返しを中断するときは_break(...)を呼ぶと、endfunc(...)が呼ばれます。
           繰り返しを続行するときは_next(...)を呼ぶと、loopfunc(_break,_next,arg1,arg2)が呼ばれます。
        endfunc(...)
           次の処理です。上記の通り、引数を受け取ることができます。
  ###
  _while: (firstarg_array, loopfunc, endfunc) ->
    if firstarg_array?
      firstarg_array = []
    (@.y (func) ->
      return ->
        loopfunc.apply this,[->
          #_break
          endfunc.apply this,[].slice.call(arguments)
        ,->
          #_next
          func.apply this,arguments
        ].concat([].slice.call(arguments))
    ).apply this,firstarg_array

  ###
     exception handling
     
     myexc = new exc
        例外管理オブジェクトを生成します。ここではmyexcとします。
     myexc._try(block_args_array,f_block,e_array,f_catch,f_finally,f_next)
        例外スコープを開始します。実処理はblock内、例外処理はf_catch内、
        終了処理はf_finally内で行い、f_nextへと進みます。
        現在のところ、f_catch,f_finally内で_throwすると、終了処理は完了しません
        block_args_array
           blockに渡す引数を、配列にして指定します。
           [arg1,arg2]を渡すと、f_block(myexc,arg1,arg2)が呼ばれます。
        f_block(myexc,...)
           実処理を行うスコープです。このexc例外を発生しうる関数を
           呼び出すときはmyexcを渡して下さい。次へ進むときは
           myexc._finally(...)を呼べば、f_finally(myexc,next,...)が呼ばれます。
        e_array
           f_catchで処理する例外の種類を列挙した配列です。
           可読性のある文字列をお勧めします。
        f_catch(myexc,_e,...)
           _throwに渡された例外の種類と、その他の_throwに渡された引数が
           得られます。e_arrayで列挙した例外は全て処理して下さい。
           処理を終えたらmyexc._finally(...)を呼んで下さい。f_blockと同様です。
        f_finally(myexc,next,...)
           終了処理を簡潔に行います。処理を終えたら、next(...)を呼んで下さい。
           f_next(...)が呼ばれます。基本的にはmyexcを引数に指定して下さい。
        f_next(...)
           次の処理を行う関数です。
     myexc._throw(_e,...)
        例外を発生させます。引数はf_catchに渡されます。
     myexc._finally(...)
        例外スコープのスタックをpopして、ユーザ終了処理を呼び出します。
        引数はf_finallyに渡されます。
  ###
  exc: class exc
    _stack = []
    constructor: ->

    _try: (block_args_array,block,e_array,arg_catch,arg_finally,arg_next) ->
      _stack.push({e_array:e_array,_catch:arg_catch,_finally:arg_finally,next:arg_next})
      block.apply this,block_args_array

    fa = ->
      stackpop = _stack.pop()
      stackpop._finally.apply this,[].slice.call(arguments)

    _finally: ->
      stackpop = _stack.pop()
      stackpop._finally.apply this,[stackpop.next].concat([].slice.call(arguments))

    y = (func) ->
      return ((p) -> return -> return func(p(p)).apply(this,arguments)
      )((p) ->       return -> return func(p(p)).apply(this,arguments)
      )
    
    _w = (firstarg_array, loopfunc, endfunc) ->
      if firstarg_array?
        firstarg_array = []
      (y (func) ->
        return ->
          loopfunc.apply this,[->
            #_break
            endfunc.apply this,[].slice.call(arguments)
          ,->
            #_next
            func.apply this,arguments
          ].concat([].slice.call(arguments))
      ).apply this,firstarg_array

    _throw: (_e) =>
      catchset = {}
      _w [],(_break,_next)=>
        catchset = _stack.pop()
        if catchset?
          _stack.push catchset
          result = catchset.e_array.indexOf _e
          if result != -1
            catchset._catch.apply this,[_e].concat([].slice.call(arguments))
          else
            if arguments.length <= 2
              fa.apply this,[_next]
            else
              fa.apply this,[].slice.call(arguments).shift().shift().unshift(_next)
        else
          _break()
      ,->
        console.log 'uncaught exception: '+_e.toString()
        process.exit 1

)
```

#javascript版ソースコード
CoffeeScript版が安全な感じになったらこっち特有の変更もする。（余計な返値の抑制）

```js:control-structures.js
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

(function(root, factory) {
  if (typeof FORCE_CLIENTSIDE !== "undefined" && FORCE_CLIENTSIDE !== null) {
    return root.cs = factory();
  } else if (typeof define === 'function' && define.amd) {
    return define([], factory);
  } else if (typeof exports === 'object') {
    return module.exports = factory();
  } else {
    return root.cs = factory();
  }
})(this, function() {
  var exc;
  return {

    /*
       y combinator
     */
    y: function(func) {
      return (function(p) {
        return function() {
          return func(p(p)).apply(this, arguments);
        };
      })(function(p) {
        return function() {
          return func(p(p)).apply(this, arguments);
        };
      });
    },

    /*
       simple continuation
       （継続のネストが深くなりすぎるのを防ぐため、
       　または、if,switchの後、容易に合流するために用います）
       
       _(firstargs,func1,func2,...)
          引数に取った非同期関数を順次実行します
          firstargs
             func1に渡す引数を配列に納めたものです
             [arg1,arg2]を渡すと、func1(func2,arg1,arg2)が呼ばれます
             この配列の要素数は可変です
          func1(next,...)
             最初に実行する関数です。次へ進むときはこの関数内でnextを呼びます。
             next(arg3,arg4) を実行すると、func2(func3,arg3,arg4)が呼ばれます
             この引数の要素数は可変です
          func2(next,...)
             以下、同様に好きな回数だけ続けられます。
     */
    _: function() {
      var args, firstargs;
      args = [].slice.call(arguments);
      firstargs = args.shift();
      return (this.y(function(func) {
        return function() {
          var arg, passargs;
          arg = args.shift();
          if (arg != null) {
            passargs = [].slice.call(arguments);
            passargs.unshift(func);
            return arg.apply(this, passargs);
          }
        };
      })).apply(this, firstargs);
    },

    /*
       Each (simultaneous)
       
       _each(obj, applyargs_array, applyfunc, endfunc)
          ArrayまたはObjectの各要素に、並行して同じ関数を適用します
          obj
             処理するArrayまたはObject
          applyargs_array
             applyfuncに渡すその他の引数を配列にして指定します。
             [arg1,arg2]を指定すると、applyfunc(val,i,next,arg1,arg2)が呼ばれます。
          applyfunc(val,i,next,...)
             各要素に適用する関数です。objがArrayの場合、valは要素、iは序数です。
             処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて
             endfuncに渡されます。
          applyfunc(val,i,next,...)
             各要素に適用する関数です。objがObjectの場合、keyは要素のキー、valueは要素の値です。
             処理が終わったらnext(...)を呼び出します。nextの各引数は、配列に代入されて
             endfuncに渡されます。
          endfunc(...)
             結果を受け取る関数です。next('foo','bar')が2要素から呼ばれる場合、
             endfunc(['foo','foo'],['bar','bar'])となります。配列内の順序は、各処理の
             終了順です。弁別する必要のある場合、iやkeyをnextの引数に加えて下さい。
     */
    _each: function(obj, applyargs_array, applyfunc, endfunc) {
      return (function(nextc) {
        var i, isarr, j, key, len, next, num, results, results1, val;
        isarr = 'Array' === Object.prototype.toString.call(obj).slice(8, -1);
        num = 0;
        next = nextc();
        if (isarr) {
          results = [];
          for (i = j = 0, len = obj.length; j < len; i = ++j) {
            val = obj[i];
            num++;
            results.push(applyfunc.apply(this, [
              val, i, function() {
                return next.apply(this, [num, endfunc].concat([].slice.call(arguments)));
              }
            ].concat(applyargs_array)));
          }
          return results;
        } else {
          results1 = [];
          for (key in obj) {
            val = obj[key];
            num++;
            results1.push(applyfunc.apply(this, [
              key, val, function() {
                return next.apply(this, [num, endfunc].concat([].slice.call(arguments)));
              }
            ].concat(applyargs_array)));
          }
          return results1;
        }
      })(function() {
        var count, result;
        count = 0;
        result = [];
        return function(num, next) {
          var arg, args, i, j, len;
          count++;
          if (arguments.length > 0) {
            args = [].slice.call(arguments).shift().shift();
            for (i = j = 0, len = args.length; j < len; i = ++j) {
              arg = args[i];
              if (result.length <= i) {
                result.push([]);
              }
              result[i].push(arg);
            }
          }
          if (count === num) {
            return next.apply(this, result);
          }
        };
      });
    },

    /*
       for Loop
       
       _for(i, f_judge, f_iter, firstarg_array, loopfunc, endfunc)
          初期値・条件・イテレータを用いて、loopfuncを繰り返します。
          i
             カウンタの初期値です。
          f_judge(n) 返値=true,false
             カウンタに対する条件です。falseが返るとループが終了します。
             ※この関数は同期関数でなければなりません。
          f_iter(n) 返値=カウンタ値
             カウンタのイテレータです。引数がカウンタの現在の値で、返値を返すと、
             カウンタに代入されます。
             ※この関数は同期関数でなければなりません。
          firstarg_array
             loopfuncへの初回の引数を、配列で示します。
             [arg1,arg2]を渡すと、loopfunc(n,break,next,arg1,arg2)が呼ばれます。
          loopfunc(n,_break,_next,...)
             繰り返し実行する内容です。nはカウンタの現在値です。
             続行する場合、_next(...)を呼び出します。_nextの引数は、次回のloopfuncの
             呼び出しの際、引き渡されます。たとえば、_next(arg3,arg4)を呼び出せば、
             loopfunc(n,next,arg3,arg4)が呼ばれます。
             繰り返しを中断する場合、_break(...)を呼び出します。すると、
             endfunc(n,...)が呼び出されます。
          endfunc(n,...)
             繰り返しの終了後に実行される関数です。nは終了時のカウンタの値です。
     */
    _for: function(i, f_judge, f_iter, firstarg_array, loopfunc, endfunc) {
      if (firstarg_array != null) {
        firstarg_array = [];
      }
      return (this.y(function(func) {
        return function() {
          if (f_judge(i)) {
            return loopfunc.apply(this, [
              i, function() {
                return endfunc.apply(this, [i].concat([].slice.call(arguments)));
              }, function() {
                i = f_iter(i);
                return func.apply(this, arguments);
              }
            ].concat([].slice.call(arguments)));
          } else {
            return endfunc.apply(this, [i].concat([].slice.call(arguments)));
          }
        };
      })).apply(this, firstarg_array);
    },

    /*
       for in Array or Object
       
       _for_in(obj, firstargs, loopfunc, endfunc)
          firstargs
             初回のloopfuncの引数を配列にして示します。
             [arg1,arg2]が渡されると、loopfunc(key,val,_break,_next,arg1,arg2)が呼ばれます。
          loopfunc(key,val,_break,_next,...)
             繰り返し実行する関数です。Objectの場合、key,valが得られます
             続行する場合_next(...)、中断する場合_break(...)を呼び出します。
             _nextの引数は、次回のloopfuncに渡されます。
             _breakの引数は、endfuncに渡されます。
          endfunc(...)
             繰り返しの終了時に呼ばれます。
     */
    _for_in: function(obj, firstargs, loopfunc, endfunc) {
      var i, indexlimit, isarr;
      i = 0;
      isarr = 'Array' === Object.prototype.toString.call(obj).slice(8, -1);
      if (isarr) {
        indexlimit = obj.length;
      } else {
        indexlimit = Object.keys(obj).length;
      }
      return (this.y(function(func) {
        return function() {
          var key, value;
          if (i < indexlimit) {
            if (isarr) {
              return loopfunc.apply(this, [
                obj[i], i, function() {
                  return endfunc.apply(this, arguments);
                }, function() {
                  return func.apply(this, arguments);
                }
              ].concat([].slice.call(arguments)));
            } else {
              key = Object.keys(obj)[i];
              value = obj[key];
              return loopfunc.apply(this, [
                key, value, function() {
                  return endfunc.apply(this, arguments);
                }, function() {
                  return func.apply(this, arguments);
                }
              ].concat([].slice.call(arguments)));
            }
          } else {
            return endfunc.apply(this, arguments);
          }
        };
      }))(firstargs);
    },

    /*
       while Loop
       
       _while(firstarg_array, loopfunc, endfunc)
          終了条件のみを指定した繰り返しを行います。リトライループなどです。
          firstarg_array
             初回のf_judge, loopfuncの引数を配列にして示します。
             [arg1,arg2]が渡されると、loopfunc(_break,_next,arg1,arg2)が呼ばれます。
          loopfunc(_break,_next,...)
             繰り返し行う非同期処理です。
             繰り返しを中断するときは_break(...)を呼ぶと、endfunc(...)が呼ばれます。
             繰り返しを続行するときは_next(...)を呼ぶと、loopfunc(_break,_next,arg1,arg2)が呼ばれます。
          endfunc(...)
             次の処理です。上記の通り、引数を受け取ることができます。
     */
    _while: function(firstarg_array, loopfunc, endfunc) {
      if (firstarg_array != null) {
        firstarg_array = [];
      }
      return (this.y(function(func) {
        return function() {
          return loopfunc.apply(this, [
            function() {
              return endfunc.apply(this, [].slice.call(arguments));
            }, function() {
              return func.apply(this, arguments);
            }
          ].concat([].slice.call(arguments)));
        };
      })).apply(this, firstarg_array);
    },

    /*
       exception handling
       
       myexc = new exc
          例外管理オブジェクトを生成します。ここではmyexcとします。
       myexc._try(block_args_array,f_block,e_array,f_catch,f_finally,f_next)
          例外スコープを開始します。実処理はblock内、例外処理はf_catch内、
          終了処理はf_finally内で行い、f_nextへと進みます。
          現在のところ、f_catch,f_finally内で_throwすると、終了処理は完了しません
          block_args_array
             blockに渡す引数を、配列にして指定します。
             [arg1,arg2]を渡すと、f_block(myexc,arg1,arg2)が呼ばれます。
          f_block(myexc,...)
             実処理を行うスコープです。このexc例外を発生しうる関数を
             呼び出すときはmyexcを渡して下さい。次へ進むときは
             myexc._finally(...)を呼べば、f_finally(myexc,next,...)が呼ばれます。
          e_array
             f_catchで処理する例外の種類を列挙した配列です。
             可読性のある文字列をお勧めします。
          f_catch(myexc,_e,...)
             _throwに渡された例外の種類と、その他の_throwに渡された引数が
             得られます。e_arrayで列挙した例外は全て処理して下さい。
             処理を終えたらmyexc._finally(...)を呼んで下さい。f_blockと同様です。
          f_finally(myexc,next,...)
             終了処理を簡潔に行います。処理を終えたら、next(...)を呼んで下さい。
             f_next(...)が呼ばれます。基本的にはmyexcを引数に指定して下さい。
          f_next(...)
             次の処理を行う関数です。
       myexc._throw(_e,...)
          例外を発生させます。引数はf_catchに渡されます。
       myexc._finally(...)
          例外スコープのスタックをpopして、ユーザ終了処理を呼び出します。
          引数はf_finallyに渡されます。
     */
    exc: exc = (function() {
      var _stack, _w, fa, y;

      _stack = [];

      function exc() {
        this._throw = bind(this._throw, this);
      }

      exc.prototype._try = function(block_args_array, block, e_array, arg_catch, arg_finally, arg_next) {
        _stack.push({
          e_array: e_array,
          _catch: arg_catch,
          _finally: arg_finally,
          next: arg_next
        });
        return block.apply(this, block_args_array);
      };

      fa = function() {
        var stackpop;
        stackpop = _stack.pop();
        return stackpop._finally.apply(this, [].slice.call(arguments));
      };

      exc.prototype._finally = function() {
        var stackpop;
        stackpop = _stack.pop();
        return stackpop._finally.apply(this, [stackpop.next].concat([].slice.call(arguments)));
      };

      y = function(func) {
        return (function(p) {
          return function() {
            return func(p(p)).apply(this, arguments);
          };
        })(function(p) {
          return function() {
            return func(p(p)).apply(this, arguments);
          };
        });
      };

      _w = function(firstarg_array, loopfunc, endfunc) {
        if (firstarg_array != null) {
          firstarg_array = [];
        }
        return (y(function(func) {
          return function() {
            return loopfunc.apply(this, [
              function() {
                return endfunc.apply(this, [].slice.call(arguments));
              }, function() {
                return func.apply(this, arguments);
              }
            ].concat([].slice.call(arguments)));
          };
        })).apply(this, firstarg_array);
      };

      exc.prototype._throw = function(_e) {
        var catchset;
        catchset = {};
        return _w([], (function(_this) {
          return function(_break, _next) {
            var result;
            catchset = _stack.pop();
            if (catchset != null) {
              _stack.push(catchset);
              result = catchset.e_array.indexOf(_e);
              if (result !== -1) {
                return catchset._catch.apply(_this, [_e].concat([].slice.call(arguments)));
              } else {
                if (arguments.length <= 2) {
                  return fa.apply(_this, [_next]);
                } else {
                  return fa.apply(_this, [].slice.call(arguments).shift().shift().unshift(_next));
                }
              }
            } else {
              return _break();
            }
          };
        })(this), function() {
          console.log('uncaught exception: ' + _e.toString());
          return process.exit(1);
        });
      };

      return exc;

    })()
  };
});
```