Topics

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

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

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


・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)について











2015年7月31日金曜日

JavaScriptで時間発展する物理系=私達が生活するこの宇宙の挙動を、関数型プログラミングでイミュータブルに記述する、という関数型リアクティブプログラミング(FRP)の概念実証

時間発展する物理系、つまり、私達が生活するこの宇宙の挙動を、関数型プログラミングでイミュータブルに記述する、という関数型リアクティブプログラミング(FRP)の概念実証をJavaScript言語でしてみましょう。

我々の馴染みの深い「放物線」を例に取ります。
「放物線を描く」と日常的によく使いますが、物理学(日本語)では正確には、「斜方投射」と言います。

斜方投射(しゃほうとうしゃ)とは物体をある初速度をもって空中に投げ出す動作である。空気抵抗が十分小さく無視できる場合、斜方投射された物体の軌跡は放物線を描く

enter image description here

水平方向にx軸、
鉛直上向きにy軸をとります。

初速 
偏角 
のとき、
斜方投射してからの経過時間   における
物体の速度および座標

という恒等式で表せます。(は 重力加速度=9.8 m/ss)

これがニュートン物理学の時間発展する恒等式です。

. タイガー・ウッズの平均ヘッドスピード57.5m/s ボール初速度85.0~86.5m/sと公表されています。

らしいですから、
初速を85.0m/s、偏角30度とし、空気抵抗を無視したゴルフのスーパーショットっぽい斜方投射を上記時間発展する恒等式をそのまま用いて、
worldtimestream
Reactを利用して関数型リアクティブプログラミングのコードを書くと以下のようになります。

code

恒等式の宣言

worldtimestreamで、物理系の時間tをコードに恒常的にマップするworldengineとして宣言しておきます。

___.world = ___((t) => { // world engine

//===========================================================================

});

その中で、恒等式の初期設定が宣言され、

  var V0 = 85.0; // m/s
  var deg = 30; //degree
  var THETA = deg / 180 * Math.PI; //radian
  var G = 9.8; //gravity const

恒等式そのものがtの時間関数coordinateEquationで宣言されています。

  var coordinateEquation = (t) => {
    var x = V0 * Math.cos(THETA) * t;
    var y = V0 * Math.sin(THETA) * t - G * Math.pow(t, 2);
    return {
      x: x,
      y: y
    };
  };

これで、すべての関係性は記述されていますが、この時間発展する時間関数の恒等式を実際の画面表示をするための「計算」は一切なされていません。

つまり、ここまでは、物理系と物理量コードのストリームとの関係性が100%抽象世界として宣言されただけで、コンピュータのハードウェアでその関係性の具現化、つまりふたたび、そのストリームが具体的な物理系としての画面に反映する「計算」タイミングなどの指示は行われていません。

計算からの画面描写(画面描写も関係式の計算に他ならない)

この「計算」部分を一手に司るのが、ReactコンポーネントReactComponentです。

componentWillMount()において、

        var init = () => {
          var T0 = t();
          var f = () => {
            ___.world = ___coordinate.appear(coordinateEquation((t() - T0) / 1000));
          };
          ___.world = t.computeInterval(f, 10); //calculate 10milsec resolution 
        };
        ___.world = t.computeTimeout(init, 0);

で、___coordinateというストリームを、10ミリセカンドの解像度をもってtの時間関数coordinateEquationで「計算」することを宣言します。

___x={___()} ___y={___()}というReactが描写するコンポーネントのXY座標に相当する、worldtimestreamのストリームとして定義されたプロパティは、___coordinateが「計算」されると、適切な画面座標に投影しながら

        var com = this;
        ___.world = ___coordinate.compute((coordinate) => {
          ___.world = com.props.___x.appear(50 + coordinate.x * Drawscale);
          ___.world = com.props.___y.appear(300 - coordinate.y * Drawscale);
          com.forceUpdate();
        });

コンポーネントを再描画します。

このように、FRPライブラリworldtimestreamとReactを利用することで、簡潔に、
時間発展する物理系、つまり、私達が生活するこの宇宙の挙動を、関数型プログラミングでイミュータブルに記述する、という関数型リアクティブプログラミング(FRP)を実現可能です。

___.world = ___((t) => { // world engine 
  //=========================================================================== 
  //MKS system of units 

  var ___coordinate = ___();

  var V0 = 85.0; // m/s 
  var deg = 30; //degree 
  var THETA = deg / 180 * Math.PI; //radian 
  var G = 9.8; //gravity const 

  var coordinateEquation = (t) => {
    var x = V0 * Math.cos(THETA) * t;
    var y = V0 * Math.sin(THETA) * t - G * Math.pow(t, 2);
    return {
      x: x,
      y: y
    };
  };

  //============================================================== 
  var Drawscale = 4; //4 dot = 1 meter 

  var ReactComponent = React.createClass(
    {
      componentWillMount() {
        var com = this;
        ___.world = ___coordinate.compute((coordinate) => {
          ___.world = com.props.___x.appear(50 + coordinate.x * Drawscale);
          ___.world = com.props.___y.appear(300 - coordinate.y * Drawscale);
          com.forceUpdate();
        });

        var init = () => {
          var T0 = t();
          var f = () => {
            ___.world = ___coordinate.appear(coordinateEquation((t() - T0) / 1000));
          };
          ___.world = t.computeInterval(f, 10); //calculate 10milsec resolution 
        };
        ___.world = t.computeTimeout(init, 0);
      },
      render() {
        var com = this;

        var el = (
        <div>
          <h1>For new shot, Just Reload the browser page</h1>
          <svg height = "100%"  width = "100%">
              <circle r="5" fill="blue"
        cx = {this.props.___x.t()}
        cy = {this.props.___y.t()}/>
          </svg>
        </div>
        );
        return el;
      }
    });

  var mount = React.render(<ReactComponent ___x={___()} ___y={___()} />, document.body);
//============================================================== 
//=========================================================================== 
});


Live Demo

http://sakurafunctional.github.io/demo/react-physics/

2015年7月30日木曜日

nodeのFRP(関数型リアクティブ)ライブラリ`worldcomponent`の後継、`worldtimestream`の公開

nodeのFRP(関数型リアクティブ)ライブラリworldcomponentの後継、worldtimestreamをnpmで公開をしました。

https://www.npmjs.com/package/worldtimestream
https://github.com/kenokabe/worldtimestream

FacebookのJSXで開発テストしており、ライブラリもテストも最終的にバニラJavaScriptへコンパイルしています。

Install

node / io.js

npm install worldtimestream

WebBrowser

https://raw.githubusercontent.com/kenokabe/worldtimestream/master/js/worldtimestream.js

をコピーペースト

Rationale

アイデアは、前回の記事です。

関数型プログラミングと古典物理学の密接な関係

Date.now() はストリームへ透過に参照する時間関数ですが「ユーザの現在時間」が暗黙に引数として渡されているため理解しにくいです。

より物理系と時間という物理量の概念を関数型のコードで明示できるように、FRPライブラリをハックしました。

worldtimestreamは命名したとおり、時間軸上のストリームです。
時間要素に依存しているので、worldtimestreamのストリームは物理量です。

___.world = ___((t) => {
//...........
});

という時間発展の恒等式を宣言することにより、
古典物理学の物理量である時間変数tが得られます。

別に時間変数nowと宣言しても構いませんが、現在時間というのは、なかなか哲学的素養がないと理解しにくい概念であるので、恒等的な値tとして宣言します。

以下、すべて基礎となる上記のスコープ内で記述します。

上記コードにより、

時間発展しているworldtimestreamのストリームの物理系の物理量t

が、↓

時間発展しているユーザが現在実行しているコード

へ恒等的にmapされているので、
t()とすることにより、それぞれの瞬間のユーザは透過にその値を参照できます。

つまりそれが、時間発展している世界のそれぞれの瞬間のユーザにとっての「現在時刻」であるということになります。

たとえば、

 ___.world = ___.log(t());

という恒等式を宣言しておくと、
このコードを意識的に計算するアクションを起こした、すべてのユーザのそれぞれの現在時刻がコンソールへ表示されます。

また、ストリーム___aを宣言しておき、

 var ___a = ___();
  ___.world = ___a.compute(() => {
    ___.world = ___.log(t(), 'a', ___a.t());

  });

と恒等式を宣言しておくと、

時間発展するストリーム___a
「過去」から「未来」方向
あるいは
「未来」から「過去」方向
で変化が生じた瞬間に、ユーザの関与なしに任意のコードを自動計算させることが可能です。

___.world = ___a.appear(0);

というのは、このコード全体が任意のユーザによって最初に実行された瞬間tにおける、ストリーム___aの値を宣言しています。

 var f = () => {
    ___.world = ___a.appear(___a.t() + 1);

  };

  var timer = setInterval(f, 1000);

とすることで、そこ瞬間を起点として、ストリームの時間軸上で「未来」方向に1秒間のインターバルの値を宣言しています。

この場合は、ストリーム___aのそのインターバル地点のある時間tにおける値が参照されており、その値に1を加えた値をストリーム___aの未来方向の次の瞬間に現れているという「関係性」を宣言しています。

念の為ですが、ストリーム___aは過去から未来まで時間軸上でイミュータブルで変化しません。ストリーム上に離散する値の関係性が上記コードによって宣言されているだけです。

たとえばこのストリーム___aをオブジェクト参照などで、コピーあるいは「バックアップ」すれば、過去から未来までまったく同じストリームがコピーされます。
つまり、未来方向の任意の時間で、
___aの値がappearされるという宣言があるのならば、まったく同じイミュータブルなストリームである___bもその同時の瞬間に値に「変化」が起こるストリームとして宣言されます。

過去未来の時間軸上で別の運命をもつ別のストリームを作成するには、同様に

var ___b = ___();

というような別個の独立したストリームを宣言します。

もし、この独立したストリーム___bが、
ストリーム___aと関係がある場合は、その都度その関係式を宣言します。

たとえば、
___aのストリーム上のある瞬間の値が、
___bのストリーム上の同一瞬間の値と等しくなり、「未来方向」へはずっとその値のままである、という我々が日常レベルで使う用語としての「値のバックアップ」という関係を宣言したいのであれば、

 ___.world = ___b.appear(a.t());

とすれば簡単に出来ます。

あるいは、

___a___bは、物理量tに依存しない形で、恒等的な関係が存在するとき、

たとえば

___b = ___a x 5
の関係を宣言したい場合は、

  ___.world = ___a.compute((x) => {
    ___.world = ___b.appear(x * 5);
  });

とすれば簡単に宣言できます。

Test

test.jsx

'use strict';

var ___ = require('./worldtimestream.js');

___.world = ___((t) => {

  var ___a = ___();
  var ___b = ___();

  ___.world = ___a.compute(() => {
    ___.world = ___.log(t(), 'a', ___a.t());

  });
  ___.world = ___b.compute(() => {
    ___.world = ___.log(t(), 'b', ___b.t());
  });

  ___.world = ___a.compute((x) => {
    ___.world = ___b.appear(x * 5);
  });

  ___.world = ___a.appear(0);
  var f = () => {
    ___.world = ___a.appear(___a.t() + 1);

  };

  var timer = setInterval(f, 1000);

});

実際に、test.jsxを実行した結果は以下のようになります。

1438234695190 ‘a’ 0
1438234695200 ‘b’ 0
1438234696204 ‘a’ 1
1438234696204 ‘b’ 5
1438234697206 ‘a’ 2
1438234697206 ‘b’ 10
1438234698208 ‘a’ 3
1438234698208 ‘b’ 15
1438234699209 ‘a’ 4
1438234699209 ‘b’ 20
1438234700211 ‘a’ 5
1438234700211 ‘b’ 25
1438234701213 ‘a’ 6
1438234701213 ‘b’ 30
1438234702214 ‘a’ 7
1438234702214 ‘b’ 35
1438234703215 ‘a’ 8
1438234703215 ‘b’ 40

関数型プログラミングと古典物理学の密接な関係

『関数型プログラミングに目覚めた!』のレビュー(Day-1)のコメント欄で計算機科学の基礎としての、プログラミングの「時間」要素、そして「時間」を考えるとは即ち、物理学を考えることである、という重要な論点が確認されています。

JavaScriptのDate.now()をimmutableなストリームと、透過な参照をする関数として考えるとき、誤解されやすいのが、

参照透過性(さんしょうとうかせい、英: Referential transparency)は、計算機言語の概念の一種で、文脈によらず式の値はその構成要素(例えば変数や関数)によってのみ定まるということを言う。 具体的には変数の値は最初に定義した値と常に同じであり、関数は同じ変数を引数として与えられれば同じ値を返すということになる。 当然変数に値を割り当てなおす演算である代入 (Assignment) を行う式は存在しない。 このように参照透過性が成り立っている場合、ある式の値、例えば関数値、変数値についてどこに記憶されている値を参照しているかということは考慮する必要がない、即ち参照について透過的であるといえる。

のうち「文脈によらず」「関数は同じ変数を引数として与えられれば同じ値を返す」という要素だと思います。
@nonstarter氏が感じる「謎」

t1 = Date.now(); (0〜9まで足してコンソールに表示); t2 = Date.now(); console.log(t1 == t2);

t1 = Date.now(); (0〜9まで足してコンソールに表示); t2 = t1; console.log(t1 == t2);
とで結果が異なる、というDate.now()の単純明瞭この上ない「参照不透明性」をどうするつもりなのかはまったく謎のママなわけです。

はまさにこの要素の疑念だと読み解けます。

まず、コメント欄で誰も注意していないので、厳しく注意しておきますと、この@nonstarter氏によるコードは関数型プログラミング、あるいは宣言型プログラミングのコードでもなんでもありません。

関数型・宣言型でない理由は単純で、

t1 = Date.now(); // ① 
<09まで足してコンソールに表示するための処理コード> ; // ②
t2 = Date.now(); // ③

はコードが
ステップ ①
ステップ ②
ステップ ③
というように、上から下へ時間遷移とともに流れ、値はコードの上下関係、時間の前後に依存する、という命令型の発想を前提として書かれている命令型のコードなので、そもそもが「参照不透明性」だとか関数型のコードとしては議論するのは間違いです。
ステップ ①

ステップ ③
では「時間」が違うので、Dateというストリームに参照透明にアクセスする時間関数now()の返り値は異なって当然です。

正しい関数型・宣言型のコードを書くと、

var f = function() {
  console.log(Date.now());
};
var dummy1 = setTimeout(f, 0); //ユーザがコードを実行した瞬間に即時実行
var dummy2 = setTimeout(f, 1000); //ユーザがコードを実行した1秒後に実行

こうなります。コードのロジックがコードの上下関係や、時間の前後関係にまったく依存していないことに注意してください。この「非同期」で宣言型のコードでは、ステップ①②③などありません。

<0〜9まで足してコンソールに表示するための処理コード> ;
を関数型で書くならば、その処理の終了のコールバック関数で書きます。@nonstarter氏が書いたような命令の終了の「同期」を待つような命令型のコードで書いてはいけません。

now()の実引数が空だから、「関数は同じ変数を引数として与えられれば同じ値を返す」事を考えるとおかしい!と思うのも、すでにコメント欄で反論されているとおり、間違った考えです。
now()というのは、その特性を表す字面でも明確ですが、時間関数であって、 値はユーザの現在時間に依存するのです。
数学的には、概念的には、コードに表記されずとも暗黙に、ユーザの現在時間が実引数として渡される関数です。

now()という時間関数が返す値はユーザの現在時間に依存するというのは、

@chimetorch氏が引用したコード
http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onmousemove_dom

document.getElementById("myDIV").onmousemove = function() {myFunction(event)};

function myFunction(e) {
    var x = e.clientX;
    var y = e.clientY;
    var coor = "Coordinates: (" + x + "," + y + ")";
    document.getElementById("demo").innerHTML = coor;
}

event値はユーザの現在時間に依存するというのと等価ですし、

elmのコード

import Graphics.Element exposing (..)
import Mouse
main : Signal Element
main = Signal.map show countClick
countClick : Signal Int
countClick = Signal.foldp (\clk count -> count + 1) 0 Mouse.clicks

の「シグナル」のインデックス値はユーザの現在時間に依存するのと等価です。

もちろん、上記elmの「シグナル」同様に、JavaScriptのDateストリームをFRP的に拡張して、

var f = function(now){console.log(now);};
var dummy = Date
            .interval(1000) // specifies a 1sec interval between each element
            .map(f);

と書けるようにするのは、大変意義あるハックですが、標準で、

var f = function(){console.log(Date.now());};
var dummy = setInterval(f, 1000);

書くほうが楽です。

私が書いた、worldcomponentも、実用的な観点から、
Date.now()と同様に、now()関数で直接値が参照できるように設計しています。これまで触った数々のFRPライブラリの経験から、この仕様のほうが取り回ししやすいという結論です。

もしくは、

var main = Date.map((now) => {
  //now はユーザの現在時刻
  //ここにすべてのコードを記述していく
});

と暗黙でなく明示的な表記にすれば、関数型のコードとしてより美しいかもしれません。
しかし、これはただ
明示的な構造の中にnowと書くか、
暗黙的な実引数でDate.now()と書くかの違いにすぎません。

いずれにせよ、@nonstarter氏による命令型のコード以外、私が書いたものも含め上記すべての関数型・宣言型のコードは、

  • コードのロジックがコードの上下関係や、時間の前後関係にまったく依存していない

のですが、

  • コードの中の値はユーザの現在時間に依存する

ことに注意してください。

前者と後者の区別がつくプログラマは、関数型プログラミング、計算機科学のほんとうの基礎の素養がきちんとありますが、前者と後者の区別がつかず、関数型でなく命令型のコードを書きながら『単純明瞭この上ない「参照不透明性」』などと発言してしまう人は勉強しなおしたほうがよろしいでしょう。

前者については、命令型でない関数型の基礎なので、特にいまさら説明を加える必要はないと思います。 

後者については、関数型プログラミングで、時間変化する領域に踏み込むことになるので、多少の説明が必要だと思います。

念の為、もう何度も繰り返しているのですが、この部分を説明していのが、
私の著書
関数型プログラミングに目覚めた! IQ145の女子高生の先輩から受けた特訓5日間
やSICP
99%のプログラマがIQ145のJKに「ダサい」と言われてしまう理由とは?【その1】「計算機科学のほんとうの基礎」を理解していない。IQ145のJKと同じ事を語るMITの権威とSICPという聖典の権威を借りてマインドコントロールを解いてみよう
であるということです。

(後者) コードの中の値はユーザの現在時間に依存する、つまり、関数型プログラミングと古典物理学の密接な関係について解説します。

まず、根本の根本の部分ですが、このように時間の値そのものを取り扱うコードにしても、イベント(シグナル)を使うマウスポインタ、クリックのコードにしても、値は時間変化します。

  • コードの中の値はユーザの現在時間に依存する

時間変化するから、「文脈によって変わる」、だから「参照不透明」なミュータブルと考えるのは間違いです。

  • コードのロジックがコードの上下関係や、時間の前後関係にまったく依存していない

というのが、「文脈によって変わらない」という意味で、「参照透明」であるということです。

私、そしてSICPの著者が関数型プログラミング、計算機科学の基礎と不可分として使う物理学の用語では、時間発展と言うのですが、

時間発展(じかんはってん)とは、時間が進むことで物理系が変化することである。
古典物理学における時間発展とは、物理量の値が時間によって変化することである。

変化しているのは、時間が進むことで変化する我々の世界の「物理系」「物理量」であることを理解してください。

古典物理学にせよ、関数型プログラミングのコードにせよ、それは数学なので、「参照不透明」に値が変化することなどはありえません。

古典物理学で絶対時間tが時間が進む(様に見えている)ことで物理系が変化し、物理量の値が時間発展するからといって、絶対時間tが破壊的代入されている、わけではありませんし、関数型プログラミングにしても全く同様です。

正しい関数型・宣言型のコード

var f = function() {
  console.log(Date.now());
};
var dummy1 = setTimeout(f, 0); //ユーザがコードを実行した瞬間に即時実行
var dummy2 = setTimeout(f, 1000); //ユーザがコードを実行した1秒後に実行

は、時間が進むことで変化する我々の世界という「物理系」において不変(immutable)で、参照透過です。

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のことは何も理解していないと断定して構いません。

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

2015年7月11日土曜日

「お絵かきアプリ」と「クリックカウンター」を簡単に実装できると見せかける誤魔化しと嘘 原理的に可能かどうか?と実用的であるかどうか?は違うが、それを「銀の弾」ではない、という言葉をもって自らの嘘、誤魔化しの言い訳として悪用するな

「お絵かきアプリ」と「クリックカウンター」を簡単に実装できると見せかける誤魔化しと嘘 原理的に可能かどうか?と実用的であるかどうか?は違うが、それを「銀の弾」ではない、という言葉をもって自らの嘘、誤魔化しの言い訳として悪用するな 

「レビュー」と称する欺瞞に満ちた、批判のための批判記事
『関数型プログラミングに目覚めた!』のレビュー(Day-1)には、こんな追記がありました。

追記(2015/05/30):デスクトップアプリケーション??

著者はブログで@camloebaさんや@esumiiさんにデスクトップアプリケーションをOCamlで作成するように求めているようです。著者が挙げている「クリックカウンター」や「お絵かき」の類を作成するのに特別なexpertiseは不要で、単に使用するGUIライブラリ・グラフィックスライブラリのドキュメントが読めれば充分なので、その趣旨は私にはまったく判然としません(当該の記事で著者がOCamlで「お絵かきロジック」を実装してみせよと要求する一方で自身ではJavaScriptによるそれを公表していない点も気になります)。もちろん、OCamlであれHaskellであれ破壊的代入の類の副作用を使用せずに書くのもなんら困難ではありません。

が、さらに「追記」の「追記」がなされたようです。

(追記:コメント欄にてyataketa9056さんに、既存のGUIライブラリを前提にするとOCamlでは困難であろうという指摘を頂いています。利点はともかくHaskellと同じようにモナドを利用したライブラリを作成するなら純粋な関数型でのプログラミングも可能になる(けれどもOCamlプログラミングとしてはわざわざそのようなことをしても嬉しくはない)ということになります。とはいえ原理的に不可能でないのなら(そして不可能でないので)、純粋な関数プログラミングでGUIアプリケーションを作成するのにFRPがなんら必要でない、という趣旨には充分です。またこの主張自体はHaskellでのコードによっても充分に示されています。)

この

コメント欄にてyataketa9056さんに、既存のGUIライブラリを前提にするとOCamlでは困難であろうという指摘を頂いています。

とは、

yataketa9056 Jul 11, 2015 04:27
幾つか補足と、間違いがありますので指摘します。
末尾呼出最適化は ECMAScript6 で導入されていますが、これをサポートする JS 実装は現在ほとんどないはずです。
js_of_ocaml 系のコードが末尾再帰でもスタックがあふれないのは、js_of_ocaml のコンパイラが末尾再帰呼出(全ての末尾呼出しではない)を goto やトランポリンで置き換えているからです。Scala の末尾再帰呼出が JVM 上でやはり同じようにコンパイルされているはずです。
OCamlでは「破壊的代入の類の副作用」を使用せずに一般の GUI アプリケーションを書くのはほとんど無理でしょう。なぜなら OCaml の全ての GUI ライブラリは状態は副作用を使うことを前提にメインループが設計されているからです。これを乗り越えるとすると @Lambada さんのようにメインループ自体を自分で書く事になり、一般的にはスケールしません。
Haskell の GUI ライブラリでは普通は状態は State なり IO を含む GUI モナドを使って書く事になりますが、そのような GUI ライブラリを OCaml 上で作れば同じような事が出来るはずです。OCaml でそこまで純粋関数型のコードを書く事に実用的意味があるとは思えませんが。

というものです。(太字は岡部によるもの)

この @nonstarterらの強弁に欺瞞を感じる良識のある指摘です。

まず、@nonstarterによる「言い分」

著者が挙げている「クリックカウンター」や「お絵かき」の類を作成するのに特別なexpertiseは不要で、単に使用するGUIライブラリ・グラフィックスライブラリのドキュメントが読めれば充分なので、その趣旨は私にはまったく判然としません

ですが、私の趣旨は明らかで、実用的なアプリを書けるのか?ということに尽きます。
前回、
『関数型プログラミングに目覚めた!』のレビュー(Day-1)の嘘と誤魔化し ー 自分たちが非実用的な「机上の空論」を語っている事をけして読者に語らないこと
において、趣旨を大前提として再確認しました。

著者が挙げている「クリックカウンター」や「お絵かき」の類

とは、FacebookのGUIコンポーネント指向、FRPライブラリである、Reactを活用した簡単なサンプルであり、同様yに、著書のDay5に示した、マウスドラッグのサンプルWebアプリです。

これらは、あくまで「実用性意味」を大前提とした投げかけでした。

住井 英二郎@esumiiさんとか、らくだの卯之助@camloebaさんに聞きたいのだけど、そもそも貴方たちはOCamlでデスクトップアプリ書けるんですか?

素朴な疑問なんですが、貴方がた、延々と私の関数型プログラミングの「無理解」やらスキルに偉そうなことを言い続けているけど、実際のところ、貴方たちは、関数型プログラミングで、実用に耐えうるデスクトップアプリとか書いたことあるんでしょうか?というか、書けるんですか??

ということです。

実用に耐えうる」こういう極めてシンプルなテーゼに徹頭徹尾誤魔化しを続けているのが、『関数型プログラミングに目覚めた!』のレビュー(Day-1)
であり、それは、最終的に、プラグマティックで真摯な読者への裏切りとして決着します。

@nonstarterらが「回答」した、「副作用を使用せずに書くのもなんら困難ではありません」とする、「クリックカウンター」や「お絵かき」は「スケールしません。」 「机上の空論」である

OCamlでは「破壊的代入の類の副作用」を使用せずに一般の GUI アプリケーションを書くのはほとんど無理でしょう。なぜなら OCaml の全ての GUI ライブラリは状態は副作用を使うことを前提にメインループが設計されているからです。これを乗り越えるとすると @Lambada さんのようにメインループ自体を自分で書く事になり、一般的にはスケールしません。

と良心的な指摘、欺瞞の看破のコメントもあるように、連中が、岡部の言うようなFRPを使わなくても、「なんら困難ではありません。」かんたんに書けるという趣旨で放言しているコードは、「スケールしません。」
つまり、問題を極めて単純化して簡単な解放で事足りるものに限定したら、それは「なんら困難ではありません。」「かんたんに書ける」わけですが、ちょっと複雑になってきたら、もう破綻する、複雑な、つまり実用的なアプリなんて書けっこない、ということです。

「机上の空論」です。

実用に耐えない「机上の空論」のコードを自ら実装した当然の結果への言い訳、「銀の弾ではない」???

「机上の空論」を自分が勝手に放言するのは、良識ある人間に看破されるだけだが、それを勝手に一般化して、言い訳するマジックワードが、「銀の弾ではない」です。

追記(2015/05/30):デスクトップアプリケーション??

の最後には、こういうデタラメが書かれています。

いずれにせよ、状態機械の数学的構成では状態遷移が状態と入力から状態への関数として表現されるというだけのことではあり(状態渡し)、状態遷移が複雑になれば状態遷移を純粋な関数として表現する作業も複雑になり困難になるので(そしてそれはしばしば綺麗な関数合成では上手くいかないようなものになることが多いので)、結局のところ少なくとも現状のFRPも(イベントのシグナルから状態のシグナルを構成する際に状態遷移をそうした関数で表現する必要が出てくるので)本質的には銀の弾丸にはならないと言わなければなりません(関数プログラミングで書けるということの恩恵はもちろんあるとしても)。

間違い、デタラメです。

まず、私は、

素朴な疑問なんですが、貴方がた、延々と私の関数型プログラミングの「無理解」やらスキルに偉そうなことを言い続けているけど、実際のところ、貴方たちは、関数型プログラミングで、実用に耐えうるデスクトップアプリとか書いたことあるんでしょうか?というか、書けるんですか??

「実用に耐えうる」複雑なケースを想定したコードを示せ、と要求した。

私が示したコードは、GUIの関数合成を具現化したコンポーネント指向のReactをベースとし、同時にイベントを合成可能なFRPのコードです。

コンポーネント指向は、関数合成であり、

綺麗な関数合成では上手くいかないようなものになることが多い

というものを最初から、根本的に解決するアプローチであり、私はそのサンプルコードを首尾一貫して、著書でも、先の「問いかけ」のブログ記事でも示しているわけです。

当然、いくらでもスケール可能なことは、FacebookやInstagramのサイトで実証されている現在進行形でアクティブに開発され多くの技術者が参入している次世代Webのデファクトである技術です。

スケールしない、複雑になったら破綻する、「クリックカウンター」や「お絵かき」のコードではありません。

一方で、@nonstarterらは、「副作用を使用せずに書くのもなんら困難ではありません」などと、読者を勘違いさせて当たり前の、極めて単純な実装でしか使えないコードしか示せませんでした。

「机上の空論」です。

極めて限定された、単純な問題でしか使えない、「机上の空論」の実用に耐えないコードを、「副作用を使用せずに書くのもなんら困難ではありません」と、あたかも簡単に書ける、その先に実用性があると読者を勘違いさせて、思い込ませて、騙してまるで平気な感じです。こういう看過できない欺瞞を暴くために、私は、

住井 英二郎@esumiiさんとか、らくだの卯之助@camloebaさんに聞きたいのだけど、そもそも貴方たちはOCamlでデスクトップアプリ書けるんですか?

素朴な疑問なんですが、貴方がた、延々と私の関数型プログラミングの「無理解」やらスキルに偉そうなことを言い続けているけど、実際のところ、貴方たちは、関数型プログラミングで、実用に耐えうるデスクトップアプリとか書いたことあるんでしょうか?というか、書けるんですか??

と、先にコンポーネント指向、FRPでサンプルコード示して問いかけたわけですが、もうその「回答」のダメさっぷりは明らかでしょう。

この自らの「回答」のダメさぷりを、まるで一般的な関数型プログラミングの限界のように、また読者を欺いて、正当化するのが、この連中の常套手段です。

どういうことか?

銀の弾丸にはならないと言わなければなりません ???

こういう言い草です。

@nonstarterが強弁するとおり、

状態遷移が複雑になれば状態遷移を純粋な関数として表現する作業も複雑になり困難になるので(そしてそれはしばしば綺麗な関数合成では上手くいかないようなものになることが多いので)、結局のところ少なくとも現状のFRPも(イベントのシグナルから状態のシグナルを構成する際に状態遷移をそうした関数で表現する必要が出てくるので)本質的には銀の弾丸にはならないと言わなければなりません

のであれば、
そもそも、私がサンプルとして示した、コンポーネント指向のReactで、UIを関数合成して見せているコードでは、「綺麗な関数合成で上手くいっている」(それがReactの関数ライブラリとしての真価)わけなので、「本質的に銀の弾丸」である、ということになります。

それを自分たちが、コンポーネント、FRPでなくても「なんら困難ではありません」などと、極めて限定的な単純化された例題にしか適用できないスケールしない実用に耐えないコードを勝手に示しておいて、だから、関数合成では上手く行かない、銀の弾ではない、などと説明されても、こちらとしては、ほらね?だからあんたらダメなんだよ、わかったかい?と再確認する意味しかありません。

関数型プログラミング、コンポーネント指向、FRPは、机上の空論でない、真に実用に耐えるアプケーションを構築するために必須な「銀の弾丸」である、この見解を改めて確認し、異論があるのであれば、こちらは、すでにスケールするコンポーネント指向のFRPのReactで構築したアプリを提示しているのだから、再度、

住井 英二郎@esumiiさんとか、らくだの卯之助@camloebaさんに聞きたいのだけど、そもそも貴方たちはOCamlでデスクトップアプリ書けるんですか?

素朴な疑問なんですが、貴方がた、延々と私の関数型プログラミングの「無理解」やらスキルに偉そうなことを言い続けているけど、実際のところ、貴方たちは、関数型プログラミングで、実用に耐えうるデスクトップアプリとか書いたことあるんでしょうか?というか、書けるんですか??

と広い読者にむけて、自らの主張を立証していただきたいと考えています。絶対無理でしょうが。

なんども繰り返し確認しますが、関数型プログラミングのGUI実用化、世界的潮流としては、

プログラミングの世界的潮流と机上の空論としての関数型プログラミング②「素人としては深入りを避けたいと思いますが、そうだとすればそもそもJavaScript選んじゃったのが関数プログラミングへの無理解を象徴的に示すものなのだということになるような気がします。」(苦笑)

で示したような状況があり、これは「銀の弾」として重宝されながら圧倒的な普及を見せているわけです。

あさっての方向をみて、あたかも実用的だと見せかけながら「机上の空論」に執着しながら、「机上の空論」で実用に耐えないから「銀の弾ではない」というような自己の規定した論理の中で無限ループするトートロジーのようなことを言ってる連中には、絶対無理でしょう。

コンポーネント指向、FRPでなければ、実用的なプログラミングはできないとする私の主張をわざわざ否定したいえばかりに、わざわざまた「机上の空論」を開陳して、結果「銀の弾ではない」、と結論づけて、あんたら何がしたいのか?読者の足を引っ張って楽しいのか?
自ら、実用的局面でなんら価値を生み出すことはなく、ありもしない限界的を設定して、こちらの言うことが間違いだと見せかけるために、批判のための批判がしたいだけだろう?ということです。

末尾再帰ループの最適化

気づきにくいといけませんので、上にあるLambadaさんのコメントの追記部分を転記させていただきます:

追記:岡部氏が最近のブログエントリで、上述のプログラムは再帰を使っているので「即効で」スタックオーバーフローすると主張していますが、関数型プログラミングの基本の一つである末尾再帰なので、スタックオーバーフローしません。もし仮に末尾再帰がスタックオーバーフローするJavaScript処理系があったとしたら、JavaScriptか、少なくともその処理系が、関数型プログラミングに不適切です。
岡部氏は上述のプログラムを無限ループに書き換えて「Chromeタグ(原文ママ)の反応がなくな」ったと主張していますが、無限ループにしたら反応がなくなって当然です。なお、少なくとも私の手元のChromeやFirefoxでは、無限ループする末尾再帰ですら(当然ながらブラウザの反応は重くなりますが)スタックオーバーフローは再現しません(メモリ消費量も特に増えません)。岡部氏がスタックオーバーフローと主張している画面には「Either Chrome ran out of memory or the process for the webpage was terminated for some other reason」(Chromeのメモリがなくなったか、もしくは何らかの他の理由でWebページのプロセスが終了されました)とあるので、スタックオーバーフローではなく、無限ループしているプロセスを強制終了した等かもしれません。
なお、他の方々から、Graphics.loop_at_exitではなく、ネイティブなOCaml処理系であればGraphics.wait_next_eventを、js_of_ocamlであればGraphics_js.loopを用いるべきとのご指摘をいただきました。そのとおりなのですが、元の文で述べたとおり、最も手っ取り早く試していただけるTry OCamlで動くよう、Graphics.loop_at_exitを用いました。また、そもそもTry OCamlのGraphics.loop_at_exitやjs_of_ocamlのGraphics_js.loopはループでも再帰でもなく、イベントハンドラの登録を行なって戻ってくるだけなのでスタックオーバーフローなど起こるわけがない、とのご指摘もいただきました。ありがとうございます。

なお、私も単なる素人ですし不適切なことを書いているだろうとは思いますが、今回のkenokabeさんの一連の主張を見ると流石に私ですら真面目に取り合う必要を感じません。しかしkenokabeさんがどのような理解の水準で関数プログラミングに関する書籍を執筆し(てしまっ)たのかが著者ご本人のお蔭で完全に明らかになったように思います。

まず、この人をはじめ、私が「末尾再帰最適化を理解していない、知らなかった」ということにしたいようです。

岡部はXXを知らない、理解していない、という勝手な決めつけ、中傷をもって、本のレビューとするという悪質な手口はこの@nonstarterによる常套手段ですが、もちろん知っています。

まず、念の為に、解説すると、
再帰ループの末尾最適化とは、
【学習者要注意!】『関数型プログラミングに目覚めた!』のレビュー(Day-1)の「状態遷移関数」を使った非FRPのGUIコードがメモリリークする実用に耐えない不良品でしかなかったことについて 読者騙せて楽しいですか?
で、発生するような問題をコンパイラレベルで回避するための言語の仕組みです。

末尾呼出最適化は ECMAScript6 で導入されていますが、これをサポートする JS 実装は現在ほとんどないはずです。js_of_ocaml 系のコードが末尾再帰でもスタックがあふれないのは、js_of_ocaml のコンパイラが末尾再帰呼出(全ての末尾呼出しではない)を goto やトランポリンで置き換えているからです。Scala の末尾再帰呼出が JVM 上でやはり同じようにコンパイルされているはずです。

とあるように、単に、スタックオーバーフローする再帰関数の無限ループを、内部で関数呼び出しでない無限ループのコードに書き換えることで実装されています。

上記のように、同じJavaScriptであってもバージョンによって、末尾再帰最適化の実装があったりなかったり、Javaも同様です。C#では、末尾再帰最適化はありませんが、F#ではあったりします。

趨勢として、JavaScript,C#,Javaなどのメインストリーム言語では、末尾再帰最適化はない、と考えていた方が良いでしょう。
OCamlは、末尾再帰最適化はあるのでしょう、よく知りませんが、しかしいずれにせよ、コンパイラがメモリリークしないように書き換えてくれる言語であったとしても、イベントハンドリングするために、コード上で無限ループを回して、1ループごとに、イベントハンドラー追加するような設計はアホな設計です。こんなことをすべきではありません。

こういうアホな設計を正当化したいのが、
なにがなんでもFRPを使わなくても書けるという机上の空論のやり方であり、延々と説明してきたとおり、スケールしません。イベント、UIを関数合成するような私が提示しているやり方ではないので、実用に耐えるコードではありません。

実用に耐えない、ということを自己正当化するために、彼らがなんて言うのか?というと、

本質的には銀の弾丸にはならないと言わなければなりません

とか、

素人としては深入りを避けたいと思いますが、そうだとすればそもそもJavaScript選んじゃったのが関数プログラミングへの無理解を象徴的に示すものなのだということになるような気がします。

とかです。

そもそも、関数型プログラミングのメリットとは、簡潔な構造であり、生産性であり、実用的であるからです。

純粋関数型プログラミングだ!と、結果、無残な無限ループごとにイベントハンドリング追加するような無理な設計であったり、末尾再帰最適化してくれるからOCamlだ、JavaScriptは使えない、という話では、ありません。

関数型プログラミングとは汎用的な概念であり、特定の関数型プログラミング言語に縛られたり依存するものではない

JavaScriptが関数型プログラミング言語としてもっとも実用的である

Reactをはじめとする、コンポーネント指向、FRPは強固な汎用性と実用性が実証されている

ということです。

とはいえ原理的に不可能でないのなら(そして不可能でないので)、純粋な関数プログラミングでGUIアプリケーションを作成するのにFRPがなんら必要でない、という趣旨には充分です。またこの主張自体はHaskellでのコードによっても充分に示されています。)

繰り返しますが、私がデスクトップアプリだのFRPだのコンポーネント指向だの書いているのは実用に耐えうるアプリについてであり、

住井 英二郎@esumiiさんとか、らくだの卯之助@camloebaさんに聞きたいのだけど、そもそも貴方たちはOCamlでデスクトップアプリ書けるんですか?

素朴な疑問なんですが、貴方がた、延々と私の関数型プログラミングの「無理解」やらスキルに偉そうなことを言い続けているけど、実際のところ、貴方たちは、関数型プログラミングで、実用に耐えうるデスクトップアプリとか書いたことあるんでしょうか?というか、書けるんですか??

実用に耐えない、関数合成もできない、極めて限定的で単純な「机上の空論」の誤魔化しコードを提示しろ、と言ったのではありません。

すでにReactを中心に実用化がバリバリとなされ活発に開発が進んでいる現状の2015年において、

とはいえ原理的に不可能でないのなら(そして不可能でないので)、純粋な関数プログラミングでGUIアプリケーションを作成するのにFRPがなんら必要でない、という趣旨には充分です。

みたいな、「原理的に不可能でない」と強弁するので、その原理的に不可能でない「FRPがなんら必要でない、という趣旨には充分」なやり方を採用したら、複雑なアプリは書けない、と来る。

だったら、「机上の空論」として、原理的に不可能でなくても、「机上の空論」ではない現実的なプログラミングにおいては、実用に耐えない、「充分」でないですよね?

挙句の果てに、そのダメさを自己正当化するために、「銀の弾丸ではない」とわけのわからない一般化をして、可能性の限定を正当化する、とめちゃくちゃな言い分をして、読者を愚弄しているのがこういう連中である、ということです。

『関数型プログラミングに目覚めた!』のレビュー(Day-1)の嘘と誤魔化し ー 自分たちが非実用的な「机上の空論」を語っている事をけして読者に語らないこと

非実用的な「机上の空論」を語っている事を最後の最後まで読者に教えないこと、あたかもその延長線上で、最終的には簡単に実用的なアプリケーションが書けると「見せかける」こと、これをやられて結局割を食うのは、そういう解説を読んで学び実務に活用しようと期待する真摯な読者、学習者です。

これが、世に広く普及して業務レベルのGUIアプリケーションを構築するプログラミング言語であると見做すことはできないプログラミング言語で解説されている、巷の多くの関数型プログラミングの解説において、私が観察する欺瞞です。

その欺瞞のアンチテーゼとして、対極に位置する私の根本的スタンスは、現状を誠実に観察して、このような世界的潮流で、これまでとは違った考え方が必要な関数型プログラミングなるものを、あくまで最後の最後まで実用に耐えるアプリを読者が書ける水準に到達するように、正しい道標(みちしるべ)を示すことです。
本当に世の中に役立つことを発信することです。

それって結局、最終的に使えるようになるの?

YES

NO

か。我々はつまるところ技術者でプラグマティストであるので、そこを気にします。

関数型プログラミングが、これまで広く一般のプログラマにとってメインストリームとして受け入れ難かったのは、主に2つ理由があるでしょう。

1.「考え方」の転換が必要だとは喧伝されるが、巷の解説はその部分をけして掘り下げることはなく、すぐに専門用語や数学的・形式的説明に終始してしまい、パラダイムシフトが体感できない。

2.先行事例が実質皆無であった。メインストリームになって当然の道具立てが存在しなかった。

この状況を踏まえると、関数型プログラミングは、結局最終的に使えるようになるのか?の答えは

NO

と審判が下され続けていたという過去の現実はなんら不思議ではありません。

私が著書『関数型プログラミングに目覚めた! IQ145の女子高生の先輩から受けた特訓5日間』 を執筆したとき、最終的な目的は、2014年にプログラミング世界を圧巻したFacebookのGUIコンポーネント指向FRPライブラリであるReactを真に実用的な関数型プログラミングのスキルのひとつの出口として提示することでした。

これって、最終的には使えるようになる、
関数型プログラミングに入門して、真剣に取り組んで、
満額回答 YES が得られる、という最終的な担保をつけたわけです。

その最終的な担保をつけた上で、もっとも根本的なレベルまでプログラミングを掘り下げて解説しました。

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

で詳説したとおり、基本的にはSICPが書いていること、世に高く評価されているかなり踏み込んだ要素まで書きました。

関数型プログラミングの「考え方」の根本には以上に示したような哲学的要素があり、そこはSICP以外にきっちりと解説している文献は見当たらなかったので、全力で書きました。

SICP自体も、この辺の真価を理解できる人ばかりではなく、まったく評価できない人が多く、賛否両論で有名な本でもあります。しかし、真価を理解できる人は高く評価し、関数型プログラミングの聖典とまで言われたりします。

結局のところ、私の試みとは、まず、Before、ほとんどのプログラマが拠り所にしているポジションからスタートして、徐々にSICPレベル以上の哲学まで踏み込み、パラダイムシフトしながら、最終的に本当に実用的なFacebookーReactまで、一本の筋を通しながら解説することでした。

「机上の空論」に執着し、実用レベルを強く期待する読者を騙すことを厭わない解説

プログラミングの世界的潮流と机上の空論としての関数型プログラミング②「素人としては深入りを避けたいと思いますが、そうだとすればそもそもJavaScript選んじゃったのが関数プログラミングへの無理解を象徴的に示すものなのだということになるような気がします。」(苦笑)
で、「机上の空論」ではない関数型プログラミング言語としてのJavaScriptの圧倒的優位性を立証しました。

今どき、

「そもそもJavaScript選んじゃったのが関数プログラミングへの無理解を象徴的に示すものなのだということになるような気がします。」

などと、平気で発言するような論者の解説など読んでも最終的には裏切られることはあらかじめ見極めていたほうが良いと思います。

既出の2点、

1.「考え方」の転換が必要だとは喧伝されるが、巷の解説はその部分をけして掘り下げることはなく、すぐに専門用語や数学的・形式的説明に終始してしまい、パラダイムシフトが体感できない。

2.先行事例が実質皆無であった。メインストリームになって当然の道具立てが存在しなかった。

という論点にまるで無頓着で、他人の解説のあら探し批判のための批判は精一杯試みるが、自分自身なにも生み出さない、生み出すつもりも鼻からない、という感じです。

2015年の多くの関数型プログラミング解説の読者は、その延長線上に、満額回答「YES」を期待している、実用レベルを強く期待しているわけですが、かなりそういう世界的潮流には無頓着です。

「そもそもJavaScript選んじゃったのが関数プログラミングへの無理解を象徴的に示すものなのだということになるような気がします。」

とのうのうと発言できるということは、この辺なんら勉強していない、というか、自身、関数型プログラミングの実用化にそれほど関心がなく、そういう現状において極めて偏った、倦厭されるべきスタンスで解説を展開しようとしているわけです。

その歪として、「机上の空論」になります。
「関数型プログラミングの考え方の違い」

「実用的なGUIライブラリを書く方法」
などは、何も教えてもらえない、最終的には読者は裏切られます。
「できない」ことを「あたかもできるように見せかける」ここがこの手の解説の典型的な欺瞞です。

本当はあさっての方向を向いており、読者をそちらへ誘導しようとしているのに、まるでその先に汎用的、実用性があるように見せかける大嘘、誤魔化し

それをやっているのが、
『関数型プログラミングに目覚めた!』のレビュー(Day-1)であり、嘘と誤魔化しですが、それにひとつ悪用されている「マジックワード」があります。

「銀の弾」ではない、という言葉を自らの嘘、誤魔化しの言い訳として悪用するな

ということですね。

この辺り、具体的に次回のエントリに続きます。

「お絵かきアプリ」と「クリックカウンター」を簡単に実装できると見せかける誤魔化しと嘘 原理的に可能かどうか?と実用的であるかどうか?は違うが、それを「銀の弾」ではない、という言葉をもって自らの嘘、誤魔化しの言い訳として悪用するな

2015年7月6日月曜日

【学習者要注意!】『関数型プログラミングに目覚めた!』のレビュー(Day-1)の「状態遷移関数」を使った非FRPのGUIコードがメモリリークする実用に耐えない不良品でしかなかったことについて 読者騙せて楽しいですか?

まず、
『関数型プログラミングに目覚めた!』のレビュー(Day-1)というのは、

@nonstarter
http://qiita.com/nonstarter
という素人かつ大学関係者を自称しながら、
言論人として文責をまったく負わずに済む、
素性のしれない匿名のQiita捨てアカウントによる、
書捨て記事であり、
「批判のための批判記事」です。

「レビュー」などと称していますが、対象となる書籍の良い部分は一切書いておらず、とにかく徹頭徹尾、揚げ足取り、歪曲捏造しているので、「批判のための批判記事」であることが露骨にあらわれてしまっていますが、本人はそのことに露ほども自覚がないのでしょう。

この【レビュー】読んでる人が、私のところへ送ってくださる感想は、

相手の方、(自称ではありますが)どっかの大学の教育者らしいのですよね。
そのような立場の人間が急いで私のコメントに返した内容は悲惨なものです。
終始、私がプログラミングの初歩も弁えない愚物であるかのように見せかけるための理屈をこねくり回しておよそ立場ある教育者としては恥ずべき、うっすら侮蔑をにじませた記述に終始しています。
ネットスラング的には「効いてる、効いてるw」様子で、泡くって焦ってる姿が見えます。

確認が取れましたら、おちょくる感じで反論コメントを書いてみようかとも思うのですが、
しかし、コメントを読んで口をついて出た言葉は
「度し難ぇ・・・」
やっぱりゾンビじゃないか!面倒くせぇーーー(笑)(失礼しました)
ちょっと心配なのが、こうした事を続けるとコメントを削除されないかという点です。
せっかくのリンクが無くなってしまいますからね・・・

といった感じです。

これから、記事をわけて、住井 英二郎@esumiiさんとか、らくだの卯之助@camloebaさんに聞きたいのだけど、そもそも貴方たちはOCamlでデスクトップアプリ書けるんですか?への「回答」の精査と、この
『関数型プログラミングに目覚めた!』のレビュー(Day-1)
シリーズによる本書への批判のための批判を精査していきます。

【学習者要注意!】『関数型プログラミングに目覚めた!』のレビュー(Day-1)の「状態遷移関数」を使った非FRPのGUIコードがメモリリークする実用に耐えない不良品でしかなかったことについて 読者騙せて楽しいですか? 

さて、私が、
住井 英二郎@esumiiさんとか、らくだの卯之助@camloebaさんに聞きたいのだけど、そもそも貴方たちはOCamlでデスクトップアプリ書けるんですか?

と示した「挑戦」、というか、

「あんたらどうせ最終的には実用に耐える関数型プログラミングのコードなんて提供できないんでしょ?(笑)」

という「確認」記事を出したところ、

案の定、メモリリークする実用に耐えない不良品しか提示されませんでした。

こういう連中の言うことを鵜呑みにしがちな【学習者要注意!】です。

問題のコメントとコード

Lambada 0 contribution May 30, 2015 11:03
 
本書の数々の重大な誤りに対し正当な技術的批判を加えても
著者から激しい誹謗中傷行為を受けるようですので匿名で失礼します。
OCamlで変数への破壊的代入を行わないでごく簡単に書くと以下のようになります。
(通常のOCaml環境ではwait_next_eventを用いるべきですが、
http://try.ocamlpro.com で動くようにloop_at_exitを用いています。
後者ではChromeもしくはFirefox推奨です。)

clickcounter.ml

(* クリックカウンター *)
open Graphics
let () = open_graph " 900x600"
let rec k c =
  clear_graph ();
  moveto 450 300;
  draw_string (string_of_int c);
  loop_at_exit [Button_down] (fun event -> k (c + 1))
let () = k 0 ;;

mousedrag.ml

(* マウスドラッグ *)
open Graphics
let () = open_graph " 900x600"
let rec k = function
  | `Up ->
      loop_at_exit [Button_down] (fun event ->
        k (`Down(event.mouse_x, event.mouse_y)))
  | `Down(x, y) ->
      loop_at_exit [Button_up] (fun event ->
        moveto x y;
        lineto event.mouse_x event.mouse_y;
        k `Up)
let () = k `Up ;;

いずれも岡部氏の主張とは正反対に、ストリームなどまったく用いず
状態渡しで実現されています。
逆に、岡部氏のコードはよく見ると state = x; という部分で
カウンタ変数への破壊的代入を行っており、何ら関数的ではありません。
(無駄に複雑なスパゲッティ・プログラムですが、よく読むと要するに
state = state + 1; と同じ破壊的代入です。)
ご参考になれば幸いです。

↑ 超おもしろいですね。既定路線すぎて。こんなことして技術者として恥ずかしくないんでしょうか?(笑)

いかにも「関数型プログラミングで実用的なGUIアプリ書いたことない、普段から書いておらず無頓着な低スキルコーダー」丸出しのコードが提示されました。

OCamlに詳しくない読者の方々にかいつまんで説明すると、これは要するに、再帰関数の無限ループを使ったコードです。

拙書にも書きましたが、再帰関数とは、自分自身を呼び出す関数のことで、無限ループになります。

それでですね、こういう再帰関数の無限ループをもって、GUIアプリを書いてはいけません。絶対に。

なんでか?メモリリークするからですよ。

関数を呼び出すと、その関数がが何やったかきちんとメモリに記憶しています。

コールスタックと言って、ちょっと気の利いたプログラマなら誰でも知っている基礎知識です。

コールスタック (Call Stack)は、プログラムの実行中にサブルーチンに関する情報を格納するスタックである。実行中のサブルーチンとは、呼び出されたが処理を完了していないサブルーチンを意味する。実行スタック (Execution Stack)、制御スタック (Control Stack)、関数スタック (Function Stack)などとも呼ばれる。また、単に「スタック」と言ったときにコールスタックを指していることが多い。コールスタックを正しく保つことは多くのソフトウェアが正常動作するのに重要であるが、その詳細は高水準言語からは透過的である。

再帰関数も当然、例外なくスタックを保持してメモリを消費します。
つまり、再帰関数を1回すごとに、ちょっとずつメモリを消費します。

しかもGUIのイベント拾うという、ただ単なる繰り返しをしたいためだけに、無意味にメモリを消費していく、つまり、メモリリークするんですね。

ひじょーにアホらしい設計であるのは、すぐ解ると思いますし、実際に、マウスポインタの動きを拾うイベントを無限再帰ループで拾ったりすると、スタックがオーバーフローします。いわゆるStackOverflow(スタックオーバーフロー)ですね。今どき耳慣れた言葉でしょうし、それほど超基本的でありがちな設計ミスというか、結構スキルレベル低めのバグです。

不良品コードの証拠として超頻繁なイベントをシミュレートしてスタックオーバーフローさせてやりました・・・

http://try.ocamlpro.com で動くようにloop_at_exitを用いています。
後者ではChromeもしくはFirefox推奨です

と、なんか偉そうに書いておられるので、実際に、その推奨されているChromeブラウザで

http://try.ocamlpro.com
にアクセスして、

動作確認のために、超頻繁なイベントをシミュレート、つまり、マウスイベントを待たずに、即時、再帰関数を呼び出すようにコードを改変して、

open Graphics
let () = open_graph " 900x600"
let rec k c =
  clear_graph ();
  moveto 450 300;
  draw_string (string_of_int c);
  k (c + 1)
let () = k 0 ;;

をペーストして、走らせてみると、即効でChromeタグの反応がなくなり、こうなりました。

enter image description here

「He’s dead, Jim!」
ChromeがRan out of memoryしたそうです。

別にコード改変せずとも、じっくり待てば、通常数万ループ程度で、どんなコードでもスタックオーバーフローするし、この提示された不良コードも最終的に必ずこうなります。

念のためですが、イベント待たずに即時呼び出ししたのは、メモリリーク、スタックオーバーフローの確認テストの「時間節約」のためです。

こんな超基本的な過ちを犯している、お粗末なコードをありがたがるQiitaの記事主

nonstarter 135 contribution May 30, 2015 11:18

Lambadaさん
著者の主張に対する反例となるOCamlコードをお示しいただきどうもありがとうございます。私の手元でも問題なく動作しました。

デスクトップ云々の件もそうですが、ブログを含む著者の一連の主張は明らかに誤ったものであることが多く、いったい何がしたいのかを忖度することがおよそ困難であるように思います。やはり、いちいち誤りに反応せず、放っておくのが正しいのでしょうか。初心者の方が早期に問題に気がつけばいいのですが。

はい、笑止千万。なんでしょうか、この茶番は。
確かに、メモリリークする不良品のコードであろうが、なんであろうが、たかだかクリック数十回程度では、それは「手元でも問題なく動作しました!」となるように見えるでしょうね。

いうまでもなくスタックオーバーフローするまで、っていう時限爆弾抱えてますが。

そういう、使い込まないと判明しないスタックオーバーフロー、メモリリークのバグが一番たちが悪くて、あなたたち、そういうのを流布しているわけです。

やはり、いちいち誤りに反応せず、放っておくのが正しいのでしょうか。初心者の方が早期に問題に気がつけばいいのですが。

とか、偉そうに書いていますが、どの口で言ってるんでしょうか?

この人たちはそんなレベルにはいませんし、というか結構多くの技術者が、「ああこれはまずいコードだな」って気がついていると思うんですよ。気が付かずに、こういう連中の戯言を鵜呑みにして「ああやっぱり著者=岡部のいうことは間違いだったんだ」とか結果的に多くの初心者を騙している自覚を持ってください。かなり有害。

念の為に、JavaScriptで、イベントつきの無限再帰ループまわすコードは以下のようになります。
原理は一緒で面倒なのでコンソールでも動くように、ユーザ入力なしのsetTimeoutイベントを使って1秒毎のタイマーにしてます。

var f = function(c) {
  console.log(c);
  setTimeout(function() {f(c+1);}, 1000);
};

f(0);

これでも、確かに「著者の主張に対する反例となるOCamlコードをお示しいただきどうもありがとうございます。私の手元でも問題なく動作しました。」と、錯覚することは可能です。

きちんと1秒ごとに数字がカウントアップされていくのがJavaScriptのコンソールで確認できるでしょう。
しかし、同様に、超頻繁なイベントをシミュレートするために、setTimeoutを外して、即時、再帰関数呼び出しするようにコードを変えてみます。

var f = function(c) {
  console.log(c);
  f(c+1);
};

f(0);

結果、

17930
17931
17932
17933
17934
17935
17936
17937
17938
17939

17734
:0

RangeError: Maximum call stack size exceeded

はい、予想通り、というかこんなの既定路線なんですが、スタックオーバーフローになりました。

再帰関数で、イベント拾うようなコードを書いてはいけません。常識レベルですが、その程度の常識もないスキルの技術者の分際で、常識的な発言をしている論者に対して、偉そうに不当に糾弾しているのか、もしくは、
ちょっとマズイかな?でも、まあ岡部の言うことが間違っていると読者に思わせて騙せればそれでいいや、みたいなことでしょうか?

可能性は2つしかないですが、どっちにしてもかなりろくでもない連中です。

逆に、岡部氏のコードはよく見ると state = x; という部分で
カウンタ変数への破壊的代入を行っており、何ら関数的ではありません。
(無駄に複雑なスパゲッティ・プログラムですが、よく読むと要するに
state = state + 1; と同じ破壊的代入です。)
ご参考になれば幸いです。

あのねえ。なにが、ご参考になれば幸いだと。

自分のコーディング能力で理解しにくいコードを、
「無駄に複雑なスパゲッティ・プログラム」
とか、てきとーなデタラメ言うなと。
ヤ●ザの因縁つけ、当たり屋となんら変わらないやり方ですね。

カウンタ変数への破壊的代入を行っており、何ら関数的ではありません。

読むと要するに
state = state + 1; と同じ破壊的代入です。

違います。

拙書にも何度も書いていますが、関数ライブラリ自体は「ハードウェアモード」の命令型であったり、破壊的代入もします。
そうやって用意した、関数ライブラリを利用するコードが関数型であるということです。

こんな因縁が通るのであれば、FacebookのReactだって、FRPのコードは破壊的代入してますから、

なんら関数的ではありません。

と因縁つけられるわけですね。

まあ、Facebookの技術者は優秀なので、間違ってもこんなアホな無限再帰ループをもって、Reactライブラリ実装していないことだけは100%確かです。実際メモリリークなんてしていないから。

追記(2015/05/30):デスクトップアプリケーション??

著者はブログで@camloebaさんや@esumiiさんにデスクトップアプリケーションをOCamlで作成するように求めているようです。著者が挙げている「クリックカウンター」や「お絵かき」の類を作成するのに特別なexpertiseは不要で、単に使用するGUIライブラリ・グラフィックスライブラリのドキュメントが読めれば充分なので、その趣旨は私にはまったく判然としません(当該の記事で著者がOCamlで「お絵かきロジック」を実装してみせよと要求する一方で自身ではJavaScriptによるそれを公表していない点も気になります)。もちろん、OCamlであれHaskellであれ破壊的代入の類の副作用を使用せずに書くのもなんら困難ではありません。
クリックカウンターはHaskellでOpenGLに対するラッパーであるGlossライブラリを使うならばこうなります(手元の環境でインストールしやすかったのでこれを使いました):
ClickCounter.png

import Graphics.Gloss.Interface.Pure.Game
main = play disp colour freq ini draw eTrans tTrans
 where
 disp = (InWindow "Click Counter" (225, 150) (40, 40))
 colour = white
 freq = 100
 ini = 0
 draw = Translate (-75) (-50) . Text . show
 tTrans _ n = n
 eTrans (EventKey (MouseButton LeftButton) Up _ _ ) n = n + 1
 eTrans _ n = n

お絵かきアプリも難しいところはなにもなく、ドラッグ中かどうかのフラグをマウスボタンの上下で切り替えて管理し、フラグを状態遷移関数(状態とイベントをとって状態を返す純粋な関数)の引数として渡していくだけです。
DrawingCanvas.png

import Graphics.Gloss.Interface.Pure.Game
import Data.Monoid(mconcat)
main = play disp colour freq ini draw eTrans tTrans
 where
 disp = (InWindow "Drawing Canvas" (400, 400) (40, 40))
 colour = white
 freq = 100
 ini = (False, [], [])
 draw (_, path, paths) = mconcat $ map line $ path:paths
 tTrans _ w = w
 eTrans (EventKey (MouseButton LeftButton) Down _ _ ) (_, path, paths) = (True, path, paths)
 eTrans (EventKey (MouseButton LeftButton) Up _ _ ) (_, path, paths) = (False, [], path:paths)
 eTrans (EventMotion pos) (True, path, paths) = (True, pos:path, paths)
 eTrans _ w = w

いずれにせよ、状態機械の数学的構成では状態遷移が状態と入力から状態への関数として表現されるというだけのことではあり(状態渡し)、状態遷移が複雑になれば状態遷移を純粋な関数として表現する作業も複雑になり困難になるので(そしてそれはしばしば綺麗な関数合成では上手くいかないようなものになることが多いので)、結局のところ少なくとも現状のFRPも(イベントのシグナルから状態のシグナルを構成する際に状態遷移をそうした関数で表現する必要が出てくるので)本質的には銀の弾丸にはならないと言わなければなりません(関数プログラミングで書けるということの恩恵はもちろんあるとしても)。

同じです、Haskellで書こうと何しようと、一緒。

いずれにせよ、状態機械の数学的構成では状態遷移が状態と入力から状態への関数として表現されるというだけのことではあり(状態渡し)

みたいなことを、GUIアプリケーションのイベントを拾うために実装してはいけません。
マウスポインタの移動みたいな超頻繁なイベントが発生するこういう「お絵かきアプリ」なんて、即死確定ですが、普段関数型プログラミングでGUIアプリなんて書く経験が未熟なので、そんな基本的なことすら気にならないのでしょう。

要するにこういう連中は、関数型プログラミングの「理論」のことしか考えておらず、状態機械という言葉が使いまわせても、それが実用的にはメモリリークしてスタックオーバフローすることすら知らない

ということです。

あとね、

当該の記事で著者がOCamlで「お絵かきロジック」を実装してみせよと要求する一方で自身ではJavaScriptによるそれを公表していない点も気になります

こういう「気になります」程度の「揚げ足取り」部分をわざわざ太字にするとかしょうもないことしているから、各所で、レビューを装った単なるネガキャンにすぎない、だと見透かされているのだから留意したほうが良いです。

こちらは、自前で、FRPのライブラリを実装しているのだから、「お絵かきロジック」でもなんでも時間変化するアプリは書けるし、本書を読んではみたが、巧妙に論評を避けている(内容を理解できていない)Day5に「お絵かきロジック」と等価のReactサンプルアプリ書いているのですが、理解できなかったから無視したわけですね。

結論

住井 英二郎@esumiiさんとか、らくだの卯之助@camloebaさんに聞きたいのだけど、そもそも貴方たちはOCamlでデスクトップアプリ書けるんですか?

あんたらどうせ最終的には実用に耐える関数型プログラミングのコードなんて提供できないんでしょ?(笑)

という「確認」記事を出したところ、

案の定、メモリリークする実用に耐えない不良品しか提示されませんでした。

FRPを利用しない自称関数型GUIアプリは、メモリリークする不良品しか書けなかった

こういう連中の言うことを鵜呑みにしがちな【学習者要注意!】です。

というか、こういうのは序の口です。
この記事主はまだまだ、やらかしているので、それ全部反論していきます。

こんないい加減な知識とコードを書きなぐった記事で、人の主張を不当に否定して、学習者騙せて楽しいですか?いい加減にしたらどうか?

nonstarter 135 contribution May 31, 2015 11:27
hiyakashi_さん
マウスポインタの現在位置を表示するというような、先行するイベントに依存しない処理のみの場合には状態を渡して云々は特にありませんが、先行するイベントに依存するようなある程度複雑な処理をしようとすれば、どうしても状態とイベントから状態への純粋な関数を使って、シグナル(特にイベントのストリーム)を畳み込んで状態のストリームを得ていくことになりますよね(FRPを支援する代表的な関数型プログラミング言語Elmのfoldp : (a -> state -> state) -> state -> Signal a -> Signal stateがまさにそれです)。
ElmClickCounter.elm
clickCount : Signal Int
clickCount = foldp (\click total -> total + 1) 0 Mouse.clicks
一定以上に複雑なものを関数型スタイルで書こうとする場合には、状態遷移を純粋な関数として表現するという手法をFRPそれ自体によって免れることはできません(私が理解する限りでは)。
私の結論は、著者であるkenokabeさんの言葉を額面通り受け取れば、彼はFRPを理解していないのだろう(或いは少なくともfoldpのような畳込みが必要になるような一定以上に複雑なFRPを経験したことがないのだろう)、というものです。
GUIアプリケーションの構築に際して、IOモナドによるプログラミングがうまくいく/うまくいかないその程度に、現状のFRP(念頭においているのはElmですが)もうまくいく/うまくいかないだろう、と私は考えています。

また、

私の結論は、著者であるkenokabeさんの言葉を額面通り受け取れば、彼はFRPを理解していないのだろう(或いは少なくともfoldpのような畳込みが必要になるような一定以上に複雑なFRPを経験したことがないのだろう)、というものです。

とか、メモリリークする不良品コードをありがたがる人が、FRP理解していないとか因縁づけ。

Lambada 0 contribution May 31, 2015 15:37
岡部氏は
『関数型プログラミングのパラダイム内で、そういうマウスボタンが押されているのか、押されていないのか?入力の状態が時間遷移していく場合、上記ブログエントリでも論じたSICPでも、拙書でも解説しているとおり、FRPの実装が必ず必要となります。
「時間」が本質的だから、時間をを集合、SICPの言葉でいえば「遅延ストリーム」とした実装が必要です。』
と断言しているにも関わらず、現に私やnonstarter氏のコードのように
「時間」も「ストリーム」も出てこない関数的状態渡しによる実装が
容易である以上、岡部氏の誤りは明らかだと思います。ご参考まで。

念の為ですが、

拙書でも解説しているとおり、FRPの実装が必ず必要となります。
「時間」が本質的だから、時間をを集合、SICPの言葉でいえば「遅延ストリーム」とした実装が必要です。

っていう主張は普通に維持します。

断言しているにも関わらず、現に私やnonstarter氏のコードのように
「時間」も「ストリーム」も出てこない関数的状態渡しによる実装が容易である以上、岡部氏の誤りは明らかだと思います。ご参考まで。

はい確かに、メモリリークしてスタックオーバーフローするような「関数的状態渡し」=再帰関数の無限ループみたいなアホな実装は容易ですね。
もちろん、気の利いた技術者は普通にアホなやり方だと知っているのでやらないだけですが。
「理論」のことだけ考えて、実用的なGUIアプリケーションを書くことが念頭にないから、こういう低レベルなことを言って、間違っていない人を誤りが明らかだ、とかいう愚を犯す。

まだまだ続きます。

Popular Posts