前へ 目次 次へ
参照: cabal-instsll 関連のリンク Distribution.Simple.Commnad Distribution.Simple.Setup 引き続き、Distribution.Simple.Command の OptDescr smart constructors セクションを見てみる。最初に今回の記事を ghci で試すための準備をする。 Prelude> :set prompt "ghci> " ghci> import Distribution.Simple.Command ghci> import Distribution.Simple.Setup ghci> import Distribution.ReadE ghci> import Data.Monoid noArg noArg のソースコードは次のようになる。 noArg :: (Eq b, Monoid b) => b -> MkOptDescr (a -> b) (b -> a -> a) a noArg flag sf lf d = choiceOpt [(flag, (sf,lf), d)] sf lf d noArg 関数は内部で choiceOpt 関数を使っている。choiceOpt 関数の定義は次のようになっている。フラグと OptFlags (ショートフラグのリストとロングフラグのリストのペア) とオプションの説明文からなるタプルのリストを引数とし、MkOptDescr 型の関数を返す。 -- | create a Choice option choiceOpt :: Eq b => [(b,OptFlags,Description)] -> MkOptDescr (a -> b) (b -> a -> a) a choiceOpt aa_ff _sf _lf _d get set = ChoiceOpt alts where alts = [(d,flags, set alt, (==alt) . get) | (alt,flags,d) <- aa_ff] choiceOpt の引数の (flag, (sf,lf), d) というタプルにはフラグ(flag)、ショートオプションのリスト(sf)とロングオプションのリスト(lf)のペア、それに、オプションの説明(d) が収められている。さらに choiceOpt 関数はこのタプル単体ではなくそのリストを引数にしている。 choiceOpt 関数はこの (flag, (sf,lf), d) のタプルから (d, flags, set alt, (==alt) . get) というタプルを作り出す。次にしめす ChoiceOpt コンストラクタの型を見ると、d は オプションの説明、flags は (sf,lf) のペア, set alt はフラグに値を設定する関数、(==alt) . get はフラグの内容を表示する関数であることが分かる。 ghci> :t ChoiceOpt ChoiceOpt :: [(Description, OptFlags, a -> a, a -> Bool)] -> OptDescr a ChoiceOpt について解読するためには実例を使うのが一番良い。この記事のシリーズの以前の記事で Distribution.Simple.Setup の globalCommand には ChoiceOpt の実例が含まれているのが分かっている。また、その取り出し方は「CommnadUI の読み方」の記事で調べたが、ここで復習してみる。ghci に冒頭で述べた処理をしておくと以下のコードを実行することができる。 Distribution.Simple.Setup の globalCommand のすべての情報は CommandUI 型の globalCommand に保存されている。ChoiceOpt は globalCommand の commandOptions フィールドの中に収められている。まず commandOptions フィールドのデータの型を見てみる。 ghci> :t commandOptions globalCommand commandOptions globalCommand :: ShowOrParseArgs -> [OptionField GlobalFlags] これは commandOptions フィールドの値が ShowOrParseArgs の値で切り替えられる OptionField のリストであることが分かる。そこで ShowArgs の場合の値の型を見てみる。データが Show クラスのインスタンスではない場合 ghci で内容の表示ができないが、:t コマンドを使うとデータの型は見ることができる。 ghci> :t commandOptions globalCommand $ ShowArgs commandOptions globalCommand $ ShowArgs :: [OptionField GlobalFlags] OptionField 型のリストを取り出せた。OptionField 型の構成は次のようになっている。 data OptionField a = OptionField {optionName :: Name, optionDescr :: [OptDescr a]} -- Defined in Distribution.Simple.Command' OptionField のリストにどんなオプションが入っているか optionName で調べてみた。 ghci> map optionName $ commandOptions globalCommand $ ShowArgs ["version","numeric-version"] そこで "version" オプションの optionDescr を取り出し、foo にバインドした。 ghci> let foo = optionDescr $ head $ commandOptions globalCommand $ ShowArgs ghci> :t foo foo :: [OptDescr GlobalFlags] foo には何個の要素が含まれているか検査してみた。 ghci> length foo 1 どうやら1個しかないようなのでそれを取り出して、bar にバインドした。 ghci> let bar = head foo ghci> :t bar bar :: OptDescr GlobalFlags これでついに ChoiceOptの実例を取り出せた。 しかし、これで終わりではない。ChoiceOpt のパラメータは次のように (Description, OptFlags, a -> a, a -> Bool) タプルのリストになっているからだ。 ghci> :t ChoiceOpt ChoiceOpt :: [(Description, OptFlags, a -> a, a -> Bool)] -> OptDescr a そこでパターンマッチで bar のパラメータを取り出し baz にバインドした。 ghci> let baz = p where (ChoiceOpt p) = bar ghci> :t baz baz :: [(Description, OptFlags, GlobalFlags -> GlobalFlags, GlobalFlags -> Bool)] baz の要素数を調べてみると 1 だったので、要素のタプルは1個だけだ。 ghci> length baz 1 そこで bas の1個だけのタプルから、タプルの要素 d, flags, set, get をとりだした。 ghci> let (d, flags, set, get) = head baz ghci> d "Print version information" ghci> flags ("V",["version"]) ghci> :t set set :: GlobalFlags -> GlobalFlags ghci> :t get get :: GlobalFlags -> Bool get を試してみた。 ghci> get defaultGlobalFlags False これは defaultGlobalFlags のパラメータの最初のフラグの値を取り出している。 ghci> :t GlobalFlags GlobalFlags :: Flag Bool -> Flag Bool -> GlobalFlags set も試してみた。 ghci> get $ set defaultGlobalFlags True set 関数で Flag True にセットしたフラグの値を get で確認している。 ChoiceOpt [(d, flags, set, get)] の説明文の d や、ショートオプションとロングオプションのペア flags :: (sf,lf) については特に問題ないので、set や get の関数を定義できればマニュアルで ChoiceOpt 型のデータが作成できる。 まず get を考える。GlobalFlags の構成は次のようになうから、 ghci> :info GlobalFlags data GlobalFlags = GlobalFlags {globalVersion :: Flag Bool, globalNumericVersion :: Flag Bool} -- Defined in `Distribution.Simple.Setup' instance Monoid GlobalFlags -- Defined in `Distribution.Simple.Setup' globalVersion アクセサで defaultGlobalFlags がら globalVersion フィールドのフラグ値が取り出せる。 ghci> globalVersion defaultGlobalFlags Flag False しかしながら、get 関数では Flag 型ではなく Bool 型が戻ってきて欲しい。そこで (== Flag True) を使うとうまい具合に上の例でも False が戻ってくる。 ghci> (== Flag True) $ globalVersion defaultGlobalFlags False つぎに set 関数の場合だが、globalVersion フィールド名を使うとフラグの値をセットできる。 ghci> globalVersion $ defaultGlobalFlags {globalVersion = Flag True} Flag True 注: デフォールトのフラグの値も利用しつつ mappend で新しい値と演算したあとでフラグをセットしたい場合もある。元々の値に新しい値をアペンドしたいときなどだ。直接に記述するとコードが長くなりすぎるので、つぎのような mget と mset を定義する。 choiceOpt 関数の定義を再掲するが、上の仕組みを頭において眺めるとコードの意味が分かる。 -- | create a Choice option choiceOpt :: Eq b => [(b,OptFlags,Description)] -> MkOptDescr (a -> b) (b -> a -> a) a choiceOpt aa_ff _sf _lf _d get set = ChoiceOpt alts where alts = [(d,flags, set alt, (==alt) . get) | (alt,flags,d) <- aa_ff] ちなみに globalCommand の version オプションの場合、 d = "Print version information" flags = (['V'], ["version"]) alt = Flag True set = (\v flags -> flags { globalVersion = v }) get = globalVersion のようになる。これらは、globalCommnad の定義をする際の option 関数の引数として使われている。Distribution.Simple.Setup の globalCommand の定義の該当部分を再掲すると次のようになる。 commandOptions = \_ -> [option ['V'] ["version"] "Print version information" globalVersion (\v flags -> flags { globalVersion = v })   trueArg ,option [] ["numeric-version"] "Print just the version number" globalNumericVersion (\v flags -> flags {globalNumericVersion = v }) trueArg ] 最後に choiceOpt 関数を使って ChoiceOpt コンストラクタ型のデータをマニュアルで作り、この記事を終わる。 ghci> let hoge = choiceOpt [(Flag True, (['V'],["version"]), "Print version information")] ['V'] ["version"] "Pring version information" globalVersion (\v flags -> flags {globalVersion = v}) ghci> :t hoge hoge :: OptDescr GlobalFlags ghci> let (_, _, hset, hget) = head h where (ChoiceOpt h) = hoge ghci> hget $ hset defaultGlobalFlags True
by tnomura9
| 2014-02-09 17:24
| Haskell
|
Comments(0)
|
カテゴリ
新型コロナウイルス 主インデックス Haskell 記事リスト 圏論記事リスト 考えるということのリスト 考えるということ ラッセルのパラドックス Haskell Prelude Ocaml ボーカロイド 圏論 jQuery デモ HTML Python ツールボックス XAMPP Ruby ubuntu WordPress 脳の話 話のネタ リンク 幸福論 キリスト教 心の話 メモ 電子カルテ Dojo JavaScript C# NetWalker ed と sed HTML Raspberry Pi C 言語 命題論理 以前の記事
最新のトラックバック
最新のコメント
ファン
記事ランキング
ブログジャンル
画像一覧
|
ファン申請 |
||