第57回勉強会でLTをしてきました
遅刻しましたごめんなさい。(>_<)
そのため、勉強会の最後にトリを務める形でLTをしてきました。
会場のリアクションはいい具合に冷たくて、心地良い死にたさでした。
プログラムを公開して欲しいとのお声を頂きましたので、主要部分をこのエントリで解説し、プロジェクトもダウンロードできる形にしました。
>> CLRH57.zip <<
今回のLTのテーマは、3つの言語の相互運用でした。
特にCLRとDLRの連携が目玉になります。
サンプルとして用意したプロジェクトは、
- GUI を C# で作り
- 機能を F# で実装し
- このアプリケーション用のShellを、IronPythonで作る
といった感じです。
C#とF#で作られたテキストエディタを、IronPythonで外部からインタラクティブに操作できるようになっています。具体的には、このアプリケーションのインスタンスをIronPythonに渡してやり、そのインスタンスを操作します。
まずC#のコードを載せますが*1、こちらはほとんど何もしていません。
handlerという変数に、F#で実装されたクラスのインスタンスが格納されています。
public partial class Form1 : Form { CoreModule.Handler handler = null; public Form1() { InitializeComponent(); this.handler = new CoreModule.Handler( this.textBox ); } private void button_Python_Click(object sender, EventArgs e) { var scriptEditor = new ScriptEditor() { Handler = this.handler }; scriptEditor.Show(); } private void button_Open_Click(object sender, EventArgs e) { this.handler.Open(); } private void button_Clear_Click(object sender, EventArgs e) { this.handler.Clear(); } }
次のF#がメインになります。
全機能の実装、およびIronPythonとの連携を担当しているというのに、何もしていないC#のコードよりも短くなっています。
[<CompiledName("Handler")>] type ``F# is beautiful !!``(textBox : TextBox) as self = let python = Python.CreateEngine() let scope = python.CreateScope() do scope.SetVariable("handler", ObjectHandle(self)) member self.Write(x) = if x <> null then textBox.Text <- textBox.Text + string x member self.WriteLine(x) = if x <> null then self.Write(string x + "\r\n") member self.Clear() = textBox.Clear() member self.Load(path) = self.Clear() ; File.ReadAllText(path) |> self.Write member self.Open() = use dialog = new OpenFileDialog() if dialog.ShowDialog() = DialogResult.OK then self.Load(dialog.FileName) member self.EvalPython(expr : string) = self.WriteLine <| try python.Execute(expr, scope) with e -> e :> obj
画面(エディタ)に出力するWrite, WriteLineメソッドや、内容をクリアするClearメソッド、ファイルの読み込みを行うOpenメソッドなどが定義されています。これらアプリケーションとしての機能の実装とは別に、IronPythonとの連携も行っています。これが最後のEvalPythonメソッドです。任意の式をPythonエンジンに渡して評価し、その結果を画面に出力するようになっています。
これだとただのPython評価器ですので、IronPythonに全知全能を与えてやらねばなりません。
コンストラクタを見てみますと、以下のようになっています。
do scope.SetVariable("handler", ObjectHandle(self))
これは何をやっているかというと、「今動いているアプリケーションのインスタンス(self)」を"handler"という名前で参照できるように!しています。こうすることによって、IronPythonはアプリケーションを操作する権利を得ます。
ただ単にインスタンスをセットしようとすると、値がディープコピーされてしまいます。そうなると今実際に動作しているアプリケーションとは別のインスタンスを操作することになりますので、画面には何も反映されません。そこで、参照を渡すためにSystem.Runtime.Remoting.ObjectHandleクラスでラップしています。
こうするとIronPythonで好き放題にできます。
例えば、
for i in range(0, 10) : handler.Write("F#!")
とやればF#の出力メソッドが呼ばれて、画面に10個出力されますし、
handler.Open()
とすればファイルを選択するダイアログが開きます。
以上です。こんな感じ。
もちろんDLRはこのような使い方しかできないわけではありませんが、発想次第で以上のようなオレオレShellを作ることもできます。自分のシステム/アプリケーションに対話環境を用意したい場合は、1から自作するよりもIronPythonなどを利用したほうが随分と楽です。同様にして、IronRubyやIronSchemeを利用することもできます。
*1:主要部分だけ載せます