yunomuのブログ

酒とゲームと上から目線

simple-config v1.0.0

一人で開発していても共通ライブラリというものは増えていくものでして、でも大体はプロジェクト内だけで収まってしまうようなものだったり、外に出すための一般化がとても面倒臭かったり、ファイル1個とか関数いくつかだからコピペでいいかってなったりして、なかなかやる気が出ないんですけど、

でもたまになんか非常に一般的な感じのものが出来上がってしまうことがあって、「あーこれそのまんま他のプロジェクトに持っていって使いたいなぁ」と思うんですけど、そうなると逆にパッケージとして分離してまとめておかないとちょっと面倒で、急速にパッケージ化欲求が高まってきて、仕方ねぇかってなるのです。

先日のconfig parserの件、簡易設定ファイルパーサジェネレータ - yunomuのブログ
を、使えるようにしてリリースしました。

HackageDB: simple-config
http://hackage.haskell.org/package/simple-config

JavaならjarコピペでいいしRubyならgemコピペでいいんだけど、Haskellだとcabalでローカルから入れる方法とかよくわかんないし、というか今はコードをgithubに上げてるからそれがビルドできないとかアレだよねぇということで、はじめてhackage登録もやってみました。

Hackage登録のやり方

余談ですが、
HackageDB: checking and uploading packages
ここに書いてある通りです。

ただし、アップロードするためにはアカウントが必要で、アカウントを取得するためにはRoss Patersonさんにメールを送る必要があります。
というのがここに書いてある。
HackageDB: User accounts

アカウント名はCamelCaseで、以下のリストを見て被ってないやつにすると良いらしいです。
http://hackage.haskell.org/cgi-bin/hackage-scripts/list-users

で、「アカウントください。usernameはYusukeNomuraがいいです」というのを、多分英語で書くといいと思います。私は10分くらいで返信が返ってきました。が、その後半日経ってもリストには名前が出ませんでした。でもアップロードはできたので問題は無いんだと思います。

あとは、Executableなら別にいいんですが、moduleをexportする場合は一応haddock書いておいた方がいいんじゃないでしょうか。
baseのバージョンは書かないと怒られます。
check用のフォームを用意してくれてるので、色々怒られてちゃんと整えてからアップロードするといいと思います。

それと注意点として、同じバージョンは2度アップロードできないので、バージョン番号は慎重に付けた方がいいです。あとリリース時に変なバージョンをアップロードしてしまわないように注意するのは言わずもがな、というか、本当に注意したほうがいい。

中身のこと

最終的にどんな感じになったかというのは一応haddockに書いたんですけど、こっちにも書いておくと、

{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
import Text.Config

mkConfig "configParser" [config|
TestConfig
    uri  URI
    text String
    list [String]
|]

という感じに書くと

data TestConfig = TestConfig
    { uri  :: String
    , text :: String
    , list :: [String]
    }

configParser :: Parser TestConfig
configParser =

といったようなものが生成される。

これを使うと

uri: http://example.com/
text: ume
list: coffee, chocolate, javakula

みたいなのがパースできる。Parserはparsec3のやつです。

QuasiQuote

前回の記事のやつに加えて、今回は設定をQuasiQuoteで書けるようにしてみました。persisitentを真似したのです。persistent使ったことないけど。

QuasiQuoteを作るのは多少TemplateHaskellの心得があれば割と簡単で、

data QuasiQuoter = QuasiQuoter { quoteExp  :: String -> Q Exp,
                                 quotePat  :: String -> Q Pat,
                                 quoteType :: String -> Q Type,
                                 quoteDec  :: String -> Q [Dec] }

この型を返す関数を定義すれば良い。

今回はクォート部分が式になってくれればよいので、っていうかこれ式以外を接合することってあんましないんじゃないかな。まあそれはともかく、quoteExpだけを実装すれば良い。
Stringを取って式を返す関数を登録してあげればいいということで、今回はConfTmpを返してほしいので、ちょうど手元にあったConfTmpを返すパーサconfTmpParserを使ってこんなかんじで実装してみました。

config :: QuasiQuoter
config = QuasiQuoter
    { quoteExp = \str -> [|confTmpParser str|]
    , quotePat = undefined
    , quoteType = undefined
    , quoteDec = undefined
    }

confTmpParser :: String -> ConfTmp
confTmpParser str = ...

これで[config| … |]みたいなのを書くとConfTmpがその部分に読み込まれるという形になります。かっこいい。

まとめ

今回作ったのはこれです。
https://github.com/yunomu/simple-config/tree/v1.0.0