コンピュータプログラミングにおいて、機能指向プログラミング(FOP)または機能指向ソフトウェア開発(FOSD)は、ソフトウェアプロダクトライン(SPL)におけるプログラム生成およびプログラムの段階的開発のためのプログラミングパラダイムである。

FOSD は、1980 年代後半のネットワーク プロトコルと拡張可能なデータベース システムにおけるレイヤー ベースの設計と抽象化レベルから生まれました。[ 1 ] プログラムはレイヤーのスタックでした。各レイヤーは、以前に構成されたレイヤーに機能を追加し、レイヤーの異なる組み合わせによって異なるプログラムが生成されました。当然のことながら、このような設計を表現するための簡潔な言語が必要でした。初等代数がその要件を満たしました。各レイヤーは、既存のプログラムに新しいコードを追加して新しいプログラムを生成する関数 (プログラム変換) であり、プログラムの設計は式、つまり変換 (レイヤー) の組み合わせによってモデル化されました。左の図は、レイヤー i、j、h の積み重ねを示しています (h は一番下、i は一番上)。これらの設計を表現するために、代数表記 i(j(h))、i•j•h、i+j+h が使用されてきました。
時間の経過とともに、レイヤーは機能と等価になり、機能はプログラム機能の増分である。プログラム設計と生成のパラダイムは、関係クエリ最適化の派生であると認識され、クエリ評価プログラムは関係代数式として定義され、クエリ最適化は式の最適化であった。[ 2 ]ソフトウェア製品ラインは、各プログラムが固有の機能構成によって定義されるプログラムのファミリーである。FOSDはその後、機能ベースのプログラム生成をサポートする機能モジュール性、ツール、分析、および設計技術の研究へと発展した。
FOSD研究の第2世代は、通信分野で始まった機能間の相互作用に関するものでした。後に、機能指向プログラミングという用語が作られました。[ 3 ]この研究は、レイヤー間の相互作用を明らかにしました。相互作用により、他の機能と組み合わせる際に機能が適応する必要があります。
第 3 世代の研究は、すべてのプログラムには複数の表現 (ソース、メイク ファイル、ドキュメントなど) があり、プログラムに機能を追加する際には、すべての表現が一貫しているように各表現を詳細化する必要があるという事実に焦点を当てています。さらに、一部の表現は他の表現から生成 (または派生) される可能性があります。以下のセクションでは、FOSD の最新 3 世代、すなわちGenVoca [ 1 ] AHEAD [ 4 ]およびFOMDD [ 5 ] [ 6 ]の数学的側面について説明し、FOSD ツールを使用して開発された製品ラインへのリンクを提供します。また、FOSD のすべての世代に適用される 4 つの追加結果として、FOSD メタモデル、FOSD プログラム キューブ、および FOSD 機能の相互作用があります。
GenVoca(GenesisとAvocaの合成語) [ 1 ]は、プロダクトラインのプログラムを定義するための構成パラダイムです。基本プログラムは、値と呼ばれる0項関数または変換です。
f -- 機能fを備えた基本プログラム h -- 機能hを備えた基本プログラム
そして、フィーチャーとは、プログラムを詳細化(変更、拡張、洗練)する単項関数/変換のことです。
i + x -- プログラム x に機能 i を追加する j + x -- プログラム x に機能 j を追加する
ここで、+ は関数合成を表します。プログラムの設計は名前付き式で表されます。例:
p 1 = j + f -- プログラム p 1は機能 j と f を持つ p 2 = j + h -- プログラム p 2は機能 j と h を持つ p 3 = i + j + h -- プログラム p 3は機能 i、j、h を持つ
GenVocaにおけるドメインまたはソフトウェア製品ラインのモデルは、基本プログラムと機能の集合体です(メタモデルとプログラムキューブを参照)。作成可能なプログラム(式)によって製品ラインが定義されます。式の最適化はプログラム設計の最適化であり、式の評価はプログラム生成です。
GenVocaの機能は当初、Cプリプロセッサ()技術を用いて実装されました。ミックスインレイヤー#ifdef feature ... #endifと呼ばれるより高度な技術は、これらの機能をオブジェクト指向のコラボレーションベースの設計に結びつけることを示しました。
アプリケーション設計のための代数的階層方程式( AHEAD ) [ 4 ] は、GenVoca を 2 つの方法で一般化しました。まず、GenVoca 値の内部構造をタプルとして明らかにしました。すべてのプログラムには、ソース、ドキュメント、バイトコード、メイクファイルなど、複数の表現があります。GenVoca 値は、プログラム表現のタプルです。たとえば、パーサーの製品ラインでは、基本パーサー f は、文法 g f、Java ソース s f、およびドキュメント d fによって定義されます。パーサー f は、タプル f=[g f、 s f、 d f ] でモデル化されます。各プログラム表現はサブ表現を持つことができ、それらも再帰的にサブ表現を持つことができます。一般に、GenVoca 値は、特定のプログラムの表現の階層を定義するネストされたタプルのタプルです。
例。終端表現がファイルであると仮定します。AHEAD では、文法 g f は単一の BNF ファイルに対応し、ソース s f はJava ファイルのタプル [c 1 ...c n ]に対応し、ドキュメント d fは HTML ファイルのタプル [h 1 ...h k ] です。GenVoca 値 (ネストされたタプル) は有向グラフとして表現できます。パーサー f のグラフは右の図に示されています。矢印は射影、つまりタプルからそのコンポーネントの 1 つへのマッピングを表します。AHEAD はタプルをファイル ディレクトリとして実装しているため、f はファイル g fとサブディレクトリ s fおよび d fを含むディレクトリです。同様に、ディレクトリ s fにはファイル c 1 ...c nが含まれ、ディレクトリ df にはファイル h 1 ...h kが含まれます。
第二に、AHEADでは、機能をデルタと呼ばれる単項関数のネストされたタプルとして表現します。デルタは、プログラムの改良(意味を保持する変換)、拡張(意味を拡張する変換)、または相互作用(意味を変更する変換)のいずれかです。FOSDではこれらのすべてが出現するため、中立的な用語である「デルタ」を使用して、これらの可能性すべてを表します。
例として、機能 j が文法をΔ g jだけ拡張し(新しいルールとトークンが追加される)、ソースコードをΔ s jだけ拡張し(新しいクラスとメンバーが追加され、既存のメソッドが変更される)、ドキュメントをΔ d jだけ拡張するとします。機能 j のデルタのタプルは j=[ Δ g j , Δ s j , Δ d j ] でモデル化され、これをデルタタプルと呼びます。デルタタプルの要素自体もデルタタプルにすることができます。例: Δ s jは、機能 j によって s fの各クラスに加えられる変更を表します。つまり、Δ s j =[ Δ c 1 ... Δ c n ] です。プログラムの表現は、ネストされたベクトル加算によって再帰的に計算されます。パーサー p 2 (GenVoca 式は j+f )の表現は次のとおりです。
p 2 = j + f -- GenVoca 式 = [ Δ g j , Δ s j , Δ d j ] + [g f , s f , df ] -- 置換 = [ Δ g j +g f , Δ s j +s f , Δ d j +d f ] -- タプルを要素ごとに合成する
つまり、p 2の文法は基本文法とその拡張 ( Δ g j +g f ) で構成され、p 2のソースは基本ソースとその拡張 ( Δ s j +s f ) で構成され、以下同様です。デルタタプルの要素自体がデルタタプルになる可能性があるため、合成は再帰的に行われます。たとえば、Δ s j +s f = [ Δ c 1 ... Δ c n ]+[c 1 ...c n ]=[ Δ c 1 +c 1 ... Δ c n +c n ] となります。まとめると、GenVoca の値はプログラムアーティファクトのネストされたタプルであり、フィーチャはネストされたデルタタプルです。ここで、+ はベクトル加算によってそれらを再帰的に合成します。これが AHEAD の本質です。
上記で提示したアイデアは、FOSDの2つの原則を具体的に示しています。均一性の原則は、すべてのプログラム成果物が同じように扱われ、変更されることを示しています(これは、上記の異なる成果物タイプの差分によって証明されています)。拡張性の原則は、すべての抽象化レベルが均一に扱われることを示しています(これは、上記のタプルの階層的なネスト構造を生み出します)。
AHEAD のオリジナルの実装は、AHEAD ツール スイートと Jak 言語であり、均一性と拡張性の原則の両方を示しています。次世代ツールには、CIDE [ 10 ] と FeatureHouse [ 11 ]があります。
機能指向モデル駆動設計( FOMDD ) [ 5 ] [ 6 ]は、AHEAD の考え方とモデル駆動設計( MDD ) (別名モデル駆動アーキテクチャ( MDA )) を組み合わせたものです。AHEAD 関数は、プログラムに機能が追加されたときのプログラム成果物の同期更新を捉えます。しかし、プログラム成果物の間には、派生を表す他の機能的な関係があります。たとえば、文法 g fとそのパーサーソース s fの関係は、コンパイラ コンパイラ ツール (javacc など) によって定義されます。同様に、Java ソース s fとそのバイトコード b fの関係は、javac コンパイラによって定義されます。これらの関係は、可換図で表されます。オブジェクトはプログラム表現、下向きの矢印は派生、水平方向の矢印はデルタです。右の図は、プログラム p 3 = i+j+h = [g 3 ,s 3 ,b 3 ]の可換図を示しています。
可換図の基本的な特性は、2 つのオブジェクト間のすべてのパスが等価であることです。たとえば、パーサー h (左上のオブジェクト) の文法 g hからパーサー p 3 (右図の右下のオブジェクト)のバイトコード b 3を導出する 1 つの方法は、バイトコード b hを導出して b 3 に精緻化することです。一方、別の方法は g h をg 3に精緻化してから b 3を導出することです。ここで、+ はデルタ合成を表し、() は関数またはツールの適用を表します。
があるパーサー h の文法 g hからパーサー p 3のバイトコード b 3を導出する可能性のあるパス。各パスは、開始オブジェクト (g f ) からターゲットオブジェクト (b 3 ) を生成するメタプログラムを表します。潜在的な最適化があります。可換図の各矢印をたどるにはコストがかかります。可換図内の 2 つのオブジェクト間の最も安価な (つまり最短の) パスは測地線であり、これは、与えられたオブジェクトからターゲットオブジェクトを生成する最も効率的なメタプログラムを表します。
移動図は、少なくとも 2 つの理由で重要です。(1) アーティファクト (測地線など) の生成を最適化できる可能性があり、(2) 開始オブジェクトからターゲットオブジェクトを構築するさまざまな方法を指定します。[ 5 ] [ 12 ]図を通るパスはツールチェーンに対応します。FOMDD モデルが一貫しているためには、あるオブジェクトを別のオブジェクトにマッピングするすべてのツールチェーンが実際に同等の結果を生成することが証明 (またはテストによって実証) される必要があります。そうでない場合は、1 つ以上のツールにバグがあるか、FOMDD モデルが間違っているかのどちらかです。