CoffeeScriptの文法拡張して非同期でネストが深くならないようにしてみた

コールバックごりごり書いてるとネストが深くなって嫌だね〜
ということで深くならない用な記法を追加するプリプロセッサをでっち上げる、というテスト。

CoffeeScript自体がJSの文法を拡張するものだから、自分で追加してもいいじゃん、ということでソースを覗いてみたが、エイリアスを追加するならともかく、新しい概念を継ぎ足すのはちょっと大掛かりっぽいので、簡単なパーサ書いてみるだけにした。

なにがやりかったというと

<- と next がペアになって非同期の結果を受け取る

fs = require 'fs'
main = ->
  err,txt1 <- fs.readFile 'hello1.txt', next
  err,txt2 <- fs.readFile 'hello2.txt', next
  err,txt3 <- fs.readFile 'hello3.txt', next
  console.log i.toString() for i in [txt1,txt2,txt3]
main()

これをコンパイルするとこうなる(まだCoffeeScript)

fs = require 'fs'
main = ->
  fs.readFile 'hello1.txt', (err,txt1)->
    [err,txt1] = (v for _,v of arguments)
    fs.readFile 'hello2.txt', (err,txt2)->
      [err,txt2] = (v for _,v of arguments)
      fs.readFile 'hello3.txt', (err,txt3)->
        [err,txt3] = (v for _,v of arguments)
        console.log i.toString() for i in [txt1,txt2,txt3]
main()

ソース

超適当

mycf

#!/usr/bin/env coffee 
lines = require('fs').readFileSync(process.argv[2]).toString().split('\n')

async_symbol = '<-'
next_symbol = 'next'

ret = []
depth = 0
params = null
start = null

ret = []
for i in [0...lines.length]
  line = lines[i]
  prefix = line.match(/^\s+/)?[0].length or 0
  str_prefix = (' ' for _ in [0...(prefix+2*depth)]).join('')

  if params
    ret.push str_prefix + "[#{params}] = (v for _,v of arguments)"
    params = null
  if line.indexOf(async_symbol) > -1
    [left,right] =  line.split(async_symbol)
    params =  left.split(" ").join("")
    ret.push str_prefix+right.replace(/\s*/,'').replace(next_symbol, "(#{params})->")
    if start is null
      start = depth
    depth++
  else
    a = lines[i].match(/^\s+/)?[0].length or 0
    b = lines[i-1]?.match(/^\s+/)?[0].length or 0
    if i>0 and a < b
      depth = start
      start = null

    str_prefix = (' ' for _ in [0...(prefix+2*depth)]).join('')
    ret.push str_prefix+line.replace(/\s*/,'')

console.log ret.join('\n')

保存

$mycf hogehoge.async.coffee > hogehoge.coffee

欠点

  • 引数の最後がコールバック関数のものしか書けない
  • 並列実行の仕組みとかない
  • 一度 <-で並列関数でよんだらネスト元に戻せない

cakefileに混ぜて使うぐらいには使えるかなー
そういう文法の提案があったっていいよね。