童話と不変性の意味


これは、特定のプロジェクトに向けられたものではなく、特定の問題に向けられている.私の最近の記事の1つは、おそらく私はおそらく私はおそらくArray.prototype.reverse 関数など

One more thing... I know you're not trying to re-create "Composing Software", but would it be worth addressing how Array.reverse() actually modifies the passed array, as well as returning a reference to it? Mutation! Mutation!

That's unlike the other JS functions, which just return something new.

And what implications does that have in a pipeline or composition?


ありがとう、グレッグ!質問とそれが意味するものは、私にいくつかを考えていました.それで……

おとぎ話


物語はよく知られています:特定の小さなtowheadedキッドは森の中で散歩をすることを決定、自宅に侵入、ラダーを襲撃、家具を破壊し、ベッドルームをransack.捕まると彼女はその場から逃げる.
はい、私はgoldilocksと3つのクマを参照しています.物語では、クマの家族は、彼らの食事時間を涼しくするために、散歩のために行きました.歩いている間、Goldilocksは彼らの家に入って、各々のものの食物と椅子と寝具をサンプリングします-そして、プロセスで、各々のもののものを消費するか、壊してください.
あなたが「Goldilocksと3匹のクマの道徳的なもの」を捜すならば、あなたはこれを見つけるかもしれません:

The moral of the story is the need to respect the privacy and property of others and how your actions hurt others. In conclusion, the story Goldilocks and the three bears illustrate the need to respect others' privacy and property. ...
(Taken from PEDIAA)


今、それは道徳的であり、悪いものではありません、しかし、私の疑いはこの物語のテラーが開発者であったということです.

異なる道徳


物語は非常に重要なポイントを示しています:クマが出て、彼らの家に彼らのものを残すのに安全であるように思えます.彼らは、彼らが戻ったとき、彼らのものが彼らがそれを残したように正確であるという事実に信頼しました.その代わりに、警告は恐ろしいものです:再び彼らの所有物を見て、物事は変わりました.赤ちゃんクマの場合、事態は悲惨に変わった.
開発者として、我々はこれをimmutabilityに関する警告とみなすことができます.変数に値を格納するとき:
const myName = "Toby";
我々はここでいくつかの異なることをしている.
  • 変数を配置します.myName , 現在の実行スコープのルックアップテーブルに;
  • 我々は文字列を配置している."Toby" , どこかに記憶する
  • 我々は、その値に変数“配線”です
  • によってconst , 我々は、それが再配線することができないという変数を話しています.
  • ですから、注意すべき点は二つあります.
  • まず、原始型は不変です.あなたは場所でそれらを変更することはできません.我々がそうであるならばtoUpperCase() この文字列は、新しいメモリ位置に新しいインスタンスを持つことになります.オリジナルは変更できません.
  • 次はconst は初期化でのみ宣言できます.その点から、その参照は不変です.値を変更できないだけでなく、変数を変更できません.
  • これは、私たちがmyName は、私たちが定義したもののままです.いつでも電話してくださいmyName , 私は同じ値を取得します.事実上、我々は家にそれを置いて、家に鍵をかけました.
    別のケースを考慮してください.
    const myFriends = ['Therese','Daniel','Greg','Annika'];
    
    ここで同じことをした.myFriendsconst さて、常に同じ配列を指します.すべての偉大な罰金と素晴らしい.これを行うまでは:
    myFriends.sort((a,b)=>a.localeCompare(b))
    
    console.log(myFriends);
    // ['Annika','Daniel','Greg','Therese']
    
    それで、私たちはその配列を変数const ……しかし、我々sorted その配列.And Array.prototype.sort これらの厄介な“in place”配列メソッドの1つです.我々は変異したmyFriends 配列.
    より単純なケースでは、それは大部分のように見えないかもしれません.つまり、私はその配列を作りました.

    問題


    私が公的にものを変異させることができるならば、私はそのことを信頼することができますか?我々が管理している管理者パネルを持っていると言います、そして、我々はそのようなデータ構造を保存しています.管理者コントロールパネルはいくつかの異なるコントロールを持っているかもしれませんが、簡単に動作させるために、データを配列に保存しますAdminControlPanel モジュールです.彼らは含まれているので、グローバルな名前空間を汚染していません.
    さらにモジュール化することを設計したと想像してください.人々は自分のコントロールパネルのコンポーネントを作成し、それらを好きなようにロードできます.彼らが走るとき、彼らはその中で必要に応じて彼らのデータプールをつくりますAdminControLPanel , 彼らは彼らの初期化をします、そして、彼らは彼らのものについて話をします.
    しかし、それらのコンポーネントのうちの2つが使われるならばFriends アレイ?別の彼らの連絡先情報を調べるかもしれない間、それらの1つは私がそれらの友人についてのメモを追加することができますと言う.我々がロードすると想像してくださいAddNotesAboutFriends admin module、配列を作成し、他のものとの間の共通のオブジェクトスタイルを持つFriends これは余分な詳細を許可します.作品偉大な、我々の友人についてのすべてのレコードをロードし、私たちは、追加、編集し、我々が作ったノートを表示することができます.すごい!
    それから、我々はViewFriendDetails コンポーネント.我々は、これらの友人のためのユニフォーム形式を定義すると、この1つは私たちの友人を検索できるように、簡単に見つけるためにそれらをソートし、選択したものの詳細ビューを表示することができます.また、偉大な、心配しない作品.
    でも.何が起こったの?我々ならばViewFriendDetails 配列の配列FriendsAddNotesAboutFriends あのアレイを見ていましたか.私たちはそこで信用を破ったかもしれない.我々は信頼できないFriends 我々のコントロールの外の何かが現在そのものを突然変異しているので、配列、予期しない、信頼できない状態にそれを残して!

    不可能性問題


    開発者としてデータを扱う必要があります.我々がそれを見逃すとき、我々はそれを見逃すことができて、位置を変えて、危険なものをするDr . Whoで、泣く天使のようでありません.私たちは、そのデータを信頼するために、短い必要があります.
    私がトップにリンクしたその記事の中で、私はreverseString ほとんどのプログラミングカリキュラムに共通のレッスン.その一つは、
    // some utility functions, curried.
    const splitOn = (splitString) =>
      (original) =>
        original.split(splitString);
    
    const joinWith = (joinString) =>
      (original) =>
        original.join(joinString);
    
    const reverse = (array) => array.reverse();
    
    
    const reverseString = compose(
      joinWith(''),
      reverse,
      splitOn('')
    );
    
    それをすべて説明しようとしないで、最後の記事はかなりうまくいったと思います.しかし、それらの小さな、シンプルな機能のいくつかは素晴らしいです.
  • splitOn 私たちの"スプリッタ"と分割する文字列を使用します.それから配列を返します.
  • joinWith 逆は、“結合”として使用する文字列を受け取り、値の配列を文字列に結合します.
  • その両方のケースで、我々はそのデータを変換しているように、我々は全く新しいものを持っている.我々は、文字列自体を場所に変換していない、我々は何か新しいものを作っている.
    それから、我々はreverse 関数.私はシンプルなラッパーを書きたかったので、私は単に配列を通過し、それをフリップすることができます.呼ぶよりarray.reverse() , 私は電話することができたかったreverse(array) . しかし、私はそれらの含意を見失った.
    reverse 関数は、この特定のケースでは、本当に重要ではありません.一時的なデータでしか使わないので、値は捨てられますarray.reverse() 新しいものを返すことはありませんね.

    悪い。


    問題だ.なぜ?なぜなら、私の関数のアプリケーションを知ることができないからです.私はそれをどこで知るかわからないreverse 関数は、行の下で使用されるかもしれません.それは偉大で便利な機能です、それはすべての場所にポップアップ表示される可能性があります.「関数型プログラミング」概念の全体のポイントは、これらの小さな単純な1つまたは2つの行の関数を作成することができ、相互接続' em.
    しかし、この場合はarray.reverse() はGoldilocksです.我々は元の配列参照に戻って、それを変異させました.JavaScriptが値を渡す方法のために、元の配列と関数の1つは共有リファレンスです.両方とも同じ記憶場所を見ます、そして、どちらもそれを変異させることができます.これは、人々は悪い考えです.

    なぜ?


    機能プログラミングにおける重要原則は「純度」である.我々が純度について話すとき、我々は我々の機能がそうすることを意味します:
  • 同じ入力を与え、同じ出力を返し、
  • 原因は副作用.
  • そのためにreverse 関数は、毎回同じことをします.配列を渡すとき、返り値は配列を反転します.しかし、我々は副作用を引き起こしている!我々は、元の配列を変異しただけでなく、それを返す.
    私たちはそれを信じることができる必要があります.例えば、元の配列を変更します.

    単純な修正


    この場合、fixは単純です.配列を単に反転するのではなく、配列のコピーを逆にしたいのです.
    // bad:
    const reverse = (array) => array.reverse();
    
    // better:
    const reverse = ([...array]) => array.reverse();
    
    その中で、我々がそれを受けているときarray , 我々はすぐにそれを新しい配列に広げた.我々はもはやオリジナルを参照してくださいので、我々はarray.reverse() 我々は独自のローカルコピーに取り組んでいます.値を返すと、元の配列はそのままになります.
    そのバージョンでは、我々がそれを使用する場所に関係なく、他の関数に構成されると、突然変異を引き起こすのではなく、変換を作成しています.

    その他のゴチャス


    私たちが監視する必要がある他の方法と場所があります.以下は典型的な警告ですMDN :

    The splice() method changes the contents of an array by removing or replacing existing elements and/or adding new elements in place. To access part of an array without modifying it, see slice().
    (Bolding added for emphasis)


    Mozillaのdocsでは、適所にマークされた配列メソッドを見ると、これらはmutatorsである.気をつけなければ、彼らは元の物を変えます.彼らは、私たちの粥を食べて、我々の椅子を壊して、我々をgoldilocksします.
    ほかにもある.露出されたデータを格納するためのオブジェクトの使用は問題があるかもしれません、露出されたデータがいつでも突然変異することができるように、そして、我々には本当の知る方法がありません.我々は非常に注意深く、非常に明示的でない限り、我々はオブジェクトを信頼することはできません-彼らは非常に簡単に変異することができます.
    私たちはそれらをより信頼できるようにすることができますclass 露出したオブジェクトを作成し、ファクトリ関数を使用し、Object.freeze() 返されるアクセサー.

    ポイント


    その話の教訓は、私の心、信頼である.我々は我々のデータを保存することができなければならなくて、それが我々がそれに戻るとき、我々が期待するものであるという信頼をすることができなければなりません.私たちは、公的に公開されたデータで、そして、変更可能な構造でそれをすることができません.我々は計画する必要があり、我々は予防する必要があります.