uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

ヒアドキュメントと複数行文字列について

「ヒアドキュメント」をなんで「ヒアドキュメント」っていうかを調べてみた。以下が参考ページ。

わかったこと

上記の内容が正しいとして、読み取ったことは以下のとおり。

  • 「ヒアドキュメント」は「"Here is" document」から来ている。
  • "Here is"は何からきているかというと、昔テレタイプ端末にあった「Here is」というキー。
  • 「Here is」キーは何かというと、押すとあらかじめ端末ごとに設定できた20文字ぐらいの文字列をホストに送り返すキー。この用途は、例えば、その文字列に端末の識別IDなどを設定しておいて、1キーで「この端末はXXXだよ」とホストに送ること。
    • さらに、ホストが送ったENQという制御文字を端末が受けとると、自動的にHere isキー登録文字列をホストに送信するように設定することも可能。ENQの送信は、ホスト側から、ログインしてるあんたの端末どれよ/あんた誰よ(操作者の名前をhere isキー文字列に登録しておいた場合か)、という情報の問合せをするのに用いられた。

ShellスクリプトにおけるHere documentの意味について

上記参考リンクには、それほど詳細には説明されていないので、ここからは推測も交えて、になる。

Shellスクリプトにおけるヒアドキュメントは、Shellが起動する、コマンドのプロセスに対して特定の文字列をパイプ(もしくはシングルタスクOSでは一時ファイル?)を通じて送りつける、という機能である。

cat <<EOT
  ...
EOT

上では、プログラム中に書かれた固定文字列「...」の部分をShellが切り出し、そのデータをパイプ(やひょっとしたら一時ファイル)を通じてcatプロセスに送り込んでいる。これがホストに対してHere isキー登録文字列(=固定文字列)を送付している振舞に似ている。だからこの機能をHere (is) documentと名付けたのではなかろうか。

複数行を含むことができる文字列定数をヒアドキュメントと呼ぶことについて

時はながれて。

これを調べたのは、現代のプログラミング言語のいくつかにおいて、「改行文字を含むことができる」程度の機能をもった文字列定数の表記法を、「ヒアドキュメント」と呼ぶ場合があることにもともと違和感があったからである。

Shellスクリプトにおいては、任意の文字列を処理対象としてコマンドに渡すのにパイプやファイルを経由するしかない場合があるが、その処理を簡易に記述する機能を「ヒアドキュメント」と呼んだのは、当時において合理性があったように思われれる。(まさか、"Here is"キーがその後消滅することなんか、誰にも想像つかないし!)

また、PerlRubyなどの言語における「ヒアドキュメント」機能は、その背景機構や元々の命名理由とは無関係にShellスクリプトとの表記上の類似性だけでそう呼んでいると推測できる。なぜなら、Shellにあった、起動したプロセスにパイプ繋いで送り込む、という様相が存在しないためである。このことを批判するつもりは別にないが、Shellスクリプトにおいて「ヒアドキュメント」という名称が背後機構をちゃんと説明するものであった、という利点は失うこととなっている。

しかし、PythonやGroovyなどの複数行文字列定数で使用する"""〜"""などには、表記上の類似性すらもないので、ヒアドキュメントと呼ぶ必要が全くないと思う*1。なので自分はそう呼ばないことにしている。なので「プログラミングGroovy」の本にもヒアドキュメントという用語を使うことは意図的に避け、確か複数行文字列定数と呼ぶように徹底したのであるよ。

(2014/12/24)
記憶をたどれば、昔のGroovy(Classic Groovy)には、「本当の(RubyPerlの意味での)」ヒアドキュメントが実際にあったのですが、JSRに提案される段階で削除されました。ヒアドキュメントの廃止 - どうせ見苦しいですから (smile)。もし使いたい場合はかわりにトリプルクォートを使いましょう
この意味でも、Groovyの複数行文字列定数をヒアドキュメントと呼ぶのはまぎらわしい。


プログラミングGROOVY
プログラミングGROOVY
posted with amazlet at 14.10.24
関谷 和愛 上原 潤二 須江 信洋 中野 靖治
技術評論社
売り上げランキング: 48,552

*1:改行を含んでいるとドキュメントっぽいから、ということが理由なら、バックスラッシュ('\')で行末エスケープした通常の文字列定数もヒアドキュメントと呼ぶべき。さらに「ヒア」の意味があるものすべてにヒアをつけるべき。ヒア整数、ヒア引数、ヒア関数…

GroovyユーザのためのRubyのクロージャ対応物(block/Proc/lambda)の理解法

  • Rubyのlambdaはgroovyクロージャと同等*1
  • RubyのblockとProcは実体は同一であり、つまりいずれもProc。違いは、受け取る側で、明示的にProc型引数として受け取る(Proc)か、暗黙の唯一のブロック引数として受け取る(ブロック)か、という受け取る側の表記の問題にすぎない*2。つまりblockというのは構文上のみの存在と考えることができる。yileldは、暗黙に受け取った場合に、明示的にcallできないのでそれをするための特別な構文。
  • RubyのblockもしくはProcには、クロージャを文法を拡張する手段として使う場合に優れた特徴がある。それは、returnがクロージャからのリターンではなく、Procやブロックリテラルが定義された場所がメソッド中ならそのメソッドからのリターンになるということ。いわば、PC(プログラムカウンタ)がレキシカルスコープで引き継がれるということ*3

整理としては、高階関数やアルゴリズムとして使うにはlambdaを、イテレータとかloanパターンとして、つまり制御構造の拡張として使う場合には専用のblock/Procを、ということでいいかな。

ちょっとだけ感想を言うと、一級市民としてのクロージャの扱い方は、実装としても*4言語仕様(文法+意味の両方において)としても、Groovyのほうがはるかにすっきりしているね。もちろんGroovyのそれにも不満がないわけではないけれども。

*1:ただしdelegateはない。ちなみにGroovyクロージャにおけるスコープ制御はたぶんRubyよりも柔軟で、クロージャインスタンスごとに[http://groovy.codehaus.org/api/groovy/lang/Closure.html#getResolveStrategy():title=スコープ解決戦略]を切り替えることができ、それをOWNER_FIRST/ONLYとすることでレキシカルスコープ、delegateで積極的に変えてDELEGATE_FIRST/ONLYにいく場合は動的スコープとすることなどができる。

*2:Procの生成・リテラルの書き方もいくつかあるけど、blockとProcを書き分けることにはならないんじゃないかな。

*3:あるいは、「スタックフレーム」という暗黙変数がレキシカルスコープで引き継がれる、か。今いち良い言い方にならないな。

*4:少なくともjrubyの実装を見る限りにおいては。

型宣言があるほうが良いのか無いほうが良いのか

こちらこちらを拝見すると、RubyJavaをめぐっての議論がされているようです。論点のひとつとして、「型宣言が有用か・無用か」といった話も出ているようですが、この点に限っては不毛に感じられます。有用なケースもあり、有害なケースもある。あとは状況依存で程度問題でしょう。


たとえば、Rubyユーザーも日々型宣言のある言語を使ってるわけです。Rubyの実装自体はCとかJavaですもんね。Rubyの実行基盤を安定させるのに型が大いに役立ってます。1システム内ですら、用途に応じて型が有用で使いたいときもあるし、使いたくないときもあるにはある。これは自明です。


だから、問題は、

  • 型宣言が不要なときでも例外なく要求されること。
  • 型宣言が必要なときにもつけることができないこと。

です。んで、このような問題に対して、Groovyの「オプショナルタイピング(型を明示したいときにつけてもいいけど省略してもいい)」はスマートな解決になってます。オプショナルタイピングとは例えば

String name

とも書けるし、

def name

とも書けると言うことです。


「型が有用で使いたいとき」「使いたくないとき」のそれぞれを具体的に言うと、まず以下のような用途は型指定の有用度が高いケースです。

  1. APIライブラリ
  2. プラットフォーム・ミドルウェア
  3. 大人数複数拠点長期間で開発する場合

要は、比較的硬くて安定していたり、あるいは多数回使われたり、開発者と利用者の距離が比較的離れていたりするところですね*1。動的型だって実行時には型が定まるんだから、定まってるならコード上に指定するかどうかは手間の問題で、その手間から得られる有用性(x利用者数)による生産性向上が手間分の稼動増を補って余りある場合には迷わず指定すればいい。


逆に、試行錯誤が必要であったり、変更が多い部分(上側・ビジネスロジック側)のコードで型指定をいちいちしていくことは特に少人数短期開発でデメリットが大きくなるので型指定を省略することで開発しやすくなるケースもあるはず。そういうケースを言語として無視してもいいけど、適用可能な領域は狭くなる。有用性が減少する。


Grailsでの型指定(あるいはJavaとの役割分担)の使い分けはまさにこうなってます。


ポイントは、1システム内で、あるいは1プロジェクト内でも(オプショナルタイピングが利用できない言語では相反する)2つの要望があるということです。


格闘技に難しいことは何もない。必要な機会に打ち、必要な機会によけるだけだ。−ジャシュガン− (「銃夢」より)



銃夢(GANNMU) 4 (ヤングジャンプコミックス)

銃夢(GANNMU) 4 (ヤングジャンプコミックス)

*1:これらは、ドキュメントの有用性が高いところとも一致するでしょう。アジャイル開発だってコードを重視しながらも「必要なときには必要な分だけドキュメントを書く」ことを提唱しているはず。型指定についても同じように思うのですが、言語のせいで型指定が書き残せないというならある意味それは「ドキュメントを書きたくても書くことができない」という状況に相当していて、直感的には間違ってます。