2014年10月3日金曜日

xmonadとHaskell(その16:レイアウトのスクリーンショット)

拡張モジュールにあるレイアウトモジュールは凄く沢山あるが、幾つかの視点から整理してみる。


基本的レイアウト

単独でレイアウトになれる型のデータを含むモジュール。
重なりか、非表示かがわかりやすい様にターミナルを透明にしてスクリーンショットを取ってみた。

工夫次第で使えそうな勧めレイアウト


XMonad.Layout.OneBig
一つだけ大きく



XMonad.Layout.TwoPane
XMonad.Layout.DragPane
画面を二分割で固定。マスターとそれ以外の2ウインドウ制。それ以外は非表示。
DragPaneの方が縦、横の選択等のカスタマイズができる。



XMonad.Layout.GridVariants
GridVariantsモジュールでは、Tallのメイン部分を任意の固定行x列にした上で、サブ部分がグリッド配置になるようにできたりする。



XMonad.Layout.ThreeColumns
メイン1行、サブ2行の固定



XMonad.Layout.MultiColumns
メイン1行、サブ行どんどん増えるが各行のウインド個数を指定できる



XMonad.Layout.Simplest
メイン1画面固定。
サブは非表示ではなく、メインの後ろに表示されている点でFullと異なる。



XMonad.Layout.StackTile
stacktileはdishesに似ているがメイン部分の高さが固定、サブ部分が増えるとサブの高さが小さくなる。



Tall改良版

hintを重視(文字がちゃんと表示される高さとか)

XMonad.Layout.FixedColumn
大きさ等指定を割合じゃなく文字数で

XMonad.Layout.ResizableTile
サブ側の高さがリサイズ出来る


その他のレイアウト

XMonad.Layout.Accordion
複数ウインドを開くとフォーカスのあるもの以外帯状に縮んだ状態になる。



XMonad.Layout.Circle
マスタが真ん中に、サブはその後ろで円状に配置



XMonad.Layout.Cross
マスタが真ん中に、サブはその後ろで十字形状に配置



XMonad.Layout.Roledex
斜めに綺麗に並べられる



XMonad.Layout.Spiral
渦巻き状にタイリング



XMonad.Layout.Mosaic
XMonad.Layout.MosaicAlt
各ウインドウの面積がある一定の法則になってる?


XMonad.Layout.Column
隣接ウインドウとの高さの比を規定



XMonad.Layout.Grid
XMonad.Layout.HintedGrid
Grid系は規則正しく格子状になるように配置をする。



XMonad.Layout.Dishes
メインの下にサブがお皿の用に積み重なっていく。



デコレーション系

xmonadでもタイトルバー的なものを付けることも出来るし、マウスによるボタンクリックによるウインドウ操作も可能にできる。

XMonad.Layout.DecorationAddons
XMonad.Layout.ButtonDecoration
XMonad.Layout.ImageButtonDecoration
各ウインドにバーを付ける



XMonad.Layout.TabBarDecoration
レイアウトにタブを追加する



XMonad.Layout.DwmStyle
各ウインドにDwmスタイルのタグを付ける



XMonad.Layout.DecorationMadness
幾つかのレイアウト(circle,accordion,tall)に飾りを付けたものを提供してくれる


XMonad.Layout.Decoration
テーマやシュリンカをカスタマイズする時のデフォルト値とか


    TopCoder

    本屋さんで「最強最速アルゴリズマー養成講座」という本を買ってみた。

    別に「アルゴリズマー」になろうとしたわけではなく、世の中には「プログラムコンテスト」なるものがあって、その筆者曰く、初心者こそ参加すべきという文言に目が留まったからだ。

    しかし、それよりもなによりも、そのコンテストは「TopCoder」といわれるものなのだが、このコンテストの優秀者(トップ0.2%)達は「RedCoder」と呼ばれ、これまた筆者曰く「『"赤いヤツは3倍早い"』というのはアニメだけの話ではありません。Redcoderは、素早くしかし華麗なコードを書き上げ、Challenge phaseでは容赦なく他人を撃墜していく存在であり、参戦者からすると恐怖の対象でもあります。」とかいう文句に心躍らされてしまった。

    昭和世代のおっさんは、アルゴリズマーであろうがなかろうが、少なくとも皆「ガンダマー」

    なので、参加しないわけには行きません。
    そう、「ザクだからって、なめるな!」なのです。


    http://www.topcoder.com/


    先の本が書かれたころよりも、ページが進化しているらしい。
    とりあえず、「Arena」に行くためには、ページ上段に並ぶ「challenges」からドロップダウンされるメニューの一番下「Topcoder arena」をクリック。

    ContestAppletProd.jnlpとかいうファイルがダウンロードされる。

    さて、windozeの場合、JREがインストールされていれば、このファイルをダブルクリックするなりでArenaが起動するが、archlinuxの場合は、すんなりいかないこともあるので、幾つか確認。


    1. icedtea-webパッケージのインストール
    *.jnlpというのは、JavaのJava Web Startという仕組みを使うらしく、そのために必要なパッケージが「icedtea-web」らしい。

    http://d.hatena.ne.jp/pogin/20120331/1333222488

    そのページを参考に

    #pacman -S icedtea-web

    で、インストール。

    そして、ダウンロードしたContestAppletProd.jnlpを以下のように実行。

    $javaws ContestAppletProd.jnlp

    セキュリティーの確認画面がいくつか出て、了解していけばArenaが立ち上がる。


    2.xmonadとjavaアプレットのFAQ

    xmonad上で、javaのアプレット扱う時にはFAQがある。
    何も設定しないと、アプレットに何も表示されない状態になって、びっくりしてしまうので、以下のページを参考に設定しておくのを忘れずに。

    http://qiita.com/xorphitus/items/efcca6652558bc5cc785


    #include XMonad.Hooks.SetWMName
    defaultConfig {startupHook = setWMName "LG3D"}

    みたいにstartupHookを設定しとけばOK(インクルード忘れずに)





    2014年2月3日月曜日

    xmonadとシステムトレイ

    うちのxmonadのステータスバーはdzenであり、システムトレイ的なものがない。

    スマホで撮った写真を簡単にパソコンで見るために、Dropboxを使ってみたんだけれど、「システムトレイアイコンからうんぬんかんぬん」という説明があったので、なんとなくつけてみたくなった。


    その前に、dropboxだ。

    Dropbox

    データ共有のアレ。
    https://www.dropbox.com/

    Archlinuxでは、AURにパッケージがある。

    また、archlinuxのwikiは以下。
    https://wiki.archlinux.org/index.php/dropbox


    取り立ててメモする程のものはないけれど、一応メモ。

    wikiにある通り、dropboxの使い方には「dropboxd」コマンドをxsession等から実行する使い方と、systemdでサービス(デーモン)として呼び出す使い方の2種類があるらしい。

    下準備


    どちらの使い方をするにしても、初期設定等についてあまり深く考えずに導入するためには以下の方法が良さげ。

    1.アカウントは事前にWeb等から取得しておく。
    2.yaourt等でdropboxのパッケージをインストールしたら、Xのターミナルエミュレータ上で、dropboxdコマンドを実行。ポップアップされる指示に従ってログインして、チュートリアルを最後までみる。

    以上で、ホームディレクトリに必要なファイル等が用意されるはず。
    あとは、適当にターミナル上でC-cとかして終了させる。

    xmonadでタスクトレイがない場合、タスクトレイアイコンからの設定が出来ないので、別途dropbox-cliパッケージ(AUR)をインストールして操作するべきかもしれない。が、うちではほったらかし。
    というか、さっき試しにインストールしようとしたら、チェックサムが合わない(?)とかいうエラーでインストール出来なかった。そのうち、覚えてたら再度試してみようかな。

    systemdから


    上記のタスクトレイアイコンからの設定では「自動起動」の項目なんかがある、DEやopenboxなんかのWMの場合は、その効果が反映されそうだが、xmonadを使ってる限りは関係なさそう。
    なので、systemdのサービスとして使う。

    systemdを使った場合、システムが動いていればアカウントにログインしていようがいまいが、ディレクトリの同期をしてくれるっぽい。一方、Xの起動を前提としていないので、タスクトレイのアイコンが出てこない。

    が、wikiにある通り、次の設定をすればシステムトレイアイコンがでてくる。

    さて、この設定の仕方なんだけどwikiでは
    「/etc/systemd/system/dropbox@.service」のファイルに

    .include /usr/lib/systemd/system/dropbox@.service
    [Service]
    Environment=DISPLAY=:0

    という内容を書くといいとなっている。
    設定の内容は、元の設定に環境変数の設定(X上に表示を行う)を追加しているものだ。

    systemdでは、この「/etc/systemd/system/」ディレクトリ以下に「ファイル」を作成すると「/usr/lib/systemd/system/」ディレクトリ以下の同名の設定ファイルの内容が上書き設定される仕組みになっている。

    しかし、今回の場合は、一部の追加であり、そのため、わざわざ、元ファイルを読み込んでいる。

    この様な時には、追加設定の方法をとる方が格好良さそう。
    その方法は、「/etc/systemd/system/」ディレクトリ以下に「設定ファイル名.d」というディレクトリを作る。上の場合なら「/etc/systemd/system/dropbox@.service.d/」というディレクトリだ。そして、次にそのディレクトリの中に「hoge.conf」というファイルを作りそこに追加したい内容を記述すればよい。

    さて、実際には、dropboxのサービスファイル名はユーザー名が入って構成される。うちの場合「neko」というユーザーなので

    # systemctl enable dropbox@neko.service

    であり、システムトレイアイコンのためには

    #mkdir /etc/system/systemd/dropbox@neko.service.d

    としてディレクトリを作成した上で、

    #vim /etc/system/systemd/dropbox@neko.service.d/env.conf

    とかして

    [Service]
    Environment=DISPLAY=:0

    とだけ、env.confファイルに記述すればよい。

    https://wiki.archlinux.org/index.php/systemd


    stalonetray


    本題の、xmonadでシステムトレイだが特に難しいことはなにもない。
    「stalonetray」という単独のシステムトレイアプリがある。

    うちのdzenは、ご存知の通り「つぎはぎ」されている。
    そして、システムトレイもこれと同様にstalonetrayを「つぎはぐ」のだ!


    本家
    http://stalonetray.sourceforge.net/manpage.html

    Archのwiki
    https://wiki.archlinux.org/index.php/Stalonetray


    とりあえず、実行しておけば、そこにシステムトレイアイコンが表示される。

    dzenとうまく「つぎはぎ」するコツは以下の2点だ。

    背景色を同じにする

    dzenの背景色と同じにすることで、一体感をだす。
    -bg "#000000"


    高さを合わせる


    dzenと高さを合わせる。
    dzenは「-h」オプションでピクセル単位の高さを指定している。

    一方、stalonetray側の高さの指定は「--geometry 」オプションで行い、その引数の書式は

    widthxheight[+x[+y]]
    となっていて、例示すれば「1x1+0-0」という感じだ。
    ここで、注意すべきはwidthもheightも単位はアイコン何個分か?である。つまり、ピクセルではない。

    では、アイコン1個の大きさはどうやって決まるか?
    「--slot-size」オプションにピクセル数を指定して使う。

    dzenの高さが20なら、「--slot-size 20」として、--geometryのheightは1にしておけば良い。

    「--slot-size」の指定は横幅も同じ数値になるので、widthの調整もこの数字に着目して計算すれば良い。

    あとは、適当でも良さげ。
    stalonetrayのオプションの指定は、コマンドラインに並べても、設定ファイル(デフォルトでは~/.stalonetrayrc)を読み込んでもどっちでもいける。

    xmonad.hsの中で呼び出した場合には、コマンドラインに並べたオプションにして、dzenとの関係から、長さを計算で出した方がスマートっぽいかも。

    上手につぎはぎ出来ました

    と思って晒してみたけど、特にスマートっぽくもないか。

    まぁ、システムトレイアイコンがあると見た目が今時っぽくなった。





    2013年12月31日火曜日

    vimとhaskell

    自己紹介の処にも書いてある通り、普段はemacs派。
    linux使い始めた頃がJE時代のMuleだったので、それしか知らなかったってのも大きな要素。

    初心者の頃、root作業中に意思に反して立ち上がるviは恐怖以外のなにものでもなく、エスケープ連打と:qだけはマスターしてた。

    ところが、なんかのきっかけでvimというのを触ってみたら、インサートモードでもバックスペースで文字が消せ、しかも、カーソルキーでカーソルが動く!!そんな普通のエディタっぽくなってたので、ドットインストールとかvimtutorとかして、最近では、とりあえず触るくらいはできるようになった。

    で、emacsでは、各プログラム言語に対応した「なんとかモード」的なものがあるのだけれど、vimってどうなってるの?という初心者的疑問から、ちょっとだけ環境整備できたので、そのメモ。


    設定ファイルvimrc

    個人の設定ファイルの場所は7.4以降(archlinuxでのパッケージは7.4)

    ~/.vim/vimrc

    vimは、「vim script」と云うもので、制御できるらしい。
    以下のページのスライドにざっと目を通すと雰囲気が確認できた。
    http://www.slideshare.net/cohama/vim-script-vimrc-nagoyavim-1

    とりあえずは




    プラグインのための準備


    vimの機能拡張の管理をするための仕組みとして「neobundle」というのを使う。

    https://github.com/Shougo/neobundle.vim

    の「Quick start」の項目をみて、準備をする。

    まず、~/.vim/bundle/ディレクトリを作成して、その中にgithubからneobundle.vimをコピー

    次に、~/.vim/vimrcにneobundleのための必要事項を記入。(例をコピペでOK)

    これで準備完了!!

    プラグインのインストール


    プラグインというものをインストールしてみる。
    まずは、ステータスバーをカッコよく!!

    https://github.com/itchyny/lightline.vim

    スクリーンショット見ただけで、ワクワク。

    「Installation」の項目をみると幾つかの方法が示されている。
    うちは「neobundle」という方法を使うので、「neobandle」の方法を見てみると

    NeoBundle 'itchyny/lightline.vim'


    とかかれている。

    この一行を、~/.vim/vimrcにコピペする。
    コピペの場所は

    NeoBundleFetch 'Shougo/neobundle.vim'


    の下辺りに書けばよい。



    haskellの環境


    vimでhaskellする人たちのページによく紹介されているものに「ghcmod-vim」という、haskellコードの中の型の表示やコードのチェックをしてくれる便利なプラグインがある。

    https://github.com/eagletmt/ghcmod-vim

    で、このプラグインは、「ghc-mod」という外部プログラムを使うので、それを先にインストールしてねと説明される。そして、そのインストールは、「cabal」というのを使ってインストールできますよと説明される。「cabal」っていうのは、haskellプログラムのパッケージマネージャのようなもので、pacmanみたいなものらしい。

    そこで、注意が必要なのだ!
    archlinuxでは、pacmanというパッケージのシステムがあるが、それを介さずにシステムに何かをインストールする時は、その部分について自分で管理しなければならない。

    以前railsとかを使うときに、rubyでも同じようなことがあった。何かのコミュニティが独自のパッケージシステムを持っているときに、ディストリビューションのパッケージシステムとどう折り合うかは面倒くさい問題だ。

    というわけで、haskellについてのarchlinuxのwikiをみてみた。
    https://wiki.archlinux.org/index.php/Haskell_Package_Guidelines

    すると、パッケージング云々の前に、archlinuxでのhaskellの環境について知っておくべき重要なことが書いてあった。

    archlinxとhaskell


    まずは、リポジトリについて。
    Archlinuxとhaskellの関わり方の方法は2つの選択肢があるらしいということを知った。

    ひとつは、archlinuxのリポジトリにあるhaskellに関するパッケージを使うという選択肢。

    もうひとつは、最新のhaskellパッケージを取り入れてある、ArchHaskellプロジェクトのリポジトリを使うという選択肢だ。

    とりあえずは、archlinuxのリポジトリで満足。

    次に、「Haskell-Platform」というものについて。
    システム上でhaskellを使う場合、通常は「Haskell-Platform」と呼ばれるひとまとまりの環境をインストールするらしい。ところが、archlinuxのパッケージングでは、これらは以下の5パッケージに細分されている。

    ghc
    cabal-install
    haddock
    happy
    alex
    特に、xmonadをインストールする時に依存で勝手にghc等がインストールされた場合には、他のものがインストールされていなかったりするので、超要確認だ

    というわけで、archlinuxでcabal installするなら、まずは

    pacman -S ghc cabal-install haddock happy alex

    しろってこと。

    cabalでghc-modをインストール

    さて、cabalによるhaskellパッケージのインストールはデフォルトでユーザー領域、~/.cabal以下にインストールされる。なので、当初の心配事であったシステムとの整合性はあまり問題ないとおもう。

    まず、cabalを使う下準備は、シェルから「cabal update」すると、はじめてのときは、~/.cabalディレクトリを作って、必要なことをやってくれる。あと、cabal自体の最新版があるからアップデートしてね的なメッセージは無視。cabal-installパッケージ自体はpacmanの方に任せることにする。

    その作業が終わったら、パッケージをインストールしてみる。ターゲットはghc-modだ。

    cabal install ghc-mod

    依存関係にある他のパッケージとかも一緒にインストールしてくれる。
    haskellのコンパイルはs101にとっては重労働らしく、結構な時間がかかるが、辛抱強く待てばそのうち終わる。

    実は、、


    実は、うちでは結構ハマったので、他にも陥っている人がいた時用のメモ。

    haskell-platformというものを知らなかったので、適当にcabal-installパッケージをpacmanでインストール。そして、「cabal install ghc-mod」すると、インストール途中で何かのファイル(モジュール)が足りないということで、エラー。
    これが、事の発端だと思う。

    そこから、あまりエラーメッセージを読まずに、上で書いたarchlinuxのwikiを見て、リポジトリ変えたり、足りないパッケージ入れたりして、試してた。その中で、cabalインストールを初期化した方が良いだろうと思って、.cabalディレクトリを消したりしてた。

    で、どうもうまくいかない。そして、エラーメッセージをよく見ると、ありそうなはずのモジュールがないといっている。。。

    で、いろいろやってて見つけたのが~/.ghcの中のパッケージデータベース。
    ~/.cabalを直接消したりしてたから、これとの整合性がとれなくなってたらしい。

    なので、はじめてcabalする時にごちゃごちゃやって、上手くいかなくなってるなら、~/.cabalと~/.ghcを両方削除して、一からやり直してみると良いかも?


    お勧め?


    とりあえず、この辺を順番に試してみようかなぁ

    VimでHaskellを書く時に入れておきたいプラグイン5つ

    すごい Vim で Haskell を書こう ... の補足

    四苦八苦して入れたghc-modを使うghcmod-vimは、s101だと、結構重かったりした。
    まだ、全部は試してないけれど、記念撮影♪




    2013年12月28日土曜日

    xmonadとHaskell(その15:レイアウト設定)

    (その14)からの続きで、xmonad関数の型クラス制約に戻ってみる。

    xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()

    前回見た単純な形の型クラス制約と比較すると、ちょっと複雑な形なので解きほぐそう。
    まずは、カンマで区切ってカッコで包まれいる書式は、複数の型クラス制約をあらわしていて、

    LayoutClass l Window



    Read (l Windw)

    に分解することができる。

    そこで、LayoutClassに付いてのみに着目してみると

    xmonad :: LayoutClass l Window => XConfig l -> IO ()

    のような形に書けるのだが、これも前回の単純なメソッドの型クラス制約と違うところがある。何が違うかといえば、型クラス制約の型の形である「l Window」と、引数「XConfig l」が一致していない。

    これは、XConfig l型データそのものが、LayoutClass型クラスにあるメソッドの引数になるものではないからである。

    メソッドの引数になるのは、XConfig l型データに内包される「l Window」型の値、すなわち、layoutHookフィールドの値だ。つまり、xmonad関数は、レイアウト操作をする時に、この値を引数にして内部的にLayoutClass型クラスのメソッドを呼び出すのだろう。

    というわけで、結論から言えば、layoutHookフィールドに定義するものが、LayoutClass型クラス(及び、Read型クラス)のインスタンスの値であれば良いということだ。

    さてさて、LayoutClass型クラスというのは、どんなもので、どんなメソッドがあるのか気になる場合には以下のリファレンスが参考になる。
    http://xmonad.org/xmonad-docs/xmonad/XMonad-Core.html#t:LayoutClass

    しかし、それらメソッドは上で話した通り、xmonad自身が内部的に呼び出すだけなので、レイアウトに関する型を自作してインスタンスとしての実装をするのでない限りは、そういうメソッドがあるという知識を持つレベルで十分ぽい。

    というわけで、「型クラス」というものがあるという知識を得た上で、あらためてレイアウトの設定に臨んでみる。

    さっそく、XMonad.Layoutモジュールのリファレンスへ!
    http://xmonad.org/xmonad-docs/xmonad/XMonad-Layout.html

    layoutHookに定義するのはLayoutClassのインスタンである型の値ということがわかったが、具体的になんの型がLayoutClassのインスタンスなのかどうやって調べるのだろう??

    実は非常に簡単!!
    リファレンスの「型」の項目には、ちゃんと「インスタンス(instances)」という項目があったりするのだ。

    Full a型なら、以下のような記述が見つかる。

    LayoutClass Full a
    Read (Full a)
    Show (Full a)

    これは、LayoutClass、Read、Showの型クラスのインスタンスだという記述なのだ。


    レイアウト設定の基本


    さて、型クラスのはなしはこれくらいにして、次は、xmonad.hs上で実際にレイアウト設定を行う上で知っておくべき更なる実践的な知識を把握しよう。

    単独基本形


    単独で、LayoutClass型クラスのインスタンスとなっている型は、大雑把に言えば、「基本的なレイアウト」を表している。例えば、「Full a型」や、「Tall a型」であり、これらの型の値は、それ単独で1つのレイアウト設定を行えるのだ。

    だから、Full a型を単独でlayoutHookフィールドに設定するのが、レイアウト設定のもっとも基本的な設定になる。

    layoutHook = Full


    レイアウトの連結


    デフォルトのレイアウト設定でお馴染みのように、レイアウトはm-spaceで切り替えることが可能だ。その仕組みがレイアウトの連結だ。

    これは、個々に独立したレイアウト設定であるLayoutClass型クラスのインスタンスの値を(|||)演算子で繋ぐだけである。

    layoutHook = Full ||| Tall 1 (1/100) (1/2)


    メッセージ


    さて、連結されたレイアウトは、m-spaceで切り替える事ができるのだが、それは何故なのか??

    実は、レイアウトの設定に働きかける「メッセージ」という仕組みが、そのキーにバインドされているのだ。defaultConfigでのm-spaceのキーバインドを覗いてみると

    ((modMask, xK_space ), sendMessage NextLayout)

    そして、sendMessage関数はXMonad.Operationsに定義がある。

    sendMessage :: Message a => a -> X ()


    ここにも型クラス制約が出てきた。すなわち、Message型クラスのインスタンスを引数に取るのだ。

    で、実際にsendMessage関数の引数に渡されている「NextLayout」は、大文字アルファベットなので、値コンストラクタである。

    そして、このNextLayoutもXMonad.Layoutのリファレンスに見つけられる。

    「ChangeLayout型」の項目をみてみよう。
    そこに、値コンストラクタとしてNextLayoutが見つかり、その他にFirstLayoutがあるのがわかる。また、インスタンス項目には「Message ChangeLayout」と書かれており、Message型クラスのインスタンスであることが確認できる。

    というわけで、xmonadでは、このsendMessage関数とMessage型クラスのインスタンスを使ってレイアウト設定を操作することができるのだ。

    すなわち、(|||)演算子を使ってレイアウトを結合している時には、このメッセージをつかってレイアウトの切り替えができる。

    XMonad.Layoutモジュールにある他のメッセージもついでに見てみよう。

    「Resize型」は、マスターペインの大きさを変化させる。
    値コンストラクタはShrinkとExpand。

    Tallレイアウト等の時、m-lやm-hで変化出来るアレだ。
    defaultConfigソースを見れば

    , ((modMask, xK_h ), sendMessage Shrink)
    , ((modMask, xK_l ), sendMessage Expand)


    となっている。

    もう一つ「IncMasterN型」は、マスターペイン側に入れるウインドウの数を変化させる。
    デフォルトでは、Tallレイアウトのメイン(左側)は、ウインドウが1つだが、この数を増やしたり減らしたりできる。
    値コンストラクタは一つの引数Intをとる。増やしたり減らしたりする数の指定だ。
    で、またdefaultConfigのソースを見れば

    , ((modMask, xK_comma ), sendMessage (IncMasterN 1))
    , ((modMask, xK_period), sendMessage (IncMasterN (-1)))


    となっている。

    このResize型やIncMasterN型のメッセージはTallレイアウトにだけ効果があるわけでなく、同じような構造のレイアウトには同じように効果がある場合もある。

    また、それぞれのレイアウトについては、独自の操作用メッセージがセットになっていたりするので、モジュールの中にMessage型クラスのインスタンスを見つけたら、何の操作をするためのものかを確認しよう!!

    ちょこっと修正系


    レイアウトの中には、自分自身が単独でレイアウトになるのではなくて、既存のレイアウトを変化させて新しいレイアウトを作るものがある。
    例えば、「Mirror l a型」だ。

    値コンストラクタの形は、

    Mirror (l a)

    となっており、インスタンスの形が

    LayoutClass l a => LayoutClass (Mirror l) a

    となっている。

    「(Mirror l) a」型がLayoutClass型クラスのインスタンスなのだ。
    つまり、LayoutClassのインスタンスをMirror値コンストラクタの引数に与えると、新たなLayoutClassのインスタンス、すなわち、レイアウトが生成される。

    このMirror l a型、具体的には、Mirror値コンストラクタに、ある既存のレイアウトを入れると、その既存のレイアウトを90度回転した新しいレイアウトを生成してくれる。

    さて、このMirror l a型のような役割を果たしてくれるものの仲間にLayoutModifierと呼ばれるものがある。
    以前(その10)でメモした、ステータスバーの部分にウインドウが重ならないようにlayoutHookに設定したavoidStruts関数がこれにあたる。

    XMonad.Hooks.manageDockモジュールのリファレンス
    http://xmonad.org/xmonad-docs/xmonad-contrib/XMonad-Hooks-ManageDocks.html


    avoidStruts :: LayoutClass l a =>
    l a -> ModifiedLayout AvoidStruts l a


    つまりは、LayoutCLassのインスタンスを引数にして渡すと、ModifiedLayoutなんたら型が帰ってくる。

    この「ModifiedLayoutなんたら型」は、XMonad.Layout.LayoutModifierモジュールで規定されているのだが、重要なのは、コレ!

    (LayoutModifier m a, LayoutClass l a) =>
    LayoutClass (ModifiedLayout m l) a


    つまりは、LayoutModifier型クラスのインスタンスを用いて修正された、「(ModifiedLayout m l) a」型はLayoutClassのインスタンス、すなわち、レイアウトになるってことだ。

    具体的な使い方のパターンは、avoidStrutsのように、修正用関数にレイアウトを引数として渡すと、新しいレイアウトが返るパターンだ。

    LayoutModifierの例をもう一つ。
    http://xmonad.org/xmonad-docs/xmonad-contrib/XMonad-Layout-Spacing.html

    実は、以前使っていたawesomeからxmonadに乗り換えたきっかけが、このspacing機能だ。

    before

    layoutHook = spacing 2 $ Tall 1 0.03 0.5

    after

    わかる??? このウインドウの周辺のクールな隙間!!

    smartSpacingなら、ウインドがひとつのときには隙間を作らないという、ちょっとだけかしこな感じを出してくれる。

    さて、このクールな隙間!!
    http://www.youtube.com/watch?v=4mMb7qXwhuU
    の人もawesomeからxmonadへ(隙間がやりたいこととは限らないけれど、そうじゃないともいいきれない)
    某掲示版でも、この隙間は話題になってて、みんな、「お前は俺か」なのだ。
    そう、xmonadを使う理由は、haskellがどうだとか、xineramaがどうだとか言ってるけど、「ほんとはお前ら単に隙間がかっこいいと思っただけなんじゃね?」と密かに思ってたりする。


    さて、話は変わって、実は、(|||)演算子の結合結果も、ある意味このレイアウト修正系の種類の一つであると考える事も出来る。(|||)関数は、もともとのレイアウトに、他のレイアウトも選択できる機能を追加した新しいレイアウトを生み出しているのだ。


    (|||) :: (LayoutClass l a, LayoutClass r a) =>
    l a -> r a -> Choose l r a


    このChoose l r a型はも、LayoutClassのインスタンスだ

    (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a


    ちなみに、(Mirror l)や(Choose l r)、(ModifiedLayout m l)は(その13)でみた例の型コンストラクタと部分適用のアレだ。

    というわけで、(|||)演算子の戻り値は、ひとつのLayoutClass型クラスのインスタンスなのだ。

    これは、何を表しているかというと、「ひとまとまり」であるということも表しているのだ。

    なので、次のような書き方は

    layoutHook = avoidStruts (Full ||| Tall 1 (1/100) (1/2))

    当然、FullにもTallにもavoidStrutsの効果が及ぶ。
    これは、カッコで括っているからではなくて、FullとTallの結合した一個のレイアウトに対してavoidStrutsしてるイメージだ。

    avoidStrutsの効果を個別に及ぼさせるには、結合させる前にもとのレイアウトを直接変化させればよい。

    LayoutHook = Full ||| avoidStruts (Tall 1 (1/100) (1/2))



    2013年12月23日月曜日

    xmonadとHaskell(その14:型クラス)

    (その13)では、XConfig l型のリファレンスで、layoutHookフィールドに定義すべき型が「l Window」型であることを確認し、この型として具体的にどんな型が使えるのかを調べるためにdefaultConfigのソースを覗いてFull a型、Tall a型などの型の値が使われていることを確認した。

    しかし、自分でlayout設定をする時に、結局何を頼りにlayoutHookフィールドを設定すれば良いのか?XConfig l型のリファレンスを見ているだけでは、「l Window」型の型変数「l」が何を指しているのか分からずじまいだった。

    今回はその答えに近づくために、xmonad関数の型シグニチャをいきなり見てみる。

    xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()

    この赤字の部分は、「型クラス制約」とよばれる。
    というわけで、今回は「型クラス」の話だ。

    型クラスって?


    まずは、これ。
    人間と猫がいて、挨拶をする関数を作りたいんだけれど、、

    「hello」という関数名で人間にも猫にも挨拶しようとしたんだけれどうまくいかない。
    普通、同じ関数名で複数の異なる処理を同時に定義することは出来ないのだ。

    だけど、、、惜しいよね。
    確かに「型」は違うけど、似たような処理じゃん??!

    そこで登場するのが「型クラス」の仕組みなのだ。

    これで、人にも猫にもhello出来るのだった。



    すなわち、型クラスを使えば、同じような処理をするために、同じ関数名を使って、色んな型を扱えるのだ。ちなみに、こういうのを「多重定義」とかいうらしい。

    だからもっと増やして、犬にだって、うさぎにだって、インコにだって、挨拶ができるようにすることができるし、更に実は、helloだけじゃなく、goodMornig関数やgoodNight関数等、複数の関数をこのグループに含めることも出来る。

    というわけで、実際に上の型クラスを改良しながら、型クラスに親しんでみる。

    型クラスを宣言してみる


    まずは、4行目が型クラスを作る宣言の部分。

    class Hello a where
    classwhere」で型クラスを作りますよという書式だ。

    そして、その中にある「Hello」が型クラスの名前。

    型クラス名は、型や値コンストラクタと同様、大文字のアルファベットから始まる文字列だ。今度は、helloだけじゃなく、色々な挨拶をするので、「Aisatu」という名前の型クラスにしてみる。

    更に、型クラスの名前に続いて、「a」と書かれている。
    これが、この型クラスで定義する関数の共通の引数になる型を表現している型変数だ。

    新しい型クラス「Aisatu」の宣言は以下のようになる。

    class Aisatu a where

    関数を登録


    whereの後ろに、共通で使いたい関数を書いていくことになる。
    この、型クラスに属する関数のことは、特に「メソッド」とか「メンバ関数」と呼ばれたりする。

    5行目にそのhelloメソッドが書かれている。

    hello :: a -> String

    関数の型表現のみが宣言されている。
    この中の型変数「a」が、型クラス名の後ろにある「a」と連動している。
    つまりは、関数の引数として、人や猫等の色々なものにしたい部分を型変数「a」にして、関数の型宣言をすればよい。

    ここで、別の挨拶関数も登録しておこう。

    hello :: a -> String
    goodMorning :: a -> String
    goodNight :: a -> String

    どの関数も、hello関数と同じで、ある型の値を一つ引数にとって文字列を返す関数だ。

    型クラスの仲間であるインスタンスにする


    型クラスの仕組みは、まず、あるグループを作って、そこでいろんな型で共通に使いたい関数を宣言する。次に、その関数を使いたい型は、それぞれ型についてのその関数を実装することで、仲間に入るという風になっている。

    というわけで、まずは、ある型が仲間になりたいと宣言するための書式が7行目だ。

    instance Hello Human where

    型クラス宣言と似た感じで「instancewhere」で、ある型をある型クラスの仲間にしますよと宣言する書式だ。そして、仲間になる型を「インスタンス」と呼んだり、ある型を仲間にすることを「インスタンスにする」とか言ったりする。

    次に「Hello」が仲間になる型クラスの名前で、その後ろの「Human」が仲間にする型の名前だ。

    Humanを新しい型クラス「Aisatu」のインスタンスにするための宣言は、次のようになる。

    instance Aisatu Human where

    仲間になりたいと宣言したら、後は実際にその具体的な型で、メソッド関数が使えるようにプログラムすればよい。

    whereの後ろ、8行目では、hello関数を実際に定義している。
    すなわち、whereの後ろで、型クラスで宣言されているメソッドについて、Humanという具体的な型を引数とする時の具体的な処理をプログラムするのだ。

    そのための仕組みであって、当たり前といえば当たり前だけれども、11行目を見ると、8行目と同じhello関数が実装している。まさに「多重定義」だ。

    さて、新しいAisatu型クラスには、3つのメソッドがあるので、Humanをインスタンスにするには、この3つのメソッドを実装する必要がある。

    instance Aisatu Human where
      hello h = "hello, " ++ h_name h
      goodMorning h = "goodMorning, " ++ h_name h
      goodNight h = "goodNight, " ++ h_name h

    同様に、Neko型もAisatuのインスタンスにし、更には、新しくInu型を定義して、これもAisatuのインスタンスにしてみる。



    さて、実際にghci上でこのコードを読み込んでみる。
    そして、hello関数の型シグニチャを確認すると、

    hello :: Aisatu a => a -> String

    一番初めに見た「型クラス制約」というものが付いていることが分かる。

    型クラス制約された関数


    今までAisatu型クラスを作ってきた流れから察することができる通り、ここでの「型クラス制約」というのは、「helloの引数aは、Aisatu型クラスのインスタンスを使ってね」ということを表している。

    すなわち、aというのは、多重定義を実装した具体的な型のことであり、その実装をしているからこそ、helloの引数に使えるのだ。逆に、実装をしていない型はhello関数の引数になりようがないだろう。

    更に、型クラス制約というのは、hello関数そのものつまり、メソッドを使う場合だけにつくものではない。当たり前だが、内部でhello関数を使うような、関数を定義した時にも、その定義された関数に型クラス制約が付く。

    例えば、

    genkina_hello x = (hello x) ++ "!!!"

    というhelloメソッドを内部で使うgenkina_hello関数の型シグニチャは

    genkina_hello :: Aisatu a => a -> String

    と表現される。

    基本クラスと自動導出

    おまけとして、巷のxmonad.hsの中でもちょくちょく見られる、便利な技の話。

    haskellの標準ライブラリPreludeは聞いたことがあると思う。
    ghciを起動するとカーソルに

    Prelude>

    とか出てくるアレだ。
    そして、そのモジュールのリファレンスがこれだ。
    http://hackage.haskell.org/package/base-4.6.0.1/docs/Prelude.html

    色々と見たり聞いたりしたことのあるようなものが、並んでいると思う。

    そんな中、「型クラス」についても標準ライブラリで見たことあるだろうものが基本クラスとして定められていたりする。

    Eq型クラスの(==)や(/=)
    Ord型クラスの(<)や(>)等

    つまりは、これらの関数はメソッドであり、自作の型についても、これらを実装してインスタンスになれば、その関数を使うことができたりするのだ。

    しかし、実は、これら基本型クラスのうち、次のものはについては、自作の型についてinstance〜where構文をつかった実装を必要とせずに、自動導出という機能を使って、一瞬でインスタンスになれたりするすごい機能がhaskellにはある。

    自動導出できる型クラスはEq,Ord,Enum,Bounded,Show,Readだ。

    使い方は、自作の型宣言の後ろに次のようにderivingキーワードとカッコで囲った型クラスを書くだけだ。

    data Human = Human {h_name :: String} deriving(Eq,Ord,Show)

    複数の型クラスのインスタンスにしたいなら、カンマで羅列すればいい。



    2013年12月8日日曜日

    xmonadとHaskell(その13:具体型と型コンストラクタ)

    タイル型ウインドマネージャの楽しい処のひとつとして、レイアウトがある。

    デフォルトのレイアウト「Tall」


    デフォルトのキーバインドでは、M-Spaceでレイアウトが切り替わる。
    「Mirror Tall」と名づけられたレイアウト


    更にM-Spaceで、画面一杯に広がる「Full」レイアウト


    xmonadにおいて、これらレイアウトは、「型」として表現されている。

    XConfig l型データのリファレンスでいつものようにコンストラクタを確認して、layoutフィールドをみると「l Window型」となっている。
    この型のパターン、いつも見ている「XConfig l型」に似た変な型だ。

    そもそも、Int型やBool型のような「なんとか型」じゃない、「なんとか なんとか型」ってなんだろう??と思っている人もいると思う。

    haskellの初心者解説をネットであちこち漁っていると、「なんとか なんとか型」の代表格である「Maybe a型」にまずは出会うかもしれない。
    そして、さらに、「Map k a型」に出会ったり、解説によれば「実はリストも [] a型」だとかいう話も読んだかもしれない。

    そこで、にわかhaskellerである我々は、解説を読みながら一生懸命イメージして理解しようとする「このわけのわからん型は具体的に何を表現しているのかな?」と。

    Maybe a型は、Just aとNothingがあって、この型はエラーを含む戻り値を表現する場合に使えたりするとか、リストの話を聞いて、aの部分は任意の型であり、それを含んで表現される[a]というのは箱のような存在だとか頑張って想像してみたかもしれない。

    やがて、そもそも、我々はxmonadのカスタマイズをするためにhaskellを読み始めていたりするので、遅かれ早かれ、「XConfig l型」とはなんぞや?と理解しようと努力するようになる。

    そして、頑張って想像してみるに、「任意のl型データを入れてある箱のような存在がXConfigだ」とか。layoutHookフィールドの中にみつけた同じ「l」の字をもつ、「l Window型」に至れば、その任意のl型データがWindow型データの箱で、、、うにゃうにゃうにゃ、、、

    さらに、不幸にもリファレンスからStackSetを覗き見たり、型クラスの領域に踏み入れたりしてしまうと、もっと大量の型引数がでてきてしまって、そっとページを閉じるのだ。「今はよくわからんが、haskell的センスが磨かれたら、想像出来るようになるにちがいない!!だって、haskellは難しいんだから、理解するのに時間がかかるんだもん」と、、、

    言わずもがな、少し前の私だ。

    しかし、上記の気弱な言い訳は見当違いのものだったりする。わからないのは、難しいからではなくて、知らないからなのだ。

    「そもそも型とは何ぞや」という壮大な問いを一生懸命イメージしたりする以前に、型について初心者が知っておくべき超重要なことが存在する。

    というよりも、このことを知れば、xmonadの設定レベルでは「そもそも、型とはなんぞや??」なんいうつまらない事で悩まなくなる。

    なので、haskellの教科書的な本もまだ持たず、ネットのhaskell情報を断片的に読み漁りながら彷徨っているxmonadカスタマイザーの中でも無謀といえる猛者たちへ、haskellの型にもう少し親しみが持てるようになれるかもしれないメモをしたい。

    型の種類


    haskellの型に親しんでいくとき、まず第一に覚えなければならないことがある。

    それは、型には種類があるということだ。

    この型の種類は、一つが「具体型」と呼ばれるもの、もう一つが「型コンストラクタ」と呼ばれるものである。

    まず、いわゆる「普通の型」だと思っていたものがだいたい「具体型」だ。
    Int型、char型、String型、Bool型

    次に、もう一つの種類、型引数をとるものを「型コンストラクタ」という。
    いつも見ている「XConfig l型」であるが、「XConfig」の部分が型コンストラクタである。そして、「l」の部分を「型引数」という。

    更に、型コンストラクタは必要な型引数が与えられると、具体型になるということを覚える。

    すなわち、XConfigという「型コンストラクタ」にlという型引数を与えると、「XConfig l型」という「具体型」になるのだ。

    この簡単な約束事を把握した上で、少し練習だ。
    ソースコードの中の型を表現する項目のある部分に

     Hoge a

    とかかれている部分があったとする。
    上の説明を見た後だと、Hogeが何で、aが何で、Hoge aが何かわかるだろうか?





    簡単?
    正解は、


    である。

    すなわち、これだけだとわからないことだらけなのだ。
    そして、この「わからないこと」がわかるようになると、色々とわかるようになるのだ。

    まず、Hogeは、大文字のアルファベットで始まっており、後ろに引数があるので「型コンストラクタ」であることがわかる。

    そして、型コンストラクタは「必要な型引数」が与えられると具体型になる。
    この「必要な」という部分が超重要なのだ。

    型コンストラクタの型引数の数


    (その11)でみた連想配列を表現する「Map k a型」を思い出してみる。
    上記の流れから言えば、Mapは型コンストラクタであり、k、aはそれぞれ型引数だ。
    連想配列のキーとなるものの型がk、値となるものの型をaとして、これを型コンストラクタMapにあたえると、それは具体型「Map k a型」となる。
    すなわち、Mapは二つの型引数を必要としている。

    では、「Map k」と書いてあると、それは何であろうか??

    なんか、よく似た事をどっかできいたことあるよね。
    関数の部分適用の話だ。

    実は、型コンストラクタは部分適用ができ、部分適用された型コンストラクタは、型コンストラクタを表す。

    すなわち、「Map k」と言う表現は「型コンストラクタ」を表している。

    さて、ここで「Hoge a」である。
    そう、Hoge aという表示単独を見た場合、それが具体型を表しているとは限らないのだ。

    なぜなら、Hogeは少なくとも型コンストラクタであるが、その必要とする引数の個数がわからないからだ。
    もし、Hogeが引数をひとつとるのなら、Hoge aは具体型であるけれども、2個以上の引数を取るのなら、Hoge aは型コンストラクタなのだ。

    型コンストラクタの型引数の種類


    更に、Hoge aの「a」だ。
    型の表現において、小文字アルファベットで始まる文字列で示される者は型変数と呼ばれる任意の「型」である。すなわち、「型」ということは、2種類あり、「具体型」であるとは限らず、「型コンストラクタ」かもしれないわけだ。

    そう、今、あなたが「もしや??!!」と思った通り、型コンストラクタは、型コンストラクタの引数をとれる。関数の引数に関数が取れたのと同じだ。

    いつも見ている「XConfig l型」の「l」は実は型コンストラクタだったりする。

    わかるために注目すべき点

    上記から言えることは、単純に、型の世界で大文字で始まっている文字列だけをみても、それがとるべき型引数の数を知るすべはなく、それが具体型か型コンストラクタかが分からないのだ。

    しかし、実はこの「Hoge a」については意地悪な問いかけであって、実際には型の表現は次の様になっている。

    data Fuga (Hoge a) b = ....

    ここで、今一度、「ソースの中で型が表現される場所」に着目してみたい。
    haskellのソースでは、型が表現される部分とそれ以外の部分が明確に区別されている。

    上の様に型定義のdataキーワードに続くイコールまでの部分、そして、値コンストラクタのフィールド(引数)部分、さらに、型シグニチャに見られる::演算子の右側部分だ。

    そして、ここには約束事がある。この部分に表現される型は、必ず具体型なのだ。

    すなわち、「Fuga (Hoge a) b」は全体として必ず具体型なのだ。
    ここでは、型表現の中でも括弧は用いられてそれがひとまとまりになる。

    なので、Fuga型コンストラクタは、型引数を2つとること「だけ」は「わかる」のである。
    (Hoge a)自体は、Hogeの必要とする引数の個数、もしくは、Fugaの取る引数の種類の情報がわからなければ、詳しくは「わからない」のだ。

    また、先に「l」は型コンストラクタだと書いた。
    XConfig l型のlayoutHookフィールドについてみると、

    layoutHook :: !(l Window)

    すなわち、::演算子の右側は「l Window」が具体型であり、「l」は型引数を一つとる型コンストラクタであることがわかる。
    そう、::の右側が全体で具体型だと決まっているので、lは型引数を一つとる型コンストラクタであるということが分かるのだ。ちなみに、Windowは具体型か型コンストラクタかはこれだけだと「わからない」ことが分かると思う。

    というわけで、型に付いても関数の時と同じように、こまめにリファレンスをみる必要がある。何を見るかというと、特に、型引数の数である。

    また、ghci上の「:k」コマンドで型の種類が調べられる。

    import XMonad.Core
    :k XConfig

    とか試してほしい。「*」が具体型を表現している。

    関数の型シグニチャ


    実はここまでの知識を活用すると凄く面白いことが見えるようになったりする。

    関数の型シグニチャだ。
    これも::演算子の右側に書かれていることをいつも見ているので知っているはずだ。

    関数については、(その8)でメモした。
    関数がカリー化されいることや、部分適用にも馴染んでいるならば、既に、引数の間に挟まれた矢印が、引数を区切る単なる飾りでは無く、「関数」を表現しているというのは何となく感じられるようになっていると思う。

    しかし、もっと驚きの秘密があったりする。
    関数が::演算子を使って型シグニチャとして表現されているのは、引数の型を表現するためではない。

    実は、そもそも「関数」自体が「型」なのだ。

    ここで、「関数が型ってどういうことよ?」といって頭を悩ませる必要はない。
    単に今まで上で見てきた「型の表現」の話として以下を読み進めて欲しい。

    例えば、足し算の関数として、add関数があったとする。
    Intを引数に二つとって足して結果を戻り値にする。
    それを型で表現すれば、以下のようになるだろう。

    add :: Int -> Int -> Int


    haskellの関数はカリー化されているので、以下の様に書き換えることが出来る。

    add :: (Int -> (Int -> Int))


    ここまでは、(その8)でメモした話だ。

    ここからが、今回の話だ。

    まずは、(Int -> Int)の部分に着目してみる。
    矢印記号は、通常の関数における中置表記の様な存在だ。
    なので、実は構造としては、二つの引数をとる関数と同じようなものであり、括弧でくるむと前置表記できたりする。

    すなわち、こうだ

    ((->) Int Int)

    ここで、「->」というのが型コンストラクタであり、この型コンストラクタが二つの具体型を型引数としてとるとすれば、、((->) Int Int)は、「(->) Int Int型」のデータとよめる。

    同じように全体も書き換えられる。

    ((->)  Int ((->) Int Int))

    (->)型コンストラクタが、Int型のデータと、((->) Int Int)型のデータを型引数としてとって具体型になっているのだ。

    つまり、「関数」というのは、(->)型コンストラクタが型引数を二つとって作る具体型なのだ。

    凄くない??
    だけど、まぁ、xmonadの設定には何の関係もなかったりする。

    レイアウトの仕組み


    さて、そんなことよりもxmonadでのレイアウトだ。
    その設定は、layoutHookフィールドに定義することになる。

    さっきもみたが、

     layoutHook :: !(l Window)

    である。
    型の話だけで言えば、Windowを型引数にとって具体型になるものである。

    で、これって、具体的になんなのか?

    まずは、XMonad.Layoutモジュールのリファレンスを見てみる。
    http://xmonad.org/xmonad-docs/xmonad/XMonad-Layout.html

    そこに見慣れた文字がある、FullやTallやMirrorだ。

    data Full a

    と書かれている。あらためて型の定義を見ると分かることがある。
    これは、Full型コンストラクタが型引数をひとつとって「Full a」型という具体型をつくるということだ。

    もう一つ、

    data Tall a

    も同様だろう。ちなみに、「Tall」は、背が高いではなく、多分、TileのTとall windowのallじゃなかろうかとおもう。

    更に、

    newtype Mirror l a

    というのがある。
    newtypeキーワードは気にしないでみると、Mirror型コンストラクタは型引数を二つとって「Mirror l a」型という具体形をつくることはわかる。そして、これは、(Mirror l)型コンストラクタが一つの型引数をとって、「(Mirror l) a」型という具体形をつくるといいかえることができる。

    すなわち、このどれもが「l Window」型に合致するのだ。

    ここまでは、型の話だ。
    では実際に、layoutHookフィールドではどうかくのか??
    型定義のイコールの右側、値の話である。

    Full a型の値コンストラクタはFullだ

    layoutHook = Full

    Tall a型の値コンストラクタは引数をとるので

    layoutHook = Tall 1 3/100 1/2

    最後のMirrorの値コンストラクタには

    Mirror (l a)

    と書かれている。これは、Mirror値コンストラクタは、「l a」型の値を引数にとるということである。その具体的な値は例えば「Tall 1 3/100 1/2」であるので、

    layoutHook = Mirror $ Tall 1 3/100 1/2

    と書く事が出来る。

    さて、これらの書き方だと、単独のレイアウトしか使うことが出来ない。実際には、M-Spaceでこれら三つのレイアウトが切り替わったりするのだ。

    どうするのか??
    というより、defaultConfigではどう定義されているかいつものリファレンスからソースを覗き見てみよう。
    http://xmonad.org/xmonad-docs/xmonad/XMonad-Config.html


    layout = tiled ||| Mirror tiled ||| Full
     where
      tiled = Tall nmaster delta ratio
      nmaster = 1
      ratio = 1/2
      delta = 3/100


    このlayoutがlayoutHookに定義される。

    注目点は(|||)演算子だ。この演算子の説明は、XMonad.Layoutのリファレンスに見つけることができる。


    (|||) :: (LayoutClass l a, LayoutClass r a) =>
           l a -> r a -> Choose l r a


    (|||)は右結合演算子なので分解して、まず「Mirror tiled ||| Full」を型で表現してみると次のようになる。

     (Mirror Tall) a -> Full a -> Choose (Mirror Tall) Full a

    (Mirror Tall)が型の部分適用で型コンストラクタとなり型引数「l」に対応だ。

    そして、同リファレンスで紹介されている通り、「Choose」は型引数を3つとる型コンストラクタであることが、「Choose l r a」型の定義からわかる。
    なので、「Choose (Mirror Tall) Full a」は「Choose (Mirror Tall) Full」を型コンストラクタ 、「a」を型引数として、分解でき、型変数を使った表現「l a」型に合致する。

    更に、

    (tiled ||| (Mirror tiled ||| Full))という式の型は

    Tall a
    -> (Choose (Mirror Tall) Full) a
    -> Choose Tall (Choose (Mirror Tall) Full) a

    と表される。
    そしてこれもまた、型変数で表せば、「l a型」となっている。

    この「l」というのは、任意だということは何となく知っていたと思うが、aを型引数としてとるために部分適用で表現された変幻自在の型コンストラクタだという驚愕の真実までは知らなかったと思う。

    まぁ、実際には、(|||)の引数になるには、一定の制限(LayoutClass l a等)があるものの、レイアウト設定の仕組みは、型引数を一つとって具体型になる型を(|||)演算子でいくつでもつなげることが出来、しかも繋げた結果も同じ型だったりするので、その型の値をそのままlayoutHookに定義してあげれば良いようだ。


    さて、ここであらためて、リファレンスで、defaultConfigの型シグニチャをみると

    defaultConfig :: XConfig (Choose Tall (Choose (Mirror Tall) Full))

    となっている。

    このメモを読む前ならきっと見て見ぬふりをしていただろう。

    しかし、今ならそこに書いてある意味がなんとなくわかったりするんじゃなかろうか?
    というより、その文字の並びは上記で既に見覚えがある。

    defaultConfigはXConfig l型の実際の値である。なので、layoutHookフィールドにも実際の値が定義される。ということは「l」の部分にもその値に基づく具体的な型が表れてくるのだ。

    その実際の型は長ったらしいけれども、単なる一つの型引数をとる型コンストラクタのことなのだ。