2013年10月16日水曜日

xmonadとHaskell(その7:ワークスペース名あれこれ)

ステータスバーをカスタマイズするためのPP型の話の続き
リファレンスはココ
XMonad.Hooks.DynamicLog

dzenPPの基礎となる、PP型のコンストラクタをざっとみる。
PP型は13個の項目から成り立つ。

初めの4つはワークスペースの表示の仕方について。

ppCurrent
 現在のフォーカスがあるワークスペース

ppVisible
 見えてるけれどフォーカスが無いワークスペース
 (複数モニタを使っている場合のみ)

ppHidden
 見えてないワークスペース(中にウインドウがある)

ppHiddenNoWindow
 見えてないワークスペース(中にウインドウがない)

ワークスペース

タイリングウインドマネージャでは定番のワークスペース。

タイリングウインドマネージャは、そもそも、画面の中に複数のウインドウを重ならないように敷き詰めて、配置作業の効率化を目指してたりすると思う。

一方、ワークスペース機能は、複数のデスクトップを切り替えられる機能で、タイリングウインドマネージャとは関係なく、昔から色んなデスクトップ環境で用意されていたと思う。

S101のような小さなノートPCでは、一つのウインドウを画面内で最大化して使う事が多く、タイル配置はあまりしない。そのかわり、ワークスペース毎に一つのウインドを配置し、複数のワークスペースを移動して、画面を切り替えるっていうのが、とても使いやすい。

ブラウザで色んなページをタブで開いて、タブを行き来するのと同じ感覚。
これが大いに気に入っている。

そして、今は、S101だけでなく、デスクトップでもタイリングウインドマネージャを使うようになり、本来のタイリングの恩恵を受け、更に、マルチモニタでも「おまえらxmonadでマルチモニタって捗りすぎ」と、「捗る」って言いたいだけなのがバレバレなくらいお気に入りになっていたりする。


さて、ワークスペースの定義は、xmonadにおいては、xconfig l型の中で

workspaces :: ![String]

として定義する項目がある。
defaultConfigでは、1から9までの数字を名前として定義してある。

 workspaces = ["1","2","3","4","5","6","7","8","9"]

みたいな感じ。

もちろん、好きな文字列を使うことも出来るし、ワークスペースの数も好きなだけ作ることが出来る。

ワークスペース名

以前使っていたawesomeは、始めて出会ったタイリングウインドマネージャだった。
スクリーンショットでみるステータスバーには何を意味するのか分からない数字の1〜9が並んでいたが、それがなんだかとてもかっこよく思えた。

そして、それがワークスペースを表すものだと知り、更には、色々なカスタマイズを見ていると、数字以外の「1:web」とか「2:chat」などの文字列が表示されているのをみて、またまた、「こんなことも出来たのか、すげーカッコいい!!」と思って、自分でもやってみたりした。

しかし、間もなく1文字で表されていた数字の偉大さを知ることとなる。

まず、ワークスペース名に文字列を使うと、その表示だけでステータスバーの大部分を占めることになり、見た目が凄くうるさいくなること。

次に、せっかく、「web」とか「chat」とかつけても、わざわざウインドウを開く(特に自動の割り振りとかをしないと)のに、面倒くさくてそのワークスペース名称通りに使わないこと。

なので、awesomeの時に結局落ちついたワークスペース名は、1から5の数字だった。



上のawesomeのスクリーンショットを見ると、多分、今のワークスペースは「2」であることがわかる。そして、さらによく見てみると、ワークスペース名である数字のすぐ左上に小さな三角がついている(5のみ付いていない)。この三角はそのワークスペース内にウインドがあることを示していたりする。

awesomeの場合、この表示のパターンが概ね決まっていた様に思う。
要するに定義済みのワークスペース名が全て表示され、カレントワークスペースは表示色が変わり、ウインドのあるワークスペースには何か好きな印を付けるという感じ。

と、ここまでは、awesomeを使ってた頃の話。

ワークスペース名の表示の仕方

先に書いたワークスペース名に文字列を付けるとステータスバーがゴチャゴチャする原因は、定義されているものが全て表示されたからだ。

なので、文字列を使ったワークスペース名であっても、使っているワークスペース、すなわちウインドが存在するワークスペース名だけを表示させれば、割とすっきりとする。

初めに書いたppCurrent等の設定で、これを簡単に実現できる。

PP型コンストラクタでみる、ppCurrentの定義は次の通り。

ppCurrent :: WorkspaceId -> String

すなわち、WorkspaceId型の引数をとって、文字列を返す関数をppCurrentに定義すればいい。

WorkspaceId型というのは、文字列型のエイリアス。
haskellでは、エイリアスのことを「型シノニム」と言う。
何にせよ、単なる文字列というよりも、ワークスペース名を表す文字列ですよといった方がわかりやすいかもっていう、お馴染みのアレだ。


さて、では、具体的にどんな関数を定義すればそれっぽいことをしてくれるか、例をみてみる。

XMonad.Hooks.DynamicLogモジュールでPP型のデフォルトとして定義されているdefaultPPを覗き見る。

何を覗くかというと、前回紹介したソースである。
リファレンスのdefaultPPの右にあるソースリンクをクリックしてみる。
引用すると以下の通り。

ppCurrentに注目してみると

ppCurrent = wrap "[" "]"

となっている。

wrapはサランラップかクレラップのラップだろうから、ワークスペース名を"["とか"]"でラップするっぽい。

実際、wrapはこのモジュール内で定義されている関数で、リファレンスのFormatting Utilitiesの項目で見つけることが出来る。

型シグニチャを見ると、3つの文字列引数を受け取って、文字列を返す。
一つ目の引数の文字列を左側に、二つ目の引数の文字列を右側につけることで、三つ目の
引数の文字列を真ん中にサンドイッチした文字列を返す。defaultPPのppCurrentは、ワークスペース名文字列について左を"["で、右を"]"ではさんで表示するための定義だとわかる。

そして、ppVisibleは同じような感じで文字列で挟み、ppHidden、すなわち、画面表示されていないワークスペースはそのままの文字列を表示、但し、ppHiddenNowWindowsの定義でウインドウの無いワークスペースはワークスペース名を表示しないということが、id関数やconst関数の意味を調べてみると分かるかもしれない。

なので、上で書いていたワークスペース名のスマートな表示については、これを参考にすれば良いことになる。

表示のカスタマイズ??

さて、ワークスペース名の表示の仕方のカスタマイズだけの話なら、両側で挟む"["や"]"の部分の文字列を別の文字に書き換えることで、なんとなくカスタマイズが出来る。

しかも、具体的には、defaultConfigと同じパターンで、レコード構文を用いて

myPP = defaultPP{ ppCurrent = wrap "[[[" "]]]"}

何て感じでいけそうかな?と予想できる。
もっと、欲張ってdefaultPPのソースの並びを見るとdzenPPなんかも当然見れてそのppCurrentは、

ppCurrent = dzenColor "white" "#2b4f98" . pad

となっており、ちょこっと複雑化しているけれど、なんとなく、whiteの部分や#2b4f98を書き換えると色が変えれるのかな?と試してみたくなる。



何度も表示しているうちのステータスバーの定義は以下の通り。

ppCurrent = dzenColor "#00ffaa" "" . wrap "[" "]"

実際にカスタマイズってのは、こんな感じで適当に出来るんだけれども、、、、

さっき話した、wrap関数の三つ目の引数は何処に行ったの?とか、そもそも、ppCurrentで関数を定義するってどういうこと??とかが気になるようになると、見よう見真似で色を変えたり、左右のデミリタ文字を変えたりするのではなくて、

ppCurrent = dzenColor "#00ffaa" "" . wrap "[" "]"

が何を意味しているのか分かって、自分でその式を書くことが出来るようになるきっかけの1行だったりする気がする。

カリー化とか部分適用とかいう例のアレだ。

ちなみに、私の持ってる「すごいHaskellたのしく学ぼう!」っていう本には変な挿絵のイラストがいっぱいあるが、私にとって二つ印象的なイラストがある。

その中ひとつがカリー化関数という項目のページに書いてあるおじさんの肖像画で下になんとかカリー(筆記体で読めない)と名前があるので、多分カリーおじさんなんだろうなぁ、、カリー化のカリーは人の名前でその人がこの人なのかなぁ?と気になる。

もう一つ印象的なのは、I/Oアクションの解説で出てくる「足がたくさん生えている箱」の絵。本文の内容でも、そういう話なんだけれど、、、そこは「ネコバスのほうがいいなぁ、、」と思う。

2013年9月13日金曜日

xmonadとHaskell(その6:ステータスバー)

その5では、dzen関数を使って取りあえずのステータスバーを付けてみたが、「これ、思ってたのと違う」と感じてるかもしれない。確かに、そこいらへんで見かけるスクリーンショットに写っているステータスバーはもう少しカッコいい(他のWMとの比較は置いといて)

そこで、dzenを使ったステータスバーをカスタマイズするための下準備だ。

xmonadとステータスバーの関係


xmonadには、xmonadの状態が変化した時、例えば、レイアウトが変わった、ワークスペースが変化した、フォーカスされているアプリケーションが変わった時などに、呼び出される関数がある。

何かのきっかけが起こると呼び出される関数は、俗に「フック」と呼ばれるが、これもそのひつとであり、xmonadでは、上のような状態変化が起こるとlogHookと呼ばれる関数が呼び出される。


このlogHookは、XConfig l型データの logHookの項目にあり、リファレンスで確認するとXConfigコンストラクタの中で logHook :: !(X ()) と定義され、「windows setが変更された時に実行されるアクション」とコメントされている。(ここで、X ()はIOアクションと同様で、何かを実行する副作用と、それから生成される結果の2つからなるXアクションと把握してOK)

xmonadでは、このフックを利用してステータスバーを作るのが一般的である。
そして、その原始的な仕組みは、

 1.情報を表示するためのアプリケーションを起動しておく。
 2.フックに現在の状況を表す文字列を表示する関数を設定する。
 3.フックが呼び出されるたびに、アプリケーション上に情報が表示される。

この原始的な方法を実践してみるには以下の通り。

XMonad.Hooks.DynamicLogモジュールで定義されたもっとも基本的な表示を行うdynamicLogでlogHookを定義してみる。dynamicLogは情報の書き出し先が標準出力となっているので、これをdzenにパイプで繋ぐため、.xsessionや.xinitrcなどの、xmonadの起動のコマンドラインを

exec xmonad | dzen2

としてみる。

特に、logHookが情報を書き出す様子だけを試すには

exec xmonad >> xmonad.log

とかして、xmonadを起動した後、ターミナルから

tail -f xmonad.log

とかすると、ステータスバーに表示されるような文字列が単にlogに記録されていくのを見ることもできる。

外部アプリとしてのdzen

dzenというアプリケーションは、そもそも、入力された情報を表示するだけのアプリケーションであり、ステータスバーに特化したアプリではない。

YouTubeでのDzen2の紹介
http://www.youtube.com/watch?v=pkKFucEXIlA

X上でのちょっとした情報を表示するためのユティリティであり、ステータスバーのような帯状の表示だけでなく、実際にはウインドウ状の表示エリアを持っているので工夫次第でいろいろな表示の仕方が可能。

一方で、基本的にはテキストしか表示できないので、イメージや動的なグラフ等の表示は難しい。

そこで、dzenを使ったステータスバーというのは、xmonadからの情報を単なる一行の文字列として受け取り、それをこの1行幅のdzenで単純に表示している。なので、そこら辺で見かけるxmonadのスクリーンショットのステータスバーは派手さのない簡素な感じのものになっているのだと思う。

実際には、表示するためのXアプリと、それにデータを送るlogHookの連携でどんなステータスバーだって作ることはできる。(xmobarをはじめ、xmonad-log-appletなんていうのもある)

xmonadの内部情報とそれ以外の情報



このスクリーンショットで見るステータスバーは、実は二つの部分から出来ていたりする。

[home] : tall : s_term1

となっている左側の部分と、それ以外の右側の部分は、別々に起動されたdzen上で表示されている。


なぜ、そうなっているかというと、左側の部分がxmonadの状態をlogHookを使って表示している部分であり、右側の部分はconky(cpuやメモリの状態等を色々と調べるアプリ)を使って収集した情報を表示している部分となっているため。


つまり、理屈的には

xmonad | dzen
conky | dzen

のように起動されて使っている。

別々に起動していても、dzenの表示位置を適切に設定してあげれば一つのステータスバーに見える。

ちなみに、xmobarは、このcpu等のパソコンの状態を収集する機能を独自で持っていたりするので、その分、ステータスバーの設置がやや簡単(単純)になってる。


xmonad.hsの中で外部コマンドを実行する方法


繰り返しになるが、上述の方法によれば情報を表示するためのdzenは外部アプリであり、xmonadとは別に起動しておく必要がある。そこで、先の例では、.xsession等のスクリプトからdzenを起動した。

しかし今度は、xmonad.hsの中から外部コマンドを呼び出す関数を使って起動してみる。

そして同時に、先の例では、シェル上でパイプを使ってストリームを繋げ、logHookからの文字列データをdzenに渡していたが、ここでは、XMonad.Util.RunモジュールにあるspawnPipe関数を使うことによって、haskell上でファイルハンドルを使ってdzenに文字列を渡す事が出来るようにする。

spawnPipe :: MonadIO m => String -> m Handle

型シグニチャは少し見たことがないものになっているが、main = doの中で使われる限り、IOアクションが戻り値となり、副作用はコマンドの実行、生成される結果は、そのアプリの入力用ハンドルであると見なしていい。

実際のxmonad.hsでは
...
import XMonad.Util.Run

main = do
     h <- spawnPipe "dzen -hoge_option"
     spawn "conky conf_file | dzen2 -hoge_option"
     xmonad defaultConfig{...}

という感じになる。

h <- spawnPipe ... の構文は前に見た通り、IOアクションから、生成された結果のみ(ここではファイルハンドル)を取り出して、hという名前に拘束するもの。

そして、この入力用ハンドル「h」をlogHookで定義する関数の中に適切に設定することにより、xmonadから外部アプリの入力に直接文字列を渡すことが出来るようになる。

また、単なる外部コマンドの起動はXMonad.Coreモジュールにあるspawnコマンドが使えるので、上記の用に書けば、conkyによる情報収集のためのプロセスを別途起動することも出来る。

logHookに定義すべき関数

内部情報がアップデートされる度に呼び出されるlogHookに定義すべき関数として、XMonad.Hooks.DynamicLogモジュールには、dynamicLogWithPPが用意されている。

dynamicLogWithPP :: PP -> X ()
dynamicLogWithPP は PPデータ型というものを引数にとるのだが、このデータ型が、書き出すデータの書式等を設定するための型である。(これは、xmonad関数に対するXConfig l型のようなもの。)

なので、logHookには、適当なPPデータ型の値を引数とするdynamicLogWithPPを定義してあげれば良い。

そして、PPデータ型の値にはXconfig l型の時のdefaultConfigと同様にデフォルト値が定義されており、その一つが「dzenPP」でdzen用の初期設定がされている。これを元にdefaultConfigの時と同様にレコード構文を使って好きな部分だけをカスタマイズしたPPデータ値を作ることが出来る。

そして、各デフォルト値の内容は、リファレンスからソースをみて確認できる。(リファレンスのページでは、型シグニチャの右側端にあるリンクからその項目のソースを見ることが出来て便利。)

dzenPPのソース

2013年9月8日日曜日

xmonadとHaskell(その5:IOアクション)

IOアクション

(その4)でみた通り、dzenやxmonadの戻り値の型は「IO(なんたら)型」だった。
これはIOアクションと呼ばれる。

IOアクションは、「アクション」というだけあって、「何かを実行するもの」を表している。と同時に(なんたら)の部分で結果を生成してくれたりする。

すなわち、IOアクションは2つの事を行う。
「副作用」を生じさせ、その副作用が「結果」を生成する。

これを、先の2つの関数で見てみると以下の通り。

xmonad関数の副作用は、xmonadウインドマネージャを実行する。
生成される結果は無い。

dzen関数の副作用は、パネルのdzenを起動する。
生成される結果は、dzen用の設定を追加したXConfig l型データ。

Haskellで 半沢直樹!

以下の基本的な入出力の関数はIOアクションを返すもので、haskellのhello worldプログラムなんかで使われるもの。

コンソールへの表示
putStrLn :: String -> IO ()

コンソールからの入力
getLine :: IO String

この2つの関数で半沢直樹してみる。


Haskellでスクリプトを実行したい場合はコンソールで
$ runghc io_hanzawa.hs

また、コンパイルして実行ファイルを作ることも出来る。
$ ghc --make io_hanzawa.hs
$ ./io_hanzawa


main、そしてdo

Haskellにおいて、IOアクションの実行は、mainという名前でのみ行われる。
(ただし、ghciを使って対話的環境は、そのまま実行される)

なので、単純に言えば、

main = putStrLn "hello world"

と、一つのIOアクションを定義づけることしかできないが、io_hanzawa.hsの様に、do構文を使うことで複数のIOアクションをひと纏めにして、mainに定義づけることが出来る。
(若しくは、その4みたいに「>>=」演算子で繋ぐ)

またHaskellの構文では、パイソンとかと同様にインデント(段落下げ)に意味があることに注意。do構文の及ぶ範囲もインデントで表されている。

Haskellのコードは、コピペした時にエラーが出たら、インデントもチェックしてみるべし。

IO(なんたら)のなんたらって何?

先に書いたようにIOアクションは実行すると2つの事を行う。
一つが「副作用」、もうひとつが「結果」の生成。

putStrLnの型シグニチャは

putStrLn :: String -> IO ()

であり、実際のコードでは

putStrLn "どれくらい怒ってますか?"

として、使ってみた。
ここでの「副作用」は、画面に

どれくらい怒ってますか?

と表示する事であり。
生成される「結果」は、型シグニチャに示されたIOの次にある()である。
()は見た目の通り空のカッコであり、何もないという結果を返してくれる。

何もない結果というのは分かりにくいので、次のgetLineを見てみる。
型シグニチャは
getLine :: IO String

であり、実際のコードでは

okorido <- getLine 

として、「<-」演算子を使っている。
「副作用」は、画面にカーソルが出てリターンキーを押すまでの文字列入力を受け付けること。
一方、生成される「結果」は、画面から入力された文字列である。型シグニチャにあるIOの後ろのStringは、この入力から生成された文字列を示している。

そして、「<-」は 、「結果」のみを名前に結びつける演算子である。

何を言っているかというと、
他の言語では、標準入力からのデータを受け取る関数の戻り値は「文字列」そのものであることが多いので、

okorido = getLine

って感じに書きたくなるが、これはダメだということ。
getLineは、IOアクションを返す関数であり、「結果」である「文字列」そのものを返す関数ではない。
上のイコールを使った式は、単に、getLineと同じことがokoridでも出来るように定義されるだけなのだ。

xmonad.hsをdo構文で

(その4)で見ていたxmonad.hsでは「=<<」演算子を使っていて、その意味はIOなんたら型から、なんたらのみを取り出して、他の関数の引数にするための演算子と説明した。
実は、この役割と同じで直接別の関数の引数にするのではなくて、名前に結びつけるのが「<-」演算子の役割。

というわけで、(その4)の「=<<」演算子を使ったxmonad.hsを

do構文を使って書き換えれば以下のようになる。


dzenのIOアクションの「副作用」は、dzen実行ファイルを実行し、画面にステータスバーの表示等を行う。そして、その「結果」は、引数として受け取ったdefaultConfigをベースとして、ステータスバーの表示に必要な加工を行った新しいXConfig l型のデータを生成する。

そして、この結果である、XConfig l型データをconfという名前にバインドし、そのconfを引数としてxmonadを呼び出す。

xmonadのIOアクションの「副作用」は、xmonadウインドマネージャの表示とか管理とかである。そして、結果は空っぽの()である。

2013年9月7日土曜日

Gist

コード部分をきれいに表現するためにGitHubのGistを使ってみてる。
シンタックスハイライトをしてくれるので、かっこよい。
Bloggerでは、埋め込み用のコードをそのままhtmlモードに張り付ければよいだけなので使い方も簡単。

このGist、バージョン管理もしてくれるんだけど、ブログに引用する場合、以前のバージョンを指定することができるのかどうかがわからなかった。

今、例で書いてるxmonad.hsは、一つのGistでバージョン管理していけばよいのかなと思っていたけど、Gistのページで以前のバージョンを表示した状態で、埋め込み用タグをコピペしてもダメっぽい。

おかげで、いったん書き換えたのを元に戻す羽目に、、、、


Gistでのコード表示を張り付けるのがかっこよいからといって、コード部分を全部Gistを使っていたらなんだか収拾つかなくなりそうかなぁ。
ブログに引用するためのアカウントを別に作って管理するほうがよさげかも。


https://github.com/


2013年9月6日金曜日

XmonadとHaskell(その4)

初心者に優しそうなリンクとか

xmonadに興味を持って初めて触って見るときには、Archlinuxユーザーならarchlinuxのxmonadのwikiをかじりつつ、"xmonad haskell"とかでググるかもしれない。

割と最近の記事で、ひと通りの設定の仕方が短く纏まってて見やすいのがここ。
Haskell に興味がある人向け xmonad 設定ガイド

haskellには、見慣れない演算子なんかも目立つが、以下のサイトは基本的な事が書いてあってざっと見ておくとよさげ。"$"や"."は何?から、リファレンスを見る際に必須となる型や関数の定義の見方の参考として
Haskell基礎文法最速マスター

そんなこんなで、あちこちを見て回って、結局戻ってくるのが、本家のドキュメントページにあるモジュールのリファレンス。awesomeの時にもリファレンスはあったけど、xmonadの方が充実している気がするので、ガンガン読んでガンガン試す!!
xmonad api docs
xmonad-contrib api docs

もうひとつは、本家のページで紹介されているいろんな人の実際のxmonad.hsファイル
config archive


ステータスバーをつけてみる

ステータスバーを利用する場合に必要となるモジュールのリファレンスページ。

ステータスバーは、xmobar、若しくは、dzenを使う。
xmobarの紹介の方が多いような気がするので、ここではdzenを使ってみる。
(その1)で紹介したように、dzenを別途インストールして呼び出せるようにしておく。(archlinuxのパッケージとしてはdzen2とかdzen2-gitとか)

とりあえず、xmonad.hsの先頭部分でモジュールを読み込む。
そして、xmonadに渡す引数をちょっと変えてみる。



リファレンスで紹介されている通り、上記の使い方がもっとも簡単なステータスバーの付け方になっている。

さて、「=<<」である。
xmonadのページをよく見ると「>>=」という記号や、これをモチーフにしたロゴっぽいものもあちこちで見られる。これが、いわゆるモナドの演算子。なんだか、暗号チックだけれど、あんまり気にしなくても大丈夫。

xmonadの引数はXConfig l型

(その3)までに見てきた形は以下のもの。
main = xmonad defaultConfig {...}

defaultConfigは、既にデフォルト値を設定されたデータであり、{...}の部分は好きな部分だけをデフォルト値に上書きすることの出来る書き方だった。
そして、defaultConfigというのはXConfigなんたら型という型のデータだった。

ここらあたりで、なんとなく一度基本部分のリファレンスを見ると良いかもしれない。
xmonad api docs

XMonad.Mainモジュールのxmonad関数をみると

xmonad :: なんたらかんたら => XConfig l -> IO()

すなわち、xmonad関数は、XConfig l 型の引数を受け取るのだから、xmonadには、XConfig l 型を渡してあげれば良い。そして、defaultConfigそのものはXConfig l 型であり、defaultConfig{...}によって新たに生成されるデータもXConfig l 型なのだ。

XMonad.Coreモジュールをには「data XConfig l」の項目があり、コンストラクタが紹介されている。(その2)辺りで見てきた「値コンストラクタ」だ。

Humanの例で見ていた時、値の種類が複数あり、Otoko、Onna、Okamaというふうに型の名前と値コンストラクタを別にしていたが、値の種類が一個の場合は型の名前と値コンストラクタの名前を一致させるのが慣例らしい。なのでXConfig l型の値コンストラクタはXConfigになっている。リファレンスには、15項目のキーワードとその説明もされており、もちろん、このキーワードでレコード構文を用いてデータを作成できるし、直接順番に引数を15個渡してデータを作成することも出来る。

xmonadが受け取る値とdzenが返す値の型が違う

ステータスバーを付けるために dzen 関数を使い、更に謎の「=<<」演算子も付け加えて、以下のような形になった。

main = xmonad =<< dzen defaultConfig

ここで、dzen関数をリファレンスでみると

dzen :: なんたらかんたら => XConfig l -> IO ( XConfig(なんたらかんたら))

となっており、確かに、「dzen defaultConfig」の部分をみると、dzen 関数が XConfig l型の引数をとっている。
そして、戻り値としては「IO ( XConfig(なんたらかんたら))」というものが返されている。

一方で、xmonadは引数としてXConfig l型をとるので、見た目では、ちょっとだけ型が食い違っている。そう、なんか「IO()」とかいうので包まれてる。

なので、「=<<」は、この包んでる「IO()」を取って、中にある「XConfig なんたら」だけを取り出すみたいな演算子なのだ。

そして、一般に見られるのは「>>=」の方向であり、

hoge >>= hage >>= fuga >>= nya

なんて感じで、左から右に連続して適用されて行ったりするらしいが、「=<<」を使って逆に書くことも出来、思考を表現しやすい方を使えば良いらしい。

すなわち、
main = dzen defaultConfig >>= xmonad

と書いても同じ意味であり、動くが、なんか「main = xmonad」とひっついてる方が良さげに見えるので、そう書いてあるのかもしれない。

2013年8月28日水曜日

xmonadとHaskell(その3)

レコード構文

さて、レコード構文の続き。

レコード構文では中括弧の中でプロパティ名の様な名前が定義でき、値を作るときにも

name = "tubasa"

の用に使うことができた。

しかし、実は、このnameはただの名前ではない。

実は、対応する値を取り出すことの出来る関数でもある。
すなわち、その2で定義されたdarekaからnameに該当する値を取り出したい時

name dareka とすると、"tubasa"を返してくれる。
saikyou dareka なら 9999 である。

凄くない??!!

defaultConfig {...}とは何者?

さて、値コンストラクタを使って値を定義する方法を見てきた。
ここで注意深く見ないとダメなのは、defaultConfigは何か?ってこと。


defaultConfigは「d」という小文字で始まっている。
つまり、値コンストラクタではない!!

例えるなら、defaultConfigは


で言うところのOkamaではなくて、darekaに該当する。
そもそも、値コンストラクタを使って、値を作成する場合、引数を省略することはできない。その2のはじめでも紹介した通り、defaultConfigに該当するデータ型の持つ項目(引数)の数は15個あるので、3つだけでは値は作れないのだ。

しかし、、、defaultConfigは中括弧を使ったレコード構文っぽいのを使ってるよ?

これがレコード構文のもうひとつ面白いところで以下のようなことが出来る。

chigau_dareka = dareka { name = "akira"}

これは、darekaで定義されている値を元に、一部分だけを変更した新しい値を作っている。なので、chigau_darekaの中身は Okama { name = "akira", saikyou = 9999 }っぽく出来上がっている。

なので、defaultConfigというのは、既にどこかで中身の各項目の値が設定されて定義された値であり、そのデフォルトの値に対してレコード構文を用いて必要な箇所だけを再設定して、新たな値を作り出しているのだった。

あちこちのxmonad.hsを探索する時、defaultConfig以外でも、このレコード構文である中括弧にはよく出くわす。
はじめ、この中括弧部分と周りの関係がちっともわからなかったので、ネットをフラフラと探索して初心者サイトなんかを見ていたんだけれど、全然紹介されているところを見つけられなかった。
なので、このメモ記事で、xmonad設定の取っ掛かりになるといいなと思う。

わかってる人はわかってるんだと思うけれど、単語の始まりが大文字か小文字かに注意することで、それが既に定義された何かか、値コンストラクタかを見分ける事が出来るだけでも、設定ファイルを読んでいく時のコツになると思う。

GHCiで試す

一応、GHCiもメモ
ターミナルからghciで起動。:qで終了。
スクリプト書いた時は、:l ファイル名(.hs拡張子はいらない)で読み込んで:rで再読み込み。

:m XMonadとかするとXMonadモジュール読み込む。

式の型を知りたい時は:t
話題のdefaultConfigは
:t defaultConfig
defaultConfig :: XConfig (Choose Tall (Choose (Mirror Tall) Full))
  -- Defined in `XMonad.Config'

XConfigうんたらかんたら型という型でした。

2013年8月27日火曜日

xmonadとHaskell(その2)

設定してみる


これを見れば、少し何かのプログラムをしたことがあるなら、Haskellを知らなくても、{}の内部でキーワードに対応して何かを設定していることがわかる。

実際に、teriminal =の右側の文字列を、"xterm"とか"gnome-terminal"とか自分の持っているものに変えてみる。

modMask = の右側には mod4Maskを指定してある。これは、ウインドーズキーに割り当てられている。デフォルトはmod1Maskでaltキーに割り当てられている。

設定を変えた後は win + q で新しい設定に更新される。



ここで、xmonadの設定の基本の一つは、簡単に言えば、この{}の中にある各項目を設定していくことにある。


上の例のxmonad.hsには3個の項目しかないが、設定すべき項目は全部で15個の項目がある。上の例で設定されている項目の値らしきものは、文字列やシンボル、数字なので初めて見た人でもわかるが、この他の項目には、何を言ってるのかわからない値もみられる。

/usr/share/xmonad-0.11/man/xmonad.hs

上のファイルには、すべての項目のデフォルトの値が示されているかもしれない。



改めて、構文をみてる。

main = xmonad defaultConfig { .... }

これは、xmonad関数に「defaultConfig {...}」で示される値を引数として渡すことを定義している。

defaultConfig {....} は一つの値を示している。


値ってなんだろう?

よく見る、値って"xterm"とか、mod4Maskとか、3とか普通に値っぽく見える。

しかし、Cなんかの構造体やら、Javaやrubyのオブジェクトやらでは、いくつかの要素をひとまとめにして一つのものとして扱えたりする。例えば、「名前」、「性別」、「体重」の3つが纏まって、ひとつの「人間」という値を表したりする。

そしてこれは、単なる"xterm"よりも、なんだかちょっとかっこ良いオブジェクト指向チックで複雑な値にもみえる。

でも、よく考えてみれば、「人間」を表す時に「名前」のみで構成されていれば、単なる"xterm"な値と変わらない。要素がひとつからなる値の方が特殊なのかもしれない。


Haskellの「型」

Haskellにも見慣れた「型」がある。
整数を表すInt、実数を表すFloat、文字を表すChar、真偽値を表すBool。

また、自作の型を定義することが出来る。
Bool型の定義で構文を大まかに例示すれば以下の通り。

data Bool = False | True
 dataキーワードに続けて「型名」を書く。ここではBoolが「型名」

そして、イコールの右側に、取り得る値の種類を定義する。|(縦棒)を使って「または」としてどんどん繋げる。つまり、Boolは「False」っていう値の種類、または、「True」っていう値の種類をとる。

ここで定義されているFalseは「値」そのものではなくて、「値の種類」であることに注意。そして、これを「値コンストラクタ」と呼ぶことを覚える。


コンストラクタは、インスタンスを作るときのアレだ!
すなわち、値コンストラクタっていうのは、値を作る関数と同じ物の事。

ここで、さっきの「人間型」を定義してみる。

<その1>
「人間」を表すのに「性別」っぽいもので、値の種類を分けてみた。
「男」、「女」、「男の娘」の3種類

data Human = Otoko | Onna | Okama

種類分けだけだと、False、TrueのBool型に似てる。


<その2>
しかし、実はOtoko、Onna、Okamaは関数なので、 引数を取ることが出来る。
なので、実は以下のような定義が出来る。
「男」を構成する要素として「名前」をString、「度胸」度をIntで表す。
「女」を構成する要素として「名前」をString、「愛嬌」度をIntで表す。
「男の娘」を構成する要素として「名前」をString、「最強」度をIntで表す。

data Human = Otoko String Int | Onna String Int | Okama String Int


これを踏まえて、もういちどBool型の定義に戻ってみる。
data Bool = False | True

他の言語で見慣れたFalseやTrueは値そのものを示すリテラルなシンボル的なものかもしれないけれど、Haskellの世界ではFalseもTrueも引数を取っていない値コンストラクタであり、それは引数が0個の関数である。
なので、値とは、Falseそのものではなく、そのコンストラクタの評価結果であると考える。

そうそう、最後に約束事として、型名も値コンストラクタも大文字で始まらなければならない。

値を作る

これまでは型を定義する話。
実際に定義された型の値は次のように作る(定義する)。

hoge :: Bool
hoge = True

なんだよ、結局、書き方は他の言語でみてるのとおんなじじゃん!!とか言わない。
Trueは値コンストラクタで関数。

上記の式は、「hogeという変数に、Trueを代入」しているように見えるが、Haskellではそうではないらしい。「純粋関数型プログラミング言語」っていうのは、まだよくわからないけれど、同じものに対する関数の結果はいつも同じであるはずなため、中身が変わることを意味する「変数」とか中身を入れ替える行為になる「代入」とかいう概念がそもそも無さそうな感じ。

では、Haskellな人はどう読んでいるのか?
「hogeをTrueと定義する」みたいな感じ。

何が違うの?じゃなくて、まずは、そう感じるんだ!と心の中で唱える。
じゃ、変数じゃない「hoge」ってなにかといえば、単なる「名前」。小文字ではじまってるのは、何かを表す「名前」なのだ。
「hogeってのはBool型の何かだよ」っていって、Bool型のTureが示す値で定義してる。

まぁ、「=」があると 何かを「代入」じゃなく「定義」してるって思えるようになる努力をするとHaskellが理解できるようになるといいなって思ってる。


それはさておき、次に、人間を作ってみる。

dareka :: Human
dareka = Otoko "taro" 10

なんとなく、人間っぽいのを作ってる感じのする式になった。

しかし、"taro"が名前で、10が度胸度を表しているとは一見してわからない。
Haskellの型の場合上で見たように、単に値コンストラクタとそれに続く引数の位置
によって、要素を識別することになる。

 一方、オブジェクト指向言語の場合、要素についてはプロパティとして、その名前がついていて値との関連性が分かりやすい。


つまり、

name = "taro"
dokyou = 10

とかいう分かりやすい書き方がHaskellにはないのかな、、、、ってどっかでみたような?

terminal = "urxvt"
modMask  = mod4Mask

忘れてたかもしれないけれど、これの話をしてたんだよね。

型の定義「レコード構文」

型の定義において、先に書いた構文とは別に、値コンストラクタが複数の引数を取る時、その引数が何を表すのかわかりやすくするための構文、「レコード構文」がある。

型の定義において、値コンストラクタの引数部分に中括弧を用いて、その中に、要素の名前を定義する。
値として定義する時には、値コンストラクタの引数部分に中括弧を用いて名前に対して「=」を使って要素となる値を定義する。

この、オカマの最強度9999を持つつばさちゃんは、xmonadの設定defaultConfigと同じようにみえる。

つまり、逆に言えばdefaultConfig {...}は、やっぱりこれでひとつの値なのだ。