ビスケットのあれこれ

ビジュアル言語ビスケット(Viscuit)に関するあれこれを書いてゆきます.

「コンピュータを粘土にしよう」の裏の意味

2012-05-31 12:51:13 | 1

プログラミング言語はいろんな種類があるけれど,ビスケットはその中のどういう位置づけのものなのか.

プログラミング言語を大きく分けると,手続き型と宣言型に分かれる.この二つの大きな違いは,時間軸.手続き型はコンピュータの計算の手順を順番に指示し,宣言型は処理の順番ではなくて,違う関係性で並べる.たとえば,宣言型言語の一つに関数型言語があるが,これは計算のデータの流れに注目して並べ,実際にどういう順序で計算がおこなわれるかは問わない.論理型言語というのも宣言型言語のひとつだが,こちらはデータの流れも記述しない.データの関係性だけ記述する.ビスケットはこれらの中では,論理型言語に最も似ているのだけれど,さらに狭く,ビスケットが記述しているのは「具体例の変化」という関係(before と after)だけである.

ユーザインタフェースの研究で,実例による操作指示というのがある.たとえば,「ファイルを開いて,印刷して,閉じて,印刷済みフォルダに入れる」という操作を人間が2,3回繰り返してやってみて,残りはコンピュータが自動的に真似をして実行する,といったものである.研究としても一時期流行ったのだが.これをプログラミングとみなして,実例によるプログラミングという研究も流行った.ビスケットはこれにも非常に近い.

しかし,違うのは,ビスケットでは,絵の動き方をマウスでドラッグしてコンピュータに教える,ということを直接教えているのではなく,絵がどう変化するか,ということを,変化の具体例を使って教えてあげているのである.動作は,複数用意された変化の具体例に対して,適当にその変化っぽいことを連続して適用する,ということをやっている.その結果,たまたまアニメーションに見えているだけで,やっていることは単純なのである.複雑な変化を書けば,プログラミングしているようにみなせるということでもある.

ビスケットが子どもに受ける理由を考えてみると,抽象化を早くから求めない,ということなのではないだろうか.

たとえば,ダンスを教えることを考えてみよう.「1拍目で右手を上げて,2拍目で左手を上げて」こういう言葉でダンスを説明するのが普通のプログラ ミングである.それに対して,「私の踊っているのを見て真似して」というのが具体例によるプログラミングである.1曲を全部教えるのでは曲の長さが変わっ たりしたら対応できなくなるので,たとえば「このフレーズがきたらここで手をたたく」といったルールで覚えることで,曲がどんなに繰り返して長くなってもちゃん と踊れるようになる.こういう省力化を行うとプログラミングになってくるが,このとき,いちいち言葉で「ここで手をたたく」,なんて教えないで,真似して踊ってい るうちに,「ああ,このフレーズで手をたたけばいいんだな」と自然とわかってくる.こういう実例だけで規則を伝えるのがビスケットでやっているプログラミングなのである.

普通のプログラミング言語では,かなり最初の段階で抽象化が求められる.scratchでも実際に動くもの(猫の絵)に対して,それを動かすための命令(ブロック)を並べるという作業が一番最初に来る.ブロックと猫は別モノである.猫という具体的なものに対して,ブロックという動き方を抽象化したものを並べて動かしている.2つの概念を分けて理解する必要がある.

普通のプログラミング言語しか知らない人は,プログラミングに抽象化は必須だと思っているかもしれない.しかし,ビスケットをはじめ,ユーザインタフェースやビジュアルプログラミングの研究では抽象化をしなくても,動作を指示することができるのだ,ということを示している.

ここで,やはり,なぜプログラミングを教える必要があるか,という大問題に立ち返る必要がある.プログラミングを教える理由が,「抽象的な指示によってものを動かすことを理解させる」ことだとすると,ビスケットはその用途には向いていない.ビスケットが「本当のプログラミングではない」と思っている人たちは,たぶん,この考えである.

もちろんそれは大事だと思うけれど,そんなハードルを上げなくても,もっと直接的にプログラミングを教えられるよ,というのがビスケットの主張である.

ビスケットのプログラミングでは,最初に実例を少しだけ与えて,動作させてみる.その動作で気に入らないところがあったときは,気に入らない特殊ケースをうまく示すような具体例を取り出して,追加の変化を教えてあげる.それが,うまく教えられれば,その変化がうまく一般化されるのだが.その例が強すぎると,過度に変化を適用しすぎて,前の動作よりも悪化してしまうこともある.こういう作業を繰り返して,自分のやりたいことを少しずつ形にしてゆく.このプログラムが出来上がってゆく過程が,ビスケットと普通のプログラミングとで全然変わらない.だからビスケットでいいのである.

本当なら,ちゃんと実験で証明する必要があると思うが,ビスケットで遊んでいると,少しずつ抽象的なものの考え方ができるようになってくるのではないかと思っている.そういう考え方ができないと,ちょっと複雑なものは作れないからである.複雑なものに挑戦しようと思えば,抽象的なレイヤでのきちんとした設計が必要なのは,どんなツールを使ったとしても同じである.

ビスケットのキャッチコピー「コンピュータを粘土にしよう」というのは,抽象的な世界を使わなくても,具体的な世界だけでプログラミングができるのだよ,という意味でもあったのでした.


過去に発明した変な言語1

2012-05-20 09:28:18 | 1

これから,数回に分けて,僕が発明してきた変なプログラミング言語について書こうと思う.

このブログの読者層をちゃんと決めていなかったけれど,このシリーズに関してはなんかプログラミング言語を知っている人を対象に書く.書く目的は,プログラミング言語の幅の広さを理解しようというのと,ビスケットのルーツを探るという二点.


最初の言語は Laplas という言語.英語のつづりは Language Processor for listing and stacking という.このころは真面目にこういうのを考えていた(ちなみにViscuitにそういう英語のつづりはありません.BiscuitのBをVに変えただけです).あと,本当はLaplaceだよと教えてくれる人が続出したけど,そんなのは知ってます.

この動機は高専から北大に編入して,山本先生のところでLispを見せてもらって,あまりの面白さに感動して,でもカッコが嫌だから,カッコを使わないのを作ろうと思ったこと.もう一つは,高専の5年のときに3Dのタートルグラフィックを発明して,そのライブラリをPascalで書いていたのだけれど(これはトンボグラフィックといいます),これを簡単に呼び出せる言語が欲しかった.ちょうどタートルグラフィックに対するLogoみたいなやつ.

Logo はもろ英語を単純化した文法だったけど,そういうのがちょっと嫌で,日本語で読めるものというのもあった.

あと,Forthが好きだったので,そういう影響もあった.

というわけで「逆ポーランド記法によるリスト処理言語」というのです.ちなみに似たものにPostScriptがあるけれど,Laplasの方が2年か3年先です.まだMacが誕生する1年くらい前だもの.

こんな感じで動きます.

> 1 2 + ?
3

これは「1と2を足して表示」と読みます.最初の > はプロンプト.

プログラムは空白で区切られていて,それぞれの動作が決まっている.数字はその値をスタックに積む.+ はスタックに積まれている2つの数字を取り出して,足して,その答えをスタックに積む.? はスタックに積まれているデータを取り出して,表示する.

(1+2)*3という計算をするなら
> 1 2 + 3 * ?
9
となる.

リスト処理はこんな感じ

> (1 2 3) head ?
1
> (1 2 3) tail ?
(2 3)
> 1 (2 3) cons ?
(1 2 3)
> (1 2 3) snoc ? ?
(2 3)
1


カッコの始まりから終わりまでをリストといって,リストは一つのデータとしてスタックに積まれる(lispと違うのは,リストにクォートが要らないということ).最初の行は(1 2 3)というデータをスタックに積んで,headはスタックから取り出したリストの先頭をスタックに積んで,?で表示している.tailはスタックの先頭以外.consはスタックから2つのデータを取り出して,リストにしてスタックに積む.最後のsnocはheadとtailを同時にやる.

この言語をデザインするときに,気を使ったのがスタックの順序.snocとconsはちょうど逆の関係にあって,
> (1 2 3) snoc cons ?
(1 2 3)
となる.snocはhead,tailの順にスタックに積んで,consはその順でスタックから取り出す.

この順序がうまくはまったときはプログラムは気持ちがいいのだけれど,うまく行かないときは,スタック操作というのが必要になる.

> 1 2 ? ?
2
1
> 1 2 swap ? ?
1
2

このswapは,スタックに積まれている2つのデータを取り出して,入れ替えてスタックに積む,というのをやる.このほかに
> 1 dup ? ?
1
1
> 1 2 drop ?
1
> 1 2 3 rot ? ? ?
2
1
3

dupはスタックの先頭の複製,dropは1つ捨てる,rotは3つのデータを入れ替えるもの.rotまでくるとほんとわけがわからなくなるんで,めったに使われなかった.

Laplasでは関数を自分で定義できる.これが実に気持ちがいい.

> 'd (2 *) .
> 5 d ?
10

名前を実行せずにスタックに積む場合にクオート'が必要.最後のピリオド.が定義で,defunみたいなやつ.これでdという名前の中身は2 *である.これはdを2 *で置き換えたようにして実行する.

Laplasの中身は普通の仮想機械と同じで,プログラムポインタ(プログラムカウンタみたいなもの),制御スタック,データスタック,定義表,で構成されている.動作は非常にシンプルで,プログラムポインタはリストを指していて,そのリストのheadを実行して,ポインタはtailに進む.tailがnilなら,制御スタックから一つリストを取り出してプログラムポインタにセットする.実行するときは,+や?のような組み込み関数ならその内部定義を呼び出すし,ユーザ定義ならば,それをサブルーチン呼び出しする.

制御構造は

> 3 (2 *) 4 repeat ?
48

これは,repeatは数字とリストを取り出し,数字の回数だけリストを実行する.つまり

> 3 2 * 2 * 2 * 2 * ?
48

と同じ動きをする.条件分岐は

> 'check (0 = ("zero" ?) ("non zero" ?) if) .
> 0 check
"zero"
> (1 2 3) check
"non zero"

のように書く.真偽値はlispと同じでnilじゃなければ真.

あと,ローカル変数というのもあって

> 'append ((a b) var
:   a (a snoc b append cons) (b) if) .

とする.aとbにスタックの値が代入され,このスコープ内でaとbはその値をスタックに積むという定義になっている.

このappendの定義はaがnilでないなら,aをheadとtailに分解して(snoc),tail側とbとをappendして,その答えとaのheadをconsしている.aがnilならbを返す.consとsnocの使われ方が非常に美しい.

もう一つ特徴的な制御構造がmapsである.

>(1 2 3) (?) maps
1
2
3
>(1 2 3) (2 *) maps ? ? ?
6
4
2

リストを二つとり,最初のリストの各要素に対して,次のリストを実行する.スタックはそのまま.

それから[ と ]

> [ 1 2 3 ] ?
(1 2 3)
> [ 1 2 3 * ] ?
(1 6)

[はマークで,スタックに[そのものを積むだけ.]はスタックから取り出してリストを作ってゆく.[が来たら終わり.[と]は組み込みではなくてLaplas自身で定義することができる.ちょっと難しいけどやってみると,

> '[ ('[) .
> '] (nil ]sub) .
> ']sub ((h t) var h '[ = (t) (h t cons ]sub) if) .

(これは,いま脳内でデバッグしているから間違っているかもしれません)

この[ ]とmapsでlispのmapcarができる.

> [ (1 2 3) (2 *) maps ] ?
(2 4 6)
> [ (1 0 2 3 0) ((x) var x = 0 () (x) if) maps ] ?
(1 2 3)

2つめは,要素が0以外のリストを返している.ちなみに,これくらい簡単な式ならローカル変数を使わなくても,

> [ (1 0 2 3 0) (dup = 0 (drop) () if) maps ] ?
(1 2 3)

と書ける.

僕としては,maps と [ ] の発明が画期的だと思っているのだけれど,この面白さを共有できる人はあまりいないかも.リスト処理とスタック処理の相性の良さだと思うんだけど.

さて,このLaplasはリスト処理も面白かったけど,本当はトンボを飛ばすために作られた.たとえば,こんな感じ.

> 'star ((100 draw 144 turn) 5 repeat) .
> 'stars ((star 30 up) 12 repeat) .

stars は星を3次元的に回転させて12個描く.これは図が無いと説明しにくいな.まあ,こんな感じで遊んでた.

lispはプログラムとデータが同じ構造であるからプログラムの自動生成にいい,という説明があるが,Laplasも同じ特徴がある.Laplasの場合,関数と変数の区別もないので,lispよりもさらに簡単である.

このトンボグラフィックで,分子模型を作って遊んでいたのだが,こんな書き方ができる.

> tominit h h
> tominit h o h
> tominit h c h h h
> tominit h c h h c h h h

こんなんじゃわからないと思うけど,メタンとエタンを画面に表示する.Laplasのデータスタックとは別に,トンボ用のスタックが用意されている.tominitは最初に2匹のトンボを画面の中央に,ちょうど互いに反対向きになるように置く.hは今のトンボから線を引いて球を描いて消える.なので,最初の例は二つの水素がつながっているように描く.oは線を引いて球を描いて108度くらい角度を変えて,また線を引く.なので2行目は水の分子を描く.cは線を引いて球を描いて,3つの方向に3匹のトンボに分かれて(つまり自分の他に2匹増える)線を引く.このとき c や h はそれぞれstarのようなプログラムで描かれているのだけど,この書き方はプログラムと言うよりデータのように見えるではないか.制御構造を使って

> tominit h (c h h) 6 repeat h

のようにCが6個の(ヘキサンだっけ)分子も描けるので,制御構造付のデータとも見える.あとメチル基とかOHとか

> 'm (c h h h) .
> 'oh (o h) .

とか.

(うーむ.これは,ちゃんと復活させないとダメだね.)

PC-9801 の一番初期のはもちろんだけど,いろんなメーカーのCP/M-86に移植した.

いま思うと,テキストをちょっと入力して,3Dグラフィックを自由自在に扱う,というのはもっと注目されてもいいかもしれないな.あまりにもGUIに毒されすぎてしまった.

当時,この言語を説明する言葉を持っていなかったので,なかなか回りを説得できなかったのだけど,今だったらどういう説明ができるだろうか.仮想機械を直接触れる言語って感じになるかな.データとプログラムを自由に行き来するというか,自分が機械と同列になって機械と遊べるというか.

スタック言語はパイプラインの一般化でもあるんで,
今風のバリエーションも考えてみたら面白い.

たとえば,データをリストじゃなくてXMLにするとか,集合を導入するとか.