Clojureなどの関数型プログラミング言語への私の(確かに限られた)露出から、データのカプセル化はそれほど重要ではない役割のようです。通常、マップやセットなどのさまざまなネイティブタイプは、オブジェクトよりも、データを表すために推奨される通貨です。さらに、そのデータは一般に不変です。
たとえば、Clojureの名声であるRich Hickeyの有名な引用の1つを 問題に関するインタビュー で示します。
Fogus:その考えに従います—一部の人々は、Clojureがそのタイプのデータ非表示カプセル化に従事していないという事実に驚いています。データを非表示にすることをやめたのはなぜですか?
ヒッキー:Clojureはプログラミングを抽象化することを強く強調しています。ただし、ある時点で、誰かがデータにアクセスする必要があります。また、「プライベート」の概念がある場合は、それに対応する特権と信頼の概念が必要です。そして、それは全体のトンの複雑さと少しの価値を追加し、システムに剛性を生み出し、そしてしばしば物事を彼らがしてはならない場所に住んでいることを強います。これは、単純な情報がクラスに入れられるときに発生する他の損失に追加されます。データが不変である限り、誰かが変化する可能性のあるものに依存するようになる可能性があることを除いて、アクセスを提供することによってもたらされる可能性がある害はほとんどありません。ええ、そうですね、人々は実生活でそれを常に行っており、物事が変化するとき、彼らは適応します。そして、彼らが合理的であるならば、彼らは将来彼らが適応する必要があるかもしれない変化する可能性がある何かに基づいて決定をするときを知っています。ですから、それはリスク管理の決定です。プログラマーは自由に作れるべきだと私は思います。抽象化するようにプログラミングしたいという欲求や、実装の詳細との結婚を警戒するような感覚がなければ、優れたプログラマーになることはできません。
OO=世界から来ると、これは私が長年にわたって学んだいくつかの神聖な原則を複雑にするようです。これらには、情報隠蔽、デメテルの法則、および統一アクセス原則などが含まれます。 。共通のスレッドは、カプセル化により、APIを定義して他の人が何をすべきか、何を触れないかを知ることができるようにすることです。基本的に、コードのメンテナーが自由に変更やリファクタリングを行えるようにするコントラクトを作成する方法を心配する必要はありません。コンシューマーのコードにバグが発生する可能性があります(オープン/クローズの原則)。また、他のプログラマーがそのデータを取得または構築するために使用できるツールを知るためのクリーンで精選されたインターフェイスを提供します。
データへの直接アクセスが許可されると、そのAPIコントラクトは破られ、カプセル化のメリットはすべてなくなります。また、厳密に不変のデータは、ドメイン固有の構造(オブジェクト、構造体、レコード)の受け渡しを、状態とその状態に対して実行できるアクションのセットを表すという意味ではるかに役に立たないようにします。
コードベースのサイズが非常に大きくなり、APIを定義する必要があり、システムの特定の部分の操作に多くの開発者が関与する場合に発生すると思われるこれらの問題に、関数型コードベースはどのように対処しますか?これがこれらのタイプのコードベースでどのように処理されるかを示す、利用可能なこの状況の例はありますか?
まず、セバスチャンの2番目のコメントから、適切な機能、動的型付けについて説明します。より一般的には、Clojureは関数型言語とコミュニティの1フレーバーであり、それに基づいてあまり一般化しないでください。 ML/Haskellの観点から、いくつかの発言をします。
Basileが述べているように、アクセス制御の概念はML/Haskellに存在し、頻繁に使用されます。 「ファクタリング」は、従来のOOP言語とは少し異なります。OOP aクラスの概念typeとmoduleの役割を同時に果たしますが、関数型(および従来の手続き型)言語はこれらを直交的に扱います。
もう1つのポイントは、ML/Haskellが型消去を伴うジェネリックスで非常に重いことであり、これを使用して、OOPカプセル化とは異なる「情報非表示」のフレーバーを提供できます。コンポーネントが知っているのは型パラメーターとしてのデータ項目の型。そのコンポーネントは、その型の値を安全に渡すことができますが、具体的な型が分からないため、それらを使用して多くのことを実行できなくなります(ユニバーサルinstanceof
はありません)またはこれらの言語でのランタイムキャスト) このブログエントリ は、これらのテクニックの私のお気に入りの紹介例の1つです。
次:FPの世界では、不透明な/カプセル化されたコンポーネントへのインターフェースとして透明なデータ構造を使用することは非常に一般的です。たとえば、インタプリタパターンは、データ構造が構文ツリーとして使用されるFPで非常に一般的ですロジックを記述し、それらを「実行」するコードに供給されます。状態は、適切に言うと、データ構造を消費するインタープリターの実行時に一時的に存在します。また、インタープリターの実装は、クライアントと通信する限り、変更できます。同じデータ型。
最後で最長:カプセル化/情報の隠蔽はテクニックであり、終わりではありません。それが提供するものについて少し考えてみましょう。カプセル化は、ソフトウェアユニットのcontractとimplementationを調整するための手法です。典型的な状況は次のとおりです。システムの実装では、その契約に従って存在すべきではない値または状態を認めています。
このように見てみると、FPは、カプセル化に加えて、同じ目的で使用できるいくつかの追加ツールを提供することを指摘できます。
このF#の「タイプを使用した設計」シリーズ は、これらのトピックのいくつか、特に#2をかなり適切に読むのに役立ちます。 (上からの「違法な状態を表現できないようにする」リンクの出所です。)よく見ると、2番目の部分では、カプセル化を使用してコンストラクターを非表示にし、クライアントが無効なインスタンスを作成しないようにする方法を示しています。上で述べたように、それはがツールキットの一部です!
可変性がソフトウェアで問題を引き起こす程度を、私は本当に誇張することはできません。私たちの頭の中で行われている慣習の多くは、可変性が引き起こす問題を補うものです。可変性を取り除いた場合、それらのプラクティスはそれほど必要ありません。
不変性がある場合は、実行時にデータ構造が予期せず変更されないことがわかっているため、プログラムに機能を追加するときに、独自の派生データ構造を作成して使用できます。元のデータ構造は、これらの派生データ構造について何も知る必要はありません。
これは、基本データ構造が非常に安定する傾向があることを意味します。新しいデータ構造は、必要に応じて、エッジの周囲から派生します。重要な関数型プログラムを実行するまで、説明するのは本当に難しいです。プライバシーを気にせず、耐久性のある一般的なパブリックデータ構造を作成することをますます考えているだけです。
Clojureがハッシュとプリミティブを使用する傾向は、私の意見では、機能的な遺産の一部ではなく、動的な遺産の一部です。 PythonとRuby(オブジェクト指向、命令型、動的の両方)で同じ傾向が見られますが、どちらも高次関数をかなりサポートしていますが)ただし、Haskellにはありません(静的に型付けされていますが、純粋に機能的であり、不変性を回避するために特別な構成が必要です)。
したがって、質問する必要があるのは、関数型言語が大きなAPIをどのように処理するかではなく、動的言語がどのように処理するかです。答えは、良いドキュメントとたくさんのユニットテストです。幸いなことに、現代の動的言語は通常両方に対して非常に優れたサポートを備えています。たとえば、PythonとClojureの両方に、コメントだけでなく、コード自体にドキュメントを埋め込む方法があります。