日本ソフトウェア科学会第 32 回大会 (2015 年度) 講演論文集

Haskell で GUI を扱うためのライブラリ Phooey の改良
眞々田 泰裕
本論文の目的は関数型言語の特徴を活かしたプログラミングによる GUI の作成法を開発することである．関数型言
語で GUI を扱うためのツールキットはその内部では大抵 C++などのオブジェクト指向な命令型言語に基づいてできている．そのため関数型言語を用いた GUI プログラムは命令的な面影を残し，関数型の力を発揮しきれていない．
Haskell の場合は GUI ライブラリのひとつである wxHaskell をより関数型らしく扱えるようにするために Phooeyと呼ばれる Haskell のライブラリが開発されたが，こちらはまだ実用的な段階まで開発が進んでいない．
本論文ではこの Phooey に更なる改良を加えて，より実用的に扱えるように実装を行う．そして新しい GUI ツー
ルキットを用いた 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 imperative 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. We also show how
to make GUI codes by Neooey.

りとりで副作用が多く混入するためにコードが複雑

1 はじめに化し易く，大規模な GUI となるとコードの生成やバ

今日，だれもがコンピュータを操作する時代となグの発見が難しい．また，オブジェクト指向プログラり，コンピュータとユーザとのギャップを視覚的な観

ミングはこの問題を解消させるために生み出された点から埋める GUI の重要性はより一層増加している．

手法でクラスやその継承を利用して類似の処理を一

その GUI の実装を行うプログラミングの手法で特に括りにして GUI のコードやその把捉をより容易にす関係が深いものとしては次の２つが挙げられる。イベ

る．こういった理由もあり GUI プログラミングで使ントドリブン（イベント駆動）型プログラミングとオわれる言語はオブジェクト指向を盛り込んだ C++やブジェクト指向プログラミングである。イベントドリ

Java 等の命令型言語が使われることが一般的である．

ブンプログラミングとはプログラムがユーザによる

GUI は副作用が付きまとうため，関数型言語にお入力やシステムによってプログラム外から発生させけるプログラミングは主流ではなかった．しかし副たものであるイベントが発生するまでイベントルー

作用と純粋な作用とを分離して考えるという特徴は

プで待機を行い，イベントの感知に伴ってその処理

コードを単純化し，より安全な GUI の作成やバグのを行う形式のプログラミングである。ユーザとのや発見を容易にする．そのため近年では C# にラムダ式が導入されるなど関数型の考え方に注目が集まって

The Improvement of Phooey: libraies to use GUI in
Haskell
Yasuhiro Mamada, 千葉大学大学院理学研究科, Graduate School of Science, Chiba University.

いる．
関数型言語で GUI を作成するためには各言語に用意されている GUI ライブラリを利用する．そのライ

ブラリは，基本的にその内部では C + + などの命令いう．ある値に変化があるとその値との関係を通して型言語のバインディングによってできているものが多別の値も再計算される．一番浸透している例を挙げる

い．そのため関数型言語を用いた GUI プログラムでと Microsoft Excel などの表計算ソフトにその考え方あってもそのコーディングは関数型言語のスタイルにが使われている．値がこの時間に対して連続的に変化よるものになりにくい．

する値のことをリアクティブ (reactive) な値と呼び，関数型言語の 1 つである Haskell では，よく使用

リアクティブな値の関係性をふるまい (behavior) とされる GUI ライブラリとして wxHaskell と Gtk2hs

よぶ．また，時間に対して断続的に与えられる値をイの 2 つが挙げられる．wxHaskell については C++でベント (event) と呼ぶ．一般に RP のコードは長さが記述されている GUI ツールキット，wxWidgets の短くなるという特徴があり，それゆえにバグが少なく

バインディングとなっており，その wxHaskell をよ

保守性が高くなる．

り関数型らしく扱えるようにするために C.Elliot に

FRP は C.Elliott, P.Hudak による [1] が原形になっ

よって Phooey と呼ばれる Haskell のライブラリが開ているといわれる．イベントやリアクティブな値につ発された．Phooey は関数型言語を用いた GUI プロ

いてしばしばストリーム (stream) と呼ばれるものを

グラミングの 1 手法である FRP の考え方も盛り込考える．ストリームは時間ごとの値のリストとして表み，Haskell らしいスタイルでのコーディングが可能現される．例えば時間と値の連想リストとして表現さとなったが実用的な段階まで開発されてはなく，汎用れる．

性が低い．そこで本論文ではこの Phooey に更なる改

type Stream a = [(Time, Value a)]

良を加えて，より実用的に扱えるように実装を行う．

リアクティブな値とイベントは連続的なものと離散的論文の構成については次の通りである．第 2 章でなもので違いはあるが，リアクティブな値は変化した

関数型言語における GUI プログラミングの手法であ時刻とその値のストリーム，イベントは発生した時

る FRP について説明する．第 3 章で本論文で改良を刻とその値のストリームとして表現することができ，加える GUI ライブラリである Phooey 及び，その背互いに類似している側面がある．C.Elliott による論景にある GUI ツールキット及び GUI ライブラリに

文 [2] ではその点を踏まえ，イベントとリアクティブ

ついて説明する．第 4 章で実際に Phooey の改良をな値を相互再帰的な定義で実装している．

行い，GUI の作成法を例を挙げて説明する．第 5 章
で結論を述べる．

type Event a = Future (Reactive a)type Reactive a = a ’Stepper’ Event a

2 FRP
FRP とは functional reactive programming の略

Stepper はイベントに初期値 (時間 0 における値)称である．reactive programming である RP を関数を付与すること，Future は時間をイベントの次の発型で表現したもののことである．

生時刻まで進めることを表す．

RP とは GUI などの副作用を伴うコードで使われこのように時間によって変化する値を逐次ふるまいている表現の 1 つである．一般的な GUI のプログラ

関数で動かすことによって FRP が実装できる．本論

ムは行わせたい操作や命令，イベントの設定を実行さ文において改良を行う Phooey においてもこの考え

せたい順番に書き並べる．これはプログラマー視点で方に基づいて作られている．

は直感的でわかりやすいかもしれないがイベントやまた，本論文では深く取り上げないがアローの一変数の数が増えてくると依存関係を捉えることが困種であるシグナル関数を利用して FRP を表現した

難になる．それに対して RP は値を時間によって変化

AFRP と呼ばれるものも存在する．シグナル関数とするものととらえ，この値との関係を利用してプログはふるまいのようなものでリアクティブな値を変換

ラミングを行う．この関係性のことをデータフローとする関数のことを指す．AFRP では複数のシグナル

関数を結合して大きなシグナル関数をつくることで

FRP を表現する． [1], [8], [5]3 Haskell における GUI ライブラリ
この章では Phooey 及びその元となっている GUI
ツールキット及び GUI ライブラリについて述べる．

図1

wxhaskell による GUI

3. 1 wxWidgets

3. 2 wxHaskell

wxWidgets は C++で記述されている GUI ツール

wxHaskell は Haskell で GUI を扱うためのツールキットである．1992 年にクロスプラットフォームにのひとつである． [4] 内部としては上述した wxWid-

動くことを目的としてエディンバラ大学で J.Smartgets をバインディングした Haskell のライブラリであによって開発が開始された当時は wxWindows といる．類似のライブラリとしては他に GUI ツールキッう名前だったが，マイクロソフト社の Windows と名ト Gtk+ をバインディングしたものである，Gtk2hs

称が被ることを受け 2004 年に今の名前に変更された．

というツールが存在する．wxHaskell と Gtk2hs は

1 つの特徴としてはクロスプラットフォームに扱え

Haskell で GUI を扱うツールとしては双璧となってることで，Windows，Mac OS X，Linux といった多いる．

C++ であること

wxHaskell のバージョンは大きく分けて 0.13 系列

から高速であり，wxWidgets を利用するにあたってと 0.90 系列がある．最新版は 2014 年の 8 月 12 日

他のソフトを用意する必要がない．また，他のプログにリリースされたものでバージョンは 0.91.0 になっ

ラミング言語からでも使用可能にするために各種バイている．こちらは公式ページから無償で入手することンディングが用意されている．例えば Python で扱うができる． [3]くの OS で動かすことが出来る．

ための wxPython，Perl で扱うための wxPerl， C#

wxHaskell の概要については例を用いて述べる．図で扱うための wx.NET がある．それ以外にも php，

1 は wxHaskell による単純な GUI の例である．この

java， lua， lisp， erlang， eiﬀel， BASIC， ruby，GUI はボタンが押されるとウィンドウを閉じるだけ

Javascript といったかなり多くのバインディングがのものである．この GUI のソースコードは次のよう用意されている．別の特徴としてそれぞれのプラッに表される．

トフォームのソフトウェア開発キットを用いてルック

module Main whereimport Graphics.UI.WX

アンドフィールを保つ．つまり Windows などの各プラットフォームでコンパイルされたプログラムは視覚的な面や使い勝手がそのプラットフォームに合わせられるというものである．体裁にこだわる反面，
プラットフォームごとに挙動が異なる場合がある．また，かなり昔から開発が続いているので，ユーティ
リティやドキュメントが充実している．現在も開発は行われており，最新のバージョンは 2014 年 10 月

6 日にリリースされた wxWidgets 3.0.2 である．こちらは公式サイトから無償で入手することができる．

main :: IO ()main = start hello
hello :: IO ()hello = dof<- 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", widget quit]]
set f [layout := widget p][7] Phooey では Haskell で扱えるようにするために

wxHaskell というバインディングを使用している．

wxHaskell では IO () 型で表される GUI 関数を記述し，それを start 関数で呼び出すことによってGUI が起動する．GUI の定義の仕方の概略は，以下型が存在する．例えばボタンウィジェットは buttonのとおりである：関数を用いることによって作成することができてウィ

まずフレームを作成し，フレームの中にパネルを作成

ジェットとしての型は Button () である．また，テする．そしてパネルの中にボタンやテキストエントリ

キストエントリウィジェットは textCtrl 関数を用いなどのコントロールを作成する．その後でウィジェッ

ることによって作成することができてウィジェットとトの大きさや配置を示すレイアウトやイベントが発しての型は TextCtrl () である．

生した際の処理は通常その後で定義をする．という順
番で行われる．

ウィジェットを作成する関数は原則として引数に親となるウィジェットとプロパティリストを取る．いく

GUI にはウィジェットという最小単位が存在する．

つかのウィジェット作成関数について型を紹介する．

これは GUI オブジェクトのようなものであり，上でframe を除いて第一引数として現れている Window a

名前の挙がったフレーム，パネル，ボタン，テキストは任意のウィジェットの型が入ることを示している．

エントリなどのコントロールは全てウィジェットの一frame の引数が少ないのはフレームに親となるウィ種である．GUI の構成はこれらウィジェットをつなぎジェットは存在しないためである．第二引数として現合わせることによって行われる．

れている [Prop (*** ())] はプロパティリストと呼フレームとはメインウィンドウ，すなわちウィンド

ばれ，ウィジェットに関する各情報を設定することがウ全体の事を指す．基本的に他のすべてのウィジェッ

出来る．

トはフレームの中に属する．frame 関数を用いるこ

frame :: [Prop (Frame ())] -> IO (Frame ())
panel :: Window a -> [Prop (Panel ())]-> IO (Panel ())
button :: Window a -> [Prop (Button ())]-> IO (Button ())
textCtrl :: Window a -> [Prop (TextCtrl ())]-> IO (TextCtrl ())とによって作成することができてウィジェットとしての型は Frame () である．
パネルはウィジェットコンテナの 1 つで，中にウィジェットを入れることのできるウィジェットである．
後に説明するレイアウトを整える目的で使われる．一
般的な GUI ではフレームの次にパネルを設置し，他各情報については属性と呼ばれ，ウィジェットの種のウィジェットをその中に設置するといった使い方が類に応じていくつかの種類の属性を定めることが出多くみられる．panel 関数を用いることによって作

来る．必ずしもすべての属性を定める必要はなく，設成することができてウィジェットとしての型は Panel

定しなかった場合はデフォルトとしての値が当てはめ() である．

られる．属性を定める場合はコントロールはコントロールウィジェットとも呼び，

属性名 := 値

ユーザやシステムによる入力を感知することのできるの形で与えることができる．あるウィジェットに対しウィジェットである．例としてボタン，テキストエンて属性の設定を行うには２つの方法がある．一つはトリ（入力欄，テキストコントロールとも），スライ

ウィジェットを生成する関数を呼び出す際に指定してダー（つまみを動かして程度を指定できるコントロー

しまう方法である．ウィジェットを生成する関数は基ル），プロパティグリッド（スプレッドシートのよう本的にプロパティリストを引数に取るようにできていなもの）などが挙げられる．コントロールはユーザにるのでその際に設定してしまうのである．もう一つは

よる選択や操作を感知するとイベントと呼ばれるサイ

set 関数を用いる方法である．set はプロパティリス

ンを発生させる．このイベントの種類と発生した時にトの再設定を行う関数である．逆に，定められた属性

行いたい処理を結びつけることができて，これによっの値を抽出する働きを持つ get と呼ばれる関数も存てさまざまなアクションを引き起こすことが可能と在する．

なる．コントロールウィジェットには多数の種類があるので，それぞれのウィジェットに対して作成関数や

ウトである．何かが表示されるわけでもない何もない
set :: w -> [Prop w] -> IO ()get :: w -> Attr w a -> IO a空間である．視覚的にまとまったスペースを開けたい
ときに有効である．
レイアウトに効果を掛ける関数については fill や属性の具体的な例としてここでは text，clientSize，

margin などがこれに該当する．fill 関数は空いていon command，layout を挙げて説明する．

るスペースを満たすようにレイアウトを拡大して埋

text :: Textual w => Attr w StringclientSize :: Dimensions w => Attr w Sizeon command :: Commanding w => Attr w (IO ())
layout :: Form w => Attr w Layout

め尽くす関数である．また，margin はレイアウトの外枠を指定されたサイズだけ縁取る関数である．このように，これらの関数は引数にレイアウトを取り，それに変更を加えるという形で定義がなされている．

text は名前や文字列としての値を当てはめる属性

レイアウトを結合する関数については row や column

である．フレームにおいてはタイトルバーに表示されなどがこれに該当する．これらの関数はリストなどをる文字列を表し，ボタンにおいてはボタンに表示され通して複数のレイアウトを引数から取り，レイアウト

る文字列を表す．また，テキストエントリにおいてはを１つに統合する．row 関数は水平方向にレイアウ

エントリ自身に書かれる文字列を表す．

トの結合を行う．この関数は第一引数に数値を取るよはウィジェットの大きさを記述する

うにできており，この数値によって各レイアウトの隙属性である．後に紹介するレイアウトと働きが重複す

間を調節することが可能である．column 関数は row

るものもあり，設定の仕方によっては想定通りに反映

関数に似ているところも多いが，垂直方向にレイアウされない恐れがある．

トの結合を行う関数となっている．

clientSize

on command はコントロールウィジェットがユーザ

また，特筆すべきレイアウト用の関数に widget がによる入力，変更を感知した際に処理を行いたいアある．この関数は子ウィジェットで定められたレイア

クションを記述する属性である．また，外的な刺激にウトを抽出する．

よって発生するイベントについてはいくつか種類があlabel :: String -> Layout
space :: Int -> Int -> Layout

り，用途に応じて command の箇所を別の関数に置き換えることによって各イベントに対する反応を記述することが可能である．例としては選択されている状態を観測する select などがある．

layoutはウィジェットのレイアウトを記述する

属性である．GUI においてレイアウトは視覚的なウィ

fill :: Layout -> Layout
margin :: Int -> Layout -> Layout
row :: Int -> [Layout] -> Layout
column :: Int -> [Layout] -> Layout
widget :: Widget w => w -> Layout

ジェットの配置を表している．wxHaskell においてはレイアウトの型を Layout 型で表現する．

以 上 が wxHaskell の概 要 で あ る ．wxHaskell はレイアウトを取り扱う関数にはいくつか種類があHaskell で GUI を 作 る た め に は 有 効 で は あ る がる．レイアウトを生成する関数，レイアウトを変換す

C++(または C) で構成されている wxWidgets をる関数，レイアウトを結合する関数である．

そのまま翻訳したかのように作られており，GUI 関

レイアウトを生み出す原始的な関数としては labelや space などがこれに該当する．label 関数は文字数の定義は命令型言語で行うような逐次実行のコーディングになっている．

列を画面に表示させるだけのレイアウトである．text

これを改善する手法として FRP を GUI に織り交ぜ属性を定めたウィジェットとは違い，表示される文字て Haskell のスタイルで GUI の作成が行える Phooey

列が変更されることはない．space 関数は引数で受けという wxhaskell 上のライブラリが開発された．これ取ったサイズの矩形の分だけスペースを陣取るレイアについては次の節に述べる．

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 型を使う．
3. 3 Phooey

構造としてはメインパネルを参照してからアクション

Phooey は wxHaskell 上に実装されたシンプルなを起こすものになっている．UI 型はその内部に FRP

Haskell の関数型ライブラリである．これは FRP にを表現するためのリアクティブなアクションとレイア関する論文 [1] [2] で有名な C.Elliott によって開発さ

ウトを情報として保有しており，UI の結合が行われれたものである．外面的には関数型らしい記述を追る際に，レイアウトの結合やリアクティブな値のも行究しており，wxHaskell で見られるような命令の逐われる．結合によって構築された UI は runUI という次実行的ではない，Haskell らしいコードを組むこと

関数を用いることによって起動することができる．

ができる．実際に Monad や Applicative といったな

runUI :: UI () -> IO ()じみのあるものを用いて GUI を実装することができる．構造的には FRP の考え方を用いて作られておPhooey の特徴としては Haskell のスタイルによるり，短いコード量によって表現することが可能となっコーディングを実現しているということが挙げられている．phooey は単語自体が持つ自虐的な意味と，る．例えば上記の例における ui1x では do 記法を用い

FUI(Functional User Interfaces) の頭文字を発音かて定義されているが，UI 型が Monad や Applicative

ら文字った Phunctional ooser ynterfaces の略とのといった型クラスのインスタンスになっていること

2 つの意味を掛け合わせたものである．最終更新はから関数 liftM2 や (>>=) を用いることが可能であ2011 年 9 月 10 日 のバージョン 2.0.0.1 である．

る．これらを用いることによって次で紹介しているよまずは Phooey の構造や特徴を GUI の例を通して説明する．
図 2 はフルーツのショッピングリストを表現した

GUI である．リンゴとバナナに対してそれぞれスラ
イダーと呼ばれるつまみが存在し，下には合計を表す
テキストエントリが置かれている．スライダーを動かすことによってフルーツの合計が再計算され，テキス
トエントリに表示される数が更新される．

うに自然で短い Haskell スタイルによる定義が実現される．
(.+.) :: Num a => UIS a -> UIS a -> UIS a(.+.) = liftA2 (liftA2 (+))
fruit :: UI (Source Int)
fruit = apples .+. bananas
ui1y :: UI ()ui1y = title "Shopping List" $ fruit >>= total

このショッピングリストを表現する GUI を Neooeyを用いて表現すると次のようになる．

また，UI 型の結合を行う際にレイアウトを指定す

て別の UI と結合した場合，その結合した相手から情報を受け取って処理するといったことができない．これは値の伝達が一方通行であれば問題ないがもし相互的で複雑に値を交換する UI を考えた場合，これを有効に表現することができない．
さらに，FRP を表現するために Phooey においては UI 型はレイアウトやソースアクションを保有するという性質があったが，それゆえにウィジェット情報
図3

Phooey によるショッピングリストの GUIについては保有しない．つまり一度作成されたウィジェットに対して後になってプロパティを設定し直す

る関数を適用することができる．これを使用することことができない．このことにより例えばボタンが押さでウィジェットの配置を変えることが可能である．

れた時のイベント処理は作成時に固定されてしまい，

次のソースコードを動かすと図 3 のようになる．
この例では fromRight 関数によってリンゴとバナナ

後になってその処理自体を変更するとなった場合に無理がでてしまう．

のウィジェットを右から配置し，fromBottom 関数によってフルーツと合計のウィジェットを下から配置し4 Neooeyている．

この章では Phooey で発生した問題，特に汎用的な

ui2 = fromBottom $title "Shopping List" $fromRight fruit >>= total

定義を行うことができない問題と UI 同士の値のやりとりが限定的なものに限られてしまう問題，UI の情
報を後になって変更することができない問題を解決す

このように Phooey を用いることで GUI のプログ
ラミングを Haskell のスタイルによって行うことが可ることを意図して Neooey と命名した新しい GUI ラ
イブラリを作成する．

能になった．しかしこのライブラリには問題点もあPhooey 自体も FUI(Functional User Interface) のる．まずは提供されているライブラリは実用的な段発音のもじりであるが，そこに new という文字を付

階までは開発がなされていないことである．例えば

け加えて New Phooey という意味合いで名前を付け

ウィジェットのプロパティはある程度固定されており，た．根底にあるコンセプトは Haskell のスタイルによ

ユーザによって設定することのできる属性はほとんどる GUI の設計を汎用的に行うことを可能とすることない．ボタンウィジェットを例に挙げるとボタンに書である．

かれる文字列については設定することが可能だがボタン自体のサイズや，ボタンを押すことが許されてい4. 1 UI

るかの可否などの詳細な情報についてはユーザーがPhooey の場合，レイアウトの情報はそれぞれの UI

自由に設定することができない．この制限は簡潔で容の値の中で保持されている．そして UI を結合する際

易にコードが書けるという点はで優れているが汎用にレイアウトについても UI の内部で結合される形を性を欠くものとなる．

とる．Neooey におけるレイアウトも Phooey と同様

また，Phooey には FRP の考え方が用いられていに UI にレイアウトの情報を保有させる形式をとる．

る．そしてそれを実現する方法との 1 つとして値やまた，Phooey においてはメインパネルを参照して動関数を投げる UI とそれを受けて反応を返す UI を結く関数として UI を定義する．Neooey においてもこ合し，全体としてひとつの UI にまとめあげる構造をの考え方を踏襲し，メインフレームとメインパネルをとる．しかし結合の際にこれら 2 つの UI の役割は固参照して動く関数として UI を定義する．Phooey と定されてしまう．言い換えると値を生成する UI とし違うのはメインパネルの他にメインフレームも参照

するようにした所である．こうすることによってメイ

うなるとウィジェットを一度作成してしまうとそのンフレームに対して働き掛けたい操作を UI に行わせウィジェットの設定が行えなくなる．これはウィジェッ

ることが可能となる．例えばメインフレームに直接追トの依存関係がより複雑になるとその表現が難しく加させる必要のあるメニューバーやステータスバーがなることを意味する．

設置可能になる．

一方，Neooey においては UI の中で使用されてい上記の働きをもつ UI 型の表現は感覚的にはメインるウィジェット情報を結果として返す．こうすること

フレームとメインパネルを引数としてとり，結果とレによってウィジェットの設定の変更を後でも行えるよ

イアウトの情報を返すものとして捉えることができうにする．例えばボタンウィジェットである Button

る．つまり次のように考えることができる．

() 型を保有する UI は UI (Button ()) 型となる．

type UI a =MWin -> Win -> IO (a,Neooey において UI に対して使うことのできる関
Layout)数は 3 種類に大別することができる．ひとつはウィジェット情報を保有する UI を作る関数，もうひとつ

しかし実際の UI 型の実装は引数に取って実行する
形式ではなく，モナド変換子を用いて行う．

は UI にレイアウト情報を付与するための関数，そして最後は複数の UI を結合するための関数である．

モナド変換子は核となるモナドに対して働くこと

ウィジェット情報を保有する UI を作る関数とはボができる．これを用いることでモナドに性質の付与を

タンウィジェットやテキストエントリウィジェットな行い，複数の働きを持つひとつの大きなモナドを構築

どのコントロールウィジェットを作成してそのウィすることが可能になる．Neooey では IO アクション

ジェット情報を結果として返す関数である．この関数

を核のモナドとしてこれにモナド変換子を適用させは引数として wxHaskell と同じ記法のプロパティやて UI を示す型である UI 型を構築する．

それ以外の最低限必要となる基本情報を受け取るこ

今回使用する変換子は Writer モナドを変換子化ととなる．内部では wxHaskell のコントロールを作させた WriterT と Reader モナドを変換子化させたる関数を用いており，関数名も参照元の関数を元に付ReaderT である．WriterT は核である IO アクション

けられている．

にレイアウト情報を保持させる目的で使用する．まtextCtrlUI:: [Prop (TextCtrl ())] -> UI (TextCtrl ())
buttonUI:: [Prop (Button ())] -> UI (Button ())

た ReaderT は核である IO アクションが外から与えられるメインフレームとメインパネルの情報を参照
して動くことを目的として使用する．
type UI =ReaderT MWin (ReaderT Win(WriterT Layout IO))

UI にレイアウト情報を付与するための関数として一般的なものは，すでに存在する UI を引数にとってその UI が持つレイアウトの変更を行うものである．

MWin 型は wxHaskell における Frame () 型，Win型は wxHaskell における Panel () 型のシノニムでmarginUI :: Int -> UI a -> UI a
fillUI :: UI a -> UI a

ある．レイアウト情報を示す Layout 型は wxHaskell
で使用されているものと同じである．
また，Phooey における UI が返す結果と Neooey

これらの関数では引数でとられる UI がもつウィジェット情報についてはそのまま引き継がれる．

における UI が返す結果はその役割が異なっている．

また，ウィジェット情報を持たないレイアウトを作

Phooey では必要に応じて UI が持つリアクティブなる関数も存在する．何も置かれていない空間やラベル値を結果として返す．これによって Phooey では FRPなど，他のレイアウトを参照しないような自立したレを利用した UI の定義が可能になっている．Phooey

イアウトを作ることができる．こういったレイアウト

ではウィジェットを保有する機構は持っていない．そは一度作成されてしまうと変更は行われない．

Phooey ではこういったアクションは UI 作成時にlabelUI :: String -> UI ()spaceUI :: Int -> Int -> UI ()ほぼ定義され，リアクティブな値の受け渡しに関するアクションだけ UI が情報として保持する．
しかし Neooey ではウィジェットの反応を表現する

そして最後は複数の UI を結合するための関数であウィジェットアクションを UI から分離した．こうす

る．UI の結合が行われた際にウィジェットやレイア

ることによってより複雑なウィジェットの依存関係を

ウトの結合が行われる．レイアウトの結合については持つアクションの指定が行えるようになる．

垂直方向に結合するか，水平方向に結合するかによって 2 種類の演算子が存在する．
(<->) :: UI a -> UI b -> UI (a, b)(<|>) :: UI a -> UI b -> UI (a, b)

ウィジェットアクションは Neooey における UI 型と同様にメインフレームとメインパネルを参照する．
また，それとは別に UI 型が結果として返すウィジェッ
ト情報についても参照する．UI 同様ウィジェットア
クションの記述のためにモナド変換子を使用する．

(<->) が水平方向，(<|>) が垂直方向にレイアウト
を結合する演算子である．ウィジェット情報についてはtype WidAct’ w = ReaderT MWin (ReaderT Win(ReaderT w IO))
type WidAct w = WidAct’ w ()引数でとられたそれぞれの UI が保有する情報を組にして保有する形式をとる．例えば型が UI (Button ())

ここで定義された WidAct 型は UI 同様に各ウィの UI と型が UI (TextCtrl ()) の UI を結合してでジェットを引数で取って動くアクションとして考える

きた UI の型は UI (Button (), TextCtrl ()) とこともできる．

なり，そのウィジェットは Button () 及び TextCtrl

type WidAct’ w a = MWin -> Win -> w -> IO atype WidAct w = WidAct’ w ()() のウィジェット情報を保有することになる．
場合によっては結果として持っておく必要のないウィジェット情報もある．そういった時の為に takeLeft

一番基本的なウィジェットアクションは属性やプロ関数や takeRight 関数を使うことができて，これをパティなどの引数を受け取ってウィジェットアクショ

用いると結果として持っているウィジェット情報を削ンを作る関数 getF 及び setF である．

ることができる．

getF :: Attr attr val -> WidAct’ attr val
setF :: [Prop a] -> WidAct a

takeLeftUI :: UI (a, b) -> UI atakeRightUI :: UI (a, b) -> UI b

これらは wxHaskell における get 及び set 関数を元に作られている．ほとんどのウィジェットアクショ

4. 2 ウィジェットアクションンを作る関数はこれらを利用して作られている．例え

UI の作成や結合によって得られる GUI はウィジェッ

ばボタンが押された際の挙動などを示すコントロー

ト情報を構造的に保持し，それと同時にレイアウト

ルの反応を記述する関数が挙げられる．

も表現する．しかし，ボタンが押された際の挙動など

setWidAct :: Commanding a =>WidAct a -> WidAct a
setWidAct2 :: Commanding a =>WidAct b -> b -> WidAct aのウィジェットの反応についてはこれだけでは行われない．そこで，ここでは UI とはまた別にウィジェッ
トアクションと呼ばれるものを作成する．ウィジェッ
トアクションはウィジェット情報を参照して動くアク

ウィジェットアクションはモナドであるので do 記法

ションである．最終的にこのウィジェットアクションや (>>=) などの結合演算子などを利用して合成する．

を UI と結びつけることによって反応を示す GUI が構築される．

このとき，ウィジェットアクションが参照する型に相違があるとそのまま合成することができない．そのため，これを解決させるために要求する型を変形して形をそろえる必要がある．これを行う関数が right及び left 関数である．
right :: WidAct’ b c -> WidAct’ (a, b) c
left :: WidAct’ a c -> WidAct’ (a, b) c

これらの関数はウィジェットアクションを引数とし

図4

Neooey によるカウンターを行う GUI の例てとり，そのウィジェットアクションが参照するウィ
ジェット情報を変える働きがある．具体的には引数のウィジェットアクションが要求するウィジェット情報

putWAinUI :: WidAct’ a b -> UI a -> UI a

を組の形にする．right 関数はウィジェットの組を作り，その中で本当に使用したいウィジェットは組の右におかれることを示し，left 関数はそれが組の左に

4. 3 例：カウンター

おかれることを示している．

本節では実際に Neooey を使用して GUI を作成す

例えば，以下の例において 2 つのウィジェットアク
ション waA と waB があった場合型が一致しないためる例を見せる．そしてその過程を通して効率的な作成手順について説明する.

にこのままでは結合をすることができない．しかし作成する GUI は図 4 のように 2 つのボタンがついleft 関数及び right 関数を用いてウィジェットアク

たカウンターである. 「+1」と書かれたボタンを押ションの型をそろえることによって一度にまとめるこ

すことによってテキストエントリに表示されている数とができるようになる．

値が 1 増加し, 「−1」と書かれたボタンを押すこと

また，効率よくウィジェットアクションを作成する
ための関数として widgetSPA 及び widgetSPAC が挙げられる．これらの関数は第一引数にくる take 関数
で抜き出されたウィジェットがトリガーとなって第二引数で与えられたアクションを引き起こす関数となっによってテキストエントリに表示されている数値が 1減少する.
この GUI を Neooey を用いて作成するには次の 3つの手順に分けて行うのがよい．

UI と型の作成ている．

→ take 関数の作成

widgetSPA :: Commanding w’ =>(w->w’)-> WidAct w-> WidAct w
widgetSPAC :: Commanding w’ =>(w1->w’) -> WidAct w2 -> WidAct (w1, w2)→ ウィジェットアクションの作成
まず始めに行う工程は UI 及びウィジェットごとの型の作成である．ウィジェットごとの型とは例えば入特に下の widgetSPAC は入力側のウィジェットと出力や出力となるウィジェットについて別名をつける

力側のウィジェットが分離されている場合に用いるこということである．単一のウィジェットのみでなく，とができて，入力側と出力側をつなげたウィジェット

ウィジェットを組にしたものについても名前をつけてアクションを構築する．

おくとよい．

ウィジェットアクションを UI に結び付けるにはputWAinUI 関数を用いる．この関数はウィジェット
アクション及び UI を引数にとり，ウィジェットアク

type Input = Button ()type Input2 = (Input, Input)
type Output = TextCtrl ()type Count = (Input2, Output)

ションが要求する挙動を UI に付与させることが可能になる．

ウィジェットごとの型に名前をつける理由はウィジェットの数が多くなった場合に煩雑でわかりにくくなってしまうのを避けるためである．もし途中ここでいう take 関数とは組で表現されたウィジェッ

で型に名前を付けない場合，今回の例ではメイント構造から特定のウィジェットの情報を抜き出す関数の GUI 関数の型は UI ((Button (), Button ()),

のことを指す．これを定義することによって目的のTextCtrl ()) となる. 型の命名を先に紹介したが，ウィジェットを抜き出しやすくする効果がある．

実際は UI の生成と同時に型の命名を行うとよい．

今回の例では，がカウントアップを行うボタンウィ

UI の構築ついては基本的に UI を生成する関数とジェット及びカウントダウンを行うボタンウィジェッ

UI を結合する関数の組み合わせによって定義する．

トを抜き出す関数を準備する．具体的には，入力側のUI の生成については各ウィジェットごとに専用のウィジェット Input2 から fst，snd を用いて目的の関数を用いて行う．今回の例の場合作成する必要があ

ウィジェットを選ぶ．

るのは値の増減を行うためのボタン 2 つ，各ボタン

takeB1, takeB2 :: Input2 -> Input
takeB1 = fst
takeB2 = sndの隣にあるラベル 2 つ，出力用のエントリ 1 つの計 5つである．
outputUI :: UI Output
outputUI = entryUI [text := "0"]label1, label2 :: UI ()label1 = labelUI "button1"
label2 = labelUI "button2"

take 関数の定義を行ったら最後に行うのはウィジェットアクションの定義である．updateWA’ 関数は引数の演算子を出力先のウィジェットに適用させる．

actB1 はtakeB1 が示すカウントアップボタンウィジェットがbutton1, button2 :: UI Input
button1 = buttonUI [text := "+1"]
button2 = buttonUI [text := "-1"]

イベントを感知した際に出力先の数値に 1 加算することを表している．また，actB2 は

takeB2 が示すカウントダウンボタンウィジェットがUI の生成を行ったら，次は垂直方向，又は水平方

イベントを感知した際に出力先の数値に 1 減算する

向に結合を行い UI をまとめ上げる．このとき 2 つのことを表している．入力側の UI と出力側の UI が分

ラベル用 UI label1, label2 についてはウィジェッ

離されている場合はこのように widgetSPAC 関数がト情報を持たないので結合の際に takeRightUI 関数

使うことが可能となる．

を用いて保持するウィジェットの情報を剪定する．こ--widget actions

れをすることによって UI の型が UI ((),Input) から UI Input になる．尚, margin10 関数はレイアウ

actB, actB1, actB2 :: WidAct Count
actB = actB1 >> actB2

トとして UI の縁を空けるために使用されている．

actB1 = widgetSPAC takeB1 (updateWA’ (+1))
preUI :: UI Count
actB2 = widgetSPAC takeB2 (updateWA’ (+(-1)))
preUI = margin10 $ buttonset1 <|> buttonset2 <|> outputUI
buttonset1, buttonset2 :: UI Input
buttonset1 = takeRightUI $ label1 <-> button1
buttonset2 = takeRightUI $ label2 <-> button2

最後にウィジェット構造やレイアウト情報を保持している UI にウィジェットに行わせる操作を担うウィジェットアクションを結合すれば完成である．これにここで作成された preUI 関数はボタンが押された際の処理を組み込んでいないためにこのまま動かしてもボタンに反応がない．ボタンの処理を記述するた

よって目的のカウンタープログラムが動く．
mainUI :: UI Count
mainUI = putWAinUI actB preUI

めにはウィジェットアクションを用意して盛り込む必要がある．そこでウィジェットアクションの定義を行

このカウンタープログラムは Phooey を用いて表現うわけだが，その前の準備として take 関数を用意しすることも可能である．しかし Neooey では Phooeyておくとよい．

で表すことのできない例についても表現することが出type InOut = Button ()type InterChange = (InOut， InOut)

カウンターの例のように入力側の UI と出力側の
図5

Neooey によるウィジェット同士で情報交換をする
GUI の例

UI が分離されている場合はtake 関数を入力側の範疇で考え，定義した．そして widgetSPAC 関数によって入力と出力の結び付けを来る．次節では Phooey で表すことのできない GUI

行った．しかし今回の例ではどちらのボタンも入出力の実装について述べる．尚，カウンタープログラムのをともに行うようになっているため，カウンターと全

全ソースコードについては付録 A に載せる．

く同じ方法を取ることはできない．
今回の例のように入力側の UI と出力側の UI を

4. 4 Phooey で表現できない GUI を Neooey
で実装する

3. 3 節で述べたように Phooey は FRP を利用して作られているが，その表現力については限定的であり，Phooey のみの力によって GUI を作ることがで分離することが出来ない時は，take 関数を入力側のウィジェット構造から考えるのではなく，入出力全体のウィジェット構造から抜き出すことを考える．
takeE1, takeE2 :: InterChange -> InOut
takeE1 = fst
takeE2 = snd

きない例も存在する．
本節では Phooey で表現できない例のうち，UI がま た ，ウィジェ ット ア ク ション に つ い て は相互的に情報を交換させる例とコントロールの処理

widgetSPAC 関数ではなく widgetSPA 関数を用いを途中で変更させる例について Neooey を用いた実て定義する．この関数は，第一引数の take 関数に装を行う．

よって摘出されるコントロールに第二引数で指定されまずは UI が相互的に情報を交換する例について考える．

たウィジェットアクションを定めるものとして解釈できる．

図 5 は 2 つのボタンウィジェットが互いに情報を交

それぞれのウィジェットアクションについては次の換させる例になっている．上のボタンを押すと双方のように従来の列挙による定義も行うことが可能であ

ボタンに書かれた文字列が入れ替わり，下のボタンをる．その際は前述したように left 及び right 関数

押すと双方のボタンに書かれた文字列を連結したもを用いてウィジェットアクションの型を統一させる．

のが下のボタンに反映される．どちらのボタンが押さ

actE, actE1, actE2 :: WidAct InterChange
actE = actE1 >> actE2

れた場合でも双方のボタンの情報について参照及び更新を行うようになっている．すなわちどちらのウィジェットも入力及び出力を行うものとなっており，この GUI は Phooey によって表現することができない
ものある．
これからこの GUI を Neooey で表現できることを実際に実装することによって示す．

Neooey で行う 3 つの工程のうち，UI 及び型の作成についてはカウンターの例と同様にして行うことができるので省略する．ただし take 関数の実装を説明するために，ここでは命名した型のみ紹介する．

actE1 = widgetSPA takeE1 change
actE2 = widgetSPA takeE2 appendTchange :: WidAct InterChangechange = do a <- left getText
b <- right getText
right (setText a)left (setText b)
appendT :: WidAct InterChange
appendT = do a <- left getText
right (updateWA (a++))れているように見えるが，実際は入力側の UI である
ボタンが自身の働きを変化させるために出力側としてUI の役割も兼ねるので実質的には分離されていない．
そのためこの例でもウィジェットアクションの結合には widgetSPA 関数を使用し，take 関数の定義を入力側のウィジェット構造から考えるのではなく，入出
図6

Neooey によるボタンの働きが切り替わる例 1

図7

Neooey によるボタンの働きが切り替わる例 2

最後にカウンターの例と同じように putWAinUI 関

力全体のウィジェット構造から抜き出す関数として考える．
takeB :: Bye -> Input
takeB = snd数を使って UI にウィジェットアクションを埋め込むことによって UI が完成される．これによって Phooey

ウィジェットアクションの定義については次のようで表現することができない GUI の例の 1 つを Neooeyに行うことが出来る．setText とはウィジェットに引で書けることが示された．

数で受け取った文字列を当てはめるための関数であ尚，このプログラムの全ソースコードについては付
録 B に載せる．
次はコントロールの処理を途中で変更させる例について考える．
図 6 で表される GUI はボタンが行う処理が変更される例となっている．まずボタンを押すと図 7 のように画面に表示されているテキストが「Hello World」

る．また，closef 関数はメインウィンドウを閉じる
ための関数である．
actWA :: WidAct Bye
actWA = widgetSPA takeB byeWA
byeWA :: WidAct Bye
byeWA = do left $ setText "Goodbye"
right $ setWidAct closef
right $ setText "Close"から「Goodbye」へと変わる．そしてそれと同時にボタンに書かれている文字列も置き換わる．ここで後はこれまでの例と同様に putWAinUI 関数を使っ

もう一度ボタンを押すと今度はウィンドウが閉じらて UI にウィジェットアクションを埋め込んで UI がれる．このとき，ボタンが押された際に行われる処理完成される．これによって Phooey で表現することは一度目は文字列の表示とボタン自体の挙動の変更，ができない例のうち，コントロールの働きを後になっ二度目はウィンドウを閉じる，といったようにその働て変更する GUI を Neooey で表現できることが示さ

きが大きく変わるものになっている．こういった操作

れた．

については Phooey を用いて表現することが出来ない例となっている．

このプログラムの全ソースコードについては付録

C に載せる．

この GUI についても Neooey で表現できることを実際に実装することによって示す．

5 結論と展望

Neooey で行う 3 つの工程のうち，UI 及び型の作

Phooey を元に Neooey を構築したことで UI につ成についてはこれまでの例と同様にして行うことがいては関数型らしい表現でコードを書けるようになっできるので省略する．ただし take 関数の実装を説明た．また，UI とウィジェットアクションの分離を行っするために，ここでは命名した型のみ紹介する．

たことによって Phooey のコードでは表せなかった

type Input = Button ()type Output = StaticText ()type Bye = (Output, Input)

GUI の例も Neooey を用いて表現することができるようになった．
しかし実装にあたって問題点もいくつか残った．そ型を見ると一見今回の例は入力側と出力側が分離されを今後の展望とする．

問題点の一つはウィジェットをリスト的に扱えないという点である．現状の Neooey における UI は複数のウィジェット情報を組の状態で結合して保有するようにできている．各ウィジェットの型が異なる可能性があるために組を用いた定義を採用した．しかし

wxHaskell ではレイアウトをリストとしてまとめて処[ 7 ] Julian Smart, et al. “The wxWidgets library.”https://www.wxwidgets.org/[ 8 ] Paul Hudak, et al. ”Arrows, robots, and functional reactive programming.” Advanced Functional
Programming. Springer Berlin Heidelberg, 2003.
159-187.

A コード:カウンター
module Main where

理する関数も多くそういった関数を Neooey で表現するには限界がある．ウィジェットをリスト的に扱う一つの案として木構造のウィジェットを考えるというものがあるがこちらについても深刻な問題があり，まだ思案の段階である．

import Graphics.UI.WX
import NeooeyUI
import NeooeyWidAct------------------------------------------------ widget types

次に Neooey において FRP が活かしきれてないことである．Phooey の場合は UI 中にアクションを保持する機能が備わっていた．そして保持したアクションを使って FRP の実装を行っていた．しかし Neooeyの場合は UI とウィジェットアクションでイベント処理に関する操作を分離してしまったために UI の中にアクションを保持させる必要がなくなってしまった．
対してウィジェットアクションの方で FRP を活かすという方法が考えられるが現在はこの考え方が用いられていない．
また，より本格的な GUI でも難なく関数的な実装ができるように追究を行っていく．
謝辞 本論文の内容について議論していただいた桜

type Input = Button ()type Input2 = (Input, Input)
type Output = TextCtrl ()type Count = (Input2, Output)------------------------------------------------ main function
main :: IO ()main= runNAGUI "counter" mainUI------------------------------------------------ UIs
mainUI, preUI :: UI Count
mainUI = putWAinUI actB preUI
preUI = margin10 $ buttonset1 <|> buttonset2 <|> outputUI

井貴文氏 (千葉大) に感謝する．
参 考 文 献[ 1 ] Conal Elliott and Paul Hudak. “Functional reactive animation.” ACM SIGPLAN Notices. Vol. 32.
No. 8. ACM, 1997.
[ 2 ] Conal Elliott. “Push-pull functional reactive
programming.” Proceedings of the 2nd ACM SIGPLAN symposium on Haskell. ACM, 2009.
[ 3 ] Daan Leijen. “The wxhaskell wiki at haskell.
org.” https://wiki.haskell.org/WxHaskell[ 4 ] Daan Leijen. “wxHaskell: a portable and concise gui library for Haskell.” Proceedings of the 2004
ACM SIGPLAN workshop on Haskell. ACM, 2004.
[ 5 ] Henrik Nilsson, Antony Courtney, and John
Peterson. “Functional reactive programming, continued.” Proceedings of the 2002 ACM SIGPLAN
workshop on Haskell. ACM, 2002.
[ 6 ] Julian Smart and Stefan Csomor. Crossplatform GUI programming with wxWidgets. Prentice Hall Professional, 2005.

outputUI :: UI Output
outputUI = entryUI [text := "0"]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"]B コード:情報の相互交換-- widget actions
actB, actB1, actB2 :: WidAct Count
actB = actB1 >> actB2
actB1 = widgetSPAC takeB1 (updateWA’ (+1))
actB2 = widgetSPAC takeB2 (updateWA’ (+(-1)))------------------------------------------------ take functions
takeB1, takeB2 :: Input2 -> Input
takeB1 = fst
takeB2 = snd

module Main whereimport Graphics.UI.WX
import NeooeyUI
import NeooeyWidAct------------------------------------------------ widget types
type InOut = Button ()type InterChange = (InOut， InOut)------------------------------------------------ main function
main :: IO ()main= runNAGUI "interchange" mainUI------------------------------------------------ UIs
mainUI, preUI :: UI InterChange
mainUI = putWAinUI actE 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"]

-- widget actions

C コード:コントロールの働き変更
module Main whereactE, actE1, actE2 :: WidAct InterChange
actE = actE1 >> actE2
actE1 = widgetSPA takeE1 change
actE2 = widgetSPA takeE2 appendTchange :: WidAct InterChangechange = do a <- left getText
b <- right getText
right (setText a)left (setText b)
appendT :: WidAct InterChange
appendT = do a <- left getText
right (updateWA (a++))------------------------------------------------ take functions
takeE1, takeE2 :: InterChange -> InOut
takeE1 = fst
takeE2 = snd

import Graphics．UI.WX
import NeooeyUI
import NeooeyWidAct------------------------------------------------ widget types
type Input = Button ()type Output = StaticText ()type Bye = (Output, Input)------------------------------------------------ main function
main :: IO ()main= runNAGUI "Bye!" mainUI------------------------------------------------ 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"]-- widget actions
actWA :: WidAct Bye
actWA = widgetSPA takeB byeWA

byeWA :: WidAct Bye
byeWA = do left $ setText "Goodbye"
right $ setWidAct closef
right $ setText "Close"------------------------------------------------ take functions
takeT :: Bye -> Output
takeT = fst
takeB :: Bye -> Input
takeB = snd