Hello, polyglot!

きっかけ: るびま これを読んでpolyglotするかーって思った.
今回のコンセプトは

  • 初めてなのでやさしくHello, world!
  • 好きな言語を盛り込む
  • 140文字以下

以下,Schemeと言いつつGauche決め打ちである.

ソースコード

できあがったのがこれ.

;(#)={-"^<"v
'v+55p44p41<
(define puts print)
;')#>:#,_@-}0;puts=putStrLn;main=
 (puts"Hello, world!")

SchemeとRubyとHaskellとBefungeで動く.102打.

言語紹介

Scheme

S式と呼ばれる単純な文法で記述する言語.括弧で括ると何かが起きる.行コメント ; ネスト可能なブロックコメント #| ... |# に加えて式を一つコメントアウトする式コメント #; expr がある.
" は文字列リテラルに使われるが, ' はquoteといって続く式は評価されずリテラルになる.'foo はシンボルへ,'(foo bar baz) はリストへと読まれる.
いろんな文字が識別子に使えるのも特徴.+ - < > など.

Ruby

独特の複雑な文法で記述する,改行が意味を持つタイプの言語.あちこちで括弧を省略できる.# で行コメントはできるが,複数行コメントはちょっと使い辛い.
" と ' はどちらも文字列リテラルに使われる.似たようなものに / で開始する正規表現リテラルがある他,%!...! や %q!...! (! は任意の非英数字)等の多様なリテラルを持つ.

Haskell

インデントが意味を持つタイプの言語.トップレベルには(多分)宣言と定義しか書けないので,今回使用した言語の中では最も文法上の制約が強い.行コメントは -- ネスト可能なブロックコメントは {- ... -} でできる.
静的型付き言語なので,型の整合性も取らなければならない.

Befunge

1文字1命令で,実行の向きを変えながら二次元格子状のコードを実行していく難解言語.コメントのための機能は無いが,意味のある命令以外は無視される.
スタック型.小さい整数を扱う事ができる.実行中のコードを読み書きできる.

Scheme着色版

;(#)={-"^<"v
'v+55p44p41<
(define puts print)
;')#>:#,_@-}0;puts=putStrLn;main=
 (puts"Hello, world!")

Ruby着色版

;(#)={-"^<"v
'v+55p44p41<
(define puts print)
;')#>:#,_@-}0;puts=putStrLn;main=
 (puts"Hello, world!")

Haskell着色版

;(#)={-"^<"v
'v+55p44p41<
(define puts print)
;')#>:#,_@-}0;puts=putStrLn;main=
 (puts"Hello, world!")

Befunge着色版



;(#)={-"^<"v
'v+55p44p41<
(define puts print)
;')#>:#,_@-}0;puts=putStrLn;main=
(puts"Hello, world!")

解説

1行目

Schemeをコメントアウト.Haskellでは演算子 (#) の定義の途中から4行目の途中までコメントアウト.これによってRubyも開き括弧だけしてコメントアウト.Befungeでは前半は無意味なコードで,後半にコード書き換え用の文字をスタックに積んで下へ.

2行目

Schemeでは quote により離脱.同時にRubyは4行目の頭まで文字列リテラルにより離脱.Befungeではここを右から実行してきて,5行目の文字列を読むための命令を5行目に書き込む.

3行目

Schemeでputsを定義.Rubyは文字列リテラル中,Haskellはコメント中,Befungeは素通りでいずれも影響無し.

4行目

Schemeはコメントアウト.Rubyはここで文字列リテラルから復帰して括弧を閉じ,あとはコメント.Befungeではここ >:#,_ が出力部になっていて,スタックに積まれた文字列を出力し終了するようになっている.後半はHaskellがコメントから復帰し,(#) の定義を終えて putsを定義,main=を置いておく.

5行目

Scheme,Ruby,Haskellはここで出力.Befungeはコード書き換えにより

;(#)={-"^<"v
'v+55p44p41<
(define puts print)
;')#>:#,_@-}0;puts=putStrLn;main=
 <pu^s"Hello, world!")

となっていて,ここで出力する文字列をスタックに積む.そして4行目の出力部へ行き,出力して終了.

まとめ

文字列リテラル部が共有できてていい感じ.最初はもっとややこしそうなコードを書いていたつもりだったのだけど,縮めていくうちにシンプルになっていった.単純は短し.
文法的にはHaskellの制約が強い一方,ショートコーディングとしてはBefungeの性格が強く出た.

没コード集

その1
;{->{55+48+v
(define putStr display)
'|#:\g5+9: <-1_'
}=>0};alias putStr puts
#>:#,_@|;-}main=
 (putStr"Hello, world!\n")

121打.(define putStr display) がRubyでも文法上は妥当なことを利用し,ラムダ式 ->{ ... } をコメント的に利用したコード.Rubyでは括弧を補うと (define(putStr(display()))) と解釈される.実行されなければどうということはない.1行目の 55+48+v はBefungeのためのコードだが,2行目と同様にRubyでも文法OKなので書ける.
Schemeで |...| が一つのシンボルになることを利用した式コメントが気持ちいい.
出力関数の名前はHaskellの putStr に合わせている.
Befungeは文字列リテラル部分をg命令で読み取るようになっていて,長さが埋め込みなのがちょっと残念.

その2
eval'=eval'&&'p'
{-0=>(alias putStr puts)}
">   55+48+v
|#:\g6++49:<-1_
>:#,_@"#t(define putStr display)'
;#-}where _&&x=x
main=(putStr"Hello, world!\n")

153打.eval がSchemeでもRubyでも定義されていて,Befungeが下に脱出できるのを利用したコード.Befungeの変なコードなんか全部文字列にしちゃえば簡単じゃないかというコンセプト.しかし長くなりすぎた.似たような方法に format を使う手がある.

その3
;{->{55+48+v
(define puts print)
 '|#:\g4+7:<-1_'
}=>':#,_@|;'}#-}puts=putStrLn;main=
 (puts"Hello, world!")

108打.その1の改良版.出力関数の名前はRubyの puts に合わせている.

その4
;{->{"2^<"^
(define puts print)
'|0/5p42p4<'
}=>':#,_@|1;'}#-}puts=putStrLn;main=
 (puts"Hello, world!")

104打.その3の改良版.Befungeのアプローチを変えて,文字列の長さが埋め込みでなくなった.