いものやま。

雑多な知識の寄せ集め

MacBook Airでマウスを使いやすくしてみた。

f:id:yamaimo0625:20190715142700j:plain

MacBook Airトラックパッドは最高なんだけど、ちょっと困ったことが。
それは何かというと・・・指が痛くなる

もちろん、短い時間使うだけなら問題ないのだけど、長時間使っていると、少しずつダメージが蓄積されていって、ちょっと無視できない痛みに。

この痛みを改善しようと、マウスを併用するようにしてみた。
ただ、そのまま使うと、かなり使いにくい・・・

そこで、ツールを導入して、MacBook Airでマウスを使いやすくしてみた。


ちなみに、Macでのキー配置の調整などについては、以下を参照:

問題点

MacBook Airでマウスを使うと出てくる問題点が、以下の2つ:

  • マウスホイールによるスクロールが普通と逆
  • マウスホイールによるスクロールがキビキビ反応しない

「スクロールが普通と逆」問題

まず、前者について。

マウスホイールを転がすと、普通は以下のようになる:

ホイールを転がす方向 スクロールする方向

これが、MacBook Airだと、以下のようになる:

ホイールを転がす方向 スクロールする方向

これがかなり使いにくい・・・

実はこれ、設定によるもので、トラックパッドの動きに合うようになっている:

トラックパッドをなぞる方向 スクロールする方向

トラックパッドの場合、これがかなり自然な設定になっている。
下になぞれば対象が下に移動し、つまり、画面としては上にスクロールするし、逆もまた然り。

けど、マウスも同じ動きをされてしまっては、困る。

一応、「システム環境設定」-「マウス」-「スクロールの方向:ナチュラル」のチェックを外すと、普通と同じマウスホイールの挙動をするようになる。
ただし、その場合、「システム環境設定」-「トラックパッド」-「スクロールとズーム」-「スクロールの方向:ナチュラル」のチェックも外れてしまい、今度はトラックパッドの挙動がおかしなことになる。

なんで独立して設定できるようにしてないんだろう・・・

なので、以下のような調整をする必要がある:

  • マウスホイールは、
    • 下に転がせば、下にスクロールする
    • 上に転がせば、上にスクロールする
  • トラックパッドは、
    • 下になぞれば、上にスクロールする
    • 上になぞれば、下にスクロールする

「スクロールがキビキビ反応しない」問題

続いて、後者について。

マウスホイールを転がすと、普通は即座に反応して、画面がスクロールする。
けど、MacBook Airだと、なぜかある程度の勢いでホイールを転がさないと、反応しない

これもかなり使いにくい・・・

例えば、2, 3行だけスクロールさせたいときに、普通ならマウスホイールをちょこっと転がせばいいだけなのに、MacBook Airだと全然思うようにならない。
ちょこっと転がすのだと反応せず、かといって、勢いよく転がすとかなりスクロールされてしまったり。
思ったように反応してくれないのは、かなりのストレス。

これは、Macの場合、スクロールの加速度を加味しているのが原因っぽい。
なので、スクロールの加速度を無視するように設定する必要がある。

対応策

これらの問題に対して、何か1つのツールで対処できればよかったのだけど、残念ながら見つけられなかった。
なので、それぞれの問題に対して、別のツールを用いて対処した。

  • Scroll Reverser
  • DiscreteScroll

Scroll Reverser

まず、「スクロールが普通と逆」問題の解決に使ったのが、Scroll Reverser

このツールは、名前の通り、スクロールの方向を逆にするもの。

上記ページからダウンロードしてきたzipファイルを解凍し、"Scroll Reverser.app"を「アプリケーション」フォルダにコピーすれば、インストールは完了。

ただし、起動しようとすると、おそらくセキュリティに阻まれて起動できないはず。
その場合、「システム環境設定」-「セキュリティとプライバシー」-「プライバシー」-「アクセシビリティ」で、"Scroll Reverser.app"のチェックを入れる必要がある。
(「システム環境設定」-「セキュリティとプライバシー」-「一般」で、実行許可を与える必要もあるかも)

起動したら、メニューバーにアイコン( \Updownarrowみたいなやつ)が表示されるので、アイコンから設定を開き、以下のように設定:

f:id:yamaimo0625:20190715155321p:plain

これで望む挙動にすることができる。

さらに、上記設定の「アプリ」で「ログイン時に開始」にチェックを入れておけば、ログイン時に勝手に開始されるので便利。

DiscreteScroll

そして、「スクロールがキビキビ反応しない」問題の解決に使ったのが、DiscreteScroll

このツールは、スクロールの加速度を無視するようにするもの。

上記GitHubの"releases"にリリースのzipが置かれているので、DiscreteScroll.zipをダウンロード。
そして、解凍して"DiscreteScroll.app"を「アプリケーション」フォルダにコピーすれば、インストールは完了。

こちらのツールもScroll Reverserと同様にセキュリティに阻まれるはずなので、まったく同じように、「システム環境設定」-「セキュリティとプライバシー」-「プライバシー」-「アクセシビリティ」で、"DiscreteScroll.app"にチェックを入れてやる。

あとは、起動させれば、スクロールがキビキビ反応してくれるようになる。

ちなみに、このツールはホントに必要最低限の機能しかないので、起動しているかどうかの確認は「アクティビティモニタ」から行うしかない。
また、終了させる場合も、「アクティビティモニタ」からプロセスを終了させる必要がある。

それと、ログイン時に勝手に起動して欲しい場合には、「システム環境設定」-「ユーザとグループ」で、「ログイン項目」に"DiscreteScroll.app"を追加しておけばいい。


以上のツールを使うことで、かなり快適にマウスを使えるようになった。
MacBook Air使いは、ぜひお試しあれ。

今日はここまで!

「Lucid, the Dataflow Programming Language」を翻訳してみた。(その13)

ちょっと間が空いてしまったけど、前回の続き。

2.8 無限プログラミング

Iswimファミリについてのこれまでの議論は、従来のオブジェクトが有限な代数に限定したものだった。
この形式のIswimは、ALGOLのような従来の命令型言語の再帰的サブセットに多かれ少なかれ一致している。
この制限により、Iswimプログラミングは本質的に、従来のプログラミングの、とても厳密に制限され構造化された形式となっている。

しかし、言語の代数をこのような「従来の」ものに限定する特別な理由はない。
オブジェクトが有限でない代数によるIswimファミリには、興味深く有用なものがたくさんある。
これらの言語は、従来の言語の単なるサブセットよりもはるかに豊かで、まったく新しいプログラミング手法を可能にする。

Iswimファミリで興味深いメンバの一例は、POP-2代数 Pのような代数 Hに基づいたもので、その宇宙(universe)には有限リストに加えて無限リストが含まれている。
リストは無限であってもよく、そのリストの要素は Hの世界の任意の要素であっていい(リストでもいい)。
このアイディアは、以下の「ドメイン方程式」によっていくらか正確にすることが出来て、POP-2の非リストなデータオブジェクトのドメイン Dとして、 Hの宇宙 H_0を定義する:

 L = 1 + H_0 \times L \quad \mathrm{and} \quad H_0 = D + L

最初の式は、「ハイパーリスト」(ドメイン Lの要素)が、空であるか(空リストはただ1つだけある)、 H_0の1つの要素(ヘッド(head))と1つのハイパーリスト(テイル(tail))で構成されていることを示している。
次の式は、 H_0の要素が Dの要素(文字列、数値、単語など)またはハイパーリストのいずれかであることを示している。
これらの式は、 Hでの演算は指定していなく、 Hの宇宙 H_0のみを指定しているので、 H自体は指定されていない。
しかし、これらの式の解が得られれば、通常のリスト演算(hdtl::などの記号で表される)は簡単に定義できる。

実際、これらのドメイン方程式は、無限の長さのリストがあるという解を持つ。
1つ注意しなければならない点は、ドメイン H_0にも豊富な種類の部分的(partial)オブジェクトが含まれていることである。
ドメイン Dでは、部分的(非標準)オブジェクトは \perpだけであり、他のすべてのオブジェクトは、正常で、健全な文字列、単語、整数などであった。
しかし、 H_0では、「混合」リストーーつまり、要素の一部(必ずしもすべてではない)が \perpであるようなリストーーを許可する必要がある。
また、ある時点で「テイルオフ(tail off、尻尾切り?)」する必要があるーーこれは、tlが何度も適用される場合に、ある時点で \perpと評価することである。
最後に、リストの構成要素は他のリストでもよいので、構成要素として部分的リストを持つようなリストを許可する必要がある。
このリストの要素は部分的リストであってもいいし、そうでなくてもいい。
要するに、ドメイン H_0は、すべてのオブジェクトの「純白さ」と \perpの「漆黒さ」との間に無限の多様な濃淡を許している。

(※正直、何を言っているのか分からないのだけど、操作的に考えたときに「無限の操作」を意味する \perpに対して、無限の長さを持つリストも操作が(場合によって)無限になる、ということを言いたいのだと思われる)

Iswim( H)でプログラミングすることと、「有限」オブジェクトの代数 AによるIswim( A)でプログラミングすることの大きな違いの1つは、(前者の場合)変数を再帰的(循環的)に定義するのに意味があるということである。
もう1つの違いは、終了条件なしで再帰関数定義を与えることも意味があるということである。
その場合、「結果」が無限であるので、評価は「永遠に」実行されることになる。

例えば、以下の定義を考えてみる:

 X = 1 \mathrm{::} X;

Iswim( P)では、この定義は \perp Xと関連づける。
というのも、 Pは有限リストのみを持ち、この定義を満たすような有限リストは存在しないからである。
しかし、Iswim( H)には無限を使った解が存在する。
すなわち、

[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ...]

という無限に1が続くリストである。
さらに、これはこの方程式の最小解であり、したがって変数 Xに関連付けられた値となる。
次の「非終端」の定義

 \mathit{bump}(X) = \left( \mathit{hd}(X) + 1 \right) \mathrm{::} \mathit{bump}(\mathit{tl}(X));

は、完全に理に適った関数を定義している。
無限の数のリストを引数として期待し、引数の各要素を1ずつ増やすことによって形成される無限のリストを結果として返す。

演算子の結合の優先度が分かりづらい(+ > ::)ので、括弧を追加している。

これを使って、

 \mathit{nats} = 0 \mathrm{::} \mathit{bump}(\mathit{nats});

と定義すれば、 \mathit{nat}はすべての自然数(※ここでは0も自然数に入れている)の無限リスト

[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ...]

を定義する。
このリストは、この方程式の最小の(そして、実際のところ、唯一の)解となっている。

無限オブジェクトを持つ代数は、非常に異なったスタイルのIswimプログラミングを可能にし、とてもエレガントな(ただし従来とはかなり異なるかもしれない)プログラムを作れるようにする。
すべての素数の無限リスト

[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 51 53 ...]

を出力(値)とするようなIswim( H)プログラムは、以下のようになる:

P
where
    nats = 1 :: bump(nats)
    where
        bump(X) = hd(X) + 1 :: bump(tl(X));
    end
    P = 2 :: primesin(tl(tl(nats)))
    where
        primesin(L) = if isprime(hd(L))
                      then hd(L)::primesin(tl(L))
                      else primesin(tl(L)) fi;
        isprime(n) = nodivsin(P)
        where
            nodivsin(Q) = (q * q > n) or ((n mod q ne 0) and nodivsin(tl(Q)))
            where
                q = hd(Q);
            end
        end
    end
end

このプログラムは、自然数の無限リストからすべての非素数を取り除くように動作する。
素数性の確認では、構築中のすべての素数の無限リスト Pを使用している。

Iswimファミリの「無限を扱える」メンバのインタプリタがすでに数多く存在している。
最もよく知られている(そして最初に現れた)のは、Turner(1979)のSASLとHenderson(1980)のLispkitである。
これらの言語は、元の高階言語に基づいていて、IswimではなくISWIMのバリエーションとなっている。
著者の1人(William W. Wadge)は、Warwick大学の同僚と一緒に、先に説明した代数 Hに基づいて、このような「ラムダ微積分」言語を実装した。
(本質的にIswim( H)である)この言語は、PIFL(POP-2 Influenced Functional Language)と呼ばれ、学部の関数型プログラミングの授業で使用されている。

PIFLはpLucidの「仲間の」言語で、同じ代数に基づいていて、同じ構文規則を使用している。
その結果、この2つの言語には大きな重なりがある(PIFLでもwhere再帰的となっている)。
多くのPIFLプログラムは(構文的に)pLucidプログラムでもあり、同じ出力を生成する。
pLucidとPIFLとの交わりが、(大雑把に言えば)一階のPIFL、すなわちIswim( P)となっている。
(PIFLではリストの再帰的定義が必ず \perpを返すわけではないので、ぴったりIswim( P)というわけではない)
Iswim( P)ーー言い換えれば一階で有限なPIFLーーのことを、「pIswim」と呼ぶことにする。
Iswim( H)(一階で無限なPIFL)のことは、「HyperpIswim」と呼ぶことにする。
したがって、pIswimはpLucidの部分言語であり、HyperpIswimはPIFLの部分言語である。
この節の例は、すべてのHyperpIswimのプログラムであり、(したがって)PIFLのプログラムでもある。

無限オブジェクトでの計算を可能にするIswimファミリのメンバは、より洗練された実装を必要とすることは明らかである。
Iswim( H)のような言語に直面したとき、Iswim( P)のような言語を(かろうじて)カバーしていた従来の逐次的な解釈は、完全に破綻することになる。
特に、変数の再帰的な定義は、完全に理に適っていて有用であるものの、代入文と見做しては意味をなさなくなる。

しかし、他の2つの実装方法(すなわち、簡約(reduction)と要求駆動(demand driven))は、拡張できる。
簡約による方法は最も簡単に拡張できる。
以下のような、無限のデータに対する演算を含む式をどうやって書き換えるかという式を使うだけである。

 \mathit{hd}(X \mathrm{::} Y ) = X;

PIFLインタプリタは、SASLやLispkitの実装と同様に、この方法で動作する。

要求駆動も拡張できるが、ここではさらに注意を要する。
ある式の値が無限の場合、その式の値を要求することは出来ない。
なので、代わりにその値の一部、すなわち、他の部分的な要求を満たすのに必要な分だけを要求しなければならない。
計算の過程で、同じ値のより多くの部分が要求されるかもしれないが、一度にすべての値を必要とすることはない(これはpLucidインタープリタの仕組みとなっている)。
しかし、オブジェクトの「一部」を構成する概念は問題のオブジェクトの性質に依存するため、一般的な要求駆動インタープリタではない。


ということで、無限リストに関する話。

Haskellで(遅延評価によって)無限リストを扱えるのと同様に、Iswimでも無限リストを含むような代数を考えることで、無限リストを使ったプログラミングが可能になる、と。
これを実際に実装した言語がPIFLという言語で、pLucidとは兄弟のような関係になっているっぽい。

というのも、pLucidだとリスト自体は有限のものしか扱えないはずなので、PIFLはpLucidの部分言語というわけではないから。
けど、じゃあpLucidは無限を扱えないのかというと、そんなことはなくて、pLucidではPIFLと違って変数自体が時間方向に関して無限リストのように伸びていっている。

対比で書くと、時間という概念は持ち込まずに無限リストというオブジェクトを作り上げたPIFLと、時間という概念を持ち込んで時間方向に無限リストにしたpLucidという感じ。
どちらも無限を扱えるように拡張されているのだけど、その伸びていく方向が違う。
なので、兄弟のような関係と言える。


何はともあれ、2章はこれで終わりで、長かったIswimの話もここまで。
3章は、このIswimに時間方向の値のリストを持つ代数を入れたLuswimという言語を見ていくことになるっぽい。

今日はここまで!

『正義の教室』を読んでみた。

飲茶さんの新刊、『正義の教室』を読んでみたので、その感想とか。

正義の教室 善く生きるための哲学入門

正義の教室 善く生きるための哲学入門

「正義」って何だろう?

この本でまず驚いたのが、小説だということ

哲学の入門書なので、飲茶さんがこれまで出してきたような解説本を想像していたのだけど、いざ読み始めてみたら、なんと小説。
しかも、本題に入るための小噺だけ物語になってるとかではなくて、哲学の説明まで含めて、すべて登場人物たちによって語られ、描かれている。
これにはホント驚かされた。
しかも、物語が普通に面白いw

飲茶さん、多才だなぁ・・・

そんな、小説を通して語られ、説明されているテーマが、「正義」。
「正義」って何なんだろうね、ということ。

これに関して、本書では「正義には3つの判断基準がある」としている。
すなわち、

という3つの立場を挙げ、それぞれについて説明し、また、問題点の指摘も行っている。

これらの説明はさすが飲茶さんで、とても分かりやすい。
それに、それぞれの立場を体現したヒロインたちを登場させてストーリーを展開しているので、面白く読み進められるし、共感もしやすい。
ぜひとも実際に本を手にとって読んでもらいたいところ。

・・・が、ぶっちゃけ、そんなのどうでもいいんだよ!

というのが、この本を最高にファンキーにして、めちゃくちゃ面白くしている部分w

そう、哲学書に教養を求める人ならば、功利主義だとか自由主義だとか、そういった知識を得ることが重要になるんだろうけど、じゃあ、そういった知識を得たからといって、何になるんだ、と。

「トロッコ問題」のような理不尽な状況に実際に置かれてしまったとしたら、そういった知識がなんか役に立つのか?
クソの役にも立たないだろ!!!

実際、飲茶さんは本書を書いた動機として、以下のようなツイートをしている:

めっちゃ分かるw
理不尽でしかないもんね。
物語内では、この理不尽さにヒロインの一人が苦しめられたり。

そして、本書で説明されている3つの立場も、それぞれ問題点が指摘され、ことごとく論破されている。

じゃあ、「正義」なんてものはないのか・・・?

いや、「正義」はあるよ!!!

そう強く主張しているのが、本書。

ロッコ問題という状況で、どんな「行動」が正義と思えるかではなく、どんな「人間」が正義だと思えるか。
(中略)
人間は、完全な正義を直観できないし、知りようもない。
それはどうしようもない現実だ。
でも、そんな何が正しいかもわからない世界の中でも、それでも「正しくありたい」と願い、自分の正しさに不安を覚えながらも「善いこと」を目指して生きていくことはできる。
きっと僕たちにはそれで十分でーーむしろ、それこそが人間にとって唯一可能な正義なんじゃないだろうか。
(『正義の教室』より引用)

つまり、大切なのは「内容」ではなく、「在り方」だということ。

結局のところ、功利主義自由主義直観主義も、「これが正義である」という「内容」に関する立場の違いでしかない。
なので、そんなのは「どうでもいい」となる。
そうではなく、「自分はどうやって生きていくのか」という「在り方」こそが重要というわけだ。

そんな主人公の最後の演説シーンで語られる内容は、まさにニーチェの「超人」を体現するようなものになっていて、とてもよかった。

そして、衝撃のラストw

いやまぁ、途中で予想はついたんだけど、まさかホントにこれをやるとはw
『史上最強の哲学入門』の東洋編で、頭で分かってるだけじゃダメで、リアルな体験をもって理解しないといけない、と書かれている通りなんだけどw

今日はここまで!

正義の教室 善く生きるための哲学入門

正義の教室 善く生きるための哲学入門

技術書典7 はじめてのサークル参加meetupに行ってきた。

f:id:yamaimo0625:20190621155528j:plain

はい、というわけで、技術書典7にサークル参加で申し込んだ。
当選したら、技術書生やす!

技術書典については、以下の公式ページや、自分が一般参加したときのレポートを参照:

はじめてのサークル参加meetup

今回行ってきたのは、以下のイベント:

技術書典にサークル参加しようと決めたはいいものの、分からないことがいろいろあったからね。
特に、今回はオフセット本にチャレンジしてみようと思っているのだけど、オフセット本は作ったことがないので、そのあたりの疑問を解決できればな、と。

ちなみに、このブログでも何度か紹介してるけど、同人誌自体は一応作ったことがある。
技術書でもないし、コピー本を凄くしたようなものなので、普通の人が想像する「同人誌」と呼んでいいのか迷って、「どきどき初参加」枠で申し込んだけど・・・

自分が作った同人誌に関しては、以下の記事から:

PDF版をBOOTHでも販売しているので、興味があれば、ぜひ。

イベントは、わかめさんひつじさんがスピーカーとなって、質疑応答を交えつつ、そも同人誌ってなんじゃらほい、というところから、その製作方法やスケジュール感、執筆での注意点や当日の様子などについての話があった。
以下は、その中で特に自分が参考になったことのまとめ。

スケジュール感

まずはスケジュール感から。

同人誌を作る場合、原稿を書いて、ハイおしまい、というわけにはいかない。
特に、印刷所を使ってオフセット本を作るとなると、その入稿の締め切りも考えないといけない。

大まかな流れは、以下の3つのステージになる:

  1. 執筆前:計画、企画
  2. 執筆 :執筆、編集
  3. 執筆後:印刷、広報、頒布

まず、計画、企画では、各工程の期日を決めて線を引いたり、書く本の内容を決めていく。

次に、執筆ではもくもくと原稿を書く。
執筆はだいたい1ヶ月を目安とするみたい。
また、レビューや校正も必要。
それと、本としてまとめるために、表紙を作ったりレイアウト・スタイルを整えたりといった編集も行なっていくことになる。

最後に、印刷所に入稿して、広報活動と、当日の頒布。
告知は積極的にやった方がいいとのこと。

上記を踏まえると、以下のような感じになる:

時期 やること
7月上旬 当落結果発表、参加費支払い、計画・企画
7月〜8月 この中で1ヶ月くらいとって執筆し、編集、レビュー、校正
9月上旬 入稿、広報活動
9月22日 イベント当日!

以下の手順でカレンダーを取り込むことも出来るみたい:

執筆環境

さて、いざ執筆しようと思ったら、執筆環境が必要なわけだけど、そこで挙がるのがRe:VIEWとかMarkdownとか。
(あるいは、 \TeXで頑張るとか)

普段、Markdownで文章を書くのに慣れているので、正直、書式が違うRe:VIEWを使うのは微妙かなぁ、とも思っていたんだけど、いくつか追加で考慮すべき点があった。

まず、Markdownだと脚注を書くのが難しいよ、という話。
実際のところ、はてなブログだと拡張が入ってて、脚注も書けるようになっているのだけど(Markdownで書いてみた。(まとめ) - いものやま。も参照)、その拡張が使えるとは限らない。
それに、この拡張の例からも分かるとおり、Markdownには方言もけっこう多いので、そのあたりも悩みのタネになる可能性も。
その点、Re:VIEWだと脚注も問題なく使える。
また、マークアップ言語なので拡張もしやすいとのこと。

あと、Re:VIEWは実際に同人誌製作に使われた実績も多いので、印刷まわりで安心できる、というのも大きい感じがする。
例えば、印刷所によっては全ページにノンブルを入れる必要があるけど、Re:VIEWだとその辺もうまくやってくれるとのこと。

う〜ん、悩ましい。
今回は \TeXの勉強も兼ねて \TeXでやろうと考えていたのだけど・・・
Re:VIEWを使う方が無難な気もする(^^;

執筆のアドバイス

執筆に関してもいくつかアドバイスがあった。

まず、書きたい内容に絞って書いた方がいいよ、という話。
説明を書いてると、どうしても「あれも説明しなきゃ」「これも書いておかないと理解してもらえないかも・・・」となっていきがち。
けど、それで書きたい内容が書けなかったら、本末転倒。
本題に入る前に力尽きたり、あるいは最悪、原稿を落としたりしたら、悲しすぎる・・・
なので、まずは書きたいところから書いていき、レビューをしてもらって「ここらへん、説明が足りないからよく分からない」と指摘されてから説明を継ぎ足すくらいでもいいんじゃないか、とのこと。

あと、書いてるときはバックスペース禁止というルールでやるのもいいっぽい。
バックスペースを許すと、書きつつ推敲を始めてしまって、いつまでたっても文章が完成しないことがあるから。
これ、めっちゃ分かる・・・
(書いてるときにどうしても推敲してしまうので、めちゃくちゃ遅筆・・・)

それと、校正に関しては、余裕がない状態でtypoを見つけたりなんか出来ないので、余裕を持ってやるようにねとのこと。
確かに・・・
あと、実際に紙に印刷した方が間違えを見つけやすい人も多いので、実際に印刷してみるといいかも、とのこと。
実際、自分も『哲学散歩道』の校正をやるときは紙に印刷してやったので、その通りだと思う。

他には、爆死しないためには、表紙の分かりやすさが大切、という話もあった。
たくさんの本があるので、まず表紙で目がつくかどうかが重要になる。
そして、その本が自分を対象としたものでない(not for me)と思われてしまったら、手にとってもらえさえしない。
なので、表紙で何について書かれた本なのかや、扱っている内容、あるいはレベル感が分かるようになっていた方がいいとのこと。

サイズや部数をどうするか

実際に同人誌を作るときに悩ましいのが、本のサイズをどうするか。
A5にするのか、B5にするのか・・・

ちなみに、A5でもB5でも、1ページに入る文字数はそれほど変わらないらしい。
これは、A5の場合は文字サイズも少し小さくすることが多いから。

一般に多いサイズはB5。
コードを載せる場合には、B5の方がプログラムのリストが見やすいという利点があるっぽい。

一方、A5だと、気軽な読み物という雰囲気を出しやすい感じがあるっぽい。
B5に比べて小さいので、軽いし。
あと、個人的には表紙のデザインがやりやすそうな気はしてる。
(面が大きい方がイラストや写真も大きいものが必要になるし、ダサいスキマを作らないデザインにするのはなかなか難しい)

あと、何部刷るのかというのもなかなか悩ましい問題。

データによれば、技術書典だと新刊が平均150部くらい出る感じらしい。
機会損失をしたくなければ、それより多めに刷っていた方がいいし、完売するというのはかなり楽しいので、少なめに刷るというのもありみたい。
なお、少なめに刷った場合、電書版のQRコードを名刺などに印刷して用意しておけば、機会損失もだいぶ抑えられるので、そういうのもあり。

ただし、注意点として、技術書典は部数に関して例外的で、他のイベントで同じような部数は刷らないように、とのこと。
在庫を大量に抱えて、悲しいことになるっぽい・・・

部数に関しては、個人的に気になっていたことも聞いてみた。
それは、同人誌の束はどれくらいのサイズ(分厚さ)になるのか、ということ。
運搬するときや、在庫が残ってしまったときには、そのサイズが重要になってくるので。

これは、同人誌100冊で、500枚入りコピー用紙の束が5つ入ったダンボールくらい、という回答をもらって、すごくイメージしやすかった。

実際、50ページの同人誌を考えてみると、1冊あたりの紙は25枚で、それが100冊となると2500枚となり、これはコピー用紙500枚x5束の2500枚と計算があう。

製本サービス

他、面白い情報として、製本サービスの話があった。

セブンイレブンのマルチコピー機はかなり優秀らしいので、コピー本を作ったり、試し刷りをしたりするのに使えるっぽい。

あと、製本直送.comというサービスもあるとのこと。

電書版のみ頒布するという場合も、見本誌があった方がいいので、そういう場合にはこういった製本サービスを使用して見本誌を用意するといいみたい。

なお、紙の本を出版する場合も、「これが見本誌」と分かるようにして見本誌を用意しておいた方が、手にとってもらいやすいみたい。
具体的には、ビニールのカバーなどをかけて「見本」とタグを貼っておくとか。
言われてみればなるほどという感じ。

印刷所とのやりとり

今回、初めてオフセット本を作ろうと思っているので、そこで気になっていたのが、問題なく印刷してもらえるのかどうかということ。
いざ印刷されたものが届いてみたら、思わぬ乱丁があったりレイアウトが崩れたりなんかしていたら、目も当てられない・・・

これに関しては、入稿後に2, 3回くらい印刷所とやりとりすることになるっぽい。
これだと印刷に問題が出そうだけど、大丈夫か、とか。
そういうのがあるなら、安心かな・・・?
もちろん、指摘に対応するだけの余裕を持って入稿する必要があるわけだけど。

ちなみに、依頼しておけば見本誌を送ってもらうとかも可能っぽい。
ただし、これは印刷されたものが送られてくるだけで、それを見て原稿を手直ししたり出来るわけではないようなので、注意。
もし、ちゃんと印刷されるかなどを確認したいなら、前述の製本サービスを使って試し刷りをして、入稿に備える感じになるみたい。

あと、わかめさんが以下のツイートをしていたので、それも参考になりそう:

かんたん後払い

あとは、技術書典特有のものといえば、「かんたん後払い」。

これは、コードを専用アプリで読み取ることで購入手続きを完了させ、あとで支払いを行えるようにする、というサービスなのだけど、サークル参加するとなると、サークル側で何か特別な準備は必要なのかどうかが気になっていた。
例えば、何か特別な機械を用意したりだとか。

結論から言えば、そういった特別な機械とかは必要なくて、規約を読んで申し込みをすればいいだけみたい。
そうすると、当日にコードが印刷された紙が渡されるので、あとはそれを専用アプリで読んでもらえばいいだけっぽい。

ただ、注意点として、現金ほどスループットは出ないという話があった。
なので、お釣りの用意はしっかりとね、とのこと。


ちなみに、作ろうとしている技術書は、以前このブログで書いたポーカーの記事を膨らませたものにしようと思っている。
技術者なら、やっぱりゲームもガチで数理モデルを使って勝ちに行きたいでしょ?w

上の記事で導き出したインプライド・ポットオッズの話は、とても興味深い(そして強化学習のモデルのいい例にもなってる)ものなので、多くの人に知ってもらいたいし、他にもいくつか調べて文章にしたいネタがあるので。

もちろん、ポーカー(テキサスホールデム)やったことないという人も多いと思うので、そこらへんも出来ればカバーする感じで。
(ただ、それで書きたいことが書けなくなったら本末転倒なので、省略するかも)

ポーカーを覚え、数理モデルを駆使して、狙え一攫千金!ってねw

今日はここまで!

「Lucid, the Dataflow Programming Language」を翻訳してみた。(その12)

前回の続き。

2.7 遅延評価

しかし、Iswimの操作的(動的)解釈として、より適切なものがある。
この方法論では、プログラムは実行されると考えるのではなく、プログラムは評価されると考え、その評価は必要に応じて行われるとする。
この評価の方法は、必要以上には評価を行わない(=必要になったときに必要なだけ評価を行う)ので、「遅延」と呼ばれる。
特に、関数呼び出しの実引数は自動的には評価されない。
この遅延評価の方法は、数学的意味論により忠実となっている。
実際、ちょっとした注意を払うことで、それらは完全に正しいものになり、形式的意味論が指定するものを正確に生成する。

これらのうちの最初のものは、「要求駆動(demand-driven)」解釈と呼ばれるかもしれない。
この方法では、プログラムの一部(最初はプログラム全体)の値に対する要求が、部分式の値に対する要求を生成する。
要求は感染症のようにプログラム全体に伝播していき、必要とされた値が要求元へと流れ返っていく。
2つの部分式 E_1 E_2の合計 E_1 + E_2を評価するために、評価マシンは E_1 E_2を(おそらく並行して)評価し、それらの結果を加算する。
個々の定数からなる式を評価するために、評価機は代数によって定数に割り当てられた値を返す。
個々の変数からなる式を評価するために、評価機は変数の適切な定義を検索し、その定義の右辺を評価する。
対象の変数が関数定義の仮引数である場合、対応する実引数を探し出してきて、それを評価する。
評価する式が if  C then  E_1 else  E_2 fiという形式であれば、まず Cを評価して、その真偽値にしたがって、 E_1 E_2のいずれかを評価する。
最後に、(例えば) f(E_1, E_2)という形式の式を評価するなら、 fの定義を見つけ出し、その右辺を評価する。

もちろん、要求駆動型の評価は、上記のざっくりした説明が示すよりも、もう少し複雑である。
それにもかかわらず、実装することはそれほど困難というわけではなく、完全に正しい。
(付録のマニュアルに記述されているpLucidインタプリタは、要求駆動の評価スキームを使用している;そして、Iswim( P)はpLucidの部分言語である)
(※データフロー言語の実装を考えるとき、pull型(シンク側から要求を出し、それに応じてソース側が計算を行う)とpush型(ソース側で計算を行い、結果をシンク側に流してシンク側を駆動させる)を考えることができるけど、この要求主導というのはpull型だという主張。実際の実装がそうなっているのかは未確認・・・)
要求駆動型のモデルは、プログラミングを助けるうえで、適切なものとなっている。
要求駆動型の観点では、定義が実行可能な順序で現れてくる必要はない。
実際、この方法だとプログラムを書くときによりトップダウンな形で書くことが出来る。
まず、欲しい出力の値となる式を書く(そこで使われる変数や関数の定義は書かない)。
この式はwhere節の主題になる。
次に、where節の本体に、必要に応じて主題で使われる変数の定義を書いていく。
したがって、要求駆動のインタプリタは、書かれたのと同じ順序で定義を進めていく(これはインタプリタにとって特別な助けになるというわけではない)。
この「必要に応じて定義する」アプローチは、プログラミングのスタイルをずっと良くし、Iswimの定義型の精神により合っている。

Iswimプログラムを実装する非逐次的な別の方法もある。
そのアイディアは、第7章で議論されるプログラム操作の規則を使用するもので、ソースプログラムを徐々に出力に変換していくものである。
変換方法は、プログラムの定義を書き換え規則として使用する。
実行する簡約(reduction)を選択する戦略が慎重に定義されている場合、実装は必要とされない式の値を評価しようとはしない。

この方法(簡約計算)は非常にシンプルであるが、プログラミングの助けとして無制限に推薦することは出来ない。
中間表現は非常に大きく、複雑になり、元のプログラムとの関係がほとんどなくなる。
この方法は主に構文的であり(※ここでの「構文的」というのは、記号操作的、という意味合いだと思う)、構文に関して排他的に考えるプログラマは、Iswimの意味論を理解するという利点を失う。
プログラマは、関数定義を「関数」ーーすなわち、データを変換する「デバイス」ーーの定義と考えるべきである。
定義をテキストを変換する書き換えルールと考えることはお勧めしない。
記号操作というより、計算であると考えたほうがいい。
(※ラムダ計算の理論では、定義による置換を行うことで計算が出来るとしていて、その置換の戦略によって遅延評価かそうでないかが変わってくる。ここで述べているのはその簡約による方法)

これらの代替操作的モデルは、どちらも \perpの意味をそれぞれの方法で大事にしている。 例えば、次のような定義を考えてみる:

X = X + 1;

要求駆動のインタプリタは、 Xの値に対する要求が Xに対する別の要求に直接つながるため、(要求が)無限に循環することになる。
そして簡約の実装は、 X X + 1に無限に置き換えることで、以下の系列を生み出すことになる:

 X +1, (X +1)+1, ((X +1)+1)+1, \cdots

どちらの場合も、計算は決して終わることなく、これはまさに \perpに対応する操作的な活動になっている。

最後に、プログラマは動的モデルをまったく使用しないことを望むかもしれないということを指摘しておく。
Iswimの最大の強みは、公式での意味論が厳密に静的であり、よく理解されている関数の概念を使用していることである。
プログラマがプログラムの効率(性能)を心配していない場合、実際の操作的な概念を考える必要はまったくない。
プログラマがもっぱら正しさにだけ関心を持つ場合、静的な意味論はプログラムの出力を完全に指定するので、静的な意味論だけで十分である。

しかし、一般的に、プログラマは性能に関する質問を無視できない(そうでなければ、プログラムはなく、仕様のみがあることになる)。
プログラマは、いくつかの操作上の観点から思考し、それを実行するのに必要なリソースの量を減らすようにプログラムを作らなければならない。
しかしこれは、プログラマは実装のあらゆる細かい部分について心配する必要があるだとか、実装のあらゆる部分の動作について理解している必要があるだとか、そういうことではない。
とても実用的なプログラミングでさえ、純粋に宣言的に理解されている部分があるだろう。
さらに、最も実用的で効率的なプログラムでさえ、その正しさは、表示的な(静的な)意味にのみ依存し、操作的なアイディアを参照することなく考えることが出来る。
「抽象性」という用語は、本質的に表現または実装の問題から独立であることを意味する。
したがって、Iswimの形式的で表示的な意味論は抽象的な意味論であり、この言語を使用したいと思う人であれば誰でも理解できるものである。


ということで、遅延評価に関して。

この節に書かれてることはとても興味深くて、遅延評価といえばHaskellが有名なわけだけど、Haskellが遅延評価を実現している方法はまさにこの節で書かれている2番目の方法(簡約による方法)で、簡約の戦略を非正格にすることで評価が遅延するようにしている。
一方で、pLucidはそれとは別の方法で遅延評価を実現していると言っていて、それが1番目に書かれた要求駆動による方法。
データフローの視点で考えることで、1番目の方法が出てくるのはとても面白い。

また、データフロープログラミングの後継と言われることの多いリアクティブプログラミングだと、(オブザーバ・パターンによる)push型の実装が多い印象なんだけど、pLucidはそうではなく、pull型の実装になっている、と。
確かに、push型だと遅延評価は無理だけど、pull型なら実装はWorkStealingアルゴリズムみたいに関数呼び出しを一つのタスクとしてスタックにプッシュする感じになりそうなので、遅延評価になりそう。
いやー、なるほどという感じ。

今日はここまで!

「Lucid, the Dataflow Programming Language」を翻訳してみた。(その11)

前回の続き。

2.6 Iswimでの呼び出しと束縛

手続き的解釈の適用には注意が必要である。
というのも、そうでなければ、スコープ規則に関して間違いが生じる可能性があるからだ。
特に、グローバル変数を含む関数定義の正しい解釈について注意する必要がある。

関数定義の右辺には、仮引数ではなく、右辺の中で関連する定義がない変数が出現する場合がある。
以下の B Cのように使用される変数は、関数定義のグローバル(グローバル変数)と呼ばれる:

f(X, Y) = X + B * Y + C;

グローバル変数を伴った「手続き定義」を言語が許可するときは、グローバルが意味(=値)を持つコンテキストでの関数呼び出しの実行において、グローバルの意味(=値)に微妙な疑問が生じる。
たとえば、

f(3, U)
where
    B = 7;
    C = 5;
    f(X, Y) = X + B * Y + C;
    U = P - Q
    where
        Q = 2;
        B = 3;
        P = f(B, Q);
    end
end

という(where節の)内側での fの「呼び出し」について考えてみる。
この結果はどうあるべきだろうか?
 Pの値は何になるだろうか?

上記のIswimの「逐次的な」解釈に単純に従えば、単に X Yに3と2を割り当て、 X + B * Y + Cという式を評価するだけである。
ところで、評価を開始するとき、 Bの値は何になっているのだろうか?
変数 Bには、この呼び出しが実行される直前に3が割り当てられている。
 Bの値はそのまま3であり、したがって呼び出しの結果が 3 + 3 * 2 + 5、すなわち14であると予想するのは論理的だろう。
一方、(外側のwhere節の)主題で fの呼び出しが実行されると、(外側のwhere節の)本体の最初の文の実行後に Bの値は7になっていることが予想される。
これを少し計算すると、最終的に得られる値は92であると予想される。
(※ f(3, U) = 3 + 7 * (14 - 2) + 5 = 92

しかし、この予想は間違っている。
Iswimの形式的意味論は、この節の値が148であることを示す。
これは、内側の fの呼び出し中に Bが(3ではなく)7を割り当てられていると仮定すると、得られる値である。
(※ P = 3 + 7 * 2 + 5 = 22なので、 f(3, U) = 3 + 7 * (22 - 2) + 5 = 148

この例は、Iswimで従来の手続き的な解釈を用いるときの1つの落とし穴を示している。
Iswimの形式的意味論は、言語が「静的束縛(static binding)」と呼ばれるものを持つことを意味していて、すなわち、グローバル変数は関数が呼び出される環境ではなく、定義されている環境(またはコンテキスト)から値を取得している。
上記の関数 fは、グローバル Bが7と定義されている節で定義されているので、関数がどこで使用されていても Bの値は7となる。
PASCALやALGOLのような本来の命令型言語も静的束縛を持っているにもかかわらず、静的束縛はIswimの逐次的な命令的解釈の観点からはむしろ理解しにくい場合がある。
(※この例の場合、命令型言語であれば、変数 Bをローカルな変数として「明示的に」定義することでグローバルな Bを隠してしまうようにすれば(シャドーイング)、グローバルな Bは(実質的に)静的に束縛されている状態になるわけだけど、Iswimの文を命令型言語と同じように見ると、ローカルを定義しているのではなく、定義済みのグローバルに値を再割り当てしているように見えるので、このような間違いを起こしやすい、ということ)

一方、純粋に宣言的な方法でIswimを解釈し、その定義を任意の仮引数に対して真となる方程式と考えると、静的束縛は完全に自然なものとなる。
以下の定義を考えてみる:

C = 5;
B = 7;
f(X, Y) = X + B * Y + C;

最初の定義は Cが5であり、2つ目の定義は Bが7であることを述べている。
そして、3つ目の定義は、 X Yにどんな値がこようとも、 f(X, Y)の値が X + B * Y + Cであることを述べている。
しかし、 B Cの値がそれぞれ5と7であることは既に分かっているので、 f(X, Y)の値は X Yの値が何であっても X + 7 * Y + 5であると考えることが出来る。

Iswimの宣言的な見方は、Iswimの形式的意味にとても近いので、正しい答えを導き出す。
Iswimの解釈は、値を個々の変数に、関数を関数変数に関連付ける。
Iswimの割り当て/呼び出しモデル(=手続き的な解釈)は、(関数ではなく)「手続き本体」を関数変数に関連付けている(ように考える)ので、誤解を招く。
手続き本体は、グローバル変数の出現を含むことができるテキスト(式)であるが、実際の数学の関数はテキストではなく(それは値の集合間の対応)、「グローバル変数」を持つことは出来ない。

Iswimの命令的解釈には、関数について誤解を招く可能性がある別の微妙な問題がある。
以下のプログラムを考えてみる:

f(3, 4)
where
    f(X, Y) = if X < 1 then 0 else f(x - 1, f(X, Y + 1) fi;
end

この出力はどうなるだろうか?
形式的意味論(および宣言的解釈)であれば、答えは0でなければならない。
変数 fは、常に0を返す2変数の関数として定義されていて、それがこの方程式を満たす唯一の解である。
(※第2引数がなんであれ、第1引数は常に値が減っていくので、どこかで X \lt 1となるため)
しかし、命令的解釈では、 fの呼び出しがいつまでたっても終わらず、プログラムの出力は \perpになると指し示されてしまうだろう。
問題は、命令的解釈に従えば、 f(3, 4)の呼び出しによって f(3, 5)が呼び出され、それはさらに f(3, 6)を呼び出し、 f(3, 7)の呼び出し、 f(3, 8)の呼び出し、と無限に続いてしまうことにある。
実際、このプログラムをPASCALで書き直すと、ずっと深いネストされた呼び出しを生成するので、実行は永遠に終わらない。

関数呼び出しに対する命令型のアプローチの問題は、結果が生成される前に、すべての実引数の値が手続き本体で実際に必要とされることを前提としていることである。
命令型の方法は、手続き本体を実行する前に、シンプルにそれらの値(=実引数の値)をすべて計算する。
実際、結果を出すのに仮引数の値をすべて使わなくてもいい関数を定義できることは、とても有益であるーーたとえ、特定の結果を出すのに、引数のどれが必要となるかを前もって正確に言うことは出来ないとしても。
例えば、PASCALプログラマが言語にif-then-else式(※C言語の3項演算子?:と考えればいいはず)がないのに嘆いて、そのギャップを以下のように定義されたcond関数で埋めようとしたとする:

function cond(P : bool; X, Y : integer) : integer;
begin
    if P then cond := X else cond := Y
end;

そして、階乗の関数を(ローカル変数への)割り当てなしで次のように記述したとしよう:

function fac(n : integer) : integer;
begin
    fac := cond(n < 1, 1, n * fac(n - 1))
end

この目論見はうまくいかず、facの呼び出しは何も値を返さない。
なぜなら、cond関数を呼び出す前に、n * fac(n - 1)を評価しようとするので、nの値によって処理が分岐せず、facの呼び出しが無限に続いてしまうからだ。(※ここ、原文のままだと意味が通じないと思ったので、文を書き変えた)

しかし、Iswimでは、このアイディアは機能する。
すなわち、ユーザは自身で選択関数を定義することが出来る。
以下のプログラムの出力は、形式的意味論に従えば、6となる:

fac(3)
where
    fac(n) = cond(n<1, 1, n * fac(n-1))
    where
        cond(p, x, y) = if p then x else y fi;
    end
end

その理由は、cond(p, x, y)の値は常にif p then x else y fiの値と同じだからであるーーたとえyの値が \perpであったとしてもーー言い換えれば、たとえyの計算が永遠に終わらないものであったとしても。
もちろん、命令的な観点だと、上記のプログラムが決して値を返さないことを示唆することになる。
その素朴な見方(=命令的な観点)は、単に間違っていることになる。

以上から、Iswim( Z)の逐次的な命令的観点は不適切であり、(素朴に従うのだとしても)間違ってさえいるというのは明らかである。
しかし、解釈を修正し、完全に正しいにも関わらずに従来通りであるような実装の基礎として使用することは可能である。
そのためには、グローバルの束縛が動的であることを保証する必要が出てくる(それはそこまで難しくない)。
しかし、関数の仮引数が(事前に評価される)「値」によってではなく、(必要に応じて評価される)「名前」によってであるようにすることも保証する必要がある。
これははるかに困難であるが、不可能ではないーーALGOLはオプションで「名前」による呼び出しが出来る。
それでも、命令的観点は、たとえそれを動かせることが出来たとしても、推薦されるべきではない。
それは単にIswimの「精神」とは違っている。


ということで、前回でIswimのプログラムを手続き型のように解釈する方法を説明したけど、その解釈で出てくる問題を指摘しているのが、今回の話。

まぁ、今の時代から見れば、変数の静的束縛はラムダ式でのキャプチャと考えればいいし、引数の評価のタイミングも、引数に関数呼び出しをベタに書くんじゃなくてラムダ式を書けば評価が遅延されるよね、とかあるけど。
もちろん、引数の式をどの順番で評価するかとかはコンパイラ実装依存となっているので、式に副作用があったりすると、微妙な問題を生み出したりするというのは正しいわけだけど。

そして、評価の遅延の話が出てきてるけど、それについては次の節が「遅延評価」のようなので、また次回に。

今日はここまで!

「Lucid, the Dataflow Programming Language」を翻訳してみた。(その10)

前回の続き。

2.4 サンプルプログラム

where節の本体が「補助定義」であるという非形式的な考え方に形式的意味論がどう一致しているかを説明するために、前に出てきた次の式を考えてみる:

C + A * Z
where
    A = 5;
    B = 5;
    C = Z * A + 1
    where
        A = 3;
        Z = A + W * B;
    end
end

 rをすべての変数にランク0を割り当てるランク付けとし、 Iを、 Zに9、 Wに7、他のすべての変数に0を割り当てる整数の r-解釈とする。
形式的意味論を用いて、 Iに関するこの式の値を決定してみる。

定義3.により、この式の値は本体の等式の「最小解」 Jに対する式 C + A * Zの値である。
 Jは、変数 A B Cにそれぞれ5、5、115が割り当てられることを除いて、 Iと同じ解釈でなければならないことは、簡単に分かる。
(※ C = Z * A + 1 = (A + W * B) * A + 1 = (3 + 7 * 5) * 3 + 1 = 115
 A B Cだけがこの節で定義された変数なので、他の変数についてはすべて、 Iで割り当てられた値に Jは一致しなければならない)。

 C + A * Zは、二項演算子 +とその引数 B A * Zからなっている。
次に、定義2.により、 Jに関する C + A * Zの値は、整数の +の解釈に2つの引数の値(つまりそれらの Jに対する値)を適用した結果である。
最初の項の値は、定義1.により、 J Cに割り当てている値、つまり115である。
2つ目の項の値は45と簡単に分かるので( J A Zにそれぞれ5と9を割り当てている)、最終結果は160である。

 Jが外側のwhere節の本体の3つの等式の解であることを確認するには、それぞれについて、 Jに対して左辺の値が右辺の値と同じであることを検証する必要がある。
最初の2つの等式については明らかだが、3つ目の等式については、 Jに関して内側のwhere節の値を計算する必要がある。

定義3.は、(内側のwhere節の)主題の式の Z * A + 1の値が、(内側のwhere節の)本体の中での定義の最小解 Kに対する値であることを示している。
 Kは、 A Zにそれぞれ3と38(=3 + 7 * 5)を割り当てていることを除いて、 Jと同じである。
したがって、(内側のwhere節の)主題の式の値は115である。

これにより、 J Cに115を割り当てることが分かるので、 Jは外側の節の等式の解であることが確認できる。
もちろん、これは Jが最小解であることを証明するものではないが、定義は非再帰的であり、解は唯一のものである。

自由(未定義)変数が単独の(=ランク0の)変数として使用されるため、上記の式は実際にはプログラムである。
以上から、このプログラムの出力は、入力が \{ (Z, 9), (W, 7) \}のとき(すなわち、 Zの入力値が9、 Wの入力値が7のとき)、160である 。

2.5 Iswimの操作的意味論

Iswimの形式的意味論は単純で正確である。
この「最小不動点(least fixed point)」という仕様は、実装が正しく、かつ完全であると判断するための標準として使用できる。
例えば、(6章で与えるような)ある変換規則が正しいことを証明するといった、言語の妥当性の判断にも使用できる。
しかしながら、形式的な定義は、プログラムの読み書きの良いガイドとはならず、また、言語を実装するのにも直接は使用できない。
幸いにも、Iswimファミリのほとんどのサブクラスは、比較的従来通りの概念を使用して理解、実装することが出来る。

「従来の」モデルは、変数の定義を代入文として解釈し、関数の定義を関数処理の宣言として解釈するという考え方に基づいている。
この解釈は、代数の世界(universe)が( Zのように)「通常の」有限オブジェクトに \perpを加えた集合である場合、適用可能である。

where節の本体の中での定義は、どのような順序であってもよいので、代入文のようにそれらを実行し、意味のある結果を得ることが出来るとは限らない。
しかし、Iswim( Z)と同様の言語では、再帰的に定義された変数は(ほとんどの場合)値が \perpとなるので、そのような定義は無意味である。
循環定義が許されないのであれば、右辺に出てくるすべての変数がすでに定義済みであるように定義を並べ替えることが出来るのは、容易に分かる。
(※変数間の参照関係を有向グラフで書いたとき、循環定義がなければツリー(もしくはフォレスト)になるので、リーフから並べていけばいい)
例えば、

X + Y
where
    C = A + B;
    X = C * C - D;
    D = A - B;
    Y = C + D * D;
end

という式では、本体を一連の代入文として解釈することは出来ない。
なぜなら、 Dの値は3つ目の文で計算されるため、2つ目の文を実行できないからだ。
しかし、次のように定義を並べ替えることが出来る:

X + Y
where
    C = A + B;
    D = A - B;
    X = C * C - D;
    Y = C + D * D;
end

こうすれば、4つの定義が順番に実行され、結果の「格納された」値を使用して主題の式が評価されると想像することが出来る。
評価後、ローカル変数( C D X Y)の値は元に戻さなければならない。

ここで、where節の本体の中で

f(X, Y) = P - Y
where
    Q = X + Y;
    R = X - Y;
    P = Q * R;
end

というような定義を持っていて、本体のどこか(上記の定義が関係する位置)で f(5, A - B)という形の式があるとする。
この定義を手続きの宣言と解釈し、式 f(5, A - B)をこの手続きの呼び出しとして考えたとき、その呼び出しはALGOLやPASCALと同様に実行されると想像することが出来る。
言い換えれば、仮引数 Xに5、 Y A Bの差が一時的に与えられ、 fの手続き本体( fの定義の右辺の式)が実行され、結果が呼び出しの結果として返され、仮引数の元の値が復元される。

「従来の」モデルは全体として、特殊なオブジェクト \perpの正しい解釈を与える。

例えば、次のプログラムを考えてみる:

f(3) where f(n) = n * f(n - 1); end

手続き的意味論をガイドとして用いると、このプログラムからは何の値も得られないと予想できる。
 f(3)の「呼び出し」は f(2)を呼び出し、それは f(1) f(0) f(-1) f(-2)と、無限の呼び出しを生じさせる。
これは手続き的意味論が失敗したように見えるかもしれないが(値が得られないため)、実際にはちゃんと動作する!
形式的意味論は、 fが常に \perpを返す関数であることを指し示し、そして、 \perpがプログラム全体の出力となる。
したがって、 \perpを「非停止(nontermination)」あるいは「出力なし」と解釈するならば、手続き的意味論は正しい。

これこそが \perpの真の意味である。
オブジェクト \perpは、永遠に実行され、決して何も返さないプログラムの「出力」である。
このオブジェクトは、動的な操作的動作の「非停止」に対応する静的な意味値である。
 \perpとの組み合わせに関するさまざまな規則はすべて、この解釈でとても自然に説明することが出来る。
例えば、3と \perpの合計が \perpであるという事実を考えてみる。
計算によって2つの値の加算が行われるが、最初の値の(部分)計算で3が得られるのに対し、2つ目の計算は結果を生み出さずに永久に実行されることになる。
したがって、(この計算は永遠に終わらないので)合計の「値」は \perpでなければならず、これは3と \perpの(拡張された意味での)合計と一致している。


ということで、前回の分かりにくかった説明の具体的な例と、それを操作的にはどう解釈するのかという話。

正直、前回の内容を理解していなかったときは、このサンプルプログラムも「?」な感じではあったのだけど(変数のスコープがちゃんと理解できていなかった)、理解すればそんなに難しくはないかな?
where節の主題の右辺のスコープがすでにローカル内だというのがちょっと間違えやすい気はするけど・・・

今日はここまで!