Forthの融通性


プログラミング言語を自分で設計する気は全くなかった。新しい言語を望むのは既存の言語に不満があるということだろう。Forth言語は非常に古く、Mopsもまた言語として比較的古い。もっとハイカラに飾り付けた言語の方が良いというのもわからないではない。

けれども、forthにしてもMopsにしても、融通が利くことこの上ない言語環境なのだ。そこでのプログラミングは、普通のアプリケーションプログラミングでさえ、どこか言語設計や言語実装に近い発想をする必要がでてくる。問題を解くための言語を実装する、というのがそこでのプログラミングの常道なのだ。プログラミングの過程で、言語そのものが、そのシンタクス(統語法)も含めて変容していくのだ。しかし、既存の部分までつられて変化していくわけではなく、基本部分は不変なままであるから、ある方向への拡張のために他の方向への拡張を制限しなければならないということにもならない。

実際、forthでは、一個の関数やプロシージャーに構文解析や意味解析の働きを持たせることができる。そしてその効果は局所的にとどめることができる。つまり、語順実行というforthのデフォルト構文を残しながら、必要に応じてアプリケーションプログラムレベルで細部を変化させることができる、ということだ。これはメタ階層について開かれたforthの著しい特色だ — ただし、この“メタ”は言語を実装する階層ということだが — 。 この意味でforthはMLP(Metalanguage protocol)を持つといってよい。言語自体が課題に対して応答的に変化できるわけだ。(もっともこの特性は、forth標準化の領域では嫌悪されているようにも見える。ある意味それももっともではあるのだけれども...)よくいわれる言語の「拡張性」は、forthにおいては単に基本的な関数や手続きを追加してカーネルを拡張できるという話とは次元が異なっている。実行の動作原理を変えてしまうことさえできるのである。Forthのコンパイラは関数を機械語の組み合わせとして構成する。インタープリターは基本的に機械語で書かれたコードの場所に実行を飛ばすだけである。その結果、他の普通の言語処理環境のように、アプリケーションとしてのバーチャルマシンにできる範囲とか、コンパイラのコード解析能力の限界とかが、プログラムをつくる上での制約になることはないのである。マシン語で実装できることならforthでも可能である。Forthが古い言語であることは最新の技術を利用することにおいて何の桎梏にもならない。ただ、既存部分のどれをどのように残し、新規部分とどのように接合するかだけが問題となるに過ぎない。

結果として、forth系環境を作ってしまえば、自前の言語を設計できる基盤をつくったようなものだ。ユーザから見れば、forth言語でのプログラミングはむしろ言語のプログラミングであり、forth言語実装の続きを書き続けていくことともいえる。それは特別のオプションとして与えられた言語のハイパー機能によってそうなのではない。それがそこでの通常のプログラミングのありかたなのである。Wizardレベルに達して始めてできる奥義ではなく、ごく初歩の段階から与えられる手段である。もっとも、それが問題の源泉であるともいわれるが。

「Forth好き」ではない


もっとも上で述べたようには使わないforthユーザ、それどころか、そのように使うべきではないとする者もいるようだ。その種の主張は結局のところforthを“普通の”プログラミング言語に近づけたいという指向から生まれてくる。そのため、プログラミングを「勉強して習得した」者は容易に引っかかり、問題が見えない。

そのような主張において特に語られがちな、何かが一般的に悪いとか一般的に良いとかいう一般規則指向的発想は、現象面への表面的な対応にすぎず、上のようなforthの融通無礙の性格を人の思考の硬直性によって台無しにすることでしかない。必要なのはもっと基礎的なところまで降りた思考だろうと思われるが、現状ではそれは理論としてもあまり期待できる状況ではないようなのが残念ではある。

そのように、forth系の言語を好む自分であっても、forthは手放しで素晴らしいと思っているかというと、実はそうでもないのである。実際、“いかにもforthらしい”コードというのは好きではない。特定のプログラミング言語に対して愛好とか崇拝のような態度を表明する人も多いが、それは少し違うだろうと思う。もちろん好き嫌いは個人の勝手ではあるが。

ただforthは不思議なのである。その威力がどこから生じてくるのか、と。古くからforthに関わっている人は、単なる利害のためにそのニッチを選択している人を除けば、ほとんど全ての人が、その不思議に惹かれ、それを解明しようと続けるうちに、ベテランとなってしまった人たちのようだ。この不思議に対する解答は、標準的ないしアカデミックなプログラミング言語理論からは引き出せそうにない。それは、標準的なプログラミング言語理論が形式論理学の一応用分野に過ぎないからではないかと思っている。例えば、forthのような原則語順実行の言語もConcatenativeという一律の硬い構文規則にされて初めて高く評されるのも、むしろそのような言語理論なるものの欠落部分ないし貧弱さを証示しているように見える(もっとも理論に基づいてConcatenative規則を採用した言語の仕様が貧弱であるとは限らないが)。当面は、この状況を簡潔明瞭に記述する言葉はない。しかし、forth的言語の不思議はけっして単に集団心理的なものではない。客観的な基盤を持つものである。気付かない、あるいは気付きたくない人がいるだけである。もちろん気付かなくても好きになれる要素もないではない。手っ取り早くアイディアを試せることは誰にとっても長所だろう。環境によってはサイズの小ささなども挙げられたりする。しかし、そうやって挙げられるメリットは、現在では特にforthでなくとも得られる。「プログラミングとは言語をつくっていくことだ」という考え方もforthの特徴と言われる。しかし、実は、同じことはLispなどの関数型言語でもいわれている。むしろ定式としてはLispの発明者であるMcCarthyあたりの考えが発端ではなかろうかと思われる。思想的共通性についてはいえそうだが、それぞれの言語における意味機能は全く異なったものになっているというべきではなかろうか。特に、「関数」という概念があることによって、純粋性を云々しなくとも、ものの見方はそちらの方向に引きつけられてしまう。比較は可能かもしれないが、forthもまたその方向に引きつけて見てしまうのは、単に曲解というものではなかろうか。
とはいえ、はじめて触れたのがMopsであったことは自分個人にとっては重要であり、最初がGforthのようなコマンドラインforthだったなら、
全く関心を持たなかったかも知れない。ライブラリコールも含めて利用できる機能が自由に制限なく簡単に使えることがMopsの第一の魅力だった。
ただのデータ処理のためだけなら他にも多くの言語があった。

Forthと他言語【補論】

ForthとLispを比較する議論は良く見かける。機能を混ぜようとする指向も見られる。その理由は、おそらく表面的なことであって、どちらも裏技的なものを許すこと、それらの言語のユーザーにはやや物好きの変わり者が多い点で共通点があること、などであろう。しかし、それを超えてなお共通点がないとはいえない。他の場所にも書いたことがあるが、その共通点とは、ともにセマンティクス(意味論)を核として構成される言語であるということである。データ処理というオペレーション上の意味である。コンピュータープログラミング言語にはを核とする言語が圧倒的に多いのである。関数は入力に対して出力を割り当てる処理である。関数名がその演算上の意味を体現している。多くの関数型言語ではそのことがコード面からも見て取れるように思われる。これが意味論を核としているということの意味である。関数を定義するのが式であったとしても、このことに変わりはない。Forthにおいても"ワード"という命名からもわかるように、そこで問題になっているのは意味である。オペレーション上の意味がスタックを経由して結合されて、新しい意味が形成されていく。この点で、forth言語は、式という見方から最も遠い言語かも知れない。もっとも、forthのオペレーションはその場で実行されていくものであるので、命令(imperative)言語として見ることも可能である。演算処理が計算機への命令である以上、そこには食い違いは生じないのである。他にセマンティクスを核とするみえるのは、Smalltalkのオブジェクト指向である。オブジェクトがオペーレーション能力を持ち、可能なオペレーションの中から、セレクターによって選別するという発想は、確かに意味指向であると感じられる。また、Objective Cの角括弧の入れ子で囲むメッセージ伝送も、意味論中心の構成といえると思う。
オブジェクト指向の考え方は、包括的に意味論指向であると思われるが、しかし、多くの新しい言語は式を中心とした言語構成の模倣が多く、
外見上は"自然"なのかも知れないが、意味を中心に構成されているようにはみえないものが多いのが残念ではある。
我々は、計算を式として扱い考える方法を、幼少の頃から学習するので、そうでないものは不自然に思えてしまうのである。
オブジェクト指向が意味論的な構成を中心としたことは、一般には衝撃的だったらしいが、Lispもforthも当初はあまり感応しなかったのは、そういった事情があるからではないかと思う。とはいえ、Lispとforthは、基本発想としては水と油であるように思う。同じように意味論的ではあっても、計算、オペーレーション等、要するに意味そのものに対するアプローチの仕方が違うからである。
例えば、数学的対象としての「関数」は、値を対応させる操作として扱うことも、結果として生じる値として扱うことも、観念的には可能である。
このとき、「関数型」言語は、ほとんど専ら後者の観点から、関数とその結果としての値とを同一視して、それを関数の意味と考え、入力パラメターを
与えることを、関数の意味の特定(確定)と考えるが、
forthでは、ほとんど専ら前者の発想から、値を対応させる操作のことをワードと呼び、ワードの定義がすでにその意味の特定であると考える。
とはいえ、forthの対極はLispよりも、むしろSchemeであるように思われる。
そこでは「セマンティクスを重視する」とはシンタクスでギリギリ絞り込むという意味になっている。
確かに、反対方向に押し進めると最後は似てくるものだが、だからといって、forthにLispの機能を混ぜて、よりハイレベルな言語にしようとすると、スタックのせいで不便な疑似Lispになりそうである。その不便を解消しようと、新機能が導入される。そしてforth要素は死んでしまう。もっとも、それとforthと比べてどちらが良いかは、意見が分かれるのだろうが。
強いてLisp等と関連づけるなら、Lisp等での正道と邪道(裏技)を反転したのがForth、ともいえる。
Lisp等でのウィザード的裏技にあたるものが、forthでは標準的方法になる。逆に、関数型言語の正統的なやり方は、forthではウィザード的裏技に当たるのである。
だからLisp等の上にforthを実現しようとしたり、逆に、forthに関数型言語の正統な機構を実装しようとする人が、特に「腕に覚えがある」人の中に多い
(ようにみえる)のだろう。


抽象論

関数型言語は抽象化によって物的枠としての計算機を捨象することを目指したように見えるが、そこでつくり出された観念ないし概念を利用するアカデミックなプログラム意味論は、逆に今度は処理手続に貼り付いてしまうかのようである。確かに計算手順を捨象してしまっては工学としての計算機科学の特性を失ってしまう。しかし、数学的に有意義な展開を考えるなら、計算機の機構よりも、まず処理手順を抽象する必要があるのではないかと思う。つまり、機能という方向へ、である。
例えば、プログラム意味論でよく云々されるのは、プログラムのカルテシアン閉構造(圏として)である。しかし、数学としてみると、このカルテシアン閉圏というのは、要するに集合の圏である。1つの母集合から複数の集合になっただけである。Higher Orderのタイプといっても、つまりは、集合の冪であり、それ自体また集合である。集合は、点構造(要素の特定可能性)はあるけれども、相互関係は後から入れるもので、それ自体は砂のようにバラバラである。けれども、数学として面白いのは構造の方である。構造を要素とその関係へと分解することなく扱えるのが圏論の面白さであり強みである。そこで、要素に分解できない、あるいはそうする必要のないものとしての機能の論理を考える可能性が見えてくる。しかし、プログラム意味論がカルテシアン閉構造云々からなかなか先に進めないのは、具体的データの処理手順から、なお離れられないからではないかと疑いたくなる。これは、別のページにも書いた、アカデミックな議論におけるオブジェクト指向について、インスタンスが属性の塊として0次の低階に止まり、そこから引き上げて考えられることがない、という事情と奇妙に符合するように感じられる。処理手順のような低い抽象度に止まったまま抽象数学の応用を試みようとすれば、「現実をそのままのものと見ない」どころか「現実そのものを見ないままの」話になる危険があるのではないか。
ついでに、やや衒学的な話をすれば、意味論としてのタイプ論をCategory theoryを用いて議論するのは、そもそも、集合という観念が持つ、
グローバルに同一性を保つ要素(element、ひいてはatom)という観念から解放されることを狙ったものと思われる。幾何学でいえば、大域のデカルト
座標系でのみ考えていた図形から多様体に移行することに対応する。そこからは記号論理はいつも脆弱層の上でのみ考えてきたように見えるのである。
これは確かにCantorの「パラダイス」と見えたかも知れない。しかし現実問題として、なんでもこれで済むというものではなかったようである。
しかしそうなると、文字通りエレメンタリーな等式には頼れなくなり、構造的対応などの同型対応を考えなければならない分だけ、計算は難しくなる。
しかし、そうしないと捉え切れないものが実際にあるのである。このサイト内で、Forthの局所性を強調するのは、そういったイメージが背景にある。
要素を、普遍的に同一性を保つシンボルとしてではなくて、機能として扱うことが出発点であるように思われる。
シンボリックなλ計算への横からの制約としてしか現れない「型」をもっとキチンと正面に据える必要があるのではないか。
結局、プログラミングの実践は、オペレーショナルな機能のコンストラクションなのであって、証明のそれではないのではなかろうか。

これに対して、forthは計算機の機械としての仕組みに貼り付いて処理を進めることに一つの特徴がある。しかし奇妙なことに、それにもかかわらず、具体的データ処理手順を抽象して、機能単位としてのモジュールを構成するという発想に容易に移行できるのである。(もっとも、'本格的'なプログラマーは、そのような発想はしないようであるが。)
そうこうするうちに、機能単位は絡み合わされて、処理内容は複雑化されていくが、機能的なまとまりとしての特性は残る。それにワードという形で名前を与えることで抽象化は進行する。この抽象化された段階でのワードは、あるデータを入力すればある処理結果データを返すもの、という見方をする必要が、必ずしもなくなってくる。それは、プログラム内の機能としてこそ、もっとも明瞭に理解できるものとなるのである。

いずれにしても「比喩」の段階に止まっている話であると思われるが、プログラム意味論の新しい方向は、機能という考え方に親和的に見えるのであって、その意味ではforthの発想は決して古びてはいないのではないかと思われるのである。


オブジェクト指向forth


Mopsのやり方は、他のforthオブジェクト指向拡張とは、やや趣を異にするようだ。多くのオブジェクト指向拡張は、forthへの標準言語理論の成果の部分的移入と考えられたし、またそのような考えで行われたものと思う。おそらくMopsの前身であるNEONという環境も例外ではなかったのではないか。1981~2年頃のことである。ところが、Mopsに同形式のオブジェクト指向システムが導入された時点(1988年:初めはNEONと互換のネイティブコンパイラ式開発環境を目指して作成された)で、微妙なヒネリが加えられたように感じられる。いや、あるいはそれは初めからではなくて、徐々にそうなっていったものかも知れない。これにはおそらく、Michael Hore (Mopsの発明者)というプログラマーの個性が反映されているのではないかと思われる。

通常のforthへのオブジェクト指向システム導入は、forthのプログラミング領域の特定部分領域に特殊な手続きセットを導入して統括する、という意識でなされているように見える。そこに完備な閉域を作り上げるというわけである。「完備な閉域」というのを様々な側面で無闇に偏愛するのはコンピュータ諸理論の癖のようでもある。ところが、Mopsのオブジェクトシステムはそのような発想ではつくられていない。それはプログラミングが特定方向にさらに伸びていくための足掛かりを与える。プログラミングの感覚が“システムの続きを書く”かの如くなのである。それは、自動的に全てがついてくる既製品を与えるのではなく、機能上基本的な部分に分解された素材を与える。それらを様々に組み合わせてもう一つのまとまった機能を作る。forthの場合はワードの合成である。Mopsでそれに対応するのはメソッドの合成であろう。しかし、オブジェクト指向システムが与える、継承、オブジェクトのインスタンス変数化— Mopsでは、この実態はクラスの中への他のクラスの埋め込みである。この点が他のオブジェクト指向拡張との最も際立った違いとなっている —といった諸手段は、さらに可能性の範囲を拡大している。通常のforthオブジェクト指向拡張は、どういうわけか、いくつかのそれらしい機能がバラバラに与えられるだけで、相乗効果のようなものが感じられない。メソッドの合成はいつまでもワードの合成と同じものでしかない。そのせいかどうかは判然とはしないが、forthプログラマの間ではメソッドセレクタとサブルーチンとしてのワードとの同一視に固執する傾向が極めて強いのである。

もちろんMopsとは別のやり方もあるだろうが、Mopsはまさにforthの指向をそのままに保ったforthの拡張のひとつという感じがするのである。言語表記の文面上の均一性を保つというありきたりの方向以外の考え方が適用できるのもまた、forthの特性のひとつであり、強みでもあるのだ。Michael Horeはかつてその問題を明瞭に指摘したことがある。システムのデザインも少なくとも半ば自覚的なものなのかも知れない。長年月を経た今日、元は同じNEON型クラスシステムを移入されたはずのWin32Forthでのその運用を見ると、その基本構文の類似にもかかわらず現在のMopsとは全くの別物にさえ見えるのである。Mopsは既に独自の言語になっており最早forthのオブジェクト指向拡張とみるべきではないと10年以上前からいわれている。表面的なforth流に流されなかったことが一つの新たな統合を生み出したのではないだろうか。Forthはいわば、オペレーショナルという意味で動的な言語である。Mopsはオブジェクト指向においても、そのオペレーショナルな性格を延長する。

標準的プログラミング言語理論からの技法の採用は、forthの融通無礙な性格を一部制約する — 論理的一貫性などともいわれる — という指向が現れることが多いように思われる。Mopsのクラス・オブジェクトシステムもその性格を持たないわけではない。しかし、それを対価として、さらに広大な融通無礙の領域が容易に見渡せるようになっていることに気付くのである。
Forthにおけるオブジェクトやアスペクトに関しては様々な見方が可能である。典型的なforthユーザは残念なことに、いつも実装方法と混同する。
Forthでは、構文変形も含めて、大抵のことは比較的容易に実装できる。結果として、それによってどんな問題の解決が容易になるのかを十分に分析しないまま
ただ単に多様な機能を求めて、実装のアイディアへ突き進みがちとなる。ここにもforthの落とし穴があるようだ。
実装アイディアとリッチで動的な機能はわかったから、それが何になるのか?特に、forthにおいて?そのような問が十分に問われていないように見える。

言語設計とは何か


言語設計といって考えるのは通常は千篇一律のあるいは万古不変の論理的に一貫整合した構文規則を設計しようとすることであって、それを厳格に遵守しつつも“簡潔で豊かな表現”ができる場合に、良い言語となるわけである。柔軟性や動的性格も“賢明な言語設計・実装者”が予め柔軟性や動的性格を慮ってそのためのタップを組み込んでくれなければ何もできない。いわゆるメタオブジェクトプロトコルズ(MOPs)である。そのようなわけで、あるプログラミング言語の柔軟性やダイナミックな性格は、言語(設計者)への賞賛となるわけである。仕様規格本は分厚くなる一方で、それに載っていないことはできないと信じられている。

そのようなパッケージ的な発想とは全く異なるところにforthは位置している。その構文は意味から生じてくるのであって逆ではない。
"パワフルなMOPSテクニック"とか、それを言語に実装する際の技法とか、かつて(今も?)様々に喧伝されたが、要するに、ForthにもMopsにも同等の機能は、何十年も前から備わっているのである。とはいえ、その機能を使うための教程が著されたのは貴重だった(いつも特定言語に偏った書き方しかされないとしても)。Forthプログラムの酷い混乱の多く、あるいは、全く理解できないという嘲笑は、この機能に関する無知に起因していたように見える。もちろん、それでも、有能なプログラマーは、教科書で勉強などしなくても、自分でコントロールできてきたのだろうが。


バックリンク

最終更新:2019年01月16日 12:41