HaskellでWebサービスをやるときに足りないと思うこと

Yesodでもscottyでもいいから、現実的にHaskellWebサービスをやりたいと思ったときに、何が足りないかなあと考えると、ホットデプロイ(サービスを止めないでアップデートする仕組み)が足りないんだと思う。
ステートレスもしくはステートを常に再構築可能なシステムなら簡単にホットデプロイできる枠組みをメインに組み込んだWebサービスプラットフォームって既に誰か作っていないんだろうか。私が知らないだけ?
少なくともyesod-binでやっているのと同じ方法を応用すれば可能だと思うんだけど。

http://hackage.haskell.org/package/yesod-bin

ちょっと中身を見てみた感じでは、別のポートでアプリを動かしてリバースプロキシーを立ててるっぽい? これと似たような方法なら割と何とかなりそうな気がする。内部向けのポートを2つ交互に使うようにしてしまうとかでもいいし。
(ビルド中以外でも)効率が多少落ちるのは我慢するとして、それ以外にproductionとしてこの方法を使うデメリットがあったりするんだろうか?
ビルド中はメモリとかはけっこう食う可能性がありそうね。ちゃんと調べてないけど。

http-conduitをWindowsで使う

http://hackage.haskell.org/packages/archive/http-conduit/1.9.4.4/doc/html/Network-HTTP-Conduit.html
の一番上に出てくるサンプルのコードを動かそうとしてもWindowsではエラーが出る。
上から三番目のサンプルを読めば書いてあるけど

Any network code on Windows requires some initialization, and the network library provides withSocketsDo to perform it. Therefore, proper usage of this library will always involve calling that function at some point. The best approach is to simply call them at the beginning of your main function, such as:

というわけでwithSocketsDoを別途行う必要がある。
うーん。こういうのは最初に出てくるサンプルに含めておいてほしいなあ。
ちゃんと読めよって言われればそれまでだけど、とりあえず一番上のサンプルをコピペして実行してみることができるってのは割と重要だと思うんだけど。みんなWindowsで使ってないのかな。

可変長引数とか

http://d.hatena.ne.jp/asakamirai/20130202/1359811868
の延長で、まだまだ理解が甘いようなので、可変長引数について調べている。

http://togetter.com/li/221528

ここにまとまっている内容がとても参考になりそうなのだけど、論文みたいなのを読みといて実践していくのは正直つらい。時間が。。。。
うーん。我ながらへぼいなあと思うものの、同時にこういう言語の制限レベルの話が言語学習の壁になってるところがHaskellのハードルをあげているんだろうなあとも思う。
rubyとかほとんどこういうところでは引っかからないものなあ。

とりあえず、PolyKindsはどういうものか見ておいた方がよさそう。
http://www.kotha.net/ghcguide_ja/latest/kind-polymorphism.html

HaskellでWPF(するための準備中)

http://d.hatena.ne.jp/asakamirai/20130104/1357324954
で書いてた.Net側のラッパーコードがだいぶ自動生成できるようになった。とりあえずvaluteType以外はビルドが通るようになったが、多分ビルドが通るだけで動かない場所は多そう。てか、Haskell側がまだ完全に手動な状況。

てか、自動生成してはじめて気づいたけど、こんなエラーあるのね。はじめて見た。
http://msdn.microsoft.com/ja-jp/library/at5879hx(v=vs.80).aspx

実際いくつくらいつっこもうとしたんだろうと思って数えてみたらとりあえず関数だけで84000あった。
派生部分とかあわせればもっとになるはず。
スーパークラスのメソッドとかも全部個別に生成してるせいもあるんだろうなあ。
ここらへんはHaskell側からどうみせるか次第か。スーパークラスを全部型クラスにしてみせるのかとか、Haskell側でどのように書かせるのかとか次第だなあ。
とりあえずコントロール系に絞って先へ進めてから考えよう。

haskellの実行速度

mrubyの速度測ってみたついでにHaskellって速いんだっけ? と気になって試してみた。

http://itpro.nikkeibp.co.jp/article/COLUMN/20070403/267180/?P=2

実際、現在のGHCは、正格のプログラムを書いた場合でも、Cでループを使ったプログラムと比べてそん色のない実行速度を持つコードを生成できます

ほーそうなのか、ということで、上記のリンクに記載されているコードを参考に以下のようなコードを書いてみるも、C言語と比較して断然遅い。実行時間にして40倍くらい差があるように見える。

import Data.List

sum' :: (Num a) => [a] -> a
sum' = foldl' (+) 0

main :: IO ()
main = putStrLn $ show $ sum' [0..1000000000]

具体的にはC言語で3秒に対してHaskellで120秒もかかってしまった。
ちなみにrubyでテキトーに書くと240秒くらいでできてしまった。動的型言語の2倍しか早くないのはなんだかしょんぼり。

うーん。と思って以下を読んでいたところ、
http://togetter.com/li/122555

C/C++と同じデータ構造を用い、同じアルゴリズムを用いている限りは、同程度の速度の実行ファイルが出来上がる可能性が高いです。

まずリスト使ったら遅いですし、

という発言を見つけた。
ああ、なるほどそういうことか。と思って、とりあえず以下のようにIntを使うようにした。

import Data.List

sum' :: (Num a) => [a] -> a
sum' = foldl' (+) 0

sumi :: [Int] -> Int
sumi = sum'

main :: IO ()
main = putStrLn $ show $ sumi [0..1000000000]

これで、12秒。10倍高速になった。
(答えがIntを超えてるので正しい答えにはならない。が、速度だけ知りたいのでそこは無視。本来無視すべきではないけどなんとなくの速度感が知りたいだけなので)。

でもまだC言語とは4倍の差があるので、そこらは上記の発言を信じるならリストが遅いということになる。
ということでやっている計算はちょっと変わるけど、以下のようにしてみた。

sumii :: Int -> Int -> Int
sumii 1000000001 x = x
sumii i          x = sumii (i+1) (x+i)

main :: IO ()
main = putStrLn $ show $ sumii 1 0

これで3秒。確かにC言語と同等の速度になった。というかほんのわずかだがC言語より早かった。(といっても、そこまで見るならもうちょい厳密にいろいろ整えるべきだと思うので、ここではだいたい同等になったという結論にしておく)
ちなみにghcに-O2とかオプションつけないと末尾再帰を展開してくれないみたいなのでスタックがあふれる点に注意。

というわけでこの内容でまとめも何もないのだがまとめると、HaskellをC並の速度で使いたければ、以下の点に注意すればいいと思う。

  1. プリミティブな型を使う
  2. 不要なリストは避ける

まあ、速度を気にするシーンってそんなにないし、あってもホットスポットだけしか最適化しないと思うけど。

(追記:コードにコピペみすってるところがあったので一応修正)

mrubyの実行速度

ふと思い立って、「mrubyって軽量とはいうけど、実行速度はどうなんだろう。普通のrubyより速いのか遅いのか」ということが気になったのでcygwinでビルドしてみた。
bygwinならmake一発でビルドできるらしいのだが、うちではなぜかうまくいかなかった(CC=gcc-3、LD=gcc-3とか環境変数を指定したら通るようになった)
適当な数え上げ系のループ処理を書いて計算させてみたところ、rubyの倍くらいの時間がかかった。普通に使っているrubycygwin環境でビルドしたわけでもないしmrubyは最適化もかけてないところなどを考えると、実行速度としてはrubyと大して変わらないと考えていいのかもしれない。
(個人的にはrubyより速いならそっちをメインに考えてもいいんじゃねーの、というところが気になってただけなのでそれができないならだいたいの速度感がわかればOKという感じ)

Haskellで引数の型を可変に

C++やら.Netやらでよくあるような同じ名称だけど異なる引数を持つ関数(正しい名称は忘れたけど)ってHaskellでできないんだろうかと思って調べた。
結論としては関数も型なのでできるのだが、FlexibleInstances拡張を入れる必要があるっぽい。
以下がすごい適当な例。

{-# LANGUAGE FlexibleInstances #-}

class TestFunc a where
    testFunc :: a

instance TestFunc Integer where
    testFunc = -1
instance TestFunc String where
    testFunc = "test!!"
instance TestFunc (() -> Integer) where
    testFunc a = -2
instance TestFunc (Integer -> Integer) where
    testFunc a = a + 1
instance TestFunc (Integer -> Integer -> Integer) where
    testFunc a b = a + b + 1
instance TestFunc ((Integer, Integer) -> Integer) where
    testFunc (a, b) = a + b - 1

ただ、これをやると、どのケースに該当するのか、型がなかなかうまく推論されてくれないので、ちょっと利便性は落ちる感じだなあ。
もっと便利に使う方法とかないのだろうか。

カリー化とか考えなくて、ある程度まで形が決まってるなら、以下みたいにやる方が型推論はうまく働いてくれるっぽい?

{-# LANGUAGE FlexibleInstances #-}

class TestF a where
    testF :: Integral b => a -> b

instance TestF Integer where
    testF a = fromIntegral $ a + 1
instance TestF Int where
    testF a = fromIntegral $ (toInteger a) + 1
 -- 以下だとUndecidableInstances拡張が必要。
 -- なんとなく理解はできるけどちょっと気持ち悪いなあ。
 -- instance (Integral a) => TestF (a) where
 --     testF a = fromIntegral $ (toInteger a) + 1
instance (Integral a, Integral b) => TestF (a, b) where
    testF (a, b) = fromIntegral $ (toInteger a) + (toInteger b) + 1
instance (Integral a, Integral b, Integral c) => TestF (a, b, c) where
    testF (a, b, c) = fromIntegral $ (toInteger a) + (toInteger b) + (toInteger c) + 1
instance TestF String where
    testF s = fromIntegral $ 0

いろいろ試してみると型推論が効いたり効かなかったりする。理解できるものもあるけど、理解しがたい結果もある。
うーむ。まだ理解が甘いってことだろうか。