注意: このページの情報は古くなっています。調べものの参考にはなる可能性がありますが、別途最新の情報を収集してください。
What is Lyee?
アメリカ合衆国(第
6138268 号)、日本(第 3133343 号)その他の国において特許権が設定されています。
Style of Lyee
先の 2 社によれば、Lyee はソフトウェア開発方法論ないし開発技法であるとされています。しかしこの言葉の意味を、近年広く知られるようになった
Extreme Programming (XP) などの「方法論」における用法と同じような意味で理解しようとすると、大きな困惑にぶつかるでしょう。
XP は、ソフトウェア開発現場においてその効果が確認されてきた数々のプラクティスを、すべて究極まで実践することで、ソフトウェア開発をよりよく進めていこうとする運動です。XP は、開発者たちが積んできた実経験に根ざす、一種のプロジェクト運用方法であると言えます。プログラマの具体的なコーディングやソフトウェアの組み立て方そのものが、XP によって何らかの方向性を与えられるわけではありません。
Lyee は違います。Lyee は、ソフトウェア開発のあり方を開発者のレベルでよりよくしていこうという技術ではありません。むしろ逆に、ソフトウェア開発者たちに対して、一定のソフトウェア開発スタイルを強制するものです。Lyee は、あらゆるソフトウェアを、ある一定の構造に即して組み立てなければならない、と規定しています。つまり、Lyee はソフトウェア開発のための巨大なフレームワークであると考えられます。
すべてのソフトウェアを単一の枠組で説明しようとするのですから、それぞれのソフトウェアをプログラミングというレベルで見たときには、その効率性は当然犠牲になっていることになります。個々のプログラムごとに最適なデータ構造とアルゴリズムを探求してきたプログラマの創意工夫も、そこでは発揮される余地がありません。
失ったものはあまりに大きいように思われます。Lyee の利点はどこにあるのでしょうか。
Advantage of Lyee 1: Atomic Programming
Lyee の利点とされるのは、どのようなソフトウェアであってもその構造が一定のものになること、そのものです。開発現場においては、プログラマは退社し、プロジェクトは移ろい、ユーザの要求は常に変化します。このような状況においては、ソフトウェアのその時どきの最適解を求めること(プログラマにそれを求めさせること)よりも、必要とされる機能をすばやく実装することの方が重要である、というのが Lyee の考え方なのです。
そのために Lyee では、プログラミングをまず最小の単位まで分割します。この最小単位を Lyee では「単語」と呼びますが、これは一般のプログラミングにおいて「変数」や「オブジェクト」に該当するものです。プログラムとは、あるデータを入力し、ある処理を加え、得られたデータを出力する、という一連の作業の集合体であると言えますが、その入出力の最小単位が、Lyee ではソフトウェア構造の基本単位になります。
例えば、以下の擬似コードを見てください。
varA = varB + varC; //varA は varB と varC とを加えた値を受け取る
このごく簡単なコードでは、varA という単語が varB と varC とを加算したものであることが表明されています。つまり varB と varC という「入力」と、加算演算子 + という「処理」を使って、varA という「出力」について説明したことになります。
これが Lyee プログラムの最小単位の例です。どうということはない変数への代入文のようですが、これは代入というより、あるオブジェクト(Lyee の用語法では単語)の「意味内容を表明する式」であると理解できます。
さて、これで varA については説明ができましたが、では varA の式の右辺に現れた varB や varC とはなにものでしょう。そうです、これら varB や varC もまた、何らかの入力を受け取り、何らかの処理をして、自分の値としているはずです。よって、これらのオブジェクトについても varA と同様に、「意味内容を表明する式」が必要です。例えば、以下のコードのようになるでしょう。
varB = varD * varD; //varB は varD の 2 乗の値を受け取る
varD = external_function(); //varD は外部関数の戻り値を受け取る
varC = user_input; //varC はユーザからの入力を受け取る
ブラックボックス化された外部関数や、ユーザからの入力値についてこれ以上意味内容を定義する必要はありませんから、ここに例としてあげたオブジェクトたちの「意味内容を表明する式」の記述はこれで完了となります。
このように細かく分割してオブジェクトの意味を定義していくことで、ソフトウェアの各部分の独立性が高まり、機能の実装や仕様変更に迅速に対応できるというのが、Lyee の主張する利点の一つです。
補足: オブジェクトの定義が細分化されることは、反面、あるオブジェクトのデータソースが最終的にどこにあるのかという情報を追跡しにくくするという欠点も生むように思われます。また、上記の定義手順を考えれば当然推測できることですが、varX = function(varX) というような、自らの定義中に自らの値の取得が含まれるような式を定義することは、Lyee では認めていません(厳密には、定義すること自体は可能ですが、varX には値が決して格納されなくなります)。先ほども触れたとおり、その意味で Lyee で扱われる式は「代入」ではなく、「意味内容の表明」「定義」であると理解すべきでしょう。
Advantage of Lyee 2: Non-Programming Programming
Lyee では生成式(前のセクションで「意味内容の表明」と説明したものを、Lyee の用語では「生成式」と呼びます)を書く、ということについては分かりました。では、その生成式をどこに、どのように書いたらよいのでしょう。
例えば、次のように書いてみます(前のセクションの説明で varD と varC の式に使った external_function と user_input については、それ以上の定義が不要だという意味では同等の、数値リテラルに置き換えました)。
main()
{
varA = varB + varC;
varB = varD * varD;
varC = 1000;
varD = 10;
}
オブジェクトの識別子が宣言されていないという点を別にしても、この C 言語風のプログラムが正しく動かないことは明らかです。varA の式で varB や varC の値を参照しているのに、それらのオブジェクトはまだ入力が行われていません。varB の式も同様です。これでは正常に動作するはずがありません。
すでにお分かりのように、この場合は次のようにすれば期待したものを得られます(識別子の宣言のことはとりあえず忘れてください)。式の順序を並べ替えました。
main()
{
varC = 1000;
varD = 10;
varB = varD * varD;
varA = varB + varC;
}
このように、オブジェクトの式を考えた後で、通常のプログラミングでは、それらの式を実行する(評価する)順序を検討しなければなりません。それぞれの式(混乱させるようですが、この文脈では通常の代入式を思い浮かべてください)が評価されるときには、その右辺に置かれるオブジェクトの値はすでに有効でなければならないからです。
Lyee の第二の利点は、プログラミングに必然的に伴うこの順序決定作業から、ソフトウェア開発者を解放することです。考えてもみてください。先ほどの例で、varC の定義がリテラルの 1000 から varD の 2 倍の値へと変更されたとします。するとプログラマは、式の変更だけではなく、varC と varD の評価順序を入れ替えるという作業まで忘れずに行わなければいけません。この例では単純に 2 つの行を入れ替えるだけで済みますが、離れた箇所の複数のデータが複雑に関連し合っている場合にはどうでしょう。プロジェクトの進行につれて、そのような変更がいったい何回必要になるでしょうか。
順序性を常に考えなければならない通常のプログラミングでは、大規模なソフトウェア開発の現場において、保守のコストが非常に大きなものになります。この束縛からソフトウェア開発者を解放することができれば、保守工程のコストダウンに大きく寄与するものがあるでしょう。Lyee は、一定の構造にしたがってプログラムを動かすことで、それを実現します。
補足: 「順序性の束縛からの解放」という言葉は Lyee のコミュニティでよく聞かれるフレーズですが、プログラマはその束縛から解放される代償として、プログラムの構造を定式化してプログラマに創意工夫を認めない、という Lyee の束縛の下へと入っていくことになります。このこと一つを取ってみても、本質的に Lyee はプログラマのためのものではないことが分かります。
Lazy Evaluation
すべてのオブジェクトの「なりたち」がしっかりと定義されていれば、それら定義が並ぶ順序については心配しなくてもよい。そのような状況を、Lyee はもたらしてくれます。しかし、どのようにしてそれを実現しているのでしょうか。
ここで、いったん Lyee からは離れて、次のように考えてみましょう。プログラム内に存在するコードの順序性を不問にするとなれば、式が評価されるときに評価対象のオブジェクトが有効であることは期待できません。しかし、もともとプログラムを原子的に分割した段階で、「式」というのは評価対象としての「代入式」ではなく、オブジェクトの意味内容を説明するだけの「定義」として考えられるべきでした。ゆえに、式が現れたからといってそれをすぐに評価するのが間違いなのです。
プログラミングの世界には「遅延評価(Lazy Evaluation)」という考え方があります。評価(処理、演算)は時に大きなコストとなるので、本当にその値が必要とされるときまで評価を留保する、という効率化のための手法です。遅延評価では、その都度データをコピーしたり演算を行って結果を保存する代わりに、データの参照先や演算の構造を保持しておきます。そして、本当に必要になったらその構造に基いて評価を行うのです。シーンは異なりますが、この手法を適用してみましょう。いわば、「式の遅延評価」です。
サンプルとして、スクリプティング言語の
Python を使います。Python では、文字列を
eval 関数の引数として与えることで、文字列を Python コードとして評価した結果を返すことができますので、これを利用しましょう。
# 定義
varA = "eval(varB) + eval(varC)"
varB = "eval(varD) * eval(varD)"
varC = "1000"
varD = "10"
# 定義を評価して値を取得
# 出力結果:
# varA: 1100
# varB: 100
# varC: 1000
# varD: 10
print "varA: %4d" % eval(varA)
print "varB: %4d" % eval(varB)
print "varC: %4d" % eval(varC)
print "varD: %4d" % eval(varD)
raise SystemExit
上記の内容を Python インタプリタに渡すと、定義部分の順序がどうであれ、期待した値が最後の
print 文によって出力されます(Python の print 文は C 言語の printf 関数とよく似ていて、文字列中のプレースホルダに式の値を埋めこんで出力してくれます)。
上記のサンプルでは、定義を文字列として変数に代入しました(少しややこしいかもしれませんが、定義中に出てくる識別子は、再帰的に再評価されるように eval() で括る必要があります)。定義の評価は留保しておきます。そして、必要のあったときにはじめて、 eval 関数でその内容を評価します。この例では定義を単純に文字列とし、また一度評価した定義も読み捨てていますが、実際には一度評価した結果は値として保存して次回以降の再計算のコストを避けたり、定義の内容は木構造に保存したりすることになるでしょう。
「式の遅延評価」という手法を取ることによって、正確な定義がありさえすればその順序如何によらず正しい答を導き出すことができる、という Lyee の発想を試すことができました。とはいえ、ちょっと考えれば分かることですが、これはそれほど目新しいアイデアであるとは言えません。開発者なら誰しも使い親しんでいる make ツールがよい例です。make ツールは、makefile にオブジェクトの「定義」を書いておくことによって、まさにその定義の順序如何を問わず、プログラマの求めるものを最新の状態で作成してくれます。
Lyee は、式の遅延評価を行ったり、make ツールの行ってきたバックトラック法による生成作業を、プログラムの内部で、変数に対して行う技術なのでしょうか。そろそろ話を Lyee に戻しましょう。
Details of Lyee
意外なことに(また残念なことに)、Lyee には遅延評価という考え方はありません。Lyee は、順序性の排除という目標を、単純な繰り返し作業によって処理しています。
Lyee が未評価の式を評価する(Lyee の用語ではこれを「値を生成する」と言います)仕組みは、おおむね次のとおりです。
- 式を評価する前に、式の右辺(入力)側のオブジェクトが評価済みかどうかを調べる。
- 右辺がすべて評価済みであれば、処理中の式も評価できるので、評価を行い、結果をオブジェクトの値として格納する。
- 右辺に評価済みでないオブジェクトがあれば、処理中の式は評価できないので、何もしないで処理を抜ける。
- 定義されたすべての式について上記を繰り返した後、もう一度はじめから同じことを繰り返す。
最初は必ずどこかに評価可能な式(ユーザの入力値やリテラルを定義とするオブジェクト)が見つかるので、2 回目はそのオブジェクトを参照する式が評価でき、次はさらにそれを参照する式が評価できます。これを何回も繰り返していき、最終的に定義を一巡する間に 1 つも新規に評価された式がなくなった時点で、ループを終了させます(新規に評価された式がなければ、それ以降ループを続けても新たに評価可能になる式は出てこないため)。
再び、以前のセクションで使った例で説明しましょう。4 つの式が、以下の順番で並んでいるとします。Lyee では、すべての式が並んでいる順に吟味されていきます。
varA = varB + varC;
varB = varD * varD;
varC = 1000;
varD = 10;
1 回目: 初回のループでは、varA と varB の式は評価できません。右辺に置かれているオブジェクトが評価済みではないためです。そのためこれらの式は素通りし、varC の式に進みます。これは、右辺がリテラルなので評価できます。続いて varD の式に進み、これも同様に評価できるので処理を進めます。
2 回目: 初回のループが終わると、再び先頭に戻って吟味をはじめます。今回も varA は評価できません。右辺のうち、varB がまだ評価済みではないためです。一方、varD がすでに評価済みなので、varB は今回は評価できます。varC と varD は 1 回目ですでに評価済みですので、素通りします。
3 回目: さらにもう一度繰り返します。ようやく今回、varA を評価できます(varB は直前の、varC は初回のループで評価済み)。他のオブジェクトの式はすべて評価済みになっているため、素通りします。
4 回目: もう一度繰り返します。3 回目のループで評価された varA を参照している式があるかもしれないという判断からです。実際にはそのような式はなく、すべての式が素通りされることになります。
以上が、この場合の処理の流れです。式を評価するためのループの再起は、Lyee のフレームワークが自律的に行ってくれます。一巡する間に 1 つも式が評価されないことを最後に確認するため、ループの回数が 1 回多くなっていることに注意してください。式の並び順を工夫してループの回数を減らすことは可能ですが、Lyee の仕組みでは、まともなプログラムなら最低 2 回はループ本体が実行されることになります。
なお、Lyee の具体的な実装のレベルでは、それぞれの式は評価済みチェックを含む一連の関数呼び出しとして記述され、値を受け取る領域はオブジェクトにラップされます。概念的には、次のコードのようになります。
//varA を定義する式のコードイメージ
void expression_for_varA()
{
//varA がすでに評価済みなら素通り
if(varA.isAlreadyValid())
return;
//参照するオブジェクトが 1 つでも評価済みでないなら、varA も評価できない
if(!varB.isAlreadyValid() || !varC.isAlreadyValid())
return;
//ここまで来たなら、varA は評価可能
else
varA = varB + varC;
}
オブジェクトと式とを結びつける方法としては、GUI システムにおけるイベント処理と同じような仕組みが候補となるでしょう。すなわち、関数ポインタや C# の delegate を使った委譲、Java における匿名内部クラスの利用が考えられます。
Lyee による開発で作られるプログラムは、このようなオブジェクトごとの式と、それらの式を順に実行する機能(これを Lyee の用語では「連鎖関数」と呼びます)の集合になります。すべてのオブジェクトが未評価である状態から、繰り返しループを実行することで徐々にオブジェクト間にデータの連関を形成していきます。そして最終的にループが止まったとき、ほしいデータが取り出せる状態になっている、という寸法です。もしそこでデータが正常に取得できない場合は、定義のどこかが間違っていたということになります。
Lyee が採用した手法について、ある程度詳しく見てきました。このスタイルは、Lyee のもともとの着想を実装したものとして、スマートなやり方なのでしょうか? どうもそうではないようです。特に、Lyee が構造的にループを多用しているという事実は、ソフトウェアの全体的なパフォーマンスを低下させている可能性があり、プログラマにとっては悩ましいところです。
Glossary
それとなく示してきたつもりですが、Lyee で使われる語彙は、私たちが親しんできた一般のプログラミングにおいて使われる用語とはかなり違っています。中には、同様の言葉をまったく異なった意味で使っていることがあるので、なおさら注意する必要があります。
このような状況は、Lyee が一般化し普及していく過程で改善されていくことだと思われますが、それまでの間は、使い慣れた言葉で Lyee の用語を読みかえることが必要になるでしょう。この記事では、これまでに次のような Lyee の用語が登場しました。
- 単語: 英語表記では word になるが、CPU の語長のことではなく、一般のプログラミングでいう変数にあたる。最小のデータ項目として、数値、文字列などの値を保持する。
- 生成式: 見た目は一般のプログラミングにおける代入に近いが、評価対象となる代入ではなく、「単語」についてそのデータ構成方法を記述した式。なお、この記事で説明のため使ってきた「意味内容の表明」や「定義」は Lyee の用語ではない。
- 生成: 生成式を評価した結果単語にデータが格納されることを、生成と呼ぶ。「まだ値が生成されていない」など。
- 連鎖関数: 登録された生成式を順に吟味し、評価可能であれば評価するための仕組み。ループ構造をもつ。実際の Lyee 構造では、いくつかのレベルの連鎖関数が多層的な制御構造を形成する、つまり多重ループ構造になる(sigh...)。
この記事には登場しませんでしたが、Lyee のコミュニティでよく聞かれる用語には次のようなものがあります。
- 空: 「から」ではなく「くう」と読み、単語に値が生成されていない状態を指す。イメージとしては、ポインタがどこも指していない状態(null ポインタ)のようなもの。単語は最初、必ず空の状態にある。
- 生成条件: 条件によって生成式の内容に差をつけたい場合、その条件のことを生成条件と呼ぶ。
他にもいろいろと Lyee 独自の用語がありますが、Lyee の構造を説明するために既存の語彙が使われていないのは、決してよい状況とは思われません。Lyee のコミュニティがよりオープン化されることを望みたいものです。
Notes
この記事で紹介してきたのは、Lyee の非常に基本的なアイデアの部分です。実際の Lyee のフレームワークは非常に大きなもので、プロジェクトを管理するデータベース、データベースに接続して単語ごとに式を記述するための統合開発環境、式以外の部分を自動作成してソースコードを出力するためのジェネレータ、Lyee プログラミングのためのライブラリなどを含む、不可分一体の開発環境になっています。これらを用いた Lyee によるソフトウェア開発に興味があれば、この記事の冒頭で紹介した 2 社に問い合わせてください。
Resources
Copyright © 2003-2010 EdelSoft, All Rights Reserved.
Contact: