言語の組み合わせ
.NET の言語とパラダイムの概要
Joel Pobar
この記事の内容 : :
- オブジェクト指向プログラミング
- 関数型プログラミング
- 動的プログラミング
- .NET 言語の新しいパラダイム
|
この記事は次のテクノロジを使用しています:
C#、C++、F#、IronPython、IronRuby、Visual Basic
|

コンテンツ
Windows オペレーティング システム は、プログラマにとってより良いプラットフォームというわけではありませんでした。何百もの言語が、Win32® API を使用して直接的に、または CLR を介して、Windows® をターゲットとしています。この記事をお読みいただいている間にも、そのような新しい言語が構築されているでしょう。
CLR の目標の 1 つは、同一ランタイム内でさまざまな言語および API をシームレスに統合することにより、互換性のある単一のエコシステムで無数の花を咲かせることでした。この目標に向けた活動はこれまで成功を収めてきました。新しい言語があらゆる場所で生まれています。動的言語である Ruby (IronRuby)、Python (IronPython)、および PHP (Phalanger) のマイクロソフトによる実装は、今では Microsoft® .NET Framework 言語の優良市民です。F# と呼ばれる関数型言語も、最近になって仲間入りしました。読者の皆さんは、これらの新しい言語および言語パラダイムの話題に触れる機会が多いと思いますが、その意味を全体的に把握することが難しいと思われているかもしれません。
今回の記事では、新しい言語パラダイムの概要および重要な実装のいくつかの側面について説明していますので、さまざまな疑問を解消していただけるでしょう。また、新しい言語や従来のパラダイムが将来の C# 言語および Visual Basic® 言語の製品の設計および実装に与える影響について考えるヒントになると思います。
新しい設計で表現される変更の種類を理解するには、従来の言語 (C# や Visual Basic など) と新しい言語 (F# や IronPython、IronRuby など) との相違を理解する必要があります。ここでは、3 つの主要なテーマについて説明します。オブジェクト指向プログラミング (C# および Visual Basic が両方とも活用されるモデル)、関数型プログラミング (F#)、および動的プログラミング (IronPython や IronRuby) です。これらのパラダイムについて説明し、それぞれの特徴となっている機能を紹介していきます。
オブジェクト指向
OO (オブジェクト指向) は、おそらく最も親しみのあるパラダイムでしょう。OO の世界を表現するには、オブジェクトとオブジェクト間のやり取りを関連付けるコントラクトを使用します。OO では、型のコントラクト、ポリモーフィズム、きめ細かい可視性などの機能を利用して、非常に有用な再利用とカプセル化が行われます。
OO は通常は静的型システムを使用することから、静的に型指定された言語と見なされます。つまり、プログラムによって作成および使用されるすべての型がコンパイル時にチェックされるため、たとえば Moo メソッドが存在しない場合は、アヒル型のオブジェクトでそのメソッドを呼び出す必要はありません。型と型の間のコントラクトに破損または誤用が存在している場合、それらはコードを実行する前にコンパイラによって検出されるため、理論上はランタイム エラーが低減されることになります。
ただし、OO に固有の欠点もあります。タイプ セーフであるため、プログラマが必要なテスト インフラストラクチャを作成せずに、エラーの検出をコンパイラに頼ってしまうということが考えられます。また、OO ではプログラマが独自のコントラクトを事前定義することが推奨されますが、これはどちらかと言えばラピッド プロトタイピング環境や複数メンバによるプログラミング環境に反しています。大規模なソフトウェア プロジェクトにおいては、コンポーネント間のコントラクト (通常は個別のチームが所有) が開発サイクルの全期間にわたって進化を続けることが多く、コントラクトのコンシューマはコードを絶えず更新しなければなりません。以上の問題が影響し、OO 言語は複雑で冗長になる可能性があります。
関数型プログラミング
関数型プログラミングは、プログラムの計算を数学的関数の評価として扱います。2 つの数字の加算が 1 つの関数である場合、2 つの入力値が 5 と 10 であれば出力値は 15 になります。関数型言語のプログラマが問題を解決する場合は、ターゲットの問題をなるべく小さいまとまりに分解し、それぞれを関数で表現した後で、目的の出力が生成されるようにこれらの関数を組み合わせます。
関数型プログラミングでは、通常、状態 (変数やオブジェクトなど) や状態の変化を使用しません。これに対して、OO では状態 (オブジェクト) を作成して操作することが主な目的であるという点を考えると、この 2 つはほとんど正反対の手法です。状態を使用しない関数型プログラムは、より厳密で、正確性が高く、立証可能なものになる傾向があります。演算と演算の間でプログラムの内部状態 (変数など) が変化した後で、1 つ以上の演算から予期しない結果が生成される場合がありますが、関数型ではこのような予測できない副作用が発生しないからです。オブジェクト指向言語および動的言語では、エラーを増加させるような予測できない副作用を低減するために、状態のカプセル化と保護をプログラマが行う必要があります。
状態を使用せず、副作用もない、純粋な形の関数型言語を構築できます。ただし、最も一般的な関数型言語は状態を操作する機能を持っており、外部 API、オペレーティング システム、および他の言語との相互運用性を促進するためにその機能が使用されます。また、プログラムによっては、状態の情報をある程度は使用して問題を表現する必要があるため、そういうケースに対応するためにもこの機能が有用です。
関数型言語に共通して利用できるさまざまな機能がありますが、私が個人的に最も気に入っている機能は、引数として別の関数を受け取って結果として関数を返す高次関数です。高次関数は、コードを再利用するための非常に強力な機能です。このメカニズムはほとんどの言語に組み込まれていますが、関数型言語の世界では優良市民に昇格しています。
高次関数の能力を示す好例として、従来の Map API があります。Map API は、データの配列またはリストの各要素ごとに関数を実行 (マップ) します。まず最初に、よく使われる Web スクリプト言語である JavaScript で記述してみましょう。次の配列を使用します。
|
var data = [1, 2, 3, 4, 5];
|
配列の各要素でコードを実行する関数を記述すると、次のようになります。
|
function map (func, array)
{
var returnData = new Array(array.length);
for (i = 0; i< array.length; i++)
{
returnData[i] = func(array[i]);
}
return returnData;
}
|
数を 1 ずつインクリメントする関数を次に示します。
|
function increment(element)
{
return element++;
}
|
では、これを使用してみましょう。
|
print (map (incremenent, data));
output: [2,3,4,5,6]
|
この Map API サンプルは単純化されていますが、高次関数の機能は十分に示されています。map は関数および配列を受け取り、各配列要素で関数を実行して、結果を新しい配列に返します。
返される結果用に新しい配列を作成するので状態の変化がないこと、および関数の結果が以前の結果に依存しないことが前提となれば、このマップ例を複数のプロセッサ コアに拡張することができます。その場合は、配列を半分に分割して 2 つの結果配列を結合します。さらに、複数のマシンに拡張することもできます。それには、複数のマシン間でデータの分割およびシリアル化を行い、コード関数をマシンに渡して、単一のホストでシリアル化された結果を結合します。状態管理などの自動実行に関する問題について心配する必要はありません。作業の単純化を実現する非常に強力な機能です。
最初に Microsoft Research の Don Syme 氏が開発した F# プログラミング言語は、.NET Framework における関数型プログラミングに対するマイクロソフトからの最終的な回答です。リリース時には、Visual Studio
® で完全にサポートされる言語になる予定です。F# と Visual Studio の統合パッケージは、両方とも
go.microsoft.com/fwlink/?LinkId=112376 からダウンロードできます。また、Robert Pickering 氏の著書である『Foundations of F#』は、この言語の理解に役立つすばらしい入門書です。
それでは、F# を使用して関数型言語の世界を探検してみましょう。先ほどの Map 例をここでは F# で使用します。
|
// increment function
let increment x = x + 1
// data
let data = [1 .. 5]
// map (func, myList)
let map func myList =
{ for x in myList -> func x }
print_any (map increment data)
|
いかがでしょう。非常に簡潔です。最初の 1 行で定義しているのは、引数として x を受け取る単純な increment 関数です。この引数は x + 1 に評価されます。次に、整数の 1 ~ 5 を含む不変リストとしてデータを定義します。map 関数は引数として 1 つの関数および 1 つのリストを受け取ります。従来のプログラミングを少々活用してリストをループし、func 関数の引数を実行して、リスト内の現在の要素に渡します。続いて、print_any 関数が increment 関数およびデータ リストを引数として使用して map 関数を実行し、結果を画面に出力します。
型はどこにあるのでしょう。この例には含まれていません。実際には、変数 (data) は System.Int32 のリストとして実質的に型指定されていますが、コンパイラにそれを通知する必要はありませんでした。コンパイラが単に型の推定機能を使用して推定します。コンパイラの仕事が少し増え、プログラマの仕事が少し減ります。実に喜ばしいことですね。コードを書き直す場合は、リスト包含と呼ばれる機能を使用し、前のコードの一部をたった 1 行で簡単に書き直すことができます。
|
print_any { for x in 1..5 -> x + 1 }
|
F# のもう 1 つの優れた機能はパターン マッチングです。
|
let booleanToString x =
match x with false -> "False" | _ -> "True"
|
この関数はブール型を受け取り、false またはそれ以外の値であるかを照合し、適切な文字列を返します。booleanToString 関数に文字列を渡すとどうなるでしょう。ここでもコンパイラは複雑な処理を行い、x 引数の型をブール型として定義しています。コンパイラは、x が関数でどのように使用されるかによって推測します (この例ではブール型に対してのみ一致する)。
パターン マッチングを使用すると、強力な関数ディスパッチ メカニズムを構築し、OO 世界での仮想メソッド ディスパッチを簡単に再現することなどもできます。仮想メソッドは、レシーバ (仮想メソッドが呼び出されるオブジェクト) およびメソッドのパラメータに基づいて動作を変える必要がある場合になると実際に働き出します。ビジター パターンはこのような状況での補助の目的で設計されたものですが、F# ではパターン マッチングにその役割が包含されているため、基本的に必要ありません。
ユーザー定義の型がサポートされており、通常は、レコード (OO 世界のクラスに似ている) または組 (通常は順序付けられたシーケンスである型) を介して公開されます。ここで、ユーザー定義によるプレーヤーとサッカー チームのレコードを示します。
|
type player =
{ firstName : string;
lastName : string;
}
type soccerTeam =
{ name : string;
members : player list;
location : string;
}
|
遅延評価は、もう 1 つの一般的な関数型言語の機能です。これは、関数型プログラミングには明示的な副作用がないという仮定に基づいて能力を発揮します。遅延評価はコンパイラに依存するだけでなく、必要な時点まで計算を遅延するために式の評価順序を決めるプログラマの能力にも依存します。コンパイラとプログラマのいずれにとっても、遅延評価の技術は、不要な計算を回避することで非常に厳密なパフォーマンスの最適化を行える手法です。また、無限の (非常に大きな) データセットでの計算を扱う場合の手法としても有用です。実際に、Microsoft Research の Applied Games リサーチ グループでは、この手法を F# で使用してテラバイトの Xbox LIVE® ログ データを解析しています。
F# における遅延評価を次に示します。
|
let lazyTwoTimesTwo = lazy (2 * 2)
let actualValue = Lazy.force lazyTwoTimesTwo
|
このコードを実行した場合、lazyTwoTimesTwo は実行時には 2 * 2 を実行する関数への軽量なポインタとして存在しているだけです。この関数の実行を強制した場合のみ、実際に結果を取得できます。
関数型プログラミング モデルは理解するまで時間がかかりますが (30 ~ 40 時間が必要)、プログラマにとっては有益なテクニックです。より少ないコードで、エラーを低減しながら、問題を解決できます。このパラダイムでは、意図しない副作用を最小限に抑えることでコードの安全性が保持され、積極的な最適化を実行して動作の高速化を維持します。さらに、簡潔さは拡張性を高めることにもつながります。マップ コードをざっと眺めてから、多数のマシンすべてにこのコードを簡単に配布する方法について考えてみると、拡張性の高さを感じていただけると思います。
ここまで、型の推定、高次関数、パターン マッチング、ユーザー定義型など、関数型プログラミングのより優れた機能のいくつかを見てきました。次は、動的言語について説明します。
動的言語
動的プログラミングは、1990 年代の中ごろ、Web ベース アプリケーションが大流行したときに人気が高まりました。サーバーで対話型の動的要素を Web サイトに追加するために非常によく使用された動的言語は、Perl と PHP の 2 つです。それ以降も動的言語は支持され、ある意味では影響力を増して、アジャイルのような新しいソフトウェア開発手法への技術的回答を提供しています。
動的プログラミング言語は、プログラム コードをコンパイルおよび実行する方法において静的言語 (C# および Visual Basic .NET は両方とも静的オブジェクト指向言語) と異なります。動的言語の場合は、通常、コードのコンパイルはプログラムが実際に動作する実行時まで遅延されます。動的言語以外の言語の場合、コードは単に解釈されます。このようにコンパイルが遅延されると、実行中のオブジェクトの拡張やプログラマによる型システムの自由な変更の許可などの動作をプログラム コードに含めて、実行することができます。
処理を終了直前まで遅延するジャスト イン タイム (JIT: just-in-time) コンパイルおよび実行の手法があるおかげで、動的言語は強力であり、多様な機能セットを備えます。そのため、動的言語はしばしば遅延バインディングと呼ばれます。すべてのアクション (メソッドの呼び出しやプロパティの取得など) がコンパイル時ではなく実行時に行われるからです。動的言語のいくつかの機能を紹介するために、IronPython および IronRuby について詳しく見てみましょう。
IronPython は、マイクロソフトによる Python プログラミング言語の .NET Framework 用の実装です。これは Jim Hugunin 氏の発案によるものです。彼が最初のプロトタイプで作業を始めたのは、自分の車庫だそうです。半年後、Hugunin 氏はマイクロソフトに入社し、IronPython コンパイラを構築しました。IronPython のソース全体およびインストーラは、
go.microsoft.com/fwlink/?LinkId=112377 からダウンロードできます。
IronRuby は、マイクロソフトによるプログラミング言語 Ruby の実装に付けられた名前です。John Lam 氏は、RubyCLR と呼ばれる最初の Ruby と CLR のブリッジを記述した活動的な Ruby ハッカーです。彼はやがてマイクロソフトに入社し、IronRuby チームで働きました。IronRuby ソース全体は ironruby.net からダウンロードできます。
ここで、IronPython を開始し、Read Eval Print ループ (REPL) と呼ばれる優れた機能を見てみましょう。REPL ループは、コード入力を一度に 1 行ずつ受け取って実行するように設計されています。これは、Python のような動的言語の遅延バインディングの性質をよく表しています。図 1 は、IronPython コマンド ライン REPL ループを示しています。
図 1 IronPython REPL ループ (画像を拡大するには、ここをクリックします)
REPL ループは Python コードを入力として受け入れ、即座に実行します。この例では、画面に "Hello, World!" と出力するように要求しています。プログラマが Enter キーを押すとすぐに、IronPython コンパイラがステートメントを中間言語 (IL) にコンパイルして実行します。すべての処理が実行時に行われます。
REPL ループは、言語の全面的なスコーピング ルールに従います。この例で、私は式の 1 + 2 に変数の "a" を割り当て、その変数をすぐ次の行で参照できました。REPL 入力のコンパイルは処理中に行われます。.dll ファイルまたは .exe ファイルを生成する必要はありません。IronPython コンパイラは、簡易コード生成 (LCG) と呼ばれる CLR のメモリ内コード生成機能を活用するだけです。
動的言語の型システムは、非常に柔軟で緩和されています。一般に、クラスやインターフェイスなどのオブジェクト指向の概念がサポートされていますが、次のような違いがあります。静的言語で行われるようなコントラクトの厳密な実施は通常は行われません。より緩やかな型システムの既存のサンプルは、一般的にサポートされている動的言語の型システム機能であるアヒル型定義の概念に見られます。アヒル型定義では、ある型がアヒルのように見え、アヒルのように鳴く場合、コンパイラはその型をアヒルと見なします。図 2 では、Ruby を使用してこの概念を実行しています。このコードは IronRuby で実行できますので、試してみてください。

Figure 2 Ruby のアヒル型定義
|
class Duck
def Quack()
puts "Quack!"
end
end
class Cow
def Quack()
puts "Cow's dont quack, they Mooo!"
end
end
def QuackQuack(duck)
duck.Quack()
end
animal = Duck.new()
QuackQuack(animal)
animal = Cow.new()
QuackQuack(animal)
|
図 2 には、Duck と Cow という 2 つのクラス定義、QuackQuack と呼ばれるメソッド、およびこれらのオブジェクトをインスタンス化して QuackQuack メソッドに渡すためのコードが示されています。このコードが読み込まれて実行されると、次のような出力が生成されます。
|
Quack!
Cow's don't quack, they Mooo!
|
QuackQuack メソッドは Quack メソッドをその引数で呼び出しますが、引数の型が何であるかではなく、引数が Quack メソッドを保持しているかどうかを識別します。Quack メソッドの検索と呼び出しはコンパイル時ではなく実行時に受け取る引数で行われるため、言語は引数がアヒルに見えるということをアヒルが鳴くのを聞く前に確認できます。
静的な型定義の世界でアヒル型定義にたとえることができるのはインターフェイスです。静的な世界で型がインターフェイスを実装する場合、静的型システムは強制的に完全なインターフェイス構造に従います。したがって、どの型の実装でもインターフェイス メソッドの呼び出しが保証されます。アヒル型定義は自身を型の構造の一部 (処理されるメソッド名) に関連付けるだけです。遅延バインディングでこれを行います。コンパイラはオブジェクトでルックアップ メソッド (通常は文字列ベースのメソッド名を使用) を実行し、検索が成功した場合は、次にそのメソッドの呼び出しを実行するだけです。
IronPython や IronRuby などの動的言語における優れた機能は、言語の機能だけではありません。動的言語では API をホストすることもできます。つまり、言語やコンパイラだけでなく、固有のアプリケーションで REPL 対話型の機能もホストできます。写真を操作するツールのように大きくて複雑なアプリケーションを構築し、自動化やスクリプトの機能をユーザーに公開するとします。IronPython または IronRuby をホストし、アプリケーションをプログラムで操作するための Python スクリプトまたは Ruby スクリプトの作成をユーザーに許可することができます。API をホストする言語に対して自分のアプリケーションの内部オブジェクトおよび API を公開し、REPL ループをホストするテキスト ウィンドウを作成するだけで、そのアプリケーションでのスクリプトが有効になります。
コマンド ライン駆動型 REPL ループのような機能を持つことにより、非常に優れた生産性という副産物が生まれます。保存、コンパイル、実行というサイクルなしで、作業プロトタイプを簡単に用意できます。インターフェイスやクラス定義コントラクトの外観に配慮せずに、頭に浮かんだソリューションから迅速にコードを作成するには、実行中に型を変更および拡張することをお勧めします。適切なソリューションを整えた後で、動的言語プロトタイプから、より厳格で安全な静的言語 (C# など) の世界へと移ることができます。
柔軟な型システムは、Web 上の HTML のような非構造化データの使用にも適しており、時間の経過と共にバージョンが変更されることの多い厳密なインターフェイスに対する変更対応の接続点として簡単に使用できます。Web ベースのコンピューティングなどの新しいアプリケーション パラダイムと共に使用すると、動的言語は不確定要素に対処するための優れたソリューションを提供できます。
API をホストすることにより、最も優れた拡張ポイントをアプリケーションに提供できます。Microsoft Excel® や VBScript のマクロに似た操作性がアプリケーションに備わります。
安全かつ有用
言語を選択する場合は、安全性と有用性も重要な考慮事項です。言語の有用性は、言語の使いやすさや生産性に関するメリットなどで測定されます。言語の安全性を測る要素には、言語の型の安全性 (型が無効なキャストを許可するかどうか)、プログラムのバリア (配列の境界を越えてスタック内の他のメモリに損傷を与える可能性があるかどうか)、セキュリティ機能などによって測定されます。有用性を優先させるために安全性を犠牲にすることも、その逆もあります。
興味深いことに、C#、C++、Visual Basic、.NET、Python などは有用性に優れていますが、安全性の面ではやや劣ります。C# などのマネージ コード言語は従来のネイティブ コードよりも安全ですが、完璧というわけではありません。これらの言語は状態の操作に重点を置いているため、一般的に未知の副作用があり、同時実行の世界での動作で問題が発生するおそれがあります。
シングルコア マシンで問題なく動作する C#/Visual Basic .NET マルチスレッド プログラムが、マルチコアでの実行時に、プログラマにとって未知の副作用が現れる競合条件が原因でクラッシュすることが考えられます。また、これらの言語はタイプ セーフの優れた汎用レベルを保持しますが、知識に乏しいプログラマがオブジェクトを void* にキャストするなどの事態を防ぐことはできません。
Haskell (学術分野で使用される純粋な関数型言語) は、安全ですが有用性が高くない言語です。コード ブロックが関数であり、変数が変化しないため、Haskell には副作用がありません。また、プログラマは自分の型および目的を事前に定義しておく必要があり、これらの理由から安全な言語だと見なされています。残念ながら、Haskell は (忍者プログラマでさえも) 理解するのが非常に困難な言語であり、一般的に自分で書いていない Haskell コードを読むのは難しい作業です。
私の意見としては、Haskell よりも F# が有用です。その理由は、.NET Framework との統合、選択的な手続き構文、およびオブジェクト指向の世界で使われるオブジェクトを扱う機能があることです。明らかに、次世代の言語では、これまでに説明した各言語の最も優れた機能を取り入れるのが賢明な方法でしょう。厳密で強制可能な静的型システムから、より動的で柔軟な型へと実行中に移行でき、関数型プログラミングの機能を持ち、同時実行の問題を回避できる言語があるとしたら、だれもが使いたがるに違いありません。
さいわいにも、C# から F#、そして IronPython へと簡単に移行することができ、共通型システム (CTS: Common Type System) も存在します。さらに、C# および Visual Basic .NET の設計者は、動的パラダイムおよび関数型パラダイムの最も優れた機能を導入し、これらを優良市民と呼べる優れた機能として従来の言語に統合しています。ここで、そのような機能の一部について説明します。上述したさまざまな言語および各パラダイムとどのように関連しているかについて確認してください。
LINQ
LINQ は、.NET Framework の新しい機能です。.NET で任意の言語から API として直接使用するか、または LINQ 言語拡張のセットを介して実際に言語機能として公開できます。T-SQL 言語の場合と同じように、LINQ を使用し、クエリ演算子のセットを公開することにより、ストレージに依存した方法でデータのクエリを実行できます。.NET Framework 3.5 では、Visual C#® 2008 および Visual Basic 2008 が両方とも LINQ を優良市民としてサポートします。
Visual C# 2008 を使用した従来の LINQ クエリは次のようになっています。
|
List<Customer> customers = new List<Customer>();
// ... add some customers
var q = from c in customers
where c.Country == "Australia"
select c;
|
このコードは、顧客のリストを調べ、"Australia" に等しい Country プロパティを含む Customer オブジェクトを探して、それらを新しいリストに追加します。最初に目を引くのは "var" キーワードではないでしょうか。これは、Visual C# 2008 に追加された新しいキーワードであり、型の推定を実行するようにコンパイラに指示します。次に、from、where、および select の各キーワードを指定して動作する C# の LINQ 拡張が続きます。これらは F# 言語の場合と同様に顧客のリストの包含 (リスト包含) を形成します。これは、動的言語で一般的に備わっている機能です。
この LINQ クエリの実行で新しいリストが作成されるため、元の顧客リストが変更されることはなく、実質的に副作用はありません。どの並列プログラムにおいても副作用は悩みの種ですが、このプログラミングのスタイルにおいては副作用を回避できるだけでなく、読みやすさと全体的な有用性が維持されます。また、副作用がないという前提で、このクエリを自由にスケール アウトできます。
実際、それがまさに LINQ に対する拡張である Parallel LINQ (PLINQ) の目的です。Parallel LINQ は、Parallel FX ライブラリ (
go.microsoft.com/fwlink/?LinkId=112368) からコミュニティ テクノロジのプレビューとして使用できます。LINQ の並列実行は複数の CPU コアにおいてクエリを行い、追加のプログラミング コストなしで速度がほぼ線形に増加します。これは、関数型プログラミングのセクションで示した Map API の例によく似ています。全体的に見ると、C# および Visual Basic への LINQ の統合により、関数型の特徴を伴って機能が拡張されるため、これらの言語の表現力が大幅に向上し、冗長性が低減されました。
Visual Basic 9.0 におけるインライン XML
インライン XML に対しては、Visual Basic に追加される最も優れた機能であるという評価も一部にあります。私も、どちらかと言えばその意見に賛成です。これは関数型パラダイムや動的パラダイムから生まれたものには見えませんが、インライン XML の実装は動的言語の世界から直接的に派生するものです。図 3 は Visual Basic における XML インラインを示しています。

Figure 3 実行中のインライン XML
|
Dim rss = From cust In customers _
Where cust.Country = "USA" _
Select <item>
<title><%= cust.Name %></title>
<link><%= cust.WebSite %></link>
<pubDate><%= cust.BirthDate %></pubDate>
</item>
Dim rssFeed = <?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel></channel></rss>
rssFeed...<channel>(0).ReplaceAll(rss)
rssFeed.Save("customers.xml")
|
このコードでは、Visual Basic の LINQ to XML 実装を使用し、米国に住んでいる顧客を取得するクエリを顧客のリストで実行して、RSS フィードの基本となる XML 要素のリストを返します。次に、RSS フィード スキームを定義し、かなり興味深い Visual Basic の "..." 演算子で XML ドキュメントを調べ、"<channel>" 要素を見つけます。続いて、その <channel> 要素で ReplaceAll を呼び出し、先に作成した XML 要素をすべて含めます。さて、一息つきましょう。
この例では、... 演算子とインライン XML 要素の使用を組み合わせたところが興味深い点です。おわかりだと思いますが、Visual Basic は XML の <channel> 要素を認識しません。実際、この要素を <foo> に変更してもコンパイルは正常に行われます。つまり、Visual Basic は要素を認識していないため、呼び出しサイトで正確性を静的にチェックするのは不可能です。代わりに、動的パラダイムで見たような動的チェックを行うコードを実行時にコンパイルします。動的言語の概念である遅延バインディングを活用しています。
IL を簡単に理解できるという人々に向けて、アセンブリを分解し、"...<channel>" ステートメントのコードを見てみましょう。
|
ldloc.1
ldstr "channel"
ldstr ""
call class [System.Xml.Linq]System.Xml.Linq.XName
[System.Xml.Linq]System.Xml.Linq.XName::Get(string,
string)
callvirt instance class Generic.IEnumerable'1<XElement>
XContainer::Descendants(System.Xml.Linq.XName)
|
ldloc.1 は rssFeed 変数を読み込み、channel 文字列を読み込んでから、XName を返す XName.Get メソッドを呼び出します。この XName の結果が Descendants メソッドに渡されると、このメソッドは、<channel> 名に一致する XML ドキュメントの子孫をすべて返します。このメカニズムは、動的言語戦略からそのまま借りてきたもので、従来の遅延バインディングされた動的ディスパッチです。
まだ続きがあります。RSS スキーマ定義を Visual Studio にドロップし、"Imports" 参照をスキーマに追加した場合、Visual Basic .NET コンパイラはこれを取得し、解析して、このスキーマに従っている XML に対して完全な IntelliSense® を有効にします。したがって、RSS XML スキーマをドロップすると、"...<channel>" ステートメントによって IntelliSense ウィンドウが即座に表示され、スキーマ定義ごとに使用可能な子孫オプションが示されます。実にすばらしいですね!
その他
C# や Visual Basic のような言語が他のパラダイムおよび言語の最良の要素をどのように組み合わせるかについて、一部の例を紹介しました。こうしている間も、言語設計のチームは、将来のプログラミングの課題に対応できる新しい機能を言語に追加しているでしょう。
プログラミング パラダイムの高度なカテゴリについて説明してきましたが、まだすべてを紹介したわけではありません。宣言型プログラミングは、XML を記述子として使用して対象の性質を説明する手法です。これは、Windows Presentation Foundation や Windows Workflow Foundation などのフレームワークによる開発に多大な影響を与えました。データの記述と操作にも、その優れた能力を発揮します。組織が何年もかけて蓄積するデータ資産は絶えず増大していくものですが、宣言型言語はそういったデータ資産をプログラム的に活用する分野で将来の推進力になるでしょう。ここまでの説明では触れていませんが、論理プログラミング (コンピュータ プログラミングのための論理の活用) もあります。これは、Prolog などの言語により、人工知能やデータ マイニングの分野で広く使用されています。
このようなあらゆる言語およびパラダイムから新しい機能が追加されることにより、皆さんに恩恵がもたらされます。お気に入りの言語の能力が他の言語との相互交流で高まることにより、プログラマは柔軟性および生産性の向上を達成できます。また、別の言語が持つ優れた機能を使用したい場合も問題ありません。お気に入りのランタイムおよびフレームワークで相互運用性シナリオがサポートされます。.NET プログラマにとっては嬉しいことばかりですね。
Joel Pobar は、前職でマイクロソフトの CLR チームのプログラム マネージャを務めていました。現在はオーストラリアのゴールドコーストで、コンパイラや言語など、さまざまな楽しいものに取り組んでいます。.NET に関する彼の最新の話題については、
callvirt.net/blog を参照してください。