Post Page Advertisement [Top]

coolhaskell

haskellのお洒落なレコード構文について

まずは、xmonad.hsの最小限の設定と呼ばれるコードを見てみましょう。

import XMonad

main = xmonad $ def
    { borderWidth        = 2
    , terminal           = "urxvt"
    , normalBorderColor  = "#cccccc"
    , focusedBorderColor = "#cd8b00" }


このコードは、初心者でhaskellの構文を知らなくても、直感的になんとなく書き換えられるものですし、xmonadを始めるための解説には必ず出てくるコードなので、きっとどこかで同じようなものを見ていると思います。

xmonad関数とその引数def

上記のコードには、windowのボーダーラインの幅や、呼び出すターミナルエミュレーターを指定するために、key = value形式での設定を{}記号で囲った部分があります。 しかし、このコードの正確な意味を把握するために余計なものを省くと、xmonadのコードは次のようになります。

import XMonad

main = xmonad def


このコードの中にあるxomnadは関数です。そして、それに続くdefがxmonad関数に渡される引数です。 このxmonad関数は、xmonadを設定するためのデータを引数として受け取り、xmonadウィンドマネージャを実行するというアクションを返します。 そして、それがmainに定義されているので、実行時にはそのアクションすなわち、xmonadウィンドマネージャが実行されます。 (アクションについては「haskellのアクション」を参照)

つまり、xmonad.hsというのは、xmonadウィンドマネージャを実行するための最小のhaskellプログラムのソースなのです。

haskellのデータ

さて、このdefは、xmonadのデフォルト設定項目をhaskellのデータとして定義したものです。 しかし、ここでは、いきなりxonadの複雑なデータを扱うのではなく、 まず、haskellで単純なデータを自作して、実物のhaskellのデータがどんなものかに親しんでみましょう。

そこで、まずは、「猫」についてのデータを作ります。

データの作り方は、2段階に分かれます。

  1. 型を定義する
  2. その型の具体的なデータを作る


型の定義の基本

自作データを作る場合、まずは、そのデータのための型を定義します。
猫のデータですから型名は「Neko」にします。そして、猫のデータとして扱うものとして以下のことを考えます。

  • 名前(文字列)
  • 色(文字列)
  • 歳(整数)
  • 強さ(整数)


では、これらをもとに猫データを表すNeko型を定義します。

data Neko = Neko String String Int Int


haskellでデータ型を定義する時には"data"というキーワードに続けて型名を書きます。ここでは、先に決めた「Neko」です。

これに続けて、"=(イコール)"を書き、その右辺にこのデータの作り方を定義します。

このイコールの右側の部分は、Neko型データを作成する場合の、関数名と引数のセットを表しています。 先頭の「Neko」の部分は、値コンストラクタと呼ばれる関数名に相当するもので、実際には「Neko」である必要はありませんが、慣例的に型名と同じものが使われます。 値コンストラクタに続いて、この型の要素になるデータ型を引数のように定義します。上記で扱うことに決めた、「名前」「色」「歳」「強さ」のそれぞれの型を引数に定義しています。

Neko型データを作成する

Neko型が定義できたので、実際にNeko型データを作成します。データの作成は、値コンストラクタに引数を渡すことで作成します。

neko1 = Neko "tama" "siro" 3 10
neko2 = Neko "mii" "kuro" 5 30


この様に、haskellでのデータは、値コンストラクタを使って普通の関数と同じ様にデータを作成することが出来ます。


レコード構文

基本的なhaskellでのデータの作成方法は、上述の通り、他の関数と同様に、値コンストラクタに引数を渡して作成するというものなのですが、この方法では、コードを見てもその要素が何を意味しているのかが分かりません。

そこで、haskellには、これらの意味をわかりやすく表現できるようにする、もう一つの構文があります。そして、その構文がレコード構文と呼ばれています。

レコード構文を使ってNeko型データを定義してみます。

--レコード構文を使ったNeko型の定義
data Neko = Neko { name::String, color::String, years::Int, strength::Int} deriving Show


最後のderiving Showは、おまじないとしてつけておいて下さい。

レコード構文では、値コンストラクタに続く中括弧の中で、フィールド名とそのフィールドの値になる型名を"::"演算子でつないでフィールドを表し、そのフィールド同士をカンマで区切って並べます。

型の定義をレコード構文で行った場合には、実際のデータ作成の時、先の関数的な定義の構文に加えて、次のような、レコード構文的なデータの作成書式が使えます。

-- レコード構文を使ったNeko型データの作成
neko1 = Neko {name = "tama", color = "siro", years = 3, strength = 10}
neko2 = Neko {name = "mii", color = "kuro", years = 5, strength = 30}


先頭が大文字で始まるものを区別する

ここまで目を通した方はの中には、「あれはそういうことだったのかー」と思っている方もおられると思いますが、実は、違います!!そして、haskellはあなたの想像を超えてもっと凄いのです!!

まず、haskellのコードの中で、先頭が大文字の単語に注目する癖をつけましょう。 先頭が大文字の単語は、「型名」もしくは「値コンストラクタ」のどちらかであり、 どちらにしても何かの「定義」に関する部分になります。上述の作業では、Neko型のデータを実際に作成して定義するために「Neko」値コンストラクタとして、 先頭が大文字の単語が出てきました。

みなさんが「あれ」だと思った、defと{}の部分は、defと言う単語であり、大文字で始まっていません。 つまり、defは、値コンストラクタではないのです。 ですから、この部分で、xmonad関数の引数となるべきデータそのものを作成しているわけではないのです。

じつは、xmonad関数の引数になるデータは、「XConfig l」型というデータの型であり、これを実際に定義するためのフィールドは全部で18個あります。 そして、その詳細は、次のページのリファレンスで見ることが出来ます。

http://hackage.haskell.org/package/xmonad-0.15/docs/XMonad-Core.html#t:XConfig

即ち、Neko型データの作成がそうであったように、XConfig l型のデータを実際に作る場合には、値コンストラクタに18全ての引数を渡して作成しなければなりません。

ではdefは何かといえば、既にデフォルト値が定義されて作成されたXConfig l型の具体的なデータなのです。上記の猫データの例で言えば、Nekoではなく、neko1やneko2と同じなのです。

レコード構文で定義されたデータの凄技

さて、レコード構文というのは、定義する時にそのフィールドの意味がわかりやすくなるだけではなく、次の凄技が自動的に使えるようになります。

一部のフィールドを変更したデータを作成する

おまたせしました、これが「あれ」なのです。

neko1のデータを使って、名前だけ"siro"というデータを作成する場合、次のようになります。 実際に、ghci等でコードを実行してみましょう。

prelude> neko3 = neko1 {name = "siro"}
prelude> neko3
Neko {name = "siro", color = "siro", years = 3, strength = 10}


つまり、レコード構文を用いると、既存のデータをベースにして、必要な箇所だけをレコード構文で書き換えた新しいデータを作ることが出来るのです。

xmonad.hsでは、デフォルトのdefという、XConfig l型データが準備されていて、それをベースにレコード構文で自分好みの新しいXConfig l型データを作pre成して、xmmonad関数に渡しているのです。

フィールド名がフィールド内容を取り出す関数になる

次に、フィールドの内容を取り出す関数として使うことが出来るようになります。

Neko型データからその名前を取り出すには、nameという関数で取り出せるようになります。

prelude> name neko3
"siro"


これも実はxmonad.hsの中で頻出するものなので、何処に出てくるのかを自分の目でも確かめて見ましょう。

xmonad/config archiveにある次のソースを見てみましょう。

https://wiki.haskell.org/Xmonad/Config_archive/Brent_Yorgey%27s_darcs_xmonad.hs

このソースでは、defではなく、古いdefaultConfigを使っていますが、 byorgeyConfigの定義の部分で、defaultConfigをベースにレコード構文でカスタマイズしています。 そして、modMaskフィールドの定義をみると、then節にmodMask defaultConigという式が見られます。 この式は、defaultConfigのmodMaskフィールドに定義されている値をmodMaskという関数で取り出しています。

-- a part of Brent Yorgey's darcs xmonad.hs
    , modMask = if host == Laptop False
                then modMask defaultConfig
                else mod4Mask


また、manageHookフィールドもみてみましょう。 ここにも、manageHook defaultConfigという式がみられます。 すなわち、defaultConfigのmanageHookに定義されている値をmanageHookという関数で取り出しているのです。

-- a part of Brent Yorgey's darcs xmonad.hs
    , manageHook = manageSpawn
                 <+> myManageHook
                 <+> manageHook defaultConfig


なんにせよ、このパターンはデフォルトの値を取り出してそのまま使う場合によく使われるパターンなのです。

0 件のコメント:

コメントを投稿

Bottom Ad [Post Page]