Topics

・関数型プログラミングとオブジェクト指向のパラダイムとしての対立 国内の【自称】関数型コミュニティと海外の論調の違い

ガラパゴス・ネットスラング=「関数型ポエム」という呪詛、先入観的読書と、フェアなレビューの登場

Qiitaの騒動から派生して、東北大学の住井 英二郎@esumii氏、らくだの卯之助@camloeba氏その他が、犯罪者集団に合流して2ちゃんねるでの誹謗中傷チームの「8賢者」「ウィザード組」とかアホなことをやってる件について

JavaScriptではES6+とReact-JSXからES5へのトランスパイルが標準に / ATOMエディタで最速環境構築 厳選パッケージ 3 + 3 2015年夏バージョン

2016年のnode、ES6、React、Babel、Atomエディタ界隈の方向性

Dockerじゃないsystemd-nspawn+machinectlが非常に良い

99%のプログラマがIQ145のJKに「ダサい」と言われてしまう理由とは?【その1】「計算機科学のほんとうの基礎」を理解していない。IQ145のJKと同じ事を語るMITの権威とSICPという聖典の権威を借りてマインドコントロールを解いてみよう

99%のプログラマがIQ145のJKに「ダサい」と言われてしまう理由とは?【その2】関数型プログラミングのイミュータブルな世界観とイミュータブルな実世界を完全に統合

10年先を行く斬新な関数型(FRP)データベースについて説明する 99%のプログラマがIQ145のJKに「ダサい」と言われてしまう理由とは?【その3】

[React (.js Facebook)解説 関数型プログラミングに目覚めた! IQ145の女子高生の先輩から受けた特訓5日間 サポート記事【静的HTML編】React 解説【動的HTML-FRP編】

量子コンピュータが超高速である原理と量子論とそれに至るまでの科学哲学史をゼロからわかりやすく解説01量子コンピュータが超高速である原理と量子論とそれに至るまでの科学哲学史をゼロからわかりやすく解説02

『関数型プログラミングに目覚めた!』のレビュー(Day-1)について

LISPデータ構造の問題点の指摘と抜本的解法としての新プログラミング言語の策定/純粋関数型言語「SPINOZA」

著書『関数型プログラミングに目覚めた! IQ145の女子高生の先輩から受けた特訓5日間』 [Day1]たち読み記事 無料公開中

『関数型プログラミングに目覚めた! IQ145の女子高生の先輩から受けた特訓5日間』を大変多くの方々にお買い求めいただき、感謝します。本書の全目次を公開し、質問を受け付けます。

2015年7月25日土曜日

worldcomponent関数リアクティブプログラミング(FRP)ライブラリの破壊的代入について

拙書の延長で、当ブログにて公開している、関数リアクティブプログラミング(FRP)ライブラリ
worldcomponentについて、拙書をレビューすると称する悪意あるQiita記事、『関数型プログラミングに目覚めた!』のレビュー(Day-1)のコメントで、激しい論争が繰り広げれられており、強い関心を持って読み通しました。

この記事では著者/開発者としての見解を提示します。

worldcomponentがオブジェクトの値の破壊的代入による実装がある、との批判ですが、その通りです。

https://github.com/sakurafunctional/worldcomponent/blob/master/worldcomponent.js

これは、JavaScriptのオブジェクトの値が破壊的代入されたときに、イベントが発生するObject.definePropertiessetの特性を利用して実装されているライブラリです。
このような言語仕様の低層のハックが原理的に関数型であるわけがない

Object.defineProperties(value,
  {
    val: //value.val
    {
      get: function()
      {
        return state;
      },
      set: function(x)
      {
        state = x;
        computingF.map(
          function(f)
          {
            f(x);
          });
        return;
      }
    }
  });

この特性により、オブジェクトの変更をWATCHするためのポーリング実装などは不要で、かなり効率的で無駄のない実用に耐えるFRP機構を実現しています。

すでに当該コメント欄でも複数名の有志により見解が示されていますが、拙書では「ハードウェアモード」の操作として表現もしているとおり、根本的に関数型プログラミングのパラダイムを実現するために、低層のライブラリまで関数型で書く必要性も合理性も一切ありませんし、実際にほとんどのJavaScriptやnode.jsで利用可能な公開されている関数ライブラリはそのライブラリの実装自体(ライブラリのソースコード)は関数型で書かれておらず命令型(もちろん破壊的代入も)で書かれています。worldcomponentもその一つの関数ライブラリで、利用することで関数型のコードが書けます。

Object.definePropertiessetというオブジェクトの値への破壊的代入を「感知」する手法を利用しているので、オブジェクトの参照への破壊的代入をもって実装すると、この機構が機能することはありません。

たとえば、

      appear: function(a) {
        var f1 = function() {
          //  value.val = a; 
          //`Object.defineProperties`の`set`機構に不可欠な「値」の破壊的代入をコメントアウト

          var o = {val: a}; //新規オブジェクトの作成
          value = o; //オブジェクト参照への破壊的代入
        };
        return f1;
      },

とコードを変更すると、Object.definePropertiessetはトリガーされません。つまり、原理的にオブジェクト参照への破壊的代入ではこのライブラリは機能しない設計になっています。

JavaScriptの関数型プログラミングのBestPractice

JavaScriptは基本的に参照の値渡し(参考 JavaScriptはオブジェクトについて参照渡しだなんて、信じない)なので、通常 
= (イコール)
で参照であれ、値であれ、オブジェクトをバックアップをしたと見做すのは危険なので、行うべきではありません。

コメント欄に親切に紹介もされていましたが、このような場合のBestPracticeとしては、これも公開されているundersocrelodashImmutable(全部摂書でも紹介しました)の関数ライブラリのオブジェクトのコピー関数を活用して、言語仕様の詳細をライブラリ実装以下に隠蔽してしまい、非関数型的なパラダイムの議論を回避します。

worldcomponentの利用も関数型のパラダイムにおいて、その実装レベルが問題となることはまったくありません。

また、worldcomponentの実装について、@Lambada氏により、無駄に複雑なスパゲッティ・プログラムなどと繰り返し激しい誹謗中傷が繰り広げられております。

私としましては、Object.definePropertiessetを使ってFRPを効率的に実現するために、無駄がないように極限まで洗練させてコードを公開したつもりですが、すでに@Lambada氏は現段階でworldcomponentの仕様も含めて十二分に精査されたことでしょうから、もちろんObject.definePropertiessetを使わない別の方法でも構わないですし、氏が無駄のない複雑でないコードを提示していただければ助かります。氏のより洗練されたコードで代替した上でnpmのバージョンを上げて公開するつもりなのでよろしくお願いします。

なぜ、関数型プログラミングのコードについて「オブジェクト参照」だとか「状態オブジェクトの再利用」の話を熱弁しているのか??

@Lambada氏による珍妙な主張、オブジェクト参照によるバックアップであるなら、

過去の状態への巻き戻しや状態オブジェクトの再利用等が容易

ということですが、このような事象はありえないこともコメントで指摘されているとおりです。

これまでの議論でも明らかですが、JavaScriptでは、worldcomponentReactのオブジェクト如何に関わらず、対象が何であれ対象の内部実装がわからない限り、自分が何をやったのか知ることはできないので、= (イコール)で何かをバックアップしたとするのは危険で問題が生じるので行うべきではなく、BestPracticeとして、何らかの適切な関数ライブラリを利用するオブジェクトのコピーを明示的に行います。

だいたい、何故この人は、オブジェクト指向の「オブジェクト参照」だとか「状態オブジェクトの再利用」の話を関数型プログラミングのコードで熱弁されているのでしょうか??
パラダイムの違いに無頓着であるからですし、一義的に私のコードのあら探し、誹謗中傷に熱心なだけで、まったく全体の景色が見えていないからですね。

たとえば、ポジションをニュートラルにして冷静にさせるためにLisp言語にでも置き換えてみると、Lispにはコピーする関数もありますが、「オブジェクト参照の破壊的代入が・・・コピーが・・・」などという議論にはならず、単に新たな値に対して、コピーでもmapでも関数を用いてイミュータブルに新規に値を明示的に作成するはずです。

この明示的な操作において、容易も困難も何もありません。
意図をもって明示的にやるか、やらないかの違いしかありません。

最後に FRPにおけるオブジェクトのバックアップというナンセンスについて

表層的な観点からは、

oldvalue(バックアップされたとする古い値)

newvalue(新規に出現する値)

などで、

過去の状態への巻き戻しや状態オブジェクトの再利用

するということを示唆しているのでしょうが、
このnewvalue
「値の破壊的代入」がされているとする、
___totalClicks.now()
と何ら違いはないことは自明です。

また、真に関数型パラダイムの観点からは、
(SICPでも解説されているレベルという意味です
99%のプログラマがIQ145のJKに「ダサい」と言われてしまう理由とは?【その1】「計算機科学のほんとうの基礎」を理解していない。IQ145のJKと同じ事を語るMITの権威とSICPという聖典の権威を借りてマインドコントロールを解いてみよう
___totalClicksというのは、イミュータブルなオブジェクトであり、ミュータブルに耐えず変化しているのは、現実世界の我々が意識している「時間」のほうであり、
この我々が常に意識している「現在時間」には、どの瞬間においても常にnow()で参照透過にイミュータブルな時間軸上の現在時刻のストリームデータにアクセスできる、という設計です。

FRP的設計思想として、現在時間のnow()が、___totalClicksというイミュータブルなオブジェクトの唯一の読み取りインターフェイスであり、「オブジェクト」のバックアップなどすべきではありません。

var ___ = worldcomponent;
var ___totalClicks = ___(0);
___.world = ___totalClicks.appear(42); // ___totalClicksの値を42に設定
var ___totalClicksBak = ___totalClicks; // ___totalClicksの「バックアップ」を保存

のようなコードはFRPのコードではないので、書いてはなりません。

真のFRPライブラリとしてのworldcomponentの設計理念としては、もちろん独立したストリームを別個に宣言しておきます。

var ___ = worldcomponent;
var ___totalClicks = ___(0);
var ___totalClicksBak = ___(___totalClicks.now()); // ___totalClicksの「バックアップ」を保存
___.world = ___totalClicks.appear(42); // ___totalClicksの値を42に設定
//...................

このコードは___totalClicksBakという命名や、「バックアップ」を保存というコメントの言葉遣いが真のFRP的に徹底的に間違っていますが、対比するためにやむを得ずそのように書いています。

もちろん、FRPの個々に定義されたストリームは勝手に相互に干渉しあうことなどありませんから、@Lambada氏による

このように、___totalClicksのほうしか更新していないのに、___totalClicksBakの値まで変わっています。

というGlitchは起こりようがありませんし、そもそもFRPにおいて、ストリームをオブジェクト参照によるバックアップするのが間違いです。

たとえばReactにしても、オブジェクト参照コピーによるバックアップが、設計上、API上全く想定されていないのと同じことです。宣言型のコードに取り組むときバックアップなどという概念はナンセンスです。このブログでイミュータブルな真に関数型のDBを紹介しました。
10年先を行く斬新な関数型(FRP)データベースについて説明する 99%のプログラマがIQ145のJKに「ダサい」と言われてしまう理由とは?【その3】
そこでは、

すべてのタイムスタンプにデータを保持するのではなくて、
「差分」が発生した瞬間のデータのみを保持する。
というアイデアは、結構使えるんですね。
というか、とっくに広く使われています。
GitHubです。
Gitのようなバージョン管理システムは、データの差分のみを記録していきます。

という真のFRPの実現の具体例の片鱗を紐解きましたが、GitHubが瞬間瞬間のバックアップでなく、すべてのタイムスタンプですべての事象をイミュータブルにパイルアップして記録していくシステムであるのはよく知られていることです。

FRP的設計思想として、現在時間のnow()以外に、任意の計算可能な、つまり現在時刻より過去という意味ですが、任意の時刻にアクセスできる読み取り可能なインターフェイスを実装しているのが、拙書の最終章Day5で掲載しているtimecomponentというFRPライブラリであるということになります。

以上のような議論に比較して、

oldvalue(バックアップされたとする古い値)

newvalue(新規に出現する値)

というバックアップというのは、いかに非関数型的発想で命令型パラダイムであるか!という事実に気がつけるか気がつけないかがFRPあるいは関数型パラダイムを真に理解できているか否かの違いです。

FRPの議論をするとき、ある論者が、バックアップというナンセンスで批評を始めた時点で、その論者はFRPのことは何も理解していないと断定して構いません。

もちろん拙書を手間暇かけて読解していただいた読者諸氏におかれましては、上記私の意図するところは哲学的根本レベルで伝わっているものと信じます。

0 コメント:

コメントを投稿

Popular Posts

Blog Archive