Fri Feb 27 13:10:24 JST 2015

インターンシップの経験をブログに書くことが要請されているらしいので下書きをここに書こう。

express v.4.12.0 を用いた簡易サーバ

以下はミドルウェア (後述) を用いない、 / への POST を読んで、 ヘッダを含めて受け取ったデータをそのまま返すだけのサーバ

express = require 'express'

app = express()

app.post '/', (req, res) ->
  buf = ''
  req.on 'data', (chunk) ->
    buf += chunk
  req.on 'end', ->
    res.end buf

app.listen 3030, (->)

これを動かして、 curl コマンドでのリクエストの飛ばし方をいくつか紹介する:

   echo HOGEHOGE | curl -X POST -F 'hoge=<-' localhost:3030
--------------------------1bee3ed01c9645db
Content-Disposition: form-data; name="hoge"

HOGEHOGE

--------------------------1bee3ed01c9645db--
   echo HOGEHOGE | curl -X POST -F 'hoge=@-' localhost:3030
--------------------------be462c8006a6ea41
Content-Disposition: form-data; name="hoge"; filename="-"
Content-Type: application/octet-stream

HOGEHOGE

--------------------------be462c8006a6ea41--
   curl -X POST -d hoge=fuga localhost:3030
hoge=fuga
   curl -X POST -d name=cympfh -d pwd=koregacympfhnopwd localhost:3030
name=cympfh&pwd=koregacympfhnopwd

なるほど、自力でパーサを書くのは辛そうである

ミドルウェアであるところの body-parser v.1.12.0 を噛ませる

express = require 'express'
bodyParser = require 'body-parser'

app = express()
app.use bodyParser.urlencoded { extended: false }
app.use bodyParser.json()

app.post '/', (req, res) ->
  res.json req.body

app.listen 3030, (->)
   echo HOGEHOGE | curl -X POST -F 'hoge=<-' localhost:3030
{}

-F で送る form-data はどうやら express では無視されるらしい? (cf. Node.jsのExpressでmultipart/form-dataを処理する方法 - himetani's blog)

   curl -X POST -d 'name=cympfh' localhost:3030
{"name":"cympfh"}
   curl -X POST -d 'name=cympfh&password=myStr0ngPassWord' localhost:3030
{"name":"cympfh","password":"myStr0ngPassWord"}

非同期的処理

nodeで動かすとはつまり非同期的処理を湯水のように行うこと

express = require 'express'
bodyParser = require 'body-parser'

app = express()
app.use bodyParser.urlencoded { extended: false }
app.use bodyParser.json()

asyncFn = (cont) ->
  setTimeout (-> cont { ok: 'ok' }), 2000

app.post '/', (req, res) ->
  asyncFn (data) ->
    res.json data

app.listen 3030, (->)

さて、η-変換とは、(lambda (x) (f x))f が等価であることをいう. CoffeeScript ないし JavaScript ではどうか

express = require 'express'
bodyParser = require 'body-parser'

app = express()
app.use bodyParser.urlencoded { extended: false }
app.use bodyParser.json()

asyncFn = (cont) ->
  setTimeout (-> cont { ok: 'ok' }), 2000

app.post '/', (req, res) ->
  asyncFn res.json # i found a bug here

app.listen 3030, (->)

これを実行する間に適切なPOSTを投げると、 これは次のようなエラーを吐いて死ぬ:

/usr/local/lib/node_modules/express/lib/response.js:226
  var replacer = app.get('json replacer');
                     ^
TypeError: Cannot call method 'get' of undefined
  at json (/usr/local/lib/node_modules/express/lib/response.js:226:22)
  at [object Object]._onTimeout (/Users/cympfh/test/test.coffee:9:18)
  at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)

最終発表の日、 私がのんびり会場に着いて、 鯖が何も返してこないとメンバに指摘され、 いやいやログをみるとこんなエラーだったから、 問題の箇所を見つけるに相当の苦労を要した。 (わかってくれるでしょう?)

asyncFn = (res) ->
  setTimeout (-> res.json { ok: 'ok' }), 2000

app.post '/', (req, res) ->
  asyncFn res

これはうまく動く。 もしかしたら、 res.json はresというオブジェクトのメソッドを読んで それはダイナミックスコープなのか?

Obj =
  i: 1
  read: -> @i

console.warn Obj.read() # 1

async = (fn) ->
  setTimeout (-> console.warn fn()), 2000
async Obj.read # undefined

世界が平和でありますように。

追記; Mon Dec 28 01:48:15 JST 2015

教訓は、 ラムダ計算のレベルで成り立つことが別な言語で成り立たなくたって、 誰を恨んでもいけないということである. 明らかに、普通プログラミング言語と呼ばれるものはラムダ式よりも高い表現力を持つ. 例えばパイ計算はラムダ項を表現できるがそれ以上の表現力を持つ. (一般に) (型の無い) パイ計算ではエータ変換は成立しない.