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 {...}は、やっぱりこれでひとつの値なのだ。


2013年8月25日日曜日

xmonadとHaskell(その1)

インストールとか


・本体
archlinuxの場合、xmonadxmonad-contribパッケージをインストール。
haskell諸々は依存関係で勝手にインストールしてくれる。

・それ以外
xmonadそのものには、他のウインドマネージャで、画面の上や下にあるステータスバーとかパネルと呼ばれるものが無い。なので、別途、好みのものを用意する。

ツールバーそのものが、dzen2やxmobar。
そして、dzen2を選択すると、これはそれほど賢いものでは無くて、主な機能は、単に与えられたテキストを表示するだけのものなので、システムの情報を表示したい(CPU使用率、メモリ使用率、ネットワーク使用状況等)なら、自分で情報を得て、それをそのツールバーに渡すことになる。なので、この情報収集のためにconkyを使う。
また、プログラム呼び出し用のメニューがdmenu。

うちでは、xftフォントを使うために

dzen2-git(aur)
conky
dmenu-xft(aur)

辺りをインストール。

xmonadの導入解説にはどこもパネルの作成の紹介があるし、ここのカスタマイズで結構を頭を悩ませるのだけれど、これ自体はあんまりxmonadとは関係ない。そして、パネルについては他の選択肢として、xfce4-panel等、高機能なパネルだけ呼び出して使うとか、DEとの連携をしてパネルはそっちに任せることも出来るらしい。アプレット等が使える派手で高機能なのがお好みなら、dzenの設定をガリガリ書くよりも、あるものを使ったほうが良さげ(by 2ch タイル型WMスレ)

起動

.xinitrcとか起動スクリプトで

exec xmonad

する。
startxで直接、displayマネージャーでも、xdmやslimの場合は上記でOK。

kdmやgdmの場合のヒントは以下(セッション選択のファイルを作る)
2.3 How can I use xmonad with a display manager? (xdm, kdm, gdm)

うちの~/.xsession



DEを使っていればいらないかもしれない、昔ながらの最低限の設定。
xmonad自体でカーソルを制御しないので、xsetrootで調節。
壁紙も設定。fehが定番っぽいけど、デュアルスクリーンでもうまく設定できるNitrogenが便利。

xmonadに関係ないけれど、コンポジット系統(ウインドウを透明にしたり、影つけたりできる)は、バグの少ないCompton(xcompmgrからの派生)が良いらしい。
また、xrandrのデュアルスクリーンもxmonadで使える。


そんな感じで、起動すると何にもなくて寂しいxmonadが立ち上がる。(もしかしたら、真っ暗で立ち上がってるのさえわからないかもしれない)

alt + Shift + Enter で、xtermが立ち上がる。

alt + Shift + q で、xmonad終了。

設定ファイル ~/.xmonad/xmonad.hs

というわけで、いよいよHaskellに対面!
xmonadの設定ファイルとして~/.xmonad/xmonad.hsを以下の内容で作ってみる。

これがxmonadの設定ファイルで、はじめてのHaskell
urxvtが3つ立ち上がってるところ。まだ、パネル等の飾りはありません。


<<追記>>
xmonadとHaskellについてのメモが長くなったので目次を追記

xmonadとHaskellのメモの目次



2013年8月24日土曜日

xmonadとHaskell(その0)

ウインドマネージャを少し前にawesomeからxmonadに変えてみた。
http://xmonad.org/


xmonadはHaskellという言語で動いているんだけれど、このHaskellって言語は、以前から「わけわからん」っていう評判をなんとなく聞いていた。

Haskellで書かれている設定のsampleになる基本的なファイルを見ても構文がパッと見では、イマイチよくわからない。それでも、いくつもの設定の例を見つつ、xmonadのページのリファレンスを見ているとなんとなく設定だけは出来るようになった。

でも、「lispよくわからんけどemacsの設定は適当に書いてる」的な感じよりももっと心もとないため、せっかく色々と楽しくできそうなのに、設定のアレンジを自分ではできないのがもどかしい。

そこで、Haskellの本を買ってきて読んでみた。やっぱ難しくて、眠くなることしばし。でも、はじめてオブジェクト指向の概念に触れた時にように、今まで考えたことのない見慣れないもの、使ったことのない思考回路を刺激されるのは楽しい。

本の中では、一般的な問題の解決をプログラミングの例とかをコンソール上でやるんだけれども、出来るならXmonadを使って遊ぶほうがきっと面白いし、親しめるに違いない。

考え方が整理できたら良いなというメモ。