関数型プログラミングのソフトウェアエンジニアリング手法はありますか?[閉まっている]


203

今日教えられているソフトウェア工学は、完全にオブジェクト指向プログラミングと「自然な」オブジェクト指向の世界観に焦点を当てています。ドメインモデルをいくつかのステップとユースケース図やクラス図のような多くの(UML)アーティファクトを持つクラスモデルに変換する方法を説明する詳細な方法があります。多くのプログラマーがこのアプローチを取り入れており、オブジェクト指向アプリケーションをゼロから設計する方法について優れたアイデアを持っています。

新しい誇大広告は関数型プログラミングであり、多くの本やチュートリアルで教えられています。しかし、機能的なソフトウェア工学についてはどうでしょうか?LispとClojureについて読んでいるとき、私は2つの興味深いステートメントについて思いつきました。

  1. 関数型プログラムは、多くの場合、トップダウンではなくボトムアップで開発されます(「On Lisp」、Paul Graham)

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

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


3
私はここにコメントがあります:多くのプログラムはトップダウン方式で書かれています。関数型言語でのソフトウェア開発のプロセスへの実用的な説明は、「Concurrent Cleanの関数型プログラミング」という本で与えられています(言語自体は非常に学術的で、ただし)。
Artyom Shalkhakov、2011

4
1. Parnasは、ほとんどのプログラムはボトムアップでなければならず、次にトップダウンのように見せかけられるべきであると主張しているので、これらのアプローチは混合されるべきであり、正しい答えはありません。
GabrielŠčerbák、2011

2
2.オブジェクトは、カプセル化された構造化状態に応じて動作を提供します。FPでは、すべての状態と構造が明示的であり、動作(関数)は構造から分離されています。したがって、データモデリングではオブジェクトのマップを使用しますが、アプリケーションを設計する場合、オブジェクトを関数で置き換えることはできません。FPはパイプラインを通じて生成および評価される大きな式であり、OOPはモデルを作成してオブジェクト間でメッセージを送信することです。
GabrielŠčerbák、2011

1
私はいつか戻って、関連する質問を:「どのように1ないモデルのClojureでのリレーショナルデータベースからデータを?」 stackoverflow.com/questions/3067261/...
サンディープ

4
Heheは、SICP講義で、Hal Abelsonは冗談半分で「有名な方法論がある、またはソフトウェアエンジニアリングと呼ばれる神話を言うべきか...」と言います。複雑な図と要件を作成してから構築します。それらを備えたシステム;それらの人々はあまりプログラムされていません。」私は「Javaスクール」の出身です。何世代にもわたってUMLやアーティファクトなどを教えてきましたが、その少しは上手くいきますが、あまりにも多くの計画と計画(しゃれが意図的)は、役に立つよりも有害です。ソフトウェアは実際にコードを書くまで続きます。
lfborjas

回答:


165

ソフトウェアエンジニアが関数型プログラミングをまだ発見していないことを神に感謝します。ここにいくつかの類似点があります:

  • 多くのオブジェクト指向の「設計パターン」は、高次関数として取り込まれます。たとえば、Visitorパターンは機能的な世界では「フォールド」(または先のとがった頭の理論家であれば「カタモルフィズム」)として知られています。関数型言語では、データ型は主にツリーまたはタプルであり、すべてのツリー型にはそれに関連する自然な異型があります。

    これらの高階関数には、しばしば「自由定理」とも呼ばれるプログラミングの特定の法則が付属しています。

  • 関数型プログラマーは、OOプログラマーよりもダイアグラムをあまり使用しません。OOダイアグラムで表現されるものの多くは、代わりにタイプまたは「シグニチャー」で表現されます。これらは「モジュールタイプ」と考える必要があります。Haskellには「型クラス」もあり、これはインターフェース型に少し似ています。

    型を使用する関数型プログラマーは、一般に、「型を正しく設定すると、コードは実際には自分で書き込む」と考えます。

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

では、LispやClojureなどの機能的アプリケーションの体系的な(モデルベースの)設計の方法論とは何でしょうか。

データの抽象化に基づく設計方法はどれもうまく機能します。言語に明示的な型があると、これは簡単だと思いますが、それがなくても機能します。簡単に関数型プログラミングに適合された抽象データ型の設計方法に関する良い本、あるプログラム開発における抽象化と仕様バーバラ・リスコフとジョン・ガタッグによる最初の版は。リスコフはその仕事の一部でチューリング賞を受賞しました。

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

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

一般的な手順:

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

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

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


2
IMO訪問者はフォールドではありません-フォールドは訪問者のサブセットです。複数のディスパッチは、(直接)foldによってキャプチャされません。
Michael Ekstrand

6
@Michael-実際には、さまざまな種類の高次カタモルフィズムを使用して複数のディスパッチを非常にきれいにキャプチャできます。Jeremy Gibbonsの仕事はこれを探す1つの場所ですが、私は一般的にデータ型ジェネリックプログラミングに取り組むことをお勧めします-私は特にcompos論文が好きです。
sclv

6
私は、機能設計を説明するために使用されるダイアグラムの頻度がはるかに低いことに同意し、それは残念だと思います。多くのHOFを使用する場合、シーケンス図に相当するものを表現することは確かに困難です。しかし、私は写真で機能的なデザインを説明する方法の空間がよりよく探索されていたことを願っています。(仕様として)UMLが嫌いなのと同じくらい、UML(スケッチとして)はJavaで非常に便利であり、同等の方法を実行するためのベストプラクティスがあることを願っています。Clojureのプロトコルとレコードでこれを行うことについて少し実験してきましたが、本当に好きなものはありません。
Alex Miller、

22
「ソフトウェアエンジニアリングの人々が関数型プログラミングをまだ発見していないことを神に感謝します」の+1。;)
Aky

1
オブジェクト指向は、それ自体が型を使用してプログラムを作成する方法であるため、アプローチはそれほど異なりません。OOデザインの問題は通常、何をしているのかを知らない人々に起因するようです。
Marcin

46

Clojureについては、古き良きリレーショナルモデリングに戻ることをお勧めします。Out of the Tarpitは感動的な読み物です。


それは素晴らしい記事です。コンピュータサイエンスの古き良き時代は、これらすべての概念が今日のルネサンスまで存続したとき、本当に印象的であったに違いありません。それはおそらく数学の強力な基礎によるものです。
Thorsten

1
この。この。この!私はこの論文を読んでおり、高度に制御された方法で最小限の変更可能な状態を維持しながら、実際のシステムを構築するために必要なすべての基礎をカバーしているように見えるのは非常に興味深いです。私はPongとTetrisをFRelPスタイルで構築することをいじっています(奇妙なイニシャリズムを許してください。しかし、もう1つのより一般的なFRP:関数型反応型プログラミングがあります)。
John Cromartie

論文を読んだ後、clojureはFR(el)Pにとって、少なくとも本質的なロジック偶発的な状態と制御、およびその他のコンポーネントにとって、完璧な言語になると思います。sqlを再発明せずに(欠陥なしに)clojure の必須状態の関係定義を作成する方法を知りたいですか?または、単に優れたリレーショナル(SQL)DBを使用し、OOPによって導入された概念的なミスマッチなしにその上に関数型プログラムを構築するという考えですか?
Thorsten

1
@Thorstenの基本的な考え方は、set = table、map = indexです。難しいのは、インデックスとテーブルの同期を維持することですが、この問題は、より適切なセットタイプで解決できます。私が実装した単純なセットのタイプの1つは、キー関数を使用して単一性をテストするセットであるキーセットです。つまり、値の挿入または更新を実行し、主キーフィールドを指定してgetを呼び出すと、行全体が返されます。
cgrand


38

個人的には、OO開発の通常のすべての優れた実践が関数型プログラミングにも当てはまることを発見しました-関数型の世界観を考慮に入れるためのいくつかのマイナーな調整だけです。方法論の観点からは、根本的に異なることを実際に行う必要はありません。

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

いくつかの例:

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

  • 設計におけるサービス指向 -実際には、FPの観点からは非常にうまく機能します。これは、典型的なサービスは、実際にはいくつかの副作用を伴う単なる関数であるためです。Lispの世界で時々採用されているソフトウェア開発の「ボトムアップ」の見方は、実際には別の見方をすれば、サービス指向のAPI設計原則に過ぎないと思います。

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

  • プロトタイピング/反復-FPでも同様に機能します。ツールやDSLの構築とREPLでの使用に非常に長けていれば、ユーザーと一緒にライブでプロトタイプを作成できるかもしれません。


3
これらの慣行は、私にはかなり馴染みのあるものに聞こえます。まだ誰かが、6番目の本「Programming in Clojure」の代わりにBruegge / Dutoitによる「UML、パターン、Javaを使用したオブジェクト指向ソフトウェアエンジニアリング」と同等の機能を書くべきだと思います。「Clojureと?? what ??を使用した機能的ソフトウェアエンジニアリング」と呼ぶことができます。FPでUMLとパターンを使用していますか?Paul Grahamが、パターンはLispでの抽象化の欠如の兆候であり、新しいマクロの導入によって是正されるべきであると書いたことを覚えています。
Thorsten

3
しかし、パターンをベストプラクティスとして翻訳すると、FPの世界にもパターンが存在する可能性があり、初期化されていない人と共有する価値があります。
Thorsten

2
PAIPブックには、いくつかの主要な設計が含まれています。norvig.com/paip.html
mathk

1
関数型プログラミングパターン(再帰のスキームなど)もあります
GabrielŠčerbák

13

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

だから本や本はありませんが、ことわざにあるように、不可能な価値を表現できないものにするための確立されたアプローチがあります。

そうすることで、特定のタイプのデータを代わりに関数として表現すること、および逆に特定の関数を代わりにデータタイプの和集合として表現することで、シリアル化、より厳密な仕様、最適化などを得られるように、幅広い選択を行うことができます。 。

次に、ある種の代数を確立するようにadtsの上に関数を記述します。つまり、これらの関数に適用される固定則があります。一部はおそらくべき等です-複数のアプリケーションの後でも同じです。いくつかは連想です。一部は推移的です。

これで、適切に動作する法則に従って構成する関数を持つドメインができました。シンプルな組み込みDSL!

もちろん、指定されたプロパティがあれば、それらの自動ランダム化テストを作成することもできます(QuickCheckとして)。これはほんの始まりにすぎません。


1
不可能な値を表現できないようにするアプローチは、HaskellやMLのような静的型付けの言語よりも、ClojureやSchemeのような動的型付けの言語には適用できません。
Zak

@Zak-まあ、それらが表現できないことを静的にチェックすることはできませんが、とにかく同じ方法でデータ構造を構築できます。
sclv

7

オブジェクト指向設計は、ソフトウェアエンジニアリングと同じではありません。ソフトウェアエンジニアリングは、要件から実際のシステムに至るまでのプロセス全体を、予定どおりに、低い不良率で行う必要があります。関数型プログラミングはOOとは異なる場合がありますが、要件、高レベルで詳細な設計、検証とテスト、ソフトウェアメトリック、推定、およびその他すべての「ソフトウェアエンジニアリング関連」を排除するものではありません。

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


5

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


1
問題のドメインに向けて言語を最初に構築するアプローチを理解しています。抽象化のレベルに達するまで、コードで反復的なパターンが発生しなくなり、その抽象化で問題を解決します。
Thorsten

1
しかし、「モデルがDSLで表現された一連のビジネスルールである」場合、どのように見えますか?Java EEアプリケーションでは、モデルはPOJO-Entitiesとして記述されます。これは、コントローラーJBから呼び出され、例えば、JSPを更新します。FPに同様のアーキテクチャパターン(MVCパターンなど)はありますか?それはどのように見えますか?
Thorsten

2
まさにそのように、FPでMVCパターンを使用できない理由はありません。FPはまだあなたは、豊富なデータ構造を構築することができますし、間違いなくのADTとパターンマッチングで、あなたは非常に構築することができます豊かなものを。どちらかといえば、FPはデータと動作を分離するため、MVCタイプシステムはより自然に発生します。
sclv

5

別の投稿への私の答えを見てください:

Clojureはどのようにして関心の分離を行っていますか?

私は、FPアプローチを使用する大規模なアプリケーションを構築する方法について、この件についてさらに書く必要があることに同意します(さらに、FP駆動のUIを文書化するために、さらに行う必要があります)


3
90%のパイプラインと10%のマクロアプローチが好きです。関数型プログラムを不変データの変換のパイプラインと考えるのは自然なことです。「コードではなく、すべてのインテリジェンスをデータに入れる」という意味が理解できるかどうかわかりません。1つのデータ構造で100個の関数を処理するアプローチ(10個のデータ構造で10個の関数を処理するのではなく)は、反対。独自の動作が組み込まれているため、OOPのデータ構造はFPよりもインテリジェントではありませんか?
Thorsten

3

これは素朴で単純なものと考えられるかもしれませんが、「デザインレシピ」(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


Northeasternページへのリンクが無効になっているようです。
James Kingsbery 2013年

1
ジェームズ、あなたは正しい、そして私はそれを修正するためにそこに何があったか覚えていない、残念ながら。私は、HtDPの作者がPyret言語を作成したことを知っているだけです(おそらく、HtDPの第2版を改訂して、ラケット(以前のPLTスキーム)の代わりに使用します)。
Artyom Shalkhakov 2013年

3

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

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

本の説明から:

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


2

リチャードバード教授とオックスフォード大学(英国)のプログラミング代数グループに関連する「プログラム計算」/「計算による設計」スタイルがありますが、この方法論を検討するのはそれほど難しいことではないと思います。

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


2

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


clojureでBDDを実行するために使用しているツールは何ですか?
murtaza52 2013年

ミジェが好きです。最新で非常に表現力豊かです。それをチェックアウト:github.com/marick/Midje
マルク・

1

正直なところ、関数型プログラムの設計レシピが必要な場合は、HaskellのPreludeなどの標準関数ライブラリを調べてください。FPでは、パターンは通常、高次のプロシージャ(関数を操作する関数)自体によってキャプチャされます。したがって、パターンが見られる場合、そのパターンをキャプチャするために高次関数が作成されることがよくあります。

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


-7

上手、

一般に、多くの関数型プログラミング言語は「小さなおもちゃの問題」のために長い間大学で使用されています。

OOPは「状態」が原因で「並列プログラミング」が困難であるため、現在人気が高まっています。また、Google MapReduceのような当面の問題には、関数型スタイルの方が適している場合があります。

functioanlの人たちが壁にぶつかったとき([1.00000.000行を超えるコードのシステムを実装しようとすると])、一部の人は流行の言葉を使った新しいソフトウェアエンジニアリングの方法論を思い付くでしょう:-)。彼らは古い質問に答えるべきです:システムを断片に分割して、一度に1つずつ「噛む」ことができるようにする方法は?[反復作業、漸進的、進化的作業]機能的スタイルを使用。

関数型スタイルがオブジェクト指向スタイルに確実に影響することは確かです。関数型システムの多くの概念を「まだ」残し、OOP言語に適合させています。

しかし、関数型プログラムはこのような大きなシステムに使用されるのでしょうか?それが問題です。

そして、そのような大きなシステムを実装せずに現実的な方法論を取り入れて、手を汚すことはできません。最初にあなたの手を汚し、次に解決策を提案するべきです。ソリューション-「本当の痛みと汚れ」のない提案は「ファンタジー」になります。


現在、関数型言語で構築された大規模なシステムは十分にあります。なかったとしても、これはまったく議論ではありません。
Svante 2013年

さて、それらのいくつかに名前を付けますか?「Erlang」システムをごくわずかしか知りません。【中型】しかしハスケル?Clojure?舌足らずの発音?
Hippiasマイナー

そして、[大きなシステムを書く]ことが本当の議論です。それはテストケースだからです。このテストケースは、この機能的なスタイルが有用であり、現実の世界で実用的なことができるかどうかを示しています。
Hippiasマイナー

2
アナリー「OOP」ではない言語の面白いところは、「設計方法論」から自由になり、自分で考え、プログラムを適切な方法でカットすることです。官僚的な定型文。申し訳ありませんが、10ポイントの3週間コースはありません。
Svante 2013年

1
信じられないことを見たことがある。
Svante 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.