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 => Attr w String margin :: Int -> Layout -> Layout on command :: Commanding w => Attr w (IO ()) layout :: Form w => Attr w Layout row :: Int -> [Layout] -> Layout text は名前や文字列としての値を当てはめる属性 column :: Int -> [Layout] -> Layout である.フレームにおいてはタイトルバーに表示され 以上が wxHaskell の概要である.wxHaskell は る文字列を表し,ボタンにおいてはボタンに表示され Haskell で GUI を作るためには有効ではあるが る文字列を表す.また,テキストエントリにおいては C++(または C) で構成されている wxWidgets を エントリ自身に書かれる文字列を表す. そのまま翻訳したかのように作られており,GUI 関 on command はコントロールウィジェットがユーザ 数の定義は命令型言語で行うような逐次実行のコー による入力,変更を感知した際に処理を行いたいア ディングになっている. クションを記述する属性である.また,外的な刺激に これを改善する手法として FRP の考え方に基づい よって発生するイベントについてはいくつか種類があ て Haskell のスタイルで GUI の作成が行えるものと り,用途に応じて command の箇所を別の関数に置 して,Phooey という wxHaskell 上のライブラリが開 き換えることによって各イベントに対する反応を記述 発された.これについては次の節に述べる. することが可能である.例としては選択されている状 態を観測する select などがある. 3. 3 Phooey layout はウィジェットのレイアウトを記述する属 Phooey[4] は wxHaskell を用いて実装されたシン 性である.GUI においてレイアウトは視覚的なウィ プルな Haskell の関数型ライブラリである.FRP の ジェットの配置を表している.wxHaskell においては 提唱者の 1 人でもある Elliott によって開発された. Vol. 33 No. 4 Nov. 2016 35

sl0 :: Int -> UI (Source Int) sl0 = islider (0, 10)

apples, bananas :: UI (Source Int) apples = title "apples" $ sl0 3 bananas = title "bananas" $ sl0 7

total :: (Show a, Num a) => Source a -> UI () total = title "total" . showDisplay

ui1x :: UI () ui1x = title "Shopping List" $ do a <- apples b <- bananas total (liftA2 (+) a b) では を表現するために 型を使う. 図 2 Phooey によるショッピングリストの GUI1 Phooey GUI UI UI 型はその内部に FRP を表現するためのリアクティ 同氏による論文[5] では FRP によるプログラムの実 ブなアクションとレイアウトを情報として保有して 装が記述されており,そこで用いられているイベント おり,UI の結合が行われる際にレイアウトやリア やリアクティブ,それらを扱うための関数が Phooey クティブな値の結合が行われる.また,Source a 型 でも使用されている.Phooey は push ベースの FRP はリアクティブな値の型を表現したものである.UI の手法を活かし,短いコード量で GUI を表現するこ (Source a) 型を持つ GUI はユーザなどにより入力 とを可能にしている.また,wxHaskell で見られる が与えられるたびにそれに応じて a 型の値を生成す ような逐次実行的ではない,Haskell のスタイルによ る.構築された UI は runUI という関数を用いること るコードを簡潔に組むことができる.実際に Monad によって起動することができる. や Applicative といった Haskell でなじみの深い構文 runUI :: UI () -> IO () を用いて GUI を実装することができる.最終更新は Phooey の特徴としては Haskell のスタイルによる 2011 年 9 月 10 日 のバージョン 2.0.0.1 であり,開 コーディングを実現しているということが挙げられ 発は止まっている. る.例えば上記の例における ui1x では do 記法を用い まずは Phooey の構造や特徴を GUI の例を通して て定義されているが,UI 型が Monad や Applicative 説明する. といった型クラスのインスタンスになっていること 図 2 はフルーツのショッピングリストを表現した から関数 liftM2 や (>>=) を用いることが可能であ GUI である.リンゴとバナナに対してそれぞれスラ る.これらを用いることによって次で紹介しているよ イダーと呼ばれるつまみが存在し,下には合計を表す うに自然で短い Haskell スタイルによる定義が実現さ テキストエントリが置かれている.スライダーを動か れる.

すことによってフルーツの合計が再計算され,テキス (.+.) :: Num a => UIS a -> UIS a -> UIS a トエントリに表示される数が更新される. (.+.) = liftA2 (liftA2 (+))

このショッピングリストを表現する GUI を Peooey fruit :: UI (Source Int) を用いて表現すると次のようになる. fruit = apples .+. bananas

ui1y :: UI () ui1y = title "Shopping List" $ fruit >>= total また,UI 型の結合を行う際にレイアウトを指定す る関数を適用することができる.これを使用すること 36 コンピュータソフトウェア

の方法は作成時に固定されてしまい,後になってその 処理自体を変更したい場合に無理がでてしまう.

4 Neooey この節では,我々が作成した新しい GUI ライブラリ である Neooey について述べる.Neooey は Phooey で用いられている構造をベースにして改良を加えた ものとなっており,前章で説明した,Phooey におい

図 3 Phooey によるショッピングリストの GUI て発生している問題を解決することを目的として設 計を行う.特に Phooey では実装することのできな でウィジェットの配置を変えることが可能である.例 い GUI を定義できるようにすることを最大の目的に えば,次のコードが示す GUI を起動するとレイアウ 置く. トは図 3 のようになる. Phooey で発生した問題を解決させるために, ui2 = fromBottom $ Neooey では Phooey が持つ UI 型の役割を二分す title "Shopping List" $ fromRight fruit >>= total る.具体的にはウィジェットの配置などのレイアウト 以上が Phooey についての説明である.この GUI を担う視覚的な役割とイベントの設定や処理を担う ライブラリを用いることで GUI を Haskell のスタイ 実行的な役割を分離する.Neooey においては前者を ルによって簡潔に実装することが可能になった.しか (Neooey における)UI,後者をウィジェットアクショ しこのライブラリには問題もあり,それ故に Phooey ンと呼ぶ. では実装できない GUI が存在する.その問題とは次 また,ウィジェットアクションについては Phooey の点である. 由来の FRP による記述と wxHaskell 由来の逐次的な • Phooey の開発が停滞し,関数が十分に用意さ 記述の両方を認めているので,適切な場合に応じてそ れていない. れぞれの記述で GUI を作成することが可能になる. • 簡潔なコードを目指す代わりに,指定可能なウィ UI の役割を分離せずに定義を行う手法も考えられ ジェットの属性が制限されている. るが,ウィジェット結合時のレイアウトとイベント処 • UI 型の構造上,表現できる UI が限定される. 理の関係が乖離している場合,分離による定義の方 特に 3 つめの問題について述べると,表現できる が定義が容易になる.また,逐次的な記述も含めて考 UI は UI 同士の依存関係が複雑でなく,イベント発 えた場合,どちらの手法にも定義が共通するレイア 生時の役割が固定されるものに限られる.前者につい ウト部分を分離することで定義スタイルの切り替え てはリアクティブな値を生成する入力側の UI と捕集 を容易にする.そういうこともあって,Neooey では して処理する出力側の UI を結合する際に,どちらが UI の役割を分離する手法を選んだ. 入力側でどちらが出力側か決める必要があるという このような視覚的な役割と実行的な役割を分離す ことが原因になっている.もし 2 つの UI が互いに値 る考え方は他の例でも見ることができる.例えばいく をやり取りしているようなものを考えた場合,このよ つかの命令型言語ではプログラムに埋め込みたい画 うな結合では表現することができないのである. 像やテキスト,レイアウトなどのファイルをリソース 後者については UI 型がウィジェットハンドラを保 ファイルという形で管理することができる. 有していないことが原因になる.これによって一度作 成されたウィジェットに対して後になってプロパティ 4. 1 UI を設定し直すことができない.プロパティの設定が行 Neooey における UI は GUI のうち視覚的な役割 えないと,例えばボタンが押された時のイベント処理 を担う部分である.UI は結合的な性質を持っており, Vol. 33 No. 4 Nov. 2016 37

それぞれ独立に作成された UI を結合演算子によって ンが外から与えられるメインフレームとメインパネ 1 つに統合することが可能である. ルの情報を参照して動くことを目的として使用する. また,UI はウィジェットハンドラとレイアウトの 尚,ウィジェットハンドラについては IO アクショ 情報という 2 つの情報を保有する.このうち,ウィ ンの結果という形で情報が引き継がれる.例えばボタ ジェットハンドラについてはウィジェットアクション ンウィジェットのハンドラである Button () 型の値 との合成の際に使用され,レイアウトについては設計 を保有する UI は UI (Button ()) という型を持つ. された GUI を実行する際に反映される.UI の結合を Neooey において UI に関係する関数は次の三種類 行うとこれらの UI が保持している情報も合成される. に大別することができる. UI の構造としてはメインフレームとメインパネル • ウィジェットハンドラを保有する UI を作る関数 を参照して動く関数という形をとる.GUI に共通し • UI にレイアウト情報を付与するための関数 ている部分を後から参照する形にすることで,個々の • 複数の UI を結合するための関数 UI の記述や結合が短く簡潔になる.メインフレーム ウィジェットハンドラを保有する UI を作る関数とは とメインパネルは UI を実行する際に作成され,参照 ボタンウィジェットやテキストエントリウィジェット が行われる. などのコントロールウィジェットを作成してそのウィ Neooey における UI の型は同名の UI である.こ ジェットハンドラを結果として返す関数である.この の型の直感的な定義はメインフレームとメインパネ 種類の関数は一般に wxHaskell と同じ記法のプロパ ルを引数としてとり,結果とレイアウトの情報を返す ティやそれ以外の最低限必要となる基本情報を引数と ものとして捉えることができる.要するに次のように して受け取る.内部では wxHaskell のコントロール 考えることができる. を作る関数を用いており,関数名も参照元の関数を元 type UI a = MWin -> Win -> IO (a, Layout) に付けられている.ここではテキストエントリを持 ただし MWin 型と Win 型についてはそれぞれ wx- つ UI を作成する textCtrlUI とボタンを持つ UI を Haskell における Frame () 型及び Panel () 型のシ 作成する buttonUI を例として挙げる. ノニムである.また,レイアウト情報を示す Layout textCtrlUI :: [Prop (TextCtrl ())] -> UI (TextCtrl ()) 型は wxHaskell で使用されているものと同じである. buttonUI UI 型の実装は直感的には上記のような引数を取る :: [Prop (Button ())] -> UI (Button ()) アクションの形であるが,実際次のようにモナド変換 UI にレイアウト情報を付与するための関数として 子を用いたアクションとして行う. 一般的なものは,UI 型の値を引数にとってその UI が type UI = ReaderT MWin (ReaderT Win 持つレイアウトに変更を加えるものである.また,引 (WriterT Layout IO)) 数で渡された UI が保有するウィジェットハンドラに モナド変換子は核となるモナドに対して機能する. ついては,変更を加えることなくそのまま引き継ぐ. これを使用すると変換子が持つ性質の付与をモナド ここでは UI の端にスペースを空ける marginUI と空 に行い,複数の性質を持つ 1 つの大きなモナドを構 いたスペースを満たすように拡大させる fillUI を例 築することが可能になる.Neooey においては IO ア として挙げる.

クションを核のモナドとしてこれにモナド変換子を適 marginUI :: Int -> UI a -> UI a 用させて UI を示す型である UI 型を構築する. fillUI :: UI a -> UI a UI 型の設計において使用する変換子は Writer モ また,レイアウトを作る関数の中には UI 型の値を ナドを変換子化させた WriterT と Reader モナドを 引数に取らない種類の関数も存在する.こういった 変換子化させた ReaderT である.WriterT は核であ レイアウトはテキストラベルや何も置かれていない る IO アクションにレイアウト情報を保持させる目的 空間などの自立したレイアウトを作ることができる. で使用する.また ReaderT は核である IO アクショ このレイアウトの作成過程でウィジェットハンドラは 38 コンピュータソフトウェア

作られないので結果でウィジェットハンドラが返され は次の runGUI 関数を用いる. ることはない.ここではテキストラベルを作成する runGUI :: UI a -> Action labelUI と何も置かない空間を作成する spaceUI を Action 型は IO () 型のシノニムである.UI 型の値 例として挙げる. が GUI を表現しており,これを引数に取ることで実 labelUI :: String -> UI () 際に動かすことができる. spaceUI :: Int -> Int -> UI ()

そして最後は複数の UI を結合するための関数であ 4. 2 ウィジェットアクション る.UI の結合が行われた際にレイアウトやウィジェッ ウィジェットアクションは GUI のうちイベント処 トハンドラの結合が行われる. 理などの実行的な役割を担う部分である.UI と同様 レイアウトの結合については次に挙げるように垂 ウィジェットアクションについても結合的な性質を 直方向に結合するか,水平方向に結合するかによって 持っており,ウィジェットアクション同士を結合させ 二種類の演算子が存在する. て 1 つのウィジェットアクションを作ることが可能で

(<->) :: UI a -> UI b -> UI (a, b) ある.また,個々のウィジェットアクションは逐次的 (<|>) :: UI a -> UI b -> UI (a, b) なスタイルと FRP のスタイルの 2 通りの方法で行 (<->) が水平方向,(<|>) が垂直方向にレイアウト えるように設計する.ウィジェットアクションが保持 を結合する演算子である. する情報はリアクティブな値である.こちらは FRP 一方,ウィジェットハンドラの結合についてはひ のスタイルで記述する場合にのみ使用される.FRP とつのハンドラに統合されるのではなく,引数とし のスタイルでウィジェットアクションの結合を行うと て与えられたそれぞれの UI が保有するウィジェット リアクティブな値とウィジェットとの結び付けが行わ ハンドラを組にして保有する形式をとる.例えば型 れる. が UI (Button ()) の UI と型が UI (TextCtrl ()) ウィジェットアクションはメインフレームとメイン の UI を結合してできた UI の型は UI (Button (), パネル,ウィジェットハンドラという 3 つの情報を参 TextCtrl ()) となり,そのウィジェットは Button 照して動くアクションとして定義される.これらのう () 及び TextCtrl () のウィジェットハンドラを組の ちウィジェットハンドラについては UI 型の結果で保 形で保有することになる.ウィジェットハンドラに関 有しているウィジェットハンドラの組に対応しており, しては結合演算子 (<->) と (<|>) に違いはなく,い UI とウィジェットアクションを結びつける際はウィ ずれも引数の順番に沿って組が作られる. ジェットハンドラの型が一致している必要がある. ウィジェットハンドラを保有する目的はウィジェッ ウィジェットアクションを表現する型である WidAct トアクションの形でウィジェットやイベント処理の設 及び WidAct’ の定義は次のようになる. 定を後で行う際に使用することにある.逆を返せば, type WidAct’ w = ReaderT MWin (ReaderT Win (ReaderT w (WriterT (Source Action) IO))) 後で使用しないウィジェットハンドラは UI に保有さ type WidAct w = WidAct’ w () せる必要がない.そういう場合のために結果として保 ウィジェットアクションに使われる型の構造は UI と 有するウィジェットハンドラを限定する関数を用意す 同様にモナド変換子を利用した形を取る. る.takeLeft 関数及び takeRight 関数である. ウィジェットアクションの 2 種類のスタイルのうち, takeLeftUI :: UI (a, b) -> UI a まずは逐次的な実行に関するものについて述べる.逐 takeRightUI :: UI (a, b) -> UI b 次的なウィジェットアクションにとって基本となる関 takeLeft 関数は組になっているウィジェットハン 数は属性の抽出や設定を行うものである getF 関数及 ドラの左側のみを抽出し,同様に takeLeft 関数は右 び setF 関数である. 側のみを抽出する. 最後に,Neooey で作成された GUI を起動する際 Vol. 33 No. 4 Nov. 2016 39

getF :: Attr attr val -> WidAct’ attr val widgetSPA :: Commanding w’ => setF :: [Prop a] -> WidAct a (w->w’) -> WidAct w -> WidAct w widgetSPAC :: Commanding w’ => これらは wxHaskell における get 及び set 関数を (w1->w’) -> WidAct w2 -> WidAct (w1, w2) 元に作られている.逐次的なスタイルの場合ほとんど これらの関数は第一引数として取る take 関数によっ のウィジェットアクションを作る関数はこれらやその て抜き出されたウィジェットハンドラが行うイベント 派生となる関数を利用して作成する.そのうちの例 処理を第二引数で与えられたウィジェットアクション として setWidAct 及び setWidAct2 関数について述 に設定する関数となっている.特に widgetSPAC 関数 べる. はイベントを発生させて値を得る入力側のウィジェッ setWidAct :: Commanding a => トとその値を結果に反映させる出力側のウィジェット WidAct a -> WidAct a が分離されている場合に用いることによって入力側と setWidAct2 :: Commanding a => WidAct b -> b -> WidAct a 出力側を連結させた 1 つのウィジェットアクションを これらの関数は引数で受け取ったウィジェットアク 構築する.ただし,take 関数とは組の形でまとめら ションをコントロールに結びつける.より具体的には れたウィジェットハンドラから特定のウィジェットハ イベントを検知した際に実行する処理をウィジェット ンドラを抜き出すための関数の総称である. アクションの形で引数に取り,その処理をウィジェッ 次にウィジェットアクションの FRP スタイルによ トに結びつけるものである.ウィジェットアクショ るコーディングについて説明する.FRP スタイルに ンが参照するウィジェットとイベント処理を定義し おけるウィジェットアクションの種類としてはリア たいウィジェットとが一致しているか否かによって クティブな値を生成して放出する入力側のウィジェッ setWidAct 関数と setWidAct2 関数を使い分ける. トアクションとリアクティブな値を受け取って計算 ウィジェットアクションの結合についてはウィジェッ を行う出力側のウィジェットアクションが存在する. トアクション自身がモナドであることから do 記法や IWidActS’ 型の値はリアクティブな値を生成するウィ (>>=) 演算子などを利用して行う.このとき,原則 ジェットアクションになっており,初期値を引数から として互いのウィジェットアクションが参照するウィ 取り,ウィジェットアクションの結果からリアクティ ジェットハンドラが一致している必要がある.もし ブな値を返す.IWidActE’ 型の値も似たような性質 ウィジェットアクションがもつウィジェットハンドラ を持つ型であるが,こちらはウィジェットアクション 情報が互いに異なる場合はこれをそろえる必要があ の結果からイベント値を返す.OWidAct’ 型の値はリ る.ウィジェットハンドラをそろえるための関数とし アクティブな値の計算を行うウィジェットアクション ては right 及び left を用いる. になっており,引数から受け取ったリアクティブな値 right :: WidAct’ b c -> WidAct’ (a, b) c を取りまとめる. left :: WidAct’ a c -> WidAct’ (a, b) c type IWidActE’ w a = WidAct’ w (Event a) type IWidActS’ w a = a -> WidAct’ w (Source a) これらの関数は引数から受け取ったウィジェットア type OWidAct’ w a = Source a -> WidAct w クションがもつウィジェットハンドラ情報を組の形に これらのウィジェットアクションが取り扱うリアク する.right 関数であれば,作成される組の右側に引 ティブな値はモナド結合によってウィジェットアク 数のウィジェットアクションが参照するハンドラが設 ションの結合が行われる際に関連付けられる. 置され,left 関数であれば作成される組の左側に設 最後に,作成したウィジェットアクションを UI に 置される. 結び付ける場合 putWAinUI 関数を用いる. また,効率よくウィジェットアクションを作成する putWAinUI :: WidAct’ a b -> UI a -> UI a ための関数として widgetSPA 及び widgetSPAC が挙 この関数は視覚面の役割を担う UI と実行面の役割 げられる. を担うウィジェットアクションとの融合を行う.この 40 コンピュータソフトウェア

関数によって作成された UI はイベント処理を兼ね備 えているものとなっており,これで Neooey における GUI が出来上がる.

4. 3 例:カウンター 本節では実際に Neooey を使用して GUI を作成す

る例を見せる.そしてその過程を通して効率的な作成 図 4 Neooey によるカウンターを行う GUI の例 手順について説明する. また,ウィジェットアクショ ンについては 2 通りの方法で実装を行い,その比較 模な GUI を考えると必要なウィジェットハンドラが を行う. 増えて型が極めてわかりづらいものになってしまう. 作成する GUI は図 4 が示すように 2 つのボタンが ウィジェットハンドラ型の定義については先に述べた ついたカウンターである. 「+1」と書かれたボタン が,GUI の作成を行う際は UI の生成と同時に型の命 を押すことによってテキストエントリに表示されてい 名を行うとよい. る数値が 1 増加し, 「−1」と書かれたボタンを押す UI の作成については UI の節で述べたウィジェット ことによってテキストエントリに表示されている数値 ハンドラを保有する UI を作る関数,UI にレイアウ が 1 減少する. ト情報を付与するための関数,複数の UI を結合する この GUI を Neooey を用いて作成する場合,次の ための関数の組み合わせによって行われる. 手順に分けて行うのがよい. まずはウィジェットハンドラを保有する UI を作る (1) UI とウィジェットハンドラの型の作成 関数を使って必要な UI を生み出す.今回の例におい (2) take 関数の作成 て必要となる UI は値の増減を行うためのボタン 2 つ, (3) ウィジェットアクションの作成 各ボタンの隣にあるラベル 2 つ,出力用のテキスト (4) ウィジェットアクションを UI に統合 エントリ 1 つの計 5 つである. まず始めに行う工程は UI とウィジェットハンドラ outputUI :: UI Output outputUI = entryUI [text := "0"] の型の作成である.ウィジェットハンドラの型の作成 とは作成する GUI において使用するウィジェットハ label1, label2 :: UI () label1 = labelUI "button1" ンドラやその組について別名をつけるということで label2 = labelUI "button2" ある.今回のカウンターの例では入力部分にあたる 個々のボタンハンドラを Input,2 つのボタンハンド button1, button2 :: UI Input button1 = buttonUI [text := "+1"] ラを組にしたものを Input2,出力部分にあたるテキ button2 = buttonUI [text := "-1"] ストエントリを Output,入出力を含めた使用するハ UI の発生が終わったら次は UI にレイアウト情報を ンドラ全体を Count と名付ける. 付与するための関数及び複数の UI を結合するための type Input = Button () 関数を用いて UI を統合していく.まず,UI の結合 type Input2 = (Input, Input) に関しては垂直方向,または水平方向に結合する演 type Output = TextCtrl () type Count = (Input2, Output) 算子を用いて UI を連結してゆく.このとき 2 つのラ このようにウィジェットハンドラに細かく名前をつ ベル用の UI label1, label2 についてはウィジェッ ける理由は GUI を作成する際に構造を捉えやすくす トハンドラを持たないので結合の際に takeRightUI るためである.ウィジェットハンドラ型に名前を付け 関数を用いて保持するウィジェットハンドラを必要な ない場合でも定義は可能であるが,今回の例におい ものだけに限定する.これをすることによって UI が ては,メインの GUI 関数の型が UI ((Button (), 保持する値が小さくなり,型が UI ((),Input) から Button ()), TextCtrl ()) となる.もう少し大規 UI Input になる.また,UI を変換するための関数 Vol. 33 No. 4 Nov. 2016 41

としてここでは margin10 関数が使用されている.こ ションの定義を行う.コーディングスタイルが 2 種類 の関数はレイアウトを整えるためのものであり,具体 あるが,まずは逐次的な定義について実装を行う. 的には作成される UI の縁を空ける目的で使用されて 今回の例では入力側の UI と出力側の UI が分離さ いる. れているので widgetSPAC 関数を用いて定義を行う preUI :: UI Count ことが可能である.actB1 では takeB1 が示すカウン preUI = margin10 $ トアップ用ボタンハンドラがイベントを感知した際 buttonset1 <|> buttonset2 <|> outputUI に出力先が示している数値に 1 加算することを表し buttonset1, buttonset2 :: UI Input ている.また,actB2 は takeB2 が示すカウントダウ buttonset1 = takeRightUI $ label1 <-> button1 buttonset2 = takeRightUI $ label2 <-> button2 ン用ボタンハンドラがイベントを感知した際に出力 ここで作成された preUI 関数は実行関数を用いて動 先が示している数値に 1 減算することを表している. かすことが可能である.視覚的に調整するために動か 双方で使用されている updateValueinWA 関数は引数 してもよいが,この時点ではまだボタンが押された で与えられた演算子を出力先が示している数値に適 際の処理が組み込まれていないのでカウンターとし 用させる働きを持つ.最後にウィジェットアクション ては機能しない.ボタンの処理を記述するためには の結合についてはモナド結合によって行われる.

ウィジェットアクションを用意して統合する必要があ --widget actions (imperative) る.そこでウィジェットアクションの定義を行うわけ actB, actB1, actB2 :: WidAct Count だが,その前の準備として 関数を用意する. take actB = actB1 >> actB2 take 関数とは組で表現されたウィジェットハンド ラの構造から特定のウィジェットハンドラを抜き出す actB1 = widgetSPAC takeB1 (updateValueinWA (+1)) 関数のことを指す.これを定義することによって目 actB2 = widgetSPAC takeB2 的のウィジェットを抜き出しやすくし,コードの可読 (updateValueinWA (subtract 1)) 性を高める効果がある.原則としてはウィジェットハ 以上により逐次的なスタイルでのウィジェットアク ンドラ全体を示す Counter 型の値を引数に取り,抽 ションの作成が完了した.次は FRP のスタイルで 出したいウィジェットハンドラを返す関数として定義 ウィジェットアクションを実装する.まずは 2 つのボ する.しかし入力側の UI と出力側の UI が分離され タンをまとめたものである upDown 関数を定義する. ていて widgetSPAC 関数を用いて結びつける場合は, この関数はカウントアップボタンが押されると 1 増加 抽出したいウィジェットハンドラが含まれている側の させる関数をイベント値として返し,カウントダウン ウィジェットハンドラ側を引数に取る関数でよい.今 ボタンが押されると 1 減少させる関数をイベント値 回の例の場合は入力側となる 2 つのボタンの組を表す として返す.そして actB’ 関数ではそのイベント値 Input2 型からカウントアップを行うボタンウィジェッ を受け取って accumR 関数によってそれを集約する.

ト及びカウントダウンを行うボタンウィジェットを --widget actions (FRP) 表す Input 型の値を抽出する関数を定義する.具体 actB’ :: WidAct Count 的には入力側のウィジェット から , Input2 fst snd actB’ = do e <- left upDown を用いて目的のウィジェットを選ぶ.作成した関数の right (showWA (0 ‘accumR‘ e)) うち,takeB1 がカウントアップを行うボタンのウィ upDown :: IWidActE’ Input2 (Integer -> Integer) ジェットハンドラであり,takeB2 がカウントダウン upDown = left (inputWA (+ 1) ) ‘mappend‘ を行うボタンのウィジェットハンドラである. right (inputWA (subtract 1) ) takeB1, takeB2 :: Input2 -> Input 尚,カウントアップに関するイベント値を出すウィ takeB1 = fst takeB2 = snd ジェットアクションとカウントダウンに関するイベント take 関数の定義を行ったら次はウィジェットアク 値を出すウィジェットアクションはいずれも inputWA 42 コンピュータソフトウェア

関数によって作られる.この関数は入力側のウィジェッ せる. トアクションを作成するために使用し,イベント発生 時に放出したいイベント値を引数によって指定するこ 4. 4 Phooey で表現できない GUI を とができる.各ウィジェットアクションは Monoid 型 Neooey で実装する クラスに属しており,ウィジェットアクションをモノ 3. 3 節で述べたように Phooey は FRP を利用して イド結合するとイベント値も結合される.ここでは増 作られているが,その表現力については限定的であ 加を担うイベント値と減少を担うイベント値が結合 り,Phooey によって作ることができない GUI も存 されて,加減を担うイベント値が作られている. 在する. 一方,出力側のウィジェットアクションを作成する 本節では Phooey で表現できない GUI のうち,UI 場合は showWA 関数を用いる.この関数は表示させ が相互的に情報を交換する例とコントロールの処理 たいリアクティブな値を表示させるウィジェットアク を途中で変更させる例について Neooey を用いた実 ションを作る.ただしリアクティブな値が可視化でき 装例を示す. ない値を持っているときは使用できない. まずは UI が相互的に情報を交換する例について考 inputWA :: (Commanding ctl, Widget ctl) える. => a -> WidAct’ ctl (Event a) showWA :: 図 5 は 2 つのボタンウィジェットが互いに情報を交 (Textual ctl, Commanding ctl, Widget ctl, 換させる例になっている.上のボタンを押すと双方の Show a) ボタンに書かれた文字列が入れ替わり,下のボタンを => OWidAct’ ctl a 以上により FRP のスタイルによるウィジェットア 押すと双方のボタンに書かれた文字列を連結したも クションの作成が完了した. のが下のボタンに反映される.どちらのボタンが押さ 最後にウィジェットハンドラやレイアウト情報を保 れた場合でも双方のボタンの情報について参照及び 持している UI に対してウィジェットアクションを統 更新を行うようになっている.すなわちどちらのウィ 合すれば完成である.これによって目的のカウンター ジェットも入力及び出力を行うものとなっており,こ プログラムを動かすことができる.尚,putWAinUI の GUI は Phooey によって表現することができない 関数の第一引数で与えられている actB 関数を actB’ ものである. 関数に差し替えるだけで FRP に基づいたウィジェッ これからこの GUI を Neooey で表現できることを トアクションの方に切り替えることができる. 実際に実装することによって示す. mainUI :: UI Count Neooey で行う GUI の実装工程 (1)~(4) のうち, mainUI = putWAinUI actB preUI (1) UI とウィジェットハンドラの型の作成 このカウンタープログラムは Phooey を用いて表現 (4) ウィジェットアクションを UI に統合 することも可能である.FRP によるウィジェットア についてはカウンターの例と同様にして行うことがで クションのコーディングが Phooey のスタイルに倣っ きるので省略する.ただし take 関数の実装を説明す ているために FRP スタイルのコーディングが可能な るために,ここでは命名した型のみ以下に紹介する.

GUI であれば Phooey によっても記述可能であると type InOut = Button () いうことを表している.しかし Phooey による FRP type InterChange = (InOut, InOut) スタイルの GUI では表すことができないものが存在 次は take 関数の作成である.今回の例では入力側 する.そういったコードに対して,Neooey では逐次 の UI と出力側の UI が互いに入れ替わり,分離する 実行による定義を用いることで表現することが出来 ことができないので widgetSPAC 関数を使用するこ る.次節では実際に Phooey で表すことのできない とができない. GUI についての実装について述べる.尚,カウンター そのため,原則通りウィジェットハンドラ全体を プログラムの全ソースコードについては付録 A に載 示す Counter 型の値を引数に取り,抽出したいウィ Vol. 33 No. 4 Nov. 2016 43

とによって UI が完成される.これによって Phooey で表現することができない GUI の例の 1 つを Neooey で書けることが示された. 尚,このプログラムの全ソースコードについては付 録 B に載せる. 図 5 Neooey によるウィジェット同士で 次はコントロールの処理を途中で変更させる例に 情報交換をする GUI の例 ついて考える. ジェットハンドラを返す関数として定義する. 図 6 で表される GUI はボタンが行う処理が変更さ takeB1, takeB2 :: InterChange -> InOut れる例となっている.まずボタンを押すと図 7 のよう takeB1 = fst takeB2 = snd に画面に表示されているテキストが「Hello World」 ここではウィジェットハンドラ全体としての型であ から「Goodbye」へと変わる.そしてそれと同時に る InterChange から上下のボタンハンドラの型であ ボタンに書かれている文字列も置き換わる.ここで る InOut を抜き出す関数を抜き出したいボタンごと もう一度ボタンを押すと今度はウィンドウが閉じら に用意する. れる.このとき,ボタンが押された際に行われる処理 次はウィジェットアクションの定義であるが,今度 は一度目は文字列の表示とボタン自体の挙動の変更, は次のように widgetSPA 関数を用いて定義する.こ 二度目はウィンドウを閉じる,といったようにその働 の関数は,第一引数の take 関数によって抽出される きが大きく変わるものになっている.Phooey では一 コントロールに第二引数で指定されたウィジェットア 度 UI を作成してしまったらコントロールの処理は固 クションを定めるものとして解釈できる.ここで使用 定となり,上記のような処理の変更については行うこ されている updateTextinWA 関数は引数で与えられ とが出来ない. た演算子を出力先が示している文字列に適用させる この例についても Neooey で表現できることを実際 働きを持つ.updateValueinWA 関数と似ているがこ に実装することによって示す. ちらは文字列操作を行う関数である. Neooey で行う GUI の実装工程 (1)~(4) のうち, actB, actB1, actB2 :: WidAct InterChange (1) UI とウィジェットハンドラの型の作成 actB = actB1 >> actB2 (4) ウィジェットアクションを UI に統合 actB1 = widgetSPA takeB1 change についてはこれまでの例と同様にして行うことがで actB2 = widgetSPA takeB2 appendT きるので省略する.ただし take 関数の実装を説明す るために,ここでは命名した型のみ以下に紹介する. change :: WidAct InterChange change = do a <- left getText type Input = Button () b <- right getText type Output = StaticText () right (setText a) type Bye = (Output, Input) left (setText b) 型を見ると一見今回の例は入力側の UI と出力側の UI が分離されているように見えるが,入力側の UI appendT :: WidAct InterChange appendT = do a <- left getText であるボタンは自身の働きを変化させる操作を伴う right (updateTextinWA (a++)) ために出力側の UI としての役割も兼ねる.つまりこ それぞれのウィジェットアクションについては do のボタンは入力側と出力側の両方の UI の性質を持つ 構文を用いて逐次的に表現されている.特定のウィ ので分離されていない.そのため,take 関数の定義 ジェットハンドラに処理を行わせたい場合は left 及 は次のように入出力全体のウィジェットハンドラから び right 関数を用いて制限を加える. ボタンハンドラを抜き出す関数として考える. 最後はカウンターの例と同じように putWAinUI 関 数を使って UI にウィジェットアクションを埋め込むこ 44 コンピュータソフトウェア

とウィジェットハンドラの保持によって解消された. ソースコードの簡潔さについては Neooey よりも Phooey の方が優れている.しかしそれは表現できる GUI の範囲を犠牲にしたものであり,Neooey では Phooey よりも表現できる GUI の範囲が広い.実際, Neooey ではウィジェットアクションにおいて Phooey 由来の FRP に基づく実装方法も備えているので, 図 6 Neooey によるボタ 図 7 Neooey によるボタ ンの働きが切り替わる例 1 ンの働きが切り替わる例 2 Phooey で表現できる GUI で Neooey で表現できな い GUI は存在しない.逆に Phooey で表現できない GUI を Neooey で表現できることについては前節で takeB :: Bye -> Input 説明した通りであり,このことはプログラマにコー takeB = snd ディングの選択肢を広げている. また,ウィジェットアクションの作成には次のよう に widgetSPA 関数を使用して行う. 5 関連研究 actWA :: WidAct Bye 他の関数型言語においても GUI ライブラリは存在 actWA = widgetSPA takeB byeWA する.しかしそのコーディングスタイルは関数型の性 byeWA :: WidAct Bye 質を活かしきれていないものとなりがちである.その byeWA = do left $ setText "Goodbye" 原因としては,ほとんどの関数型言語における GUI right $ setWidAct closef right $ setText "Close" ライブラリがオブジェクト指向に基づいて設計され setText 関数はウィジェットに引数で受け取った文 た GUI ツールキットをバインディングして,元々の 字列をボタンやラベルに当てはめるために使用する. コーディングスタイルをそのまま翻訳したような記述 また,closef 関数はメインウィンドウを閉じるため になっていることにある. に使用する. 例えば Lisp における GUI ツールキットは Ltk や 最後にこれまでの例と同様に putWAinUI 関数を使っ CLIM[14] が挙げられる.Ltk は Tcl/ で記述され て UI にウィジェットアクションを埋め込んで UI が た GUI ライブラリを Lisp で扱えるようにバインド 完成される.以上により Phooey で表現することがで された GUI ツールキットであり,スクリプト言語の きない例のうち,コントロールの働きを後になって変 特徴を活かして GUI を容易に記述することができる. 更する GUI を Neooey で表現できることが示された. また,CLIM はオブジェクト指向による GUI である このプログラムの全ソースコードについては付録 が,その概念はよりオブジェクトを分離して考えるア C に載せる. スペクト指向に近いものとなっている. Scala[13] で GUI を作成する場合,Scala. と 4. 5 Phooey と Neooey の比較 いう GUI ライブラリを用いる.このライブラリは この節では本論文で作成した Neooey とその元と Java における Swing を元に作られている.Scala が なった Phooey の比較を行う. Java の環境で動かすようにできているため,GUI ラ Phooey では簡潔なコードを目指す代わりに,指定 イブラリも Java のライブラリのバインディングと 可能なウィジェットの属性が制限されているという問 なっているのである. 題があった.より具体的に言えばウィジェットの作成 OCaml において GUI ライブラリを作成する場 時においては必要最低限の情報しか扱わず,その設定 合,一般的には LablTk や LablGTK[9] を使用する. を後から変えるということもできなかった.この点に LablTk は GUI ライブラリ Tk を,LablGTK は GUI ついて Neooey では wxHaskell のプロパティの記法 ライブラリ GTK+をそれぞれ OCaml で扱えるよう Vol. 33 No. 4 Nov. 2016 45

にバインディングしたものであり,関数型のスタイル pull 方式の双方の手法で FRP によるコードの作成 による記述についてはまだ追究されていない. が行えるものを設計しているが,Phooey においては そこで,関数型言語による GUI の設計として FRP push 方式の FRP の構造はまだ実装されていない. が注目されている.その FRP を利用した GUI ライ ブラリについては特に Haskell において活発に開発 6 結論と展望 されている.ここではその具体例として Functional Phooey を元に Neooey を構築したことで UI につ Forms,TV,Fruit,Phooey についてふれる. いては関数型らしい表現でコードを書けるようになっ Functional Forms[8] は wxHaskell の上に作られた た.また,UI とウィジェットアクションの分離を行っ GUI ライブラリである.表現できる GUI はダイアロ たことによって Phooey のコードでは表せなかった グの形式に限られるが,このライブラリは Neooey や GUI の例も Neooey を用いて表現することができる Phooey のように GUI を結合的に表現する手法を実 ようになった. 現している.ボタンやテキストエントリなどのウィ しかし実装にあたって問題点もいくつか残った.そ ジェットを atomic forms と呼び,これらは Compos- れを今後の展望とする. ite forms によって GUI 同士を結合させることができ 問題点の 1 つは場合によってはウィジェットを処理 る.また,references と呼ばれる考え方を利用するこ する関数を効率的に生成できない点である.現状の とによって,コントロールが発生させるイベントを扱 Neooey における UI は複数のウィジェット情報を組 いやすいように合成したり変換したりすることを可 の状態で結合して保有するようにできている.これ 能にしている. は各ウィジェットハンドラの型が異なる場合が十分に また,視覚的な GUI の作成を目指したものとして あるために採用した手法である.しかし wxHaskell Elliot は TV(tangible value) を利用したプログラミ ではレイアウトをリストとしてまとめて処理する関 ングの手法[6] を提唱している.この論文では値や関 数も多くそういった関数を Neooey で表現するには 数を視覚化することによって直感的な関数や GUI の 限界がある.この問題を解決させるための案として 合成を可能にしたものである. heterogenious list を用いたり,木構造のウィジェット 次に Fruit[3] は,Yampa を基本モデルとする GUI を考えるというものがあるがこちらについても深刻 ツールキットである.Yampa は Fran[7] に基づいて な問題があり,まだ思案の段階である. 開発された Haskell のドメイン固有言語であり,Yale また,相互的に情報をやりとりする例について,本 Haskell Group[20] によって開発された.GUI の設計 論文では Phooey で実装できないという結論に至った 方針としてアローを利用した pull 方式の FRP である が設計次第では Phooey で実装できる可能性がある. AFRP を用いる.Fruit は時間によって連続的に変化 その案としては monadic fixpoint やアローにおける するリアクティブな値 signal とアローの性質を持つ loop コンビネータを利用する方法が考えられる.こ ふるまい関数 signal function によって GUI を表現す のような GUI の設計についても考えていきたい. る.類似のライブラリに,Fudgets[1] 及び FranTK また,Phooey は push ベースの FRP に基づく GUI [16] がある. ライブラリであり,Neooey は Phooey を基に改良を 本研究で一番関係の深い GUI ライブラリは Phooey 加えたものであるからこちらも push ベースの実装と [5] である.Phooey では FRP を表現するために fu- なっている.しかし Phooey の元となった論文[5] で ture value や improving value といった特殊な値を は push-pull な FRP に基づく実装について考察して 利用してリアクティブな値を表現している.さらに いる.Neooey においても pull ベースの FRP に基づ Haskell のスタイルで GUI を記述するためにリアク く GUI ライブラリを作成することが可能かどうか考 ティブな値や UI を Haskell で親しみ深い型クラスに えたい. 所属させている.元になった論文では push 方式と そして,より本格的で複雑な GUI でも難なく関数 46 コンピュータソフトウェア

的な実装ができるように追究を行っていく. [15] Nilsson, H., Courtney, A. and Peterson, J. : Functional reactive programming, continued, in 尚,本論文で紹介した Neooey のソースコード及び † Proceedings of the 2002 ACM SIGPLAN workshop GUI の実装例についてはウェブサイト上で公開 1 し on Haskell, ACM, 2002. ている. [16] Sage, M. : FranTk a declarative GUI language for Haskell, in International Conference on Func- tional Programming, ACM, Vol. 35, No. 9, 2000, 謝辞 本論文の内容について議論していただいた桜 106118. [17] Smart, J. and Csomor, S. : Cross-platform GUI 井貴文氏 (千葉大) に感謝する. programming with wxWidgets, Prentice Hall Profes- sional, 2005. [18] Smart, J., et al. : The wxWidgets library, 参 考 文 献 https://www.wxwidgets.org/ [ 1 ] Carlsson, M. and Hallgren, T. : Fudgets - Purely [19] The GTK+ Project, http://www.gtk.org/ Functional Processes with applications to Graphical [20] Yale Haskell Group. http://haskell.cs.yale.edu/ User Interfaces, Ph.D. thesis, Chalmers University of Technology, 1998. [ 2 ] Chakravarty, M., Coutts, D. and Simons, A . : The Gtk2Hs library, https://wiki.haskell.org/ Gtk2Hs/ [ 3 ] Courtney, A. and Elliott, C. : Genuinely func- tional user interfaces, in Haskell workshop, 2001. [ 4 ] Elliott, C. : Phooey, https://wiki.haskell.org/ Phooey/ [ 5 ] Elliot, C. : Push-pull functional reactive pro- gramming, in Proceedings of the 2nd ACM SIG- PLAN symposium on Haskell, ACM, 2009. [ 6 ] Elliot, C. : Tangible functional programming, ACM SIGPLAN Notices, Vol. 42, No. 9(2007), pp. 59–70. [ 7 ] Elliott, C. and Hudak, P. : Functional reac- tive animation, ACM SIGPLAN Notices, Vol. 32, No. 8(1997), pp. 263–273. [ 8 ] Evers, S., Achten, P. and Kuper, J. : A Func- tional Programming Technique for Forms in Graph- ical User Interfaces, in Proceedings of the 16th International Workshop on Implementation and Application of Functional Languages (IFL 2004), vol. 3474 of LNCS, Springer-Verlag, 2005, pp. 35–51. [ 9 ] Garrigue, J., et al. : LablGTK: an objective caml interface to GTK+, http://lablgtk.forge.ocamlcore. org/2006. [10] Hudak,P., et al. : Arrows, robots, and func- tional reactive programming, Advanced Functional Programming, Springer Berlin Heidelberg, 2003, pp. 159–187. [11] Leijen, D. : The wxhaskell wiki at haskell. org, https://wiki.haskell.org/WxHaskell/ [12] Leijen, D. : wxHaskell: a portable and concise gui library for Haskell, in Proceedings of the 2004 ACM SIGPLAN workshop on Haskell, ACM, 2004. [13] Odersky, M., Spoon, L. and Venners, B.: Pro- gramming in scala, Artima Inc, 2008. [14] McKay, S. : CLIM: the Common Lisp interface manager, Vol. 34, No. 9 (1991), pp. 58–59.

†1 http://www.math.s.chiba-u.ac.jp/∼ymamada/ neooey.html Vol. 33 No. 4 Nov. 2016 47

付録 A コード:カウンター ------module Main where -- widget actions (FRP) import Graphics.UI.WX hiding (Event) upDown :: IWidActE’ Input2 (Unop Integer) upDown = left (inputWA (+ 1) ) ‘mappend‘ import NeooeyUI right (inputWA (subtract 1)) import NeooeyTypes import NeooeyWidAct counter2 :: WidAct Count import NeooeyFRP counter2 = do e <- left upDown right (showWA (0 ‘accumR‘ e)) import Control.Compose(Unop) import Data.Reactive hiding (Action) counter :: WidAct Count import Data.Monoid(mappend) counter = left upDown >>= right.showWA.(0 ‘accumR‘)

------widget types -- widget actions (Imperative) type Input = Button () actB, actB1, actB2 :: WidAct Count type Input2 = (Input, Input) actB = actB1 >> actB2 type Output = TextCtrl () type Count = (Input2, Output) actB1 = widgetSPAC takeB1 (updateValueinWA (+1)) actB2 ------= widgetSPAC takeB2 (updateValueinWA (subtract 1)) -- main function ------main :: IO () -- take functions main = runNAGUI "counter" mainUI takeB1, takeB2 :: Input2 -> Input takeB1 = fst ------takeB2 = snd -- UIs mainUI, preUI :: UI Count --mainUI = margin10 $ putWAinUI actB preUI mainUI = margin10 $ putWAinUI counter preUI preUI = buttonset1 <|> buttonset2 <|> outputUI

outputUI :: UI Output outputUI = entryUI [] buttonset1, buttonset2 :: UI Input buttonset1 = takeRightUI $ label1 <-> button1 buttonset2 = takeRightUI $ label2 <-> button2 label1, label2 :: UI () label1 = labelUI "button1" label2 = labelUI "button2" button1, button2 :: UI Input button1 = buttonUI [text := "+1"] button2 = buttonUI [text := "-1"] 48 コンピュータソフトウェア

付録 B コード:情報の相互交換 -- widget actions module Main where actB, actB1, actB2 :: WidAct InterChange import Graphics.UI.WX actB = actB1 >> actB2 import NeooeyUI import NeooeyWidAct actB1 = widgetSPA takeB1 change actB2 = widgetSPA takeB2 appendT ------widget types change :: WidAct InterChange change = do a <- left getText type InOut = Button () b <- right getText type InterChange = (InOut, InOut) right (setText a) left (setText b) ------main function appendT :: WidAct InterChange appendT = do a <- left getText main :: IO () right (updateTextinWA (a++)) main = runNAGUI "interchange" mainUI ------take functions ------UIs takeB1, takeB2 :: InterChange -> InOut takeB1 = fst mainUI, preUI :: UI InterChange takeB2 = snd mainUI = putWAinUI actB preUI preUI = margin10 $ buttonset1 <|> buttonset2

buttonset1, buttonset2 :: UI InOut buttonset1 = takeRightUI $ label1 <-> button1 buttonset2 = takeRightUI $ label2 <-> button2 label1, label2 :: UI () label1 = fillUI $ labelUI "change" label2 = fillUI $ labelUI "append" button1, button2 :: UI InOut button1 = buttonUI [text := "a"] button2 = buttonUI [text := "b"] Vol. 33 No. 4 Nov. 2016 49

付録 C コード:コントロールの働き変更 module Main where -- widget actions import Graphics.UI.WX actWA :: WidAct Bye import NeooeyUI actWA = widgetSPA takeB byeWA import NeooeyWidAct ------widget types byeWA :: WidAct Bye type Input = Button () byeWA = do left $ setText "Goodbye" type Output = StaticText () right $ setWidAct closef type Bye = (Output, Input) right $ setText "Close"

------main function -- take functions main :: IO () takeT :: Bye -> Output main takeT = fst = runNAGUI "Bye!" mainUI takeB :: Bye -> Input takeB = snd ------UIs mainUI, preUI :: UI Bye mainUI = putWAinUI actWA preUI preUI = margin10 $ messageUI <|> focusOnUI byeUI messageUI :: UI Output messageUI = staticTextUI [text := "Hello World"] byeUI :: UI Input byeUI = buttonUI [text := "Bye"]

眞々田泰裕 2011 年千葉大学理学部情報数理学科 卒業.2013 年同大学大学院理学研究 科基盤理学専攻修士課程修了.現在, 同大学院理学研究科基盤理学専攻博 士課程在籍.