2013/08/16

ApacheでWebSocketを通すリバースプロキシ(mod_proxy_wstunnel)

mod_proxyがWebSocketを通してくれないので困っていましたが,Apache 2.4.5になってmod_proxyがWebSocketも通してくれるようになるモジュールmod_proxy_wstunnelが出来たようです.

まずはApacheのバージョンを2.4.5以降にあげてください.Apache 2.2のままやる方法もあるそうですが知りません.
そうしたらhttpd.confに次のような行が追加されると思います.コメントアウトされていたら外してください.
LoadModule proxy_wstunnel_module libexec/apache24/mod_proxy_wstunnel.so
最後にプロキシの設定をします.
ProxyPassなどは上から順にマッチさせていくので,厳しい条件から書かなくてはいけません.また,httpとwsの2つのプロトコルを同じパスで共存させることはできないようですので,うまくhttpとwebsocketのパスを分けてマッチさせる必要があります.
ProxyPass /socket.io/1/websocket/ ws://81.la:8080/socket.io/1/websocket/
ProxyPass / http://81.la:8080/ retry=3
ProxyPassReverse /socket.io/1/websocket/ ws://81.la:8080/socket.io/1/websocket/
ProxyPassReverse / http://81.la:8080/
上はNode.jsのWebSocketライブラリであるsocket.ioのサーバの設定例です.httpもwsも喋るNodeアプリを8080番で起動し,8080番ポートは開かずに80番のApacheがクライアントとの間を仲介するというようなユースケースです.websocket通信が/socket.io/1/websocket/以下で行われるという前提を利用して条件が書かれています.

2013/07/16

zipWithフィボナッチ関数は正格評価でも書ける

fibs = 0:1:zipWith (+) fibs (tail fibs)
Haskellでフィボナッチ数列を返す関数の一例.2番めを取りに行くとfibsの0番目と1番目を足したものが返り,3番目を取りに行くとfibsの1番目と2番目を足したものが返るというややキモい流れで,これをもって遅延評価怖いとする声があるが,実はこれは正格評価でも問題なく動く.

正格評価で結果を得るには基底部を用意して有限列で止める必要があるため,fibs関数の引数でカウントダウンするようにする.(fibs(n)=フィボナッチ数列のn番目という意味ではないので注意)

function fibs(n){
    return n<=0 ? [] : [0,1].concat(zipWithPlus(fibs(n-1), fibs(n-1).slice(1)));
}

function zipWithPlus(arr1, arr2){
    return arr1.map(function(e, n){return e+arr2[n]}).slice(0,arr2.length);
}

nが0になったら空配列を返す以外はHaskellの場合と変わっていない.

なお,Wikipediaにはこのfibsの定義を「共再帰の例」と書いてあるが,corecursionの訳としては余再帰が正しい.(coに対するcontraが存在すれば「共〜」と「反〜」という訳が当てはまる.)余再帰とは圏論でいう再帰の双対概念である.圏論勉強会 at ワークスアプリケーションズで聞きかじったところによると,F始代数にあたるデータ型を帰納的データ型(inductive data type),F終余代数にあたるデータ型を余帰納的データ型(coinductive data type)と呼び,前者が有限,後者が無限のデータ型を扱えるらしい.Wikipediaではcorecursionの実例としてこのfibsを与えているが,要は無限データ列を返す再帰関数なら全部corecursionなのだろう.

2013/05/28

オブジェクト指向言語とHaskellの対応関係について

どうも,Haskellerかつ圏論愛好家の卵です.
すごいH本をひと通り読み終えて次はReal World Haskellを読み始めています.

Haskellは関数型言語ながらオブジェクト指向と共通するものを感じたので,その比較・対応関係をまとめました.

HaskellJava
クラス・メソッド定義データ型(ここではレコード)
data Human = Human{
  name::String,
  age::Int,
  sex::Sex
}

関数
sayHello :: Human->String
sayHello h = "my name is "++(name h)
クラス
class Human{
  string name;
  int age;
  Sex sex
  Human(nam,ag,se){
    name=nam;
    age=ag;
    sex=se
  }
  //メソッド
  sayHello(){
    return "my name is"+name;
  }
}
列挙型データ型
data Sex = Male | Female
列挙型
enum Sex { Male, Female }
インスタンス・メソッド呼び出しデータ
let uhyosan = Human{name="uhyo",age=18,sex=Male}

関数適用
sayHello uhyosan
インスタンス
Human uhyosan = new Human("uhyo",18, Male);

メソッド呼び出し
uhyosan.sayHello();
クラスの性質型クラス定義
class Runner a where
  run :: a->Int
  run' _ = 0 --既定の実装も書ける

型のインスタンス化
instance Runner Human where
  run _ = 1
インターフェイス定義
interface Runner{
  int run();
}

クラスの実装
class Human implements Runner{
  int run(){
    return 1;
  }
}
性質の継承型クラスの継承
class (Runner a, RoadRacer a, Swimmer a) => Triathlete a where
  triathlon :: a->Int
インターフェイスの継承
interface Triathlete extends Runner, RoadRacer, Swimmer{
  int triathlon(){
    return 2;
  }
}
抽象データ型抽象データ型
(LeafとNodeがモジュールの外から見えず,別の関数で生成する場合)
data Tree a=Leaf|Node a a
makeTree :: [a] -> Tree a
makeTree = ~

データ生成
let tree = makeTree [1,2,3]
ファクトリパターン
(コンストラクタが隠されファクトリが見える場合)
class Tree{
  private Tree(){}
  public static make(){
    return new Tree()
  }
}
インスタンス生成
Tree t = Tree.make();
(抽象クラス)(抽象データ型の"抽象"は,その型のデータを持てないという意味ではない)抽象クラス
abstract class Shape{
  abstract void draw();
}
(クラスの継承)(型間の継承はない)
(抽象クラスとそれを継承するクラスのようにも見える例)
data Shape = Circle Int Int Int | Rectangle Int Int Int Int
クラスの継承
class Circle extends Shape{
  void draw(){~};
}
class Circle extends Shape{
  void draw(){~};
}
多相データデータ型の引数
data MyList a = MyNil | Cons a (MyList a)

--MyList Human型 …Humanに固定
Cons uhyosan MyNil

--Num a=>MyList a型 …多相性を残している
Cons 1 (Cons 2 (Cons 3 MyNil))

--DoubleもIntもNumのインスタンスだが
--別のRunnerとは交換不可
Cons (1::Double) (Cons (2::Int) MyNil)

ジェネリクス
class MyList<T>{
  T data[10];
  void set(T t){
    data[0]=t;
  }
}

//Humanに固定
MyList<Human> ml = new MyList<Human>();
ml2.set(uhyosan);

//多相性を残している
MyList<Runner> ml2 = new MyList<Runner>();
ml2.set(uhyosan);

//別のRunnerと交換可能
class Robot implements Runner{int run(){return 100;}}
ml2.set(new Robot())
Haskellの型クラスは,オブジェクト指向言語でいう「既定の実装が書けるinterface」または「多重継承の出来るabstract class」と言えると思います.多くの単一継承の言語経験者にとっては「型クラスとはインターフェイスである」という表現がしっくり来るのではないでしょうか.

2013/04/30

Haskellで多項間比較演算子

Haskell入門しています.
すごいHaskellたのしく学ぼう! よいですね.最初のほうでHaskellの機能が紹介されていて面白い機能があるんだなあと思ったら,後半でファンクタ,アプリカティブファンクタ,モナドなどが尽くそれらの機能を統一的に説明し,面白い機能とはただの糖衣構文であったということを思い知らされます.まだ読みきっていませんが非常にわかりやすく,かつ引き込まれる内容だと思います.おすすめです.

さて,Schemeでは<が多引数関数となっていて
(< 1 2 3) ; 1<2かつ2<3
のような記述ができるそうです.Haskellに(<)は2引数関数だけど 1 < 2 < 3みたいに書きたいなあ.Haskellに出来ないはずはない(断言).

ひとつには,リストを引数としてそれらが昇順だったらokと言う関数myLTが考えられます.
myLT::[Int]->Bool
myLT = fst . foldl f (True, minBound)
  where f (accBool, accNum) num = if accBool then (accNum<num, num) else (False, num)

myLT [1,3,5] -- True
myLT [1,3,2,4] -- False
アキュムレータを (今のところ昇順かどうか, 直前の値) :: (Bool, Int) として畳み込んでいます.初期値のminBoundはどんな値よりも小さいCのINT_MINのようなものです(本当はJSの-Infinityのほうが真に小さい).
でも,「ファンクタ,アプリカティブファンクタ,モナド等は値を文脈で包むものだ,Maybeは失敗するかもしれないという文脈を表し,リストは結果がどれか1つに決まらない非決定性の文脈を表すものだ」と習いました.要素をリストで渡すのはモナド的にはおかしいのでは?

そこで2つめの実装をしてみました.仮想的には,
1 < 2 < 4 < 3
こんなことがしたいです.モナドでは,
return 値 >>= (値を取り文脈に包まれた値を返す関数) >>= (値を取り文脈に包まれた値を返す関数)...
のように左から右に食わせていくことが出来たので,Maybeのような感じで昇順でない2項を発見した段階でNothingに落とすような流れが考えられます.
data OrderingOrNot a = OutOfOrder | OrderingForNow a deriving (Show)
OutOfOrder .< _ = OutOfOrder
(OrderingForNow a) .< b = if a<b then OrderingForNow b else OutOfOrder

OrderingForNow 1 .< 2 .< 4 -- OrderingForNow 4
OrderingForNow 1 .< 0 .< 4 .< 3 -- OutOfOrder
まずはOrderingOrNotというデータ型を用意していますが,これはMaybeと瓜二つです.今までのところ昇順になっていればOrderingForNowに直前の値が入り,それ以前に照準でない箇所があれば以降はOutOfOrderのみを持ちます.これで仮想的なコードにそっくりな演算子が出来上がりました.
なおMaybeでやっていれば,パターンマッチに失敗した時のfailの実装が「Nothingを返す」なので,2行目がなくて済みます.モナドのインスタンスにしてfailを実装したいところですね.

ところで,この.<ができるOrderingForNowはファンクタ,アプリカティブファンクタ,モナドのどれなのでしょう.それぞれの演算子の型を見比べてみます.
fmap :: (a -> b) -> f a -> f b -- ファンクタ
(<*>) :: f (a -> b) -> f a -> f b -- アプリカティブファンクタ
(>>=) :: m a -> (a -> m b) -> m b -- モナド
(.<) :: Ord a => OrderingOrNot a -> a -> OrderingOrNot a -- OrderingOrNotを送っていく何か
ファンクタは,値から値への関数で,文脈から文脈へ値を送りました.
アプリカティブファンクタは,文脈に入った値から値への関数で,文脈から文脈に値を送りました.
モナドは,値から文脈に入った値への関数で,文脈から文脈に値を送りました.
でも.<は,文脈に入った値と値を使って別の文脈に入った値を作っています.ほかの3つと違って関数を受け取っていません.
多分.<が両側を受け取る関数を吸収してしまっているためで,うまく分離すれば関数と>>=等になって,関数のほうはモナド等になれるのではないかと思います.が,いまいちピンときていません.

また追記すると思います.

4/30 15:00
更に読み進めた所,モナド版畳み込みであるfoldMを知りました.
foldMにより,前者のfoldlを使った実装をタプルではなくモナドの文脈に包むことが出来,より直感的で単純な書き方ができます.
import Control.Monad
myLTM::[Int]->Bool
myLTM = toBool . foldM f minBound  
    where f acc num = if acc<num then Just num else Nothing
          toBool (Just _) = True
          toBool Nothing = False

myLT [1,3,5] -- True
myLT [1,3,2,4] -- False
fが値を文脈に包まれた値に写しています.実にモナドらしいですね.

2013/03/07

レジューム付きアップローダ作った

DnD対応しててWebSocketで送ってレジューム機能にも対応したアップローダ150行足らずで書いたよ
https://github.com/na2hiro/uploader

レジュームが欲しかったので作った.


2013/01/12

MacBook Pro Retina 15inchを購入

このたびMacBook Pro Retinaの15inchを購入.
3年前に買ったMacBook Air (Mid 2009)のメモリ不足(2GB)が決定打となりポチりました.
カスタマイズは,CPU標準(2.3GHz Core i7 Quad),メモリ最大(16GB),SSD標準(256GB).
MBP Retina 15inchの理由は,
  1. 画面が大きいのが良かった.持ち運べるのならいくらでも大きいワークスペース欲しい.
  2. メモリをなるべく多く(16GB).MBAで耐えられなくなったのがメモリ不足なので,同様の悩みを壊れるまで持たなくてすむように.
  3. 非RetinaのProは重い(Retina 2.0kgに対し非Retina 2.5kg.)
  4. SSD(熱とか音とか)
など.実家で親がMBP Retina 15を使っていたので,重さや大きさなど確認できてよかった.

さて,開封写真並べます.

到着.

 箱がAirの時くらい薄っぺらい.

Hello, world 

本体とACアダプタとミニ説明書だけ. 

薄すぎ.

開いた様子.13インチMBP/MBAと違ってキーボード左右にスピーカーがある.

電源つける.

 初期設定中,モーダルメッセージが二重に出て,一方消してももう一方が消せなくなった.大丈夫か.
無事完了.右上は偽Growl通知センター.

MBP Retina 15 と MBA 13.薄さは負けていない.

数日使った印象では

  • Retina綺麗すぎワロタ
  • やはり画面大きくて良い
  • 空きメモリ2桁ギガ圧勝
  • バッテリMax残量が公称7時間よりなぜか多い
  • 音が良い(旧MBAではキーボードの右手の直下あたりに1つだけだった)
  • 仮想マシン複数起動してもへっちゃら
  • ファンあまりない,稼働しても静か
と,文句なし.バリバリ使っていきたい.
旧Airはおでかけ用になりそうです.

2013/01/01

JavaScript S式パーサ "S.js"を公開

General Game Playing (そのうち話す)の中でHTTPでS式をやりとりする話が出てきたのですが,JavaScriptによるS式のパーサが見当たらないので書いてみました.
https://github.com/na2hiro/S.js

パーサジェネレートはJisonを使用.
そのパーサはTypeScriptで書いたオブジェクトをツリー状に構成して返すようにしました.
Jisonの出力したパーサのjsと,TypeScriptのコンパイル後のjsを,単純につなげるだけでnodejsでrequire可能なファイルになっていて分かりやすかったです.外から見えるようにしたいもの(exportしたいもの)についてはtsファイル中の関数宣言または変数名にexport修飾子をつけるだけでok.

内容ですが,
(1 2 (3 . (4)) 5)
といったS式をパースし,
["1", "2", ["3", "4"], "5"] //配列をリストとみなすList array方式
["1", ["2", [["3", ["4", []]], ["5", []]]]] //配列を対とみなすCons array方式
のような配列を返したり,またその逆で配列をS式の文字列に直したりできたりします.

6年ぶり2度目のテスト

投稿テスト