2012年に読んだ本

今年読んだ本で良かったもの。

すごいHaskellたのしく学ぼう!

これはピカイチだった。RWHで停滞感が漂いまくっていた僕のHaskell理解力がかなり上がったのは間違いない。そういえば、最近Haskellしか書いてないなぁと2012年のエントリに付けられたタグを数えたらHaskell 90, Python 70, javascript 44だった。

ProductName すごいHaskellたのしく学ぼう!
Miran Lipovača
オーム社 / 2940円 ( 2012-05-23 )


あとはtwitterで色々教えてもらったりとか、三島Haskell無名関数の会が出来てモチベーションが上がったりとか色々タイミングが良かったということもあるが。来年も引き続きハスケりたい。

型というか、閉じている、自己同型といったイメージは数学ガールが良いかも

ProductName 数学ガール ガロア理論 (数学ガールシリーズ 5)
結城 浩
ソフトバンククリエイティブ / 1995円 ( 2012-06-01 )


チケット駆動開発

自分の仕事にアジャイルな要素を入れたいというのはここ数年ずっと考えていて、やっと来年すこし取り組めそうで嬉しい。

僕のチケット駆動に対する期待は、創薬研究への応用なので、チケット駆動開発の背景にある考え方がぎっしり詰まった本書は、色々な発見や再発見があったり、今の仕事のアナロジーを見つけたりとかなり満足度の高い本だった。

ProductName チケット駆動開発
小川 明彦
翔泳社 / 3444円 ( 2012-08-24 )


今年システムを少し運用してみて、意識の高低のバラツキを吸収する仕組みとしてゲーミフィケーション的なものも考えて行かないといけないし、受動的な情報伝達手段も考えて行かないといけないなぁと感じた。

(そもそも潜在的に)意識の高いマネジメント層は、能動的に情報アクセスしない研究者層(というより労働者層)が存在することを理解できないので、「そんなのホームに登録しておけばいいだけなんんじゃないか?」なんて言うんだけど、それすら能動的な情報アクセスなんだよなぁ。

デジタルサイネージのようなものにも手を出してみたい。

ProductName 幸せな未来は「ゲーム」が創る
ジェイン・マクゴニガル
早川書房 / 2940円 ( 2011-10-07 )


JavascriptとTitanium Mobile

今年はクライアントサイドのMVCも熱かった。去年Javascriptを勉強してた時には、まさかiPhoneアプリの方に進んでいくとは思わなかったが。

ステートフルJavaScriptはjavascript MVCフレームワークの本でSpine.jsの解説に近い。そしてこの知識はTitanium MobileでJavascriptを使ったiPhoneアプリ開発で役立つ!

AlloyはTitaniumのためのMVCフレームワークでBackbone.jsを使ってつくられている。これを使うとjavascriptを利用してiPhoneアプリとかAndroidアプリが作れちゃうわけだ(下のエントリ参照)。

バージョンあげたら実機転送がうまくいかなくなって、最近は停滞気味ですが、来年はもうちょっと力を入れて取り組みたいと思っている(なんかアプリをリリースしたい)。

それから僕はCoffeeScriptが好きなので使っていますが、他にもNode.jsのテンプレートエンジン(Jade,eco,ejs)なんかも使えるので好みの開発スタイルを探求するためにCoffeeScriptやNode.jsの入門書もあわせて読んでおくとよいかも。

ProductName サーバサイドJavaScript Node.js入門
清水俊博
アスキー・メディアワークス / 3990円 ( 2012-10-26 )


過去に読んだ本

snapでさくっと作るウェブサーバー

snap init しないで動かす場合は、ハンドラとルータを書いてquickHttpServeで動かせばいい。

ハンドラの型がSnap ()で、route関数をパスとハンドラの組みのリストに適用した結果もまたSnap ()型なのはなんとなく不思議だなぁと。

{-# LANGUAGE OverloadedStrings #-}

import Snap.Core
import Snap.Http.Server

hello :: Snap ()
hello = writeText "Hello, Snap!"

app :: Snap ()
app = route [("/", hello)]

main :: IO()
main = quickHttpServe app

snapのソースコードを読んでみるのもイイかもしれないと思った。

24 Days of Hackage

24 Days of Hackageを見つけたので楽しく読んでいた。

面白かったのが

特にoptparse-applicativeはpythonのoptparseに似ていて使いやすそうだった。

こんな感じでオプションのパーズとかhelpなんかをよろしくやってくれる。

import Options.Applicative

data Sample = Sample
  { hello :: String
  , quiet :: Bool }

sample :: Parser Sample
sample = Sample
  <$> strOption
      ( long "hello"
      <> metavar "TARGET"
      <> help "Target for the greeting" )
  <*> switch
      ( long "quiet"
      <> help "Whether to be quiet" )

greet :: Sample -> IO ()
greet (Sample h False) = putStrLn $ "Hello, " ++ h
greet _ = return ()

main :: IO ()
main = execParser opts >>= greet
  where
    opts = info (helper <*> sample)
      ( fullDesc
      <> progDesc "Print a greeting for TARGET"
      <> header "hello - a test for optparse-applicative" )

実際にサンプルを動かしてみると使いやすそう

$ ./optest 
Usage: optest --hello TARGET [--quiet]

$ ./optest --hello Haskell
Hello, Haskell

$ ./optest --help
hello - a test for optparse-applicative

Usage: optest --hello TARGET [--quiet]
  Print a greeting for TARGET

Available options:
  -h,--help                Show this help text
  --hello TARGET           Target for the greeting
  --quiet                  Whether to be quiet

yesod-tutorialがわかりやすい

Yesodの本のBlogのサンプルはスニペットすぎて微妙にわかりにくて困ったなぁと、yesodのチュートリアルを探していたら見つけたyesod-tutorialが分かりやすかった。gitのログとdiff見ながら進めていけば、どこが変更されたのかすぐわかるし。

個人的にはFormのところが理解できてかなりハッピーだった。

電子書籍にGit組み込んでdiffれるようになってたりすれば作って学ぶ系の本は良い感じかもしれないと思った。

ProductName Developing Web Applications With Haskell and Yesod
Michael Snoyman
Oreilly & Associates Inc / 2805円 ( 2012-05-04 )


virthualenvwrapperをつくった

この前mkvirthualenvを書いたんだけど、消したりリストできないと使いづらいのでvirthualenvwrapperという名前でGitHubにあげておいた。

git cloneしてcabal installすればmkvirthualenv,rmvirthualenv,lsvirthualenvの3つのコマンドが使えるようになるはずですが、cabalで設定するの初めてなのでおかしかったら指摘してください。

仮想環境をつくる

mkvirthualenv yesod

で仮想環境が作られるのでworkhonで入る

$ workhon yesod
Activating yesod Virtual Haskell Environment (at /Users/kzfm/.virthualenv/yesod).

Use regular Haskell tools (ghc, ghci, ghc-pkg, cabal) to manage your Haskell environment.

To exit from this virtual environment, enter command 'deactivate'.
(yesod)localhost@kzfm:~ $

好きなライブラリを入れまくる。そもそもpersistentがおかしくて、仮想環境を作っては消しを繰り返していたのが、ラッパー構築の動機

cabal install yesod-platform-1.1.5
cabal install persistent-sqlite-1.0.1

仮想環境一覧表示

lsvirthualenvコマンドを使う

$ lsvirthualenv 
diagrams
hakyll
per2
persistent
scotty
snap
yesod

ちょっとyesodが嫌になってsnapに浮気しかけた時期(今朝とか)がありました。

仮想環境を削除する

rmvirthualenvコマンド

$ rmvithualenv snap
$ lsvirthualenv 
diagrams
hakyll
per2
persistent
scotty
yesod

個人的にはフルスタックのフレームワークよりも、Flaskのように好きに組み合わせて作っていくのが好きなのでsnapに惹かれたんだけど、Modelのsnapletがコアについていってない感じがした。

HDBCもエラーになるし、sqlite-simpleもORマッパーぽくなくてなんかいまいちだった。あとドキュメントが少ないので困ったので、yesodで行こうと思った。

ProductName Developing Web Applications With Haskell and Yesod
Michael Snoyman
Oreilly & Associates Inc / 2805円 ( 2012-05-04 )


ScottyはHaskellのSinatraクローン

ScottyはHaskell版のSinatraですね。

普段はFlask使ってるし、NodeでWebアプリ使う時にはExpressを使うので、これ以上Sinatraクローンを覚えたいとは思わないが、ソースコードを読んで勉強するにはちょうどいいサイズだった。

newtype ActionM a = AM { runAM :: ErrorT ActionError (ReaderT ActionEnv (StateT Response IO)) a }
    deriving ( Monad, MonadIO, Functor
             , MonadReader ActionEnv, MonadState Response, MonadError ActionError)

モナド変換子の重ね方が参考になった。Scottyの場合はErroTでリダイレクトとかの処理をするようになっている。

defaultHandler :: ActionError -> ActionM ()
defaultHandler (Redirect url) = do
    status status302
    header "Location" url
defaultHandler (ActionError msg) = do
    status status500
    html $ mconcat ["<h1>500 Internal Server Error</h1>", msg]
defaultHandler Next = next

参考

括弧が省略できてインデントでネストが表現できる言語だとコードの見通しが良くていいですね。

{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Data.Monoid

main :: IO ()
main = scotty 3000 $ do
    get "/" $ text "Helllo Scotty"

    get "/lang" $ do
        v <- param "type"
        html $ mconcat ["<h1>", v, "</h1>"]

カタカナのしりとりの圏

モナモナ言うモナド入門を読んでプチ圏論ブームがやってきたっぽいので、改めてはじめての圏論 その第1歩:しりとりの圏を読んでみた。

読んだ当時は、射の集合がひらがなの文字列っていうのがいまいちイメージがわかなかったのだけど、改めて読んでみるとdomとcodを決めるルールなんだから文字列でいいのかと納得した。

さて、カタカナの文字の集合を考えてしりとりの圏を作ったとする。この場合、ひらがなの圏をカタカナの圏に関連付けるものはfunctorなのだろう。

アルファベットのしりとりの圏を作ってそれをひらがなの圏と対応付けるのもまたfunctorなのだろうか(一対一で対応しないけど)?と思ったが、これの圏論の説明をみるかぎりそんな気がする。

ProductName Basic Category Theory for Computer Scientists (Foundations of Computing)
Benjamin C. Pierce
The MIT Press / 2302円 ( 1991-08-07 )


PersistentのMigrationが便利そうだ

Persistentを触ってみている

{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-}
{-# LANGUAGE GADTs, FlexibleContexts #-}
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH
import Control.Monad.IO.Class (liftIO)

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
Person
    name String
    age Int Maybe
    loc String default="Japan"
    deriving Show
|]

main :: IO ()
main = withSqliteConn "test.db" $ runSqlConn $ do
    runMigration migrateAll
    johnId <- insert $ Person "kzfm" (Just 20) "Fuji"
    liftIO $ print johnId

実行するとデータベースが作られる。実行されるSQLはごく普通のcreate文

CREATE TABLE "Person"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL)

続いてEntityを変更してみる。locを追加してdefaultも付けておく。ついでにinsertのとこも修正しておく

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
Person
    name String
    age Int Maybe
    loc String default="Japan"
    deriving Show
|]

main :: IO ()
main = withSqliteConn "test.db" $ runSqlConn $ do
    runMigration migrateAll
    johnId <- insert $ Person "kzfm" (Just 20) "Fuji"
    liftIO $ print johnId

バックアップテーブルにデータが移され、もとのテーブルが変更された後、データが戻されて移行が無事に完了する。

CREATE TEMP TABLE "Person_backup"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL,"loc" VARCHAR NOT NULL DEFAULT "Japan")
INSERT INTO "Person_backup"("id","name","age") SELECT "id","name","age" FROM "Person"
DROP TABLE "Person"
CREATE TABLE "Person"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL,"loc" VARCHAR NOT NULL DEFAULT "Japan")
INSERT INTO "Person" SELECT "id","name","age","loc" FROM "Person_backup"
DROP TABLE "Person_backup"

Entityのデフォルトはデータ移行時のもとのカラムに付加される値であって、insertで省略できるわけではないので、insertは明示的に値を入れないといけない。databaseを見てみるともとのデータのlocにはEntityのデフォルトで指定したJapanという値が入っていることがわかる。

sqlite> select * from person;
1|kzfm|20|Japan
2|kzfm|20|Fuji

ちなみにEntityのデフォルトを指定しないとマイグレーションに失敗するが、Maybe型にしておけばnullが入るので移行できる。

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
Person
    name String
    age Int Maybe
    loc String Maybe
    deriving Show
|]

開発している時にスキーマの変更はそれなりに苦痛を伴う作業なので、ある程度面倒見てくれるのはありがたい。ダメなときはダメって言ってくれるし。

Persistentのマイグレーションは保守的なので安心らしい。次のケースで自動的にスキーマの変更がなされる。

  • フィールドのデータタイプが変更された時 。ただし変更できる時のみ
  • (The datatype of a field changed. However, the database may object to this modification if the data cannot be translated.)
  • フィールドが追加された時で、フィールドがnot nullの場合はdefaultが指定されている時
  • (A field was added. However, if the field is not null, no default value is supplied (we'll discuss defaults later) and there is already data in the database, the database will not allow this to happen.)
  • フィールドがnot nullからnullに変更された時。逆のケースも試みるがデータベース次第
  • (A field is converted from not null to null. In the opposite case, Persistent will attempt the conversion, contingent upon the database's approval.)
  • 新しいエンティティが追加された時
  • (A brand new entity is added.)

ProductName Developing Web Applications With Haskell and Yesod
Michael Snoyman
Oreilly & Associates Inc / 2805円 ( 2012-05-04 )


Haskellのpersistentが動かない

persistent-1.1.0.1,persistent-template-1.1.1が入っている。

Persistentのsynopsisの例を実行すると

No instance for (Control.Monad.Trans.Resource.MonadResource IO)
  arising from a use of `selectList'
Possible fix:
  add an instance declaration for
  (Control.Monad.Trans.Resource.MonadResource IO)
In a stmt of a 'do' block:
  oneJohnPost <- selectList [BlogPostAuthorId ==. johnId] [LimitTo 1]
In the second argument of `($)', namely
  `do { runMigration migrateAll;
        johnId <- insert $ Person "John Doe" $ Just 35;
        janeId <- insert $ Person "Jane Doe" Nothing;
        insert $ BlogPost "My fr1st p0st" johnId;
        .... }'
In the second argument of `($)', namely
  `runSqlConn
   $ do { runMigration migrateAll;
          johnId <- insert $ Person "John Doe" $ Just 35;
          janeId <- insert $ Person "Jane Doe" Nothing;
          insert $ BlogPost "My fr1st p0st" johnId;
          .... }'

困った。

ProductName Developing Web Applications With Haskell and Yesod
Michael Snoyman
Oreilly & Associates Inc / 2805円 ( 2012-05-04 )


追記 121220

これは動く

{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-}
{-# LANGUAGE GADTs, FlexibleContexts #-}
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH
import Control.Monad.IO.Class (liftIO)

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
Person
    name String
    age Int Maybe
    deriving Show
|]

main :: IO ()
main = withSqliteConn "test.db" $ runSqlConn $ do
    runMigration migrateAll
    -- johnId <- insert $ Person "John Doe" $ Just 35
    -- liftIO $ print johnId

insertしたりselectListしたりするとNo instance for (Control.Monad.Trans.Resource.MonadResource IO)が出る。

追記 121220

これか? - Simplified Persistent Types

追記 121220

バージョンを下げたら動いたので当分この組み合わせで使う

  • persistent-1.0.2.2
  • persistent-sqlite-1.0.1
  • persistent-template-1.0.0.2

diagramsが素敵すぎる

diagramsを使ってみた。

{-# LANGUAGE NoMonomorphismRestriction #-}
import Diagrams.Prelude
import Diagrams.Backend.SVG.CmdLine
import Data.Colour

tri c n = dots <> (strokeT edges # lc c # lw 0.2 # fcA (c `withOpacity` 0.5))
  where rows = map (hcat' with { sep = 1 })
             . zipWith replicate [n,n-1..1]
             . repeat
             $ dot c
        dots = decorateTrail (rotateBy (1/6) edge) rows
        edge = fromOffsets . replicate (n-1) $ unitX # scale 3
        edges = close (edge <> rotateBy (1/3) edge <> rotateBy (2/3) edge)

dot c = unitCircle # lw 0 # fc c

rowSpc = height (rotateBy (1/6) $ strutY 1 :: D R2)
row k n s c = hcat' with {sep = 1 + 3*s} (replicate k (tri c n))
law4 k n c1 c2 = vcat' with {sep = rowSpc} (map tRow [1..k])
  where tRow k = (row k n 0 c1 # centerX # alignT)
                 <>
                 (row (k-1) (n-1) 1 c2 # reflectY # centerX # alignT)
exampleRow f = hcat' with {sep = 4} . map (alignB . f)

law4Dia = exampleRow law4' [2..4]
  where law4' k = law4 k 3 purple gold

example = pad 1.1 $ law4Dia # centerXY

main = defaultMain example

これだけで下のような図が作成できる

diagrams