自宅のPCのディスクの奥底からHello Worldを発掘し始めて2週間経った。我ながら色々な言語でHello Worldを書いてきたものだと、つくづく呆れている。
多少なりとも本格的に言語に触れたいならFizzBuzzを書くべきだろう。プログラムの実行環境との関係性を探るならargsなども考えられる。
でも、ほんのちょっとだけ、気の迷いでも構わないなら、Hello Worldは悪くない題材だ。
Hello Worldの起源はBCPLらしいが、有名になったのはC言語のバージョンだろう。C99以降にて合法的な版はこんな感じだろうか?
#include <stdio.h>
int main(void)
{
puts("hello, world");
}
良くも悪くもC言語の影響は大きい。C言語の後継を目指した言語は色々とあるが、割とどれもmain関数から処理が始まる。例えばD言語の場合:
import std.stdio;
void main()
{
writeln("hello, world");
}
愚考するに、C言語では、プログラムは「関数の塊」として抽象化されている節がある。ごく一部の例外の除き、式や文は関数の中に書く。プログラムは関数の入れ子として階層化される。
ここで、入れ子のもっとも外側の関数について、ホスト環境では便宜上mainという名前を付けることにしよう――といった塩梅だろうか? 今では言語仕様にすらなっている名前である。
時が経ち、モジュール機構やオブジェクト指向プログラミングの機能が言語に組み込まれるようになっても、下記のC++版のように関数で始まるスタイルは続いたように思う。
#include <iostream>
int main()
{
std::cout << "hello, world" << std::endl;
}
――が、そのうちに、今度はプログラムを「オブジェクトの塊」として抽象化する言語が出てきた。その起源は知らないのだが、有名なのはJavaだろう。
class Hello {
public static void main(String[] args) {
System.out.println("hello, world");
}
}
Javaの後に出てきたC#もそうである。
namespace HelloWorld
{
using System;
class Hello
{
static void Main(string[] args)
{
Console.WriteLine("hello, world");
}
}
}
後知恵になるが、いちプログラマとしては、言語仕様のレベルで「オブジェクトの塊」として抽象化してしまうのは、ちょっとやりすぎだったように思う。
プログラムを「オブジェクトの塊」として抽象化する――というスタイルの一貫性の観点では、割とキレイな抽象化だと思うのだ。
が、しかし、そんな言語を使うのは、曖昧で一貫性のかけらもない、薄汚れた我らプログラマである。ちょっとしたスクリプトを書くなら関数化すら不要と嘘吹き、小ツールを実装するには関数による機能分割で十分と豪語する。要するに、作るものの大きさに合わせて、どのレベルで抽象化するのかを自分でコントロールしたいのだ。わがまま極まりない。
――そういう事情があったか否かは定かではないのだが、JavaやC#よりも後に出てきた言語では、再びmain関数から始まるスタイルに回帰している。
Dartも:
void main() {
print('hello, world');
}
Goも:
package main
func main() {
println("hello, world")
}
Rustも:
fn main() {
println!("hello, world");
}
全てはmain関数に始まり、main関数で終わる。
――と言いたいところだが、昨今はSwiftなどのようにmain関数すらない(少なくともコードの見た目としては存在しない)汎用のプログラミング言語もある。
print("hello, world")
main関数のようなブツの有無に関して想像するに、スクリプト言語の系譜では「そんなものはない」スタイルが常なように感じる。
まず、言語ではなく、コマンドラインインタプリタの一種であるUnixシェルや:
echo hello, world
Windowsのバッチファイル:
@echo off
echo hello, world
これらの原型というか先祖の世代のバッチ言語やらマクロ言語の潮流から、REXXみたいなスクリプト言語が出てきたのかなあ、と妄想している。REXXの時点でmain関数が無い。
#!/usr/bin/env rexx
say "hello, world"
exit 0
スクリプト言語と言えば、私の世代ではPerl/Python/Rubyの三銃士の印象が強い。なぜか一括りにされているのだが、しかしよく見てみると、Perlと:
#!/usr/bin/env perl
use 5.030;
use strict;
use warnings;
say "hello, world";
Pythonと:
"""hello, world
"""
print('hello, world')
Ruby:
#!/usr/bin/env ruby
puts "hello, world"
Hello World程度ではまだ分からないが、もう少し手の込んだ小スクリプトを書こうとすると、どれも似てない。みんな違って、みんないい。でもmain関数が無いのは共通している。
Webクライアント開発の言語であるJavaScriptや:
#!/usr/bin/env node
console.log('hello, world');
TypeScriptのようなaltJSとして出てきた言語の大半:
#!/usr/bin/env -S deno run
console.log('hello, world');
これらもmain関数が無い。こう考えると、割とWeb開発系はフロントエンドもバックエンドもmain関数が無い言語の割合が多いのかもしれない。
C言語の影響でmain関数のフォロワーが続出したと仮定するなら、では、同世代や前世代を起源とする言語はどうだろうか?
例えばPascal――は世代ではないし、Object Pascal(原典の方)も掠ってすらないので、Free Pascalで書いてみた版:
{$CODEPAGE UTF8}
{$MODE OBJFPC}
{$LONGSTRINGS ON}
program Hello;
begin
WriteLn('hello, world');
end.
programで始まるスタイル。元ネタはFortranだろうか? Fortran 90版もprogramで始まる。
program hello
print *, 'hello, world'
end program hello
FortranといえばCobolだが(偏見が過ぎる)、Cobolのコードは素人目にも異彩を放っているように感じられる。
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO.
AUTHOR. EEL3.
DATE-WRITTEN. 2010-06-29.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. PC.
OBJECT-COMPUTER. PC.
PROCEDURE DIVISION.
MAIN.
DISPLAY "hello, world"
STOP RUN.
Pascalは元々教育用途メインで開発されたらしいが、同じく教育分野で古典的な言語であるLOGOはどうだろうか?
; Worked on UCBLogo (Berkeley Logo)
print [hello, world]
シンプルに1行だけ。スクリプト言語っぽいスタイルである。元々インタプリタ的というかREPLで対話的というか、そういう感じだからなのかなあ?
そして古典の中でも異彩を放つ文法のLisp。今の時代でもCommon Lispや:
#!/usr/bin/env clisp
(format t "hello, world~%")
(quit)
もしくはSchemeの処理系は手に入りやすい。
#!/usr/bin/env gosh
(begin (display "hello, world")
(newline))
Lispといえば、汎用の処理系ではないが、Emacs Lispも一大勢力を築いている。
(message "hello, world")
なぜかよく対比されるviについても、Vimならスクリプトを記述できる。
echo "hello, world"
quit!
秀丸エディタもそうだが、テキストエディタにスクリプト機能を組み込むスタイルの起源はどこにあるのだろうか?
insert "hello, world\n";
テキストエディタ以外にも、Hello Worldを書けるツールは色々ある。例えばmake(1)とか:
.PHONY: hello
hello:
GDBのようなデバッガも、スクリプト機能を使えばOK。
echo hello, world\n
quit
そして、一応計算機という立て付けのbc(1)でも:
"hello, world
"
quit
計算機のハードの上でOSを動かし、その上で動作する計算機を作っておいて、なお計算機らしくない使い方をする。何とも度し難い。
ここまで見てきたHello Worldの大半は式や文(命令?)をともなうものだったが、それすら省いたパターンもある。
モダンかつ一応言語っぽいところだとPowerShellだろうか?
.SYNOPSIS
.INPUTS
.OUTPUTS
.EXAMPLE
Set-StrictMode -Version Latest
'hello, world'
色々と余分なものを書いているが、肝の部分は'hello, world'だけである。
マクロプロセッサの類を含めると、事態は悪化する。例えばm4の版:
hello, world
これだと身も蓋もなさすぎるので、もう少し小細工を労してみると、こんな感じだろうか?
define(`HELLO', `hello,')
define(`WORLD', `world')
HELLO WORLD
m4ほとではないが、T4(Visual Studio付属のテキストテンプレート変換)の版も身も蓋もない:
<#@ template language="C#" #>
<#@ output extension=".txt" #>
hello, world
もうちょっとだけ特別感を出したいなら、こんな感じだろうか?
<#@ template language="C#" #>
<#@ output extension=".txt" #>
<#= "hello, world" #>
身も蓋もないといえば、私は暇を持て余すあまりに「cat(1)用のHello World」というもはやHello Worldなのかすら怪しい代物を考え出したことがある。
hello, world
あまりに身も蓋もなさすぎて、さすがにrev(1)の方がマシだろうと考えたのだが……:
dlrow ,olleh
この手の一発ネタは、なかなか思いつかない。どうしても手を加えすぎてダメになってしまう。
paste(1)を使う版も割と失敗作である。
paste -d '' - - - <<'END'
hello
,
world
END
paste(1)版には元ネタがある。元々はxargs(1)とprintf(1)を使った版を考えていたのだが、うまくいかなくてpaste(1)版を作成したのである。
その後にxargs(1)とprintf(1)を使う版を完成させた。
xargs printf %s%s%s\\n <<'END'
hello
', '
world
END
肝は', 'である。xargs(1)の仕様を調べていて「へーそうなんだ」と思った次第である。
Hello Worldは時に既視感をもたらす。
スタック指向の言語、例えばForthとか:
s" hello, world"
type
あるいは、特にPostScriptとか:
(hello, world\n) print
quit
この辺のHello Worldを書いた後に、オブジェクト指向言語のIoと:
"hello, world" println
GNU Smalltalkの版を見てみると:
#!/usr/bin/env -S gst -f
'hello, world' displayNl.
……どれも「テキスト → 命令」の順序になっている。だからと言って、特に何もないのだが。
うまくいかなかったHello Worldも多い。
例えば、sed(1)を使うHello Worldを考えてみたのだが、どう頑張っても何かしらの入力が必要だった。
s/^.*$/hello, world/
1q
echo '' | ./hello.sedなら動作するがecho -n '' | ./hello.sedだとダメなのだ。
XSL版も考えたが、こちらもやはり入力が必要だ。何かしらのXMLファイルを与えなくてはならない。
xml version="1.0" encoding="utf-8"
<xslstylesheet version="1.0" xmlnsxsl="http://www.w3.org/1999/XSL/Transform">
<xsloutput method="text" encoding="utf-8" />
<xsltemplate match="/">
<xsltext>hello, world
</xsltext>
</xsltemplate>
</xslstylesheet>
まあ、Hello Worldを書いたXSL自体(つまり自分自身)を入力とすることで何とかなるのだが……。
もう少し未完成度が高いのはProlog版だろう。B-Prologで動かす前提で書いてみたのだが、規則の部分はファイルに記述できるものの、質問は別である。対話的に実行するか、もしくはコマンド引数などで与える必要がある。
hello :- write('hello, world'),nl.