Haskell で GUI を扱うための ライブラリ Phooey の改良
Total Page:16
File Type:pdf, Size:1020Kb
30 特集●ソフトウェア論文 Haskell で GUI を扱うための ライブラリ Phooey の改良 眞々田 泰裕 本論文の目的は関数型言語の特徴を活かしたプログラミングによる GUI の作成法を開発することである.関数型言 語で GUI を扱うためのツールキットはその内部では大抵 C++などのオブジェクト指向な命令型言語に基づいてで きている.そのため関数型言語を用いた GUI プログラムは命令的な面影を残し,関数型の力を発揮しきれていない. Haskell の場合は GUI ライブラリのひとつである wxHaskell をより関数型らしく扱うために Phooey と呼ばれる Haskell のライブラリが開発されたが,こちらはまだ実用的な段階まで開発が進んでいない.本論文では Phooey を 改良し,新しい GUI ライブラリ Neooey を実装する.具体的には GUI をレイアウトと処理を独立させて別々に定 義可能にすることにより GUI の表現力を増加させる.また Neooey を用いた GUI の実装の手順を紹介する.さら に Neooey の有効性について Phooey では表現できない GUI の例を用いて言及する. The goal of this paper is to develop the GUI library for the functional programming language Haskell. There are several GUI toolkits for functional programming languages but they are based on the object-oriented programming languages such as C++ and Java. Therefore functional GUI programs often take form of im- perative codes, and cannot use their functional competence fully. In Haskell, the GUI library called Phooey was developed based on the GUI library wxHaskell to make use of the features of the functional language, but the development of Phooey has been stopped before it reaches to the stage of practical use. In this paper, we improve Phooey and implement the new GUI library called Neooey. Specifically, we divide GUI into layout and process and define them separately to increase the expressiveness of GUI. We also show how to make GUI codes by Neooey. Furthermore, we mention the efficacy of Neooey by use of GUI examples Phooey is not able to express. 手法.イベントとはマウスクリックなどのユーザ 1 はじめに による入力やシステムによってプログラム外から 今日,だれもがコンピュータを操作する時代とな 発生させたもののことを指す.イベントが発生す り,コンピュータとユーザとのギャップを視覚的な観 るまでイベントループで待機し,イベントの発生 点から埋める GUI の重要性はより一層増加している. をイベントハンドラが感知するとイベントの種 GUI の実装を行う場合,よく用いられるプログラミ 類に応じた処理を行う. ングの手法としては次の 2 つが挙げられる. • オブジェクト指向プログラミング • イベントドリブン (イベント駆動) 型プログラミ オブジェクト同士の関係や作用によって GUI を ング 表現するプログラミング手法である.オブジェク イベントの発生を引き金として GUI の操作が行 トとは性質が類似した値と処理をまとめたもの われるという考え方に基づいたプログラミング を指す.オブジェクト指向プログラミングではオ The Improvement of Phooey: Libraies to use GUI in ブジェクトの活用によって似たような処理や機能 Haskell. を一括りにするため,GUI のコードやその把捉 Yasuhiro Mamada, 千葉大学大学院理学研究科, Grad- をより容易にする. uate School of Science, Chiba University. コンピュータソフトウェア, Vol.33, No.4(2016), pp.30{49. 一般的に GUI プログラミングは C++や Java など [ソフトウェア論文] 2015 年 10 月 30 日受付. の命令型言語で行われる.関数型言語の場合は参照透 Vol. 33 No. 4 Nov. 2016 31 過性をもち,副作用を排除するという考え方で設計 Phooey では各 GUI がそれぞれレイアウトに関 されているために副作用に満ちた GUI プログラミン する情報とイベント処理に関する情報を保有し グには不向きとされていた.しかし副作用と純粋な ている.2 つの GUI を結合すると 1 つの大きな 作用とを出来る限り分離して設計することによって GUI が構築され,このときレイアウトとイベン 関数型言語においても GUI プログラミングを行うこ ト処理の結合も同時に行われる.この特徴によっ とが可能である.またこの手法はコードを単純化し, て短いコードで GUI を簡潔に表現することが可 より安全な GUI の作成やバグの発見を容易にすると 能になっている.しかし一方で,それぞれの GUI いう利点がある.そのため近年では C# 言語にラム が値を相互的に受け渡すように関連付けさせる ダ式が導入されるなど関数型を用いた GUI の考え方 ことはできない.Neooey ではレイアウトに関す に注目が集まっている. る処理とイベントに関する処理を (Neooey にお 関数型言語で GUI を作成する場合,各言語ごとに ける)UI とウィジェットアクションと呼ばれる構 用意されている GUI ライブラリを利用する.このラ 造に分離することでこの問題を解決させる. イブラリは C++ などの命令型言語のバインディン • ウィジェットハンドラの保持 グ,すなわちクラスや関数の翻訳によって形成されて Phooey では FRP の考え方に従って GUI が定義 いるものが多い.そのため関数型言語を用いた GUI されるために,ウィジェットの情報を定めたり変 プログラムであってもそのコーディングはバインディ 更を加えたりする際に使用するウィジェットハン ング元の命令型言語のスタイルに類似したものとな ドラを保持しない.このことによって実行の途中 りがちであり,関数型言語のスタイルになりにくい. でイベント処理を全く別種のものに変更したり, 関数型言語の 1 つである Haskell でよく使用される ウィジェットの情報を置き換えたりすることがで GUI ライブラリとしては wxHaskell[12] と Gtk2Hs きない.Neooey ではウィジェットハンドラを保 [2] の 2 つが挙げられる.wxHaskell は C++言語で記 持することによってこの問題を解決させる. 述されている GUI ツールキット,wxWidgets[18] の また,これらの改良によって Phooey では実装す バインディングであり,Gtk2hs は C 言語で記述され ることができない GUI の例を Neooey を用いて実装 ている GUI ツールキット,GTK+[19] のバインディ する. ングである.このうち wxHaskell についてはより関 論文の構成については次の通りである.第 2 章で 数型らしいスタイルで記述できるようにするために 関数型言語における GUI プログラミングの手法であ Phooey[4] と呼ばれる Haskell のライブラリが Elliot る FRP について説明する.第 3 章で本論文で改良を によって開発された. 加える GUI ライブラリである Phooey 及び,その背 Phooey の設計には関数型言語を用いた GUI プ 景にある GUI ツールキット及び GUI ライブラリに ログラミングの手法の 1 つである FRP(Functional ついて説明する.第 4 章で実際に Phooey の改良を Reactive Programming ,原形 Fran [7] ) が使用され 行い,GUI の作成法を例を挙げて説明する.そして ている.この手法により Haskell らしいスタイルでの 第 5 章で関連研究,第 6 章で結論を述べる. コーディングが可能となったが実用的な段階まで開発 されているわけではなく,汎用性が低い.そこで本論 2 FRP 文ではこの Phooey に更なる改良を加えて,より実 FRP (Functional Reactive Programming) は RP 用的に扱えるように実装を行う. (Reactive Programming) を関数型で表現した手法で 本論文では Phooey を改良したものである Haskell ある.まずは RP について述べる. のライブラリ,Neooey を作成する.Phooey と比較 RP とは GUI などの副作用を伴うコードで使われ した大きな改良点は次の通りである. ている表現の 1 つである.一般的な GUI のプログラ • レイアウトとイベント処理の分離 ムは行わせたい操作や命令,イベントの設定を実行さ 32 コンピュータソフトウェア せたい順番に書き並べる.これはプログラマー視点で あるシグナル関数 (signal function) を利用して FRP は直感的でわかりやすいかもしれないがイベントや を表現した AFRP(Arrows-based Functional Reac- 変数の数が増えてくると依存関係を捉えることが困 tive Programming) も存在する.シグナル関数はふ 難になる.それに対して RP は値を時間によって変化 るまいのようなものでシグナル (signal) と呼ばれる するものととらえ,この値との関係を利用してプログ リアクティブな値を変換する関数である.AFRP で ラミングを行う.この関係性のことをデータフローと は複数のシグナル関数を結合し,大きなシグナル関数 いう.ある値に変化があるとその値との関係を通して をつくることでプログラムの構築を行う[10][15][3]. 別の値も再計算される.一番浸透している例を挙げる FRP はリアクティブな値の扱い方によって push 方 と Microsoft Excel などの表計算ソフトにその考え方 式と pull 方式の 2 つに分類することができる.push が使われている.このとき時間に対して連続的に変化 方式は値が変化した時点でその変化を他の値にも伝 する値のことをリアクティブ (reactive) な値と呼び, え,再計算させる方式である.pull 方式は値が変化し リアクティブな値の関係性をふるまい (behavior) と てもその変化をほかの値に伝えることはせず,値の参 よぶ.また,時間に対して断続的に与えられる値をイ 照が行われる際に再計算させる方式である. ベント (event) と呼ぶ.一般に RP のコードは長さが この 2 つの方式のどちらが優れているかは値が変 短くなるという特徴があり,それゆえにバグが少なく 化する頻度によって変わる.変化の頻度が高い場合は 保守性が高くなる. 変化を逐一伝える push 形式よりも,その情報が必要 FRP は Elliott, Hudak による論文,Fran [7] が原 となるタイミングで再計算を行う pull 形式の方が優 形になっているといわれる.FRP ではリアクティブな れている.逆に値の変化の頻度が低い場合は変化に対 値やイベントについてはしばしばストリーム (stream) して即時計算できてタイムラグの小さい push 形式の によって表現される.ストリームは時間によって変化 方が優れている. する値のリストである.例えば,次のように時間と値 の連想リストとして表現される場合もある. 3 Haskell における GUI ライブラリ type Stream a = [(Time, Value a)] この章では Phooey 及びその元となっている GUI リアクティブな値とイベントは連続的なものと離散 ツールキット及び GUI ライブラリについて述べる. 的なもので違いはあるが,リアクティブな値は変化し た時刻とその値のストリーム,イベントは発生した時 3. 1 wxWidgets 刻とその値のストリームとして表現することができ, wxWidgets[17] は C++で記述されている GUI ツー 互いに類似している側面がある.Elliott による論文 ルキットである.クロスプラットフォームに扱えるとい [5] ではその点を踏まえ,イベントとリアクティブな う特徴を持ち,Windows,Mac OS X,Linux といっ 値を相互再帰的な定義で実装している. た多くの OS で動かすことを可能にしている.C++で type Event a = Future (Reactive a) 記述されていることから実行が高速であり,wxWid- type Reactive a = a 'Stepper' Event a gets を利用するにあたって他のソフトを用意する必 Stepper はイベントに初期値 (時間 0 における値) 要がない.また,他のプログラミング言語からでも使 を付与すること,Future は時間をイベントの次の発 用可能にするために各種バインディングが用意されて 生時刻まで進めることを表す. いる.例えば Python で扱うための wxPython,Perl このように時間によって変化する値をふるまい関数 で扱うための wxPerl, C# で扱うための wx.NET で動かすことによって FRP による実装が行われる. がある.それ以外にも PHP, Java, Lua, Lisp, 本論文において改良を行う対象である Phooey でも Erlang, Eiffel, BASIC, Ruby, JavaScript と この考え方に基づいて設計されている. いったかなり多くのバインディングが用意されてい また,本論文では取り上げないが,アローの一種で る.現在も開発は行われており,最新のバージョン Vol. 33 No. 4 Nov. 2016 33 は 2016 年 2 月 29 日にリリースされた wxWidgets 3.1.0 である.こちらは公式サイト[18] から無償で入 手することができる.Phooey では Haskell で扱える ようにするために wxHaskell というバインディング を使用している. 図 1 wxHaskell による GUI 3. 2 wxHaskell wxHaskell[12] は wxWidgets を Haskell で扱える ジェットに対する処理を記述する. ようにバインディングした GUI ツールキットである. フレームとはメインウィンドウ,すなわちウィンド 同様のライブラリとしては他に GUI ツールキット ウ全体の事を指す.基本的に他のすべてのウィジェッ GTK+ をバインディングしたものである,Gtk2Hs トはフレームの中に属する.frame 関数を用いるこ [2] というツールが存在する.wxHaskell と Gtk2Hs とによって作成することができてウィジェットハンド は Haskell で GUI を扱うツールとしては双璧となっ ラの型は Frame () である. ている. パネルはウィジェットコンテナの 1 つで,中にウィ wxHaskell のバージョンは大きく分けて 0.13 系列 ジェットを入れることのできるウィジェットである. と 0.90 系列がある.最新版は 2015 年の 12 月 27 日 panel 関数を用いることによって作成することがで にリリースされたものでバージョンは 0.92.2 になっ きてウィジェットハンドラの型は Panel () である. ている.こちらは公式ページ[11] から無償で入手する コントロールはコントロールウィジェットとも呼び, ことができる. ユーザやシステムによる入力を感知することのでき wxHaskell の概要については例を用いて述べる.図 るウィジェットである.例としてボタン,テキストエ 1 は wxHaskell による GUI の例である.この GUI は ントリ (入力欄,テキストコントロールとも),スラ ボタンが 1 つだけ表示され,そのボタンが押される イダー (つまみを動かして程度を指定できるコント とウィンドウを閉じるというシンプルなものである. ロール) などが挙げられる.コントロールはユーザに この GUI のソースコードは次のように表される. よる選択や操作を感知するとイベントと呼ばれるサ hello :: IO () インを発生させる.発生したイベントの種類に応じ hello = do f <- frame [text := "Hello!"] て行いたい処理を結びつけることでさまざまなアク p <- panel f [] ションを引き起こすことが可能となる.コントロール quit <- button p [text := "Quit", ウィジェットには多数の種類があるので,それぞれの on command := close f] set p [layout := margin 10 $ ウィジェットに対して作成関数やハンドラの型が存在 column 5 [label "Hello wxHaskell" する.例えばボタンウィジェットは button 関数を用 , widget quit]] set f [layout := widget p] いることによって作成することができてウィジェット ハンドラの型は Button () である.また,テキスト GUI にはウィジェットという最小単位が存在する. エントリウィジェットは textCtrl 関数を用いること これは GUI オブジェクトのようなものであり,上で によって作成することができてウィジェットハンドラ 名前の挙がったフレーム,パネル,ボタン,テキス の型は TextCtrl () である. トエントリなどのコントロールは全てウィジェットの ウィジェットを作成する関数は原則として引数に親 一種である.GUI の構成はこれらウィジェットをつ となるウィジェットとプロパティリストを取る.いく なぎ合わせることによって行われる.wxHaskell では つかのウィジェット作成関数について型を紹介する. ウィジェットを作成するとウィジェットハンドラが作 frame を除いて第一引数として現れている Window a られる.このハンドラに働きかけることによってウィ は任意のウィジェットハンドラの型が入ることを示し 34 コンピュータソフトウェア ている.frame の引数が少ないのはフレームに親と レイアウトの型を Layout 型で表現する. なるウィジェットは存在しないためである.第二引数 レイアウトを取り扱う関数は大別すると次の三種 として現れている [Prop (*** ())] はプロパティリ 類存在する. ストと呼ばれ,ウィジェットに関する各情報を設定す • レイアウトを生成する関数 ることが出来る. • レイアウトを変換する関数 frame :: [Prop (Frame ())] -> IO (Frame ()) • レイアウトを結合する関数 panel :: Window a -> [Prop (Panel ())] まず,レイアウトを生み出す原始的な関数とはレイ -> IO (Panel ()) button :: Window a -> [Prop (Button ())] アウトを引数にとることなくレイアウトを生成する関 -> IO (Button ()) 数のことでスタティックなテキストを表示する label textCtrl :: Window a -> [Prop (TextCtrl ())] -> IO (TextCtrl ()) や何もない空間を配置する space などがこれに該当 ウィジェットに関する細かい情報は属性と呼ばれる. する.また,レイアウトを変換する関数とは 1 つの 属性の設定を行うには 2 つの方法がある.ウィジェッ レイアウトを引数に取り,レイアウトを返す関数のこ トを生成する際に指定する方法とプロパティリスト とで空いているスペースを満たすようにレイアウト の再設定を行う set 関数を用いる方法である.逆に, の拡大を行う fill やレイアウトの縁を開けるための 定められた属性の値を抽出する働きを持つ get と呼 margin などがこれに該当する.最後に,レイアウト ばれる関数も存在する.属性は を結合する関数とは複数のレイアウトを引数に取り, 属性名 := 値 1 つのレイアウトを返す関数のことで水平方向にレイ の形式をとる. アウトの結合を行う row や垂直方向にレイアウトの set :: w -> [Prop w] -> IO () 結合を行う column などがこれに該当する. get :: w -> Attr w a -> IO a label :: String -> Layout 属性の具体的な例としてここでは text,on com- space :: Int -> Int -> Layout mand,layout を挙げて説明する. fill :: Layout -> Layout text :: Textual w =>