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年4月17日金曜日

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

enter image description here

バールーフ・デ・スピノザ(Baruch De Spinoza, 1632年11月24日 - 1677年2月21日)は、オランダの哲学者、神学者。一般には、そのラテン語名ベネディクトゥス・デ・スピノザ(Benedictus De Spinoza)で知られる。デカルト、ライプニッツと並ぶ合理主義哲学者として知られ、その哲学体系は代表的な汎神論と考えられてきた。また、ドイツ観念論やフランス現代思想へ強大な影響を与えた。
スピノザの汎神論はプラトン哲学的な一元論でもあり、後世の無神論(汎神論論争なども参照)や唯物論(岩波文庫版『エチカ』解説等参照)に強い影響を与え、または思想的準備の役割を果たした。生前のスピノザ自身も、神を信仰する神学者でありながら、無神論者のレッテルを貼られ異端視され、批判を浴びている。
スピノザの肖像は1970年代に流通していたオランダの最高額面の1000ギルダー紙幣に描かれていた。

純粋関数型プログラミング言語 spinoza

JavaScriptでつくられた、
JavaScriptで動作する、
純粋関数型プログラミング言語
spinoza (スピノザ)。

開発者である筆者の科学的世界観、宗教観である「汎神論」を形成した
哲学者スピノザをリスペクトした命名です。

spinoza Programming Language

spinoza

- is a Functional Reactive Programming (FRP) language

- employs Lazy evaluation strategy

- runs on JavaScript Engines (browsers & node.js)

- is written in JavaScript

spinoza 3.0.2

http://libraries.io/npm/spinoza

spinoza pure functional programming language

Homepage: https://github.com/kenokabe/spinoza

Platform: npm

Language: JavaScript

License: MIT

View on registry: https://www.npmjs.com/package/spinoza/

Hello world

spinoza.world = $('hello')(out);

spinoza.world> [ ‘hello’ ]

スピノザの汎神論はプラトン哲学的な一元論、
つまり数学世界の論理への一元論です。

何をどう一元化したのか?というと、
デカルトが「我思う故に我あり」と方法的懐疑の末に見出した
疑いきれない我々がもつ意識、人間の【精神世界】
そこから合理的に導出したプラトン哲学で語られるイデア論の【論理世界】
あともちろん、デカルトが終生解決できなかった、この我々の目の前に広がる【物質世界】
の3つの要素をすべてイデアの世界の論理へ一元化したのでした。

spinoza.world = $('hello')(out);

この方程式の左辺の
spinoza.world【物質世界】の「現在の事象」です。

この方程式の右辺の
$('hello')(out)【論理世界】の「論理」です。

この方程式の左辺と右辺が等しい、と決めたのはプログラマなので
=【精神世界】における人間的行為である「計算」です。

これら【論理世界】【物質世界】【精神世界】の3つの世界は、
コードという【論理世界】の「論理」として、
コード上で表現される数学の方程式により一元化されています。

$('hello')(out)は、純粋な「論理」です。
これ自体は、【論理世界】にある「論理」でしかなく、
これ自体では【物質世界】には全く反映されません。

しかし、
spinoza.world =と【物質世界】の現在事象と等しい、
と関係性を【精神世界】の人間の意識が規定すると
$('hello')(out)という【論理世界】の「論理」がはじめて
【物質世界】の現在事象として「物質」化されます。
つまり「計算」されます。

spinoza.world> [ ‘hello’ ]

とコンソールに表示されますが、
「論理」からそういう「副作用」として
この【物質世界】にあるコンソールで「物質」化されたという意味です。
この「副作用」は(out)という「論理」により引き起こされています。

$('hello')(out)

というコードならば、
「論理」がただそこにあるだけで、「計算」されません。(遅延評価)

spinoza.world = $('hello');

というコードならば、
「計算」という「物質」化をしても、
$('hello')という「論理」が計算機内部で「計算」されるのみで、
コンソールには何も表示されません。

spinoza npm test


$('hello');
// no computing, lazy evaluation

spinoza.world = $('hello');
// computing, but no output

spinoza.world = $('hello')(out);
// ['hello']

spinoza.world = $('------------')(out);

spinoza.world =
  $('one')(out)
  ($('two')(out))
  ($('three')(out));
//[ 'one' ]
//[ 'two' ]
//['three']


spinoza.world =

  ($('=================')(out))
  ($('hello')('world')(out))
  // [ 'hello', 'world' ]
  ($('------------')(out))
  ($('hello')(out)(out))
  // ['hello']
  // ['hello']
  ($('------------')(out))
  ($('hello')(out)('world')(out))
  // ['hello']
  // [ 'hello', 'world' ]
  ($('------------')(out))
  ($(1)(2)(3)(out))
  // [ 1, 2, 3 ]
  ($('------------')(out))
  ($(1)(2)(3)(plus10)(out))
  // [ 11, 12, 13 ]
  ($('------------')(out))
  ($($(1)(2)(3)(plus10))(out))
  // [ 1, 2, 3, [Function] ]
  ($('=================')(out));



===== spinoza =====
mode: node/io.js
spinoza.world> [ ‘hello’ ]
spinoza.world> [ ‘————’ ]
spinoza.world> [ ‘one’ ]
spinoza.world> [ ‘two’ ]
spinoza.world> [ ‘three’ ]
spinoza.world> [ ‘=================’ ]
spinoza.world> [ ‘hello’, ‘world’ ]
spinoza.world> [ ‘————’ ]
spinoza.world> [ ‘hello’ ]
spinoza.world> [ ‘hello’ ]
spinoza.world> [ ‘————’ ]
spinoza.world> [ ‘hello’ ]
spinoza.world> [ ‘hello’, ‘world’ ]
spinoza.world> [ ‘————’ ]
spinoza.world> [ 1, 2, 3 ]
spinoza.world> [ ‘————’ ]
spinoza.world> [ 11, 12, 13 ]
spinoza.world> [ ‘————’ ]
spinoza.world> [ 1, 2, 3, [Function] ]
spinoza.world> [ ‘=================’ ]

圏論の考え方とは?

圏論とは、もともとは、
【数】というものの代数的構造を論じるための数学理論です。

圏論で考える「【数】というものの代数的構造」とは?

【数】というのは、
無限に自己参照する再帰構造になっている「何か」です。

「1たす1」

で、

「1」という【数】

「たす1」という【数への操作】

が、まったく同じものであるという

【数】=【数への操作】

と、無限に自己参照する再帰構造になっている「何か」であり、
こういう代数構造を論じるのが「圏論」のはじまりです。

圏論で考える「【数】というものの代数的構造」をそのままプログラミングに適用してやると?

【数】

【数への操作】
はまったく同じものです。

プログラミングの言葉で、まったくそのまま言い換えると、

【値】

【値への操作】
はまったく同じものです。

また言い換えると、

【値】

【関数】
はまったく同じものです。

また言い換えると、
【集合】

【写像】
はまったく同じものです。

関数型プログラミングとは、【集合】と【写像=関数】で論理操作を繰り返す数学的なプログラミング手法である

関数型プログラミング言語では、

【関数】が【値】(オブジェクト)として扱える、
【関数】が「ファーストクラス・オブジェクト」であるのは、

プログラミングの土台として存在する数学の代数構造で、

【数】

【数への操作】

あるいは
【値】

【関数】 

あるいは
【集合】

【写像】

の区別が一切ない、同じものである、という根源的な原理をそのまま反映しているのです。

純粋関数型プログラミング言語「spinoza」

以上のような極めてかんたんで至極明快な事実をそのままプログラミング言語としてJavaScriptで実装したのが、

純粋関数型プログラミング言語「spinoza」である、ということになります。

【値】

【関数】
はまったく同じものである必要があるので、
いくら、JavaScriptという関数型プログラミング言語では関数がファーストクラスになっているからといっても、あくまで
【値】≠【関数】
と別々の存在として扱われてしまっています。

これでは我々が希求する「純粋関数型」のプログラミング言語としてははかなり都合が悪いので、
JavaScriptでも徹底的に、
【値】=【関数】
となるようにハックしてやります。

spinozaの基本構造

spinozaの基本構造はもちろん
【値】であり【関数】であり、その両者の区別がまったくない「何か」です。
代数の基本構造として
【数】が【数への操作】とまったく区別がつかない「何か」であることを論じる
「圏論」と同じはなしです。

Lispデータ構造の問題点

ジョン・マッカーシーが開発したLISPは

(関数 引数 引数 引数)

という

S式
で表記され、プログラミング構文が存在しません。

> (+ 1 2)
3

データ=コード
コード=データ
という(一見)美しい構造で、コードを変更するにはデータを変更すればよい、というコード自身を第一級(ファーストクラス)オブジェクトとして扱うことができます。

最近、巷で若干注目を集めている、Javaでも実装されはじめた リフレクション
の最上級機能を言語構造として先天的に実装しているようなもので、自己定義する人工知能研究の分野(マッカーシーの専門分野)でよく使われてきたようです。
 
しかし、私がLISPを関数型言語の大御所として試すなかで問題だと思った事をざっくばらんに書きます。

すでに【脱アルゴリズム宣言】シリーズで述べたように、関数型パラダイムは、まず最初にデータをまるごと用意し、そこに関数操作を加えていくという宣言をするという思考・指向でした。

こういうデータをまるごと用意する、データのコレクション、リストという発想は、LISPのリストがオリジナルなのですが、遺憾なことに

(関数 引数 引数 引数)

っていうのが命令型パラダイムであるのに気づくでしょうか?

(Do what what what)

という構造になっている。

関数型パラダイムであるならば、こう書くべきでしょう。

(What Do Do Do)

(データ 関数 関数 関数)

そして、実際のところ

(関数 引数 引数 引数)

というデータ構造のために様々な目に見える弊害がLISP/Schemeにはあります。

(関数 引数 引数 引数)とS式の最初が(データでなく)関数と決め打ちしているので、

(データ データ データ) たとえば

(1 2 3)

というデータをそのまま書くとエラーになるんですね。だってまず 1を関数として実行しようとするけど、関数ではないので。

すべてがデータでありコードであり、コード自身を第一級(ファーストクラス)オブジェクトとして扱うことができる、という看板の割にあまりにもお粗末な結果です。

これを回避するために、

>(list 1 2 3)
(1 2 3)

という、見た目、カッコの位置がずれてデータ構造が変わってしまう方法
だったり

カッコの位置が一緒のデータ構造を維持しようと思えば、

> (quote (1 2 3))
(1 2 3)

あるいは、quoteのショートカット記号を用いて

> '(1 2 3)
(1 2 3)

と書く必要があります。このクオート記号すぐ打つの忘れるんですよね。非常に面倒くさい。美しくない。

そしてよく考えてみると、S式の要件定義によれば、

(関数 引数 引数 引数)

なので、

この

(quote (1 2 3))

の中身のかっこ(1 2 3)
はS式じゃないだろ!ってことになります。

そもそも(1 2 3)ってそのまま書いたらエラーになるから、quote付けたけど、定義どうりならばどっちにせよ無理で、

最初の

(list 1 2 3)

って書くしか整合性を保つ方法がない!ってことになります。
どうなってるのか?

実は、

(quote (1 2 3))

の場合、引数である
(1 2 3)
っていうのは、S式として評価されません。

S式でない評価しない特別なデータとして扱います。例外です。

例外、美しくないですね。余剰なルールが追加されるわけで単純さが損なわれるからです。

こんな調子では、

「すべてがデータでありコードであり、コード自身を第一級(ファーストクラス)オブジェクトとして扱うことができる」

と鵜呑みにして統一的に操作できると突き進んでも、いろいろ面倒なパッチを加えながらやるしかない未来が待ち受けているのは明々白々な状況です。

  • 命令型オリエンテッドなS式の表記

  • 破綻したデータ表現

どうにか回避策は無いものか?と試行錯誤しましたが

「ありませんでした」

「そして根本的な理由がわかりました」

S式のリストは、
Singly_linked_list
というリスト構造になっています。

LISPのすべてを束縛するS式の表記は、このSingly_linked_list
というリスト構造と等価であり、問題を解決するにはここからどうにかしないといけない。

LISPのリストで
((a b c)(d e f)(g h i))
は、
http://www.ki.nu/OHP/dot.emacs/list-drawing.html
(ToDo 図作成)

のようになっており、データの末尾を示すため終端にnilという空データをにつけます。

これは美しくない。

  • データの末尾を示すため終端にnilという空データをにつける

というのもルールの一つです。

このルールどおりのものをPureList(純リスト)とし、LISPのS式はそれで構成されているのですが、このルールをぶちやぶって、任意のアトムを終端につけることだって可能です。この場合は
PureListじゃないので各種操作は破綻する。

セルを自由自在に組み合わせられる、柔軟な表現方法だとおもいきや、こういうルールがある。
そしてルールどおりにするならば、新たなリストを既存のリストに結合する際には、終端のnilを一旦外し、そこに接続するという2ステップの置換になる。
自由に接続じゃなくて、置換です。

何かがおかしい、何もかもがしっくりこない、と思ったので、思案の上、自分が理想とする純粋関数型言語をJavaScript上で書きました。

LISPを純粋関数型言語として全面的に書きなおす。

LISPと逆方向のリスト構造を基盤とする。

リストはEmplyPairという無限再帰構造をもつ0に相当するペアから始まる任意の構造である。

終端に特別な操作がない、特別ルールがないのですべての構造がPureである。

遅延評価であり、必要な構造のみ随時評価されていく。

関数はファーストクラスオブジェクトであり、さらにLISPのように関数とデータの区別はしない

すべての関数はデータである。

すべてのデータは関数である。

(1 2 3)というデータは(1 2 3)とクオート無しでクリーンにデータどおり表記され遅延評価される。。

もはやLISPとは別物の新言語である。

JSで書かれている、JSで動作する。

そこで私はLispのリストデータ構造を逆にしてみました。

enter image description here

こういうパーツがあるところまでは同じ。
でも、最初のパーツは必ず自分自身を参照させることにした。

enter image description here

要するに、リスト構造の頭に、数学の0(ゼロ)に対応するものを用意した。

enter image description here

こいつは、当然、無限再帰構造を成します。

数学の0が無限と対称になっているのと同じ構造です。

この0もしくは無限を起点に

enter image description here

こうやってどんどん別のパーツを接続していけば、
何をどうやろうと、全部が全部「純リスト」になります。
ターミネーターをつける置換も必要ありません。


【値】=【関数】なので、
【値】を【関数】として作用してやると、
自分自身を新たに含めた【値】=【関数】という配列になります。

[ 1 ][ 2 ]を作用させると、[ 1, 2 ] になります。

[ 1, 2 ][ 3 ]を作用させると、[ 1, 2, 3 ]になります。

あらゆる階層でこの無限ループになります。

リスト構造の頭に、数学の0(ゼロ)に対応するパーツ
enter image description here

enter image description here

数学の0が無限と対称になっているのと同じ構造の無限再帰構造です。

spinozaによる「評価=計算」

【値】であり同時に【関数】であるものは、
「計算」される必要があるのですが、
同じものなので、唯一の「計算機」しか必要ありません。

それがcomputeです。
いわゆるEVALとAPPLYを融合した唯一の「計算機」です。

computeは当然無限再帰構造をなします。

数も操作も、
値も関数も、
まったく同じもので自分自身を参照する無限再帰構造になっているのだから、
それを計算する「計算機」だって無限再帰構造になるんですね。

実際「1」っていう数は代数の世界で、
無限再帰構造になっているというのは最初から説明しているとおりです。

論理の世界ではそうなんです。
しかし、プログラミングの世界では、
「1」は「1」としてしかもはや表現のしようもないもので、
「1」を延々と計算すること、物質化することは不可能ですし、
そんなことをする必要は一切ないわけです。
数学でも「1」は紙に「1」って書いてしまうだけで、
1+1というような問題を計算するときに、
「1」が無限再帰構造であるからという理由で延々と無限ループの計算にはなりません。

spinozaの数学的基礎

Foundation


Inspired by John McCarthy’s LISP

spinoza is founded on an inverted data structure of
Singly linked list and S-expression.

Pair

001

This is the fundamental unit of spinoza.
Pair has a pair of hands to point a pair of any objects.


Pair points a pair of objects

Now, a Pair points objects: a and b.


Pair notation

When a Pair points objects: a and b, it’s expressed as {a b} in Pair notation.


Pair can point itself


Empty Pair

When a Pair points itself, since it’s a form of Self-reference, an Infinite recursion occurs.

Accordingly, the Pair notaion is { { {…} {…} } { {…} {…} } }, so we simply express the entity as { }, and let’s call it Empty Pair.


Push Pair

A Pair can point another Pair so that we can joint Pairs.

Now let a pair point another Pair and 5 by each hands.
Let’s call this special action Push.

In this case, we push 5 to an Empty Pair.

The Pair notation is { {} 5 }.


Push another Pair

In the same manner, we can push another Pair.

We push 2 to the previous sequence.

The Pair notation is { { {} 5 } 2 }.


Push to any sequence


Sequence

Here, spinoza explicitly defines a term Sequence for this form.
Please note this is the same term and meaning of Sequence in Mathematics.

At the same time, instead of Pair notation : { { { {} 5 } 2 } 7 }, it can be simply expressed as ( 5 2 7 ).


Push function

A Pair can point function.

Accordingly, we can push function to any Sequence.

In this case, we push a function to (5).


A case of Push function

When we push a function : plus2 to (5), the result is (7).


Function is Sequence

The function : plus2 is fundamentally some Sequence.

plus2 consists of a Sequence : ( plus (2) ).

(2) is an attribute Sequence of the function.

( 5 (plus (2)) ) is equivalent to (7).


Everything is a function and Sequence

Everything is function in spinoza.

In this case, 3 is a function that maps a source : ( 5 ) to a target : ( 5 3 ).

Consequently, since function is Sequence in spinoza, everything is Sequence in spinoza.

Therefore, 3 is a function and at the same time, is a Sequence.

However, 3 is 3. There is no other way to express than just 3 in spinoza.


Every Sequence is a result of function to Empty Pair


Function composition

Function composition is naturally expressed in a form :
( source function fucnction ) in *spinoza.

Please note the source = ( 1 ) as a Sequence, not 1.


1 +2 +3 = 6

spinoza has a short-cut notation : + corresponding to plus function.


1 +(2 +3) = 6

( 1 (+ ( 2 (+ ( 3 ) ) ) ) ) = ( 6 )


0 コメント:

コメントを投稿

Popular Posts