web-dev-qa-db-ja.com

関数型プログラミングのためのソフトウェア工学方法論はありますか?

今日のソフトウェアエンジニアリングは、オブジェクト指向プログラミングと「自然な」オブジェクト指向の世界観に完全に焦点を当てています。いくつかの手順とユースケース図やクラス図などの多くの(UML)アーティファクトを使用して、ドメインモデルをクラスモデルに変換する方法を説明する詳細な方法論があります。多くのプログラマーはこのアプローチを内部化し、オブジェクト指向アプリケーションをゼロから設計する方法について良い考えを持っています。

新しい誇大広告は関数型プログラミングであり、多くの本やチュートリアルで教えられています。しかし、機能的なソフトウェアエンジニアリングはどうでしょうか。 LISPとClojureについて読んでいると、2つの興味深い声明が出てきました。

  1. 機能プログラムは、トップダウンではなくボトムアップで開発されることがよくあります( 'On LISP'、Paul Graham)

  2. 機能プログラマーは、オブジェクト指向プログラマーがオブジェクト/クラスを使用するマップを使用します(「Clojure for Java Programmers」、Rich Hickleyによる講演)。

それでは、機能的なアプリケーション、つまりLISPまたはClojureの体系的な(モデルベースの?)デザインの方法論は何ですか?一般的な手順は何ですか、どのアーティファクトを使用しますか、それらを問​​題空間からソリューション空間にどのようにマッピングしますか?

200
Thorsten

ソフトウェア工学の人々が関数型プログラミングをまだ発見していないことを神に感謝します。以下にいくつかの類似点を示します。

  • 多くのOO「デザインパターン」は高階関数としてキャプチャされます。たとえば、Visitorパターンは機能世界では「フォールド」として知られています(または、先の尖った理論家の場合関数型言語では、データ型はほとんどがツリーまたはタプルであり、すべてのツリー型には自然なカタモフィズムが関連付けられています。

    これらの高階関数には、多くの場合、プログラミングの特定の法則、別名「自由定理」が伴います。

  • 関数型プログラマーは、OOプログラマーよりもはるかに少ない図を使用します。OO図で代わりに) types、または "signatures"で、これは "モジュールタイプ"と考える必要があります。Haskellには "type classes"もあり、これはインターフェイスタイプに少し似ています。

    型を使用する関数型プログラマは、一般に「型を正しく取得すると、コードは実際にそれ自体を記述する」と考えます。

    すべての関数型言語が明示的な型を使用するわけではありませんが、Scheme/LISP/Clojureを学習するための優れた本である How To Design Programs bookは、型に密接に関連する「データ記述」に大きく依存しています。

それでは、機能的なアプリケーション、つまりLISPまたはClojureの体系的な(モデルベースの?)デザインの方法論は何ですか?

データの抽象化に基づいた設計方法はすべて有効です。言語に明示的な型がある場合、これは簡単だと思いますが、それがなくても動作します。関数型プログラミングに簡単に適応できる抽象データ型の設計方法に関する優れた本は、Barbara LiskovとJohn Guttagによるプログラム開発の抽象化と仕様です。 、firstエディション。リスコフはその仕事の一部でチューリング賞を受賞しました。

LISPに固有のもう1つの設計方法は、作業している問題の領域でどの言語拡張が役立つかを決定し、衛生マクロを使用してこれらの構造を言語に追加することです。この種の設計について読むのに適した場所は、Matthew Flattの記事 Craating Languages in Racket です。記事はペイウォールの背後にある可能性があります。 「ドメイン固有の埋め込み言語」という用語を検索して、この種の設計に関するより一般的な資料を見つけることもできます。 Matthew Flattがカバーする範囲を超えた特定のアドバイスと例については、おそらくGrahamの On LISP またはおそらく ANSI Common LISP .

一般的な手順は何ですか、どのアーティファクトを使用しますか?

一般的な手順:

  1. プログラム内のデータとその操作を特定し、このデータを表す抽象データ型を定義します。

  2. 一般的なアクションまたは計算のパターンを特定し、それらを高階関数またはマクロとして表現します。リファクタリングの一環としてこのステップを踏むことを期待してください。

  3. 型付き関数型言語を使用している場合は、型チェッカーを早期かつ頻繁に使用してください。 LISPまたはClojureを使用している場合、ベストプラクティスは、ユニットテストを含む関数コントラクトを最初に記述することです。これは、テスト駆動型の開発です。また、プラットフォームに移植されたQuickCheckのバージョンを使用することもできます。この場合、このバージョンは ClojureCheck と呼ばれます。高次関数を使用するコードのランダムテストを構築するための非常に強力なライブラリです。

161
Norman Ramsey

Clojureについては、古き良きリレーショナルモデリングに戻ることをお勧めします。 Out of the Tarpit は心に強く訴える読書です。

44
cgrand

個人的には、OO開発からのすべての通常の良い慣行は、関数型プログラミングにも適用されます-関数型の世界観を考慮に入れるためのわずかな微調整があります。方法論の観点からは、根本的に異なることを本当に行う必要があります。

私の経験は、近年、JavaからClojureに移行したことから来ています。

いくつかの例:

  • ビジネスドメイン/データモデルを理解する-オブジェクトモデルを設計するか、ネストされたマップを使用して機能的なデータ構造を作成するかは、同様に重要です。いくつかの点で、FPはデータモデルを関数/プロセスとは別に考えることをお勧めしますが、まだ両方を行う必要があるため、より簡単になります。

  • 設計におけるサービスの方向-典型的なサービスは実際には何らかの副作用を伴う単なる機能であるため、FPの観点から実際に非常にうまく機能します。 LISPの世界で時々支持されているソフトウェア開発の見方は、実際には別の装いでサービス指向のAPI設計原則にすぎません。

  • テスト駆動開発-FP言語で動作します。実際には、純粋な関数はセットアップを必要とせずに明確で反復可能なテストを作成するのに非常に適しているためステートフル環境。データの整合性をチェックするために個別のテストを構築することもできます(たとえば、このマップには、OO言語でクラス定義により、コンパイル時にこれが強制されます)。

  • プロトタイピング/反復-FPでも同様に機能します。ツール/ DSLの構築とREPLでの使用が非常に得意であれば、ユーザーとライブでプロトタイプを作成することさえできます。

38
mikera

オブジェクト指向プログラミングは、データと動作を密接に結び付けます。関数型プログラミングは2つを分離します。したがって、クラス図はありませんが、データ構造はあり、特に代数データ型があります。これらのタイプは、構築によって不可能な値を排除するなど、ドメインに非常に厳密に一致するように記述できます。

そのため、本やそれに関連する本はありませんが、言葉が示すように、不可能な価値を代表できないものにするための確立されたアプローチがあります。

そうすることで、代わりに特定のタイプのデータを関数として表現することについて、また逆に、特定の関数をデータタイプの集合として表現することについて、一連の選択を行うことができます。 。

そして、それを考えると、何らかの種類の代数-を確立するように、あなたのadtsの上に関数を書きます。すなわち、これらの関数を保持する固定法則があります。いくつかはmaybe等であるかもしれません-複数のアプリケーションの後も同じです。いくつかは連想的です。推移的なものなど.

これで、行儀の良い法則に従って構成する関数を含むドメインができました。シンプルな組み込みDSL!

ああ、そして与えられたプロパティ、あなたはもちろんそれらの自動化されたランダム化されたテストを書くことができます(ala QuickCheck)..そしてそれはほんの始まりです。

13
sclv

オブジェクト指向設計は、ソフトウェアエンジニアリングとは異なります。ソフトウェアエンジニアリングは、要件から稼働中のシステムに移行するプロセス全体を時間通りに低欠陥率で処理する必要があります。関数型プログラミングはオブジェクト指向とは異なる場合がありますが、要件、高レベルで詳細な設計、検証とテスト、ソフトウェアメトリック、推定、およびその他すべての「ソフトウェアエンジニアリング」を排除しません。

さらに、機能プログラムはモジュール性やその他の構造を示します。詳細な設計は、その構造の概念の観点から表現する必要があります。

7
Kaz

別の投稿に対する私の答えをご覧ください。

Clojureは懸念の分離をどのようにアプローチしますか?

FPアプローチを使用する大規模なアプリケーションを構築する方法に関する主題について、さらに書く必要があることに同意します。

5
drcode

1つのアプローチは、選択した関数型プログラミング言語内で内部DSLを作成することです。 「モデル」は、DSLで表現されたビジネスルールのセットです。

5
James Kingsbery

これは単純で単純なものと考えられるかもしれませんが、「デザインレシピ」(Felleisen et al。の本で提唱されているプログラミングに適用される問題解決への体系的なアプローチ HtDP )はあなたに近いと思います探しているようです。

ここに、いくつかのリンク:

http://www.northeastern.edu/magazine/0301/programming.html

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.86.8371

3

私は最近この本を見つけました: 機能的で反応的なドメインモデリング

あなたの質問と完全に一致していると思います。

本の説明から:

機能的およびリアクティブドメインモデリングは、純粋な機能の観点からドメインモデルを考える方法と、それらを構成してより大きな抽象化を構築する方法を教えます。関数型プログラミングの基本から始め、複雑なドメインモデルを実装するために知っておく必要のある高度な概念とパターンに徐々に進みます。この本は、代数的データ型、型クラスベースの設計、副作用の分離のような高度なFPパターンが、読みやすさと検証可能性のためにモデルを構成する方法を示しています。

2
elviejo79

行動駆動開発は、ClojureとSBCLの両方でコードを迅速に開発するのに適していることがわかりました。関数型言語でBDDを活用することの本当の利点は、手続き型言語を使用するときよりもはるかに細かい粒度のユニットテストを書く傾向があることです。なぜなら、問題をより小さな機能のチャンクに分解するより良い仕事をするからです。

2
Marc

リチャード・バード教授とオックスフォード大学(英国)のプログラミング代数グループに関連付けられた「プログラム計算」/「計算による設計」スタイルがありますが、これを方法論と見なすのはあまりにも遠すぎないと思います。

個人的には、AoPグループが制作した作品が好きですが、自分でこの方法でデザインを練習する規律はありません。しかし、それは私の欠点であり、プログラムの計算の1つではありません。

1
stephen tetley

関数型プログラムの設計レシピが必要な場合は、Haskell's Preludeなどの標準関数ライブラリをご覧ください。 FPでは、パターンは通常、高次の手順(関数を操作する関数)自体によってキャプチャされます。そのため、パターンが見られる場合、多くの場合、そのパターンをキャプチャするために高次関数が作成されます。

良い例がfmapです。この関数は、引数として関数を取り、2番目の引数のすべての「要素」に適用します。 Functor型クラスの一部であるため、Functorのインスタンス(リスト、グラフなど)は、この関数の2番目の引数として渡すことができます。 2番目の引数のすべての要素に関数を適用する一般的な動作をキャプチャします。

1
nightski