JavaScriptで関数型プログラミングを学ぶ理由

スモークアートキューブからスモーク— MattysFlicks —(CC BY 2.0)
注:これは、JavaScriptES6 +で関数型プログラミングおよび合成ソフトウェア技術をゼロから学習する「Composing Software」シリーズ(現在の本です!)の一部です。乞うご期待。まだまだたくさんあります!
本を購入する|インデックス| <前へ|次へ>

JavaScriptについて知っていると思うものは何でも忘れて、初心者の心でこの資料にアプローチしてください。これを支援するために、JavaScriptを一度も見たことがないかのように、JavaScriptの基本を一から見直します。あなたが初心者なら、あなたは幸運です。最後に、ES6と関数型プログラミングをゼロから探索するものです!願わくば、すべての新しい概念が説明されていることを願っていますが、甘やかしすぎないでください。

すでにJavaScriptに精通している経験豊富な開発者、または純粋な関数型言語を使用している場合、関数型プログラミングの探索にはJavaScriptが面白い選択だと考えているかもしれません。それらの考えを脇に置き、心を開いて資料にアプローチしてみてください。 JavaScriptプログラミングには別のレベルがあることに気付くかもしれません。あなたが決して知らなかったものが存在した。

このテキストは「ソフトウェアの構成」と呼ばれ、関数型プログラミングはソフトウェアを構成する明白な方法です(関数構成、高次関数などを使用)。Haskell、ClojureScript、またはElmについて話していないのはなぜかと思われるかもしれません、JavaScriptの代わりに。

JavaScriptには、関数型プログラミングに必要な最も重要な機能があります。

  1. ファーストクラス関数:関数をデータ値として使用する機能:関数を引数として渡し、関数を返し、変数とオブジェクトプロパティに関数を割り当てます。このプロパティは、部分的な適用、カリー化、および合成を可能にする高次関数を可能にします。
  2. 無名関数と簡潔なラムダ構文:x => x * 2はJavaScriptの有効な関数式です。簡潔なラムダを使用すると、高階関数を簡単に操作できます。
  3. クロージャー:クロージャーとは、関数とその語彙環境を束ねたものです。クロージャーは関数の作成時に作成されます。関数が別の関数の内部で定義されている場合、外部関数が終了した後でも、外部関数の変数バインディングにアクセスできます。クロージャーは、部分的なアプリケーションが固定引数を取得する方法です。固定引数は、返される関数のクロージャースコープにバインドされた引数です。 add2(1)(2)では、1はadd2(1)によって返される関数の固定引数です。

JavaScriptに欠けているもの

JavaScriptはマルチパラダイム言語です。つまり、さまざまなスタイルのプログラミングをサポートしています。 JavaScriptがサポートするその他のスタイルには、手続き型(命令型)プログラミング(Cなど)が含まれます。関数は、再利用と整理のために繰り返し呼び出すことができる命令のサブルーチンを表します。オブジェクト指向プログラミング。そしてもちろん、関数型プログラミング。マルチパラダイム言語の欠点は、命令型のオブジェクト指向プログラミングがほとんどすべてが可変である必要があることを暗示している傾向があることです。

突然変異は、インプレースで発生するデータ構造の変更です。例えば:

const foo = {
  バー: 'baz'
};
foo.bar = 'qux'; //突然変異

通常、オブジェクトは、プロパティをメソッドで更新できるように変更可能である必要があります。命令型プログラミングでは、ほとんどのデータ構造は可変であり、オブジェクトと配列の効率的なインプレース操作を可能にします。

JavaScriptにはない、一部の機能言語にある機能を次に示します。

  1. 純度:一部のFP言語では、言語によって純度が強制されます。副作用のある式は許可されていません。
  2. 不変性:FP言語の中には、突然変異を無効にするものがあります。式は、配列やオブジェクトなどの既存のデータ構造を変更する代わりに、新しいデータ構造に評価されます。これは非効率に聞こえるかもしれませんが、ほとんどの関数型言語は、内部で構造共有を特徴とするトライデータ構造を使用します。つまり、古いオブジェクトと新しいオブジェクトは同じデータへの参照を共有します。
  3. 再帰:再帰とは、関数が反復のために自身を参照する機能です。多くのFP言語では、反復が反復する唯一の方法です。 for、while、doループなどのループステートメントはありません。

純度:JavaScriptでは、慣例により純度を達成する必要があります。純粋な機能を作成してアプリケーションの大部分を構築していない場合、機能スタイルを使用してプログラミングしているわけではありません。残念ながらJavaScriptでは、不純な関数を誤って作成して使用することにより、簡単に軌道に乗ることができます。

不変性:純粋な関数型言語では、不変性がしばしば強制されます。 JavaScriptには、ほとんどの関数型言語で使用される効率的で不変のトライベースのデータ構造がありませんが、Immutable.jsやMoriなどの役立つライブラリがあります。 ECMAScript仕様の将来のバージョンで不変のデータ構造が採用されることを期待しています。

ES6にconstキーワードを追加するなど、希望を与える兆候があります。 constで定義された名前バインディングは、別の値を参照するために再割り当てできません。 constは不変の値を表さないことを理解することが重要です。

constオブジェクトは、完全に異なるオブジェクトを参照するように再割り当てすることはできませんが、参照するオブジェクトのプロパティを変更することはできます。 JavaScriptにはオブジェクトをfreeze()する機能もありますが、これらのオブジェクトはルートレベルでのみフリーズされます。つまり、ネストされたオブジェクトはプロパティのプロパティを変更できます。つまり、JavaScript仕様に真の複合不変物が登場するまでには、まだ長い道のりがあります。

再帰:JavaScriptは技術的に再帰をサポートしていますが、ほとんどの関数型言語には末尾呼び出し最適化と呼ばれる機能があります。末尾呼び出しの最適化は、再帰関数が再帰呼び出しにスタックフレームを再利用できるようにする機能です。

末尾呼び出しの最適化を行わないと、呼び出しスタックが際限なく成長し、スタックオーバーフローが発生する可能性があります。 JavaScriptは技術的に、ES6仕様で限定された形式のテールコール最適化を取得しました。残念ながら、主要なブラウザエンジンの1つだけがそれを実装し、最適化は部分的に実装され、その後、Babel(古いブラウザで使用するためにES6をES5にコンパイルするために使用される最も人気のある標準JavaScriptコンパイラ)から削除されました。

結論:大規模な反復に対して再帰を使用することは、テール位置で関数を呼び出すことに注意を払っていても、依然として安全ではありません。

JavaScriptには純粋な関数型言語にはないものがある

純粋主義者は、JavaScriptの可変性が主な欠点であることを教えてくれますが、それは事実です。ただし、副作用や突然変異が有益な場合もあります。実際、副作用のない最新の便利なアプリケーションを作成することは不可能です。 Haskellのような純粋な関数型言語は副作用を使用しますが、モナドと呼ばれるボックスを使用して純粋な関数からそれらをカモフラージュし、モナドによって表される副作用が不純であってもプログラムを純粋のままにします。

モナドの問題は、その使用が非常に簡単であるにもかかわらず、多くの例に不慣れな人にモナドが何であるかを説明することは、視覚障害者に「青」の色がどのように見えるかを説明することに似ていることです。

「モナドはエンドファンクターのカテゴリーのモノイドです。問題は何ですか?」〜フィリップ・ワドラーをフィクションで引用し、サンダース・マック・レーンの本当の引用を言い換えるジェームズ・アイリー。 「プログラミング言語の簡潔で不完全な、ほとんど間違った歴史」

通常、パロディは物事を誇張して面白いポイントを面白くします。上記の引用では、モナドの説明は実際には元の引用から簡略化されており、次のようになります。

「Xのモナドは、Xの内積関数のカテゴリーのモノイドであり、積×は内積関数の構成と単位内積関数によって設定された単位に置き換えられます。」〜Saunders Mac Lane。

それでも、私の意見では、モナドに対する恐怖は弱い推論です。モナドを学ぶ最良の方法は、このテーマに関するたくさんの本やブログの投稿を読むことではなく、飛び込んでそれらを使い始めることです。関数型プログラミングのほとんどのものと同様に、理解できないアカデミックな語彙は、概念よりも理解するのがはるかに困難です。私を信じてください。関数型プログラミングを理解するためにSaunders Mac Laneを理解する必要はありません。

JavaScriptはすべてのプログラミングスタイルに完全に理想的ではありませんが、JavaScriptは、さまざまなプログラミングスタイルと背景を持つさまざまな人々が使用できるように設計された汎用言語です。

ブレンダン・アイヒによると、これは最初から意図的なものでした。 Netscapeは2種類のプログラマをサポートする必要がありました。

「…C ++または(希望)Javaで記述したコンポーネントの作成者。そして、HTMLに直接埋め込まれたコードを記述する「スクリプター」、アマチュアまたはプロ。」

元々、Netscapeは2つの異なる言語をサポートし、スクリプト言語はおそらくScheme(Lispの方言)に似ていることを意図していました。繰り返しますが、ブレンダン・アイヒ:

「ブラウザで「スキームを実行する」という約束でNetscapeに採用されました。」

JavaScriptは新しい言語でなければなりませんでした:

「上層部のエンジニアリング管理からの特徴は、言語は「Javaのように見える」必要があるということでした。そのため、SchemeとともにPerl、Python、およびTclが除外されました。」

だから、ブレンダン・アイヒの頭の中のアイデアは最初から:

  1. ブラウザのスキーム。
  2. Javaのように見えます。

最終的にはさらにミッシュマッシュになりました。

「私は誇りに思っていませんが、Schemeらしいファーストクラスの機能とSelf-ish(単数ではありますが)プロトタイプを主要な要素として選んだことを嬉しく思います。 Javaの影響、特にy2k Dateのバグだけでなく、プリミティブとオブジェクトの区別(たとえば、文字列と文字列)も残念でした。」

最終的にJavaScriptに組み込まれた「不幸な」Javaのような機能のリストに追加します。

  • コンストラクター関数と新しいキーワード。ファクトリ関数とは異なる呼び出しセマンティクスと使用セマンティクスを備えています。
  • 単一の祖先を持つクラスキーワードは、主要な継承メカニズムとして拡張されます。
  • クラスを静的なタイプであると考えるユーザーの傾向(そうではない)。

私のアドバイス:できる限り避けましょう。

JavaScriptがこのような有能な言語になったことは幸運です。なぜなら、スクリプトによるアプローチが「コンポーネント」によるアプローチに勝ったことがわかったからです(現在、インストールされている膨大な数のブラウザではJava、Flash、ActiveX拡張がサポートされていません)。

最終的には、ブラウザで直接サポートされている1つの言語であるJavaScriptが完成しました。

つまり、ブラウザは1組の言語バインディングであるJavaScriptのみをサポートする必要があるため、肥大化やバグの発生が少なくなります。 WebAssemblyは例外であると考えているかもしれませんが、WebAssemblyの設計目標の1つは、互換性のある抽象構文ツリー(AST)を使用してJavaScriptの言語バインディングを共有することです。実際、最初のデモでは、WebAssemblyをASM.jsとして知られるJavaScriptのサブセットにコンパイルしました。

Webプラットフォームの唯一の標準的な汎用プログラミング言語としての地位により、JavaScriptはソフトウェアの歴史の中で最大の言語人気の波に乗ることができました。

アプリは世界を、ウェブはアプリを、JavaScriptはウェブを食べました。

複数の手段により、JavaScriptは現在、世界で最も人気のあるプログラミング言語です。

JavaScriptは関数型プログラミングの理想的なツールではありませんが、非常に大規模で分散したチームで大規模なアプリケーションを構築するための優れたツールです。

一部のチームは、命令型プログラミングが特に役立つスクリプトグルーに集中する場合があります。他の人は、アーキテクチャの抽象化を構築することに集中するかもしれません。さらに、関数型プログラミングを採用し、アプリケーション状態の決定論的でテスト可能な管理のための純粋な関数を使用して、ユーザーのアクションを減らします。これらのチームのメンバーは全員同じ言語を使用しているため、アイデアを交換したり、お互いから学び、お互いの成果をより簡単に構築できます。

JavaScriptでは、これらのすべてのアイデアが共存できるため、より多くの人々がJavaScriptを採用でき、世界最大のオープンソースパッケージレジストリ(2017年2月現在)npmにつながりました。

JavaScriptの真の強みは、エコシステム内の思考とユーザーの多様性です。関数型プログラミングの純粋主義者にとって絶対に理想的な言語ではないかもしれませんが、想像できるほぼすべてのプラットフォームで動作する1つの言語を使用して一緒に作業するのに理想的な言語かもしれません。 、またはC。

JavaScriptは、機能的なプログラマにとって最適な言語ではないことに同意します。ただし、他の関数型言語は誰もが使用および採用できる言語であると主張することはできません。また、ES6で実証されているように、JavaScriptは関数型プログラミングに興味のあるユーザーのニーズに応えることができます。 JavaScriptと世界中のほぼすべての企業で使用されている信じられないほどのエコシステムを放棄する代わりに、それを受け入れて、ソフトウェア合成のためのより良い言語にしてください。

現状では、JavaScriptはすでに十分に機能的なプログラミング言語です。つまり、人々は関数型プログラミング手法を使用して、JavaScriptであらゆる種類の便利で興味深いものを構築しています。 Netflix(およびAngular 2+で構築されたすべてのアプリ)は、RxJSに基づいた機能ユーティリティを使用しています。 Facebookは、Reactの純粋関数、高次関数、および高次コンポーネントの概念を使用して、FacebookおよびInstagramを構築します。 PayPal、KhanAcademy、およびFlipkartは、状態管理にReduxを使用します。

それらは単独ではありません:Angular、React、Redux、およびLodashは、JavaScriptアプリケーションエコシステムの主要なフレームワークおよびライブラリであり、それらはすべて、関数型プログラミングの影響を大きく受けています。実際のJavaScriptアプリケーションで関数型プログラミングパターンを有効にする目的。

「なぜJavaScriptなのか?」JavaScriptは、ほとんどの実際の企業が実際のソフトウェアを構築するために使用している言語だからです。好むと好まざるとにかかわらず、JavaScriptはLispから「最も人気のある関数型プログラミング言語」のタイトルを奪いました。Lispは何十年もの間標準的な担い手でした。確かに、Haskellは今日の関数型プログラミングの概念にはるかに適した標準的な担い手ですが、Haskellで実際のアプリケーションを構築している人はそれほど多くありません。

現時点では、米国には10万近くのJavaScriptの求人があり、世界中にはさらに数十万の求人があります。 Haskellを学ぶことで関数型プログラミングについて多くを学ぶことができますが、JavaScriptを学ぶことで実際の仕事のためのプロダクションアプリの構築について多くを学ぶことができます。

アプリは世界を、ウェブはアプリを、JavaScriptはウェブを食べました。

本を購入する|インデックス| <前へ|次へ>

EricElliottJS.comで詳細を見る

EricElliottJS.comのメンバーは、インタラクティブなコードチャレンジのビデオレッスンを利用できます。メンバーでない場合は、今すぐサインアップしてください。

Eric Elliottは、分散システムの専門家であり、「Composing Software」および「Programming JavaScript Applications」という本の著者です。 DevAnywhere.ioの共同創設者として、彼は開発者にリモートで作業し、ワーク/ライフバランスを受け入れるために必要なスキルを教えています。彼は暗号化プロジェクトの開発チームを構築して助言し、Adobe Systems、Zumba Fitness、The Wall Street Journal、ESPN、BBC、およびUsher、Frank Ocean、Metallicaなどのトップレコーディングアーティストのソフトウェアエクスペリエンスに貢献しています。

彼は世界で最も美しい女性との遠隔生活を楽しんでいます。