龍虎氏の記事に対するコメント。
今回はRustの重要な性質から見てみよう。
一般的に、C言語系のプログラミング言語では{...}と言う表現はスコープを引き連れたブロックを意味する。
ところで、C言語で次のようなプログラムを書くとしよう。
![](https://blogimg.goo.ne.jp/user_image/5a/00/b17381b6a0919b682cc60b52793b5f76.png)
一見良さそうに見えるが、コンパイルエラーが出る。
C言語ではブロックは単独では存在出来ず、あくまで他の構文(関数定義とか条件分岐とか)の「要素」としての機能しかない事が伺える(※1)。
一方、Rustでは次のような記述は許される。
![](https://blogimg.goo.ne.jp/user_image/41/49/816fe294aac21366ffc02dba426131ac.png)
ここがRustとC言語が決定的に違う部分の一つだ。Rustではブロックは単独で存在出来る。
さて、確かに「どっちが正しいか?」と言う問いは無意味と言えば無意味だが、Racketを初めとするLisp観点だとどうだろうか?圧倒的にRustの方が正しく思えるんじゃないか。
Racketでは次のように書くのは許容される、からだ。
(define (main)
(let ((z (let ((x 1) (y 2))
(+ x y))))
(println z)))
こう書きたいかどうかはさておき、だ(※2)。
そう、Rustの{...}はCのそれとは全く違う。むしろRacketの(begin ...)に近い、んだ(※3)。
Rustの{...}は単独で存在出来、「実行可能だ」と言う事を覚えておこう。
ちなみに、これはRacketに直訳すれば以下のようになるだろう(※4)。
(define (main)
(displayln "Guess the number")
(let ((secret-number (random 1 101)))
(call/cc
(lambda (break)
(let loop ()
(displayln "Please input your guess.")
(let ((guess (read-line)))
(let ((guess
(cond ((string->number (string-trim guess)) => (lambda (num)
num))
(else (loop)))))
(printf "You guessed: ~a~%" guess)
(cond ((< guess secret-number) (displayln "Too small!"))
((> guess secret-number) (displayln "Too big!"))
(else (displayln "You win!")
(break)))
(loop))))))))
Rustのmatchはパターンマッチ、と言いながらRacketのmatchとはちと毛色が違って、Racketのcondが混ざってるような機能だ、って事が分かる。
同様に、変数に結果が代入出来るトコを見ても、Pythonのmatch文と違い、match式である事が分かる(式は返り値がある)。
また、match式とOrderingの組み合わせも強力で、明らかにパターンマッチ以上の事をやってのけてる(結果、与えられた関数を使って高階関数的に真偽値判定をしている)。
Racketもマクロを駆使すれば似たような事は出来るだろうが、「マクロを書く」方がメンドイかもしんない(笑)。
※1: C言語の仕様上、これは「許される」のでは?と思ったが許されなかった(笑)。
仕様的には{...}は単独でも存在出来そうに書いてて、構文「要素」ではなく、構文の一つとなってるようで、「誰もこう書かないけど実は書ける?」とか思ったがやっぱダメだ、って結果にガッカリしてる(笑)。
※2: 通常はlet*を使う局面となる。
※3: Lispのletは理論的にはラムダ式の構文糖衣だ、と言う事を思い出そう。そしてPythonのラムダ式と違って、Racketのラムダ式には「暗黙のbegin」が含まれる。
※4: 初心者向けのベタ書きコードだ。
しかし、本来ならやはりRead-Eval-Print Loop構造で書くべきだろう。