ログ日記

作業ログと日記とメモ

Haskellからデータベース接続

apt-get install libghc6-haskelldb-dev

関連パッケージがもさもさっとインストールされる。
やたら沢山ある。


使い方はこの辺
HaskellDB



使い方

テーブル定義の作成

まずはテーブル定義ファイルを作成する。

module DBSpec where

import Database.HaskellDB.DBSpec
import Database.HaskellDB.FieldType
import Database.HaskellDB.DBSpec.DBSpecToDBDirect

dbInfo = DBInfo {
                 dbname = "username",
                 opts = DBOptions {useBString = False},
                 tbls = [myTable]
                }

myTable = makeTInfo "tablename" [tRow1, tRow2]


tRow1 = makeCInfo "column1" (StringT, True)
tRow2 = makeCInfo "column2" (StringT, True)

createDBInfo = dbInfoToModuleFiles "" "DBInfo" dbInfo

だいたい見ての通り。
DBOptionsはよく分からない・・。
(StringT, True)のTrueは、nullが含まれるかどうか。
これをTrueにすると、値はMaybe型で返ってくる。


そして

import DBSpec

main = createDBInfo

でテーブル定義ファイルを作成する。
これを実行すると自動的に(上の例では)DBInfo/Tablename.hs が生成される。

DBの利用

import Database.HaskellDB
import Database.HaskellDB.GenericConnect

import DBInfo.Tablename

dbDriver = "postgresql"
dbServer = "localhost"
dbDatabase = "dbname"
dbPassword = ""
dbUsername = "username"

withDB q = genericConnect dbDriver [dbServer,dbDatabase,dbPassword,dbUsername]
             (printAndPerformQuery q)

printAndPerformQuery q db =
    do putStrLn "Query:"
       print q
       result <- query db q
       putStrLn "Results:"
       mapM_ print result

selectAllFromMyTable = table tablename

main = withDB selectAllFromMyTable

取り敢えず例題をそのままコピってみた。



型を細かく見てみると

main = mapM_ print =<< getAllData

getAllData = conn tableDataQuery

conn :: (Database -> IO a) -> IO a
conn f = genericConnect dbDriver
          [dbServer,dbDatabase,dbPassword,dbUsername] f

--tableDataQuery :: Database -> IO a
tableDataQuery db = query db (table tablename)

このようになる。
tableDataQueryの戻り値はIO [Record ...]になる。細かい定義は makeTInfo で作ったファイルに基づく。


細かいデータベース処理は隠蔽されているので、queryに渡す関数を定義していくことになるのかな。

main = mapM_ print =<< getAllData

--
-- データベースクエリ関数
--
getAllData = genericQuery getAllDataQuery

getAllDataQuery = table tablename

--
-- 汎用操作
--
genericQuery = conn . result
conn :: (Database -> IO a) -> IO a
conn = genericConnect dbDriver
          [dbServer,dbDatabase,dbPassword,dbUsername]
result f db = query db f

こんな感じで。

Haskellの基本

emacsはカーソルを合わせるだけで型が表示されるので凄く便利。

. :: (b -> c) -> (a -> b) -> a -> c

みたいな。


この辺の基本的なところでよく悩む・・。

main = f 1 >>= print

f   n = g $ h n
f'  n = (g . h) n
f'' n = g . h
-- $ :: (a -> b) -> a -> b

g :: String -> IO String
g = return . (++ "a")
h :: Int -> String
h = show

$ の右に来る関数は、部分適用じゃダメ。
言い換えると、. の右に来る関数は変数を一つ取る関数。
・・・・っていうのは、頭では分かっていたのに実感として持ててなかった。


自分で書いておきながら、さっきの

genericQuery = conn . result

で悩んでしまった。

genericQuery :: Query -> Database -> IO a

になって(実際に書くとエラーになるけど)、Queryに適用させると (Database -> IO a)の関数になり、genericConnectを適用できるようになる。
うーん・・適用っていう言葉の使い方が間違ってるっぽい雰囲気。


本に書いていた

map ($ 0) xs
zipWith ($) fs xs

の使い方も謎・・奥が深い。

xreaでHaskell

コメントをいただきまして(http://d.hatena.ne.jp/n314/20060917/1158475378#c
無事xreaHaskellを動かすことができた。


どうやら完全にスタティックリンクするには -optl-static オプションが要るらしい。
http://www.haskell.org/pipermail/glasgow-haskell-users/2006-January/009545.html
この辺かな?
Haskellじゃないオブジェクトとスタティックリンクするには -optl-static をつけなさいということらしい。(たぶん


http://www.n314.com/Haskell/wiki/
バグが多々あるような気もするしコードは汚いし、っていうので実用的じゃないけれど取り敢えず進捗をアップ。