title: Perl6の内部表現 author: Takahiro Shimizu profile: lang: Japanese ## このセッションの内容 - Perl6の主要な実装であるRakudoの内部構造を探ります - Rakudoの内部で利用されているVMや, Perl6のサブセットなどについて探索します - スクリプト言語で主に使われているバイトコードインタプリタの気持ちになります ## 内容 - Perl6とは? - スクリプト言語処理系の動き - Perl6の内部構造 - NQP - MoarVM - MoarVMのバイトコード実行 - まとめ ## Perl6とは - 当初Perl5の時期バージョンとして開発されていたプログラミング言語 - 現在は別の言語として開発がそれぞれ進んでいる - 仕様と実装が分離しており, 現在はテストが仕様となっている - 実装は歴史上複数存在しているが,主流な実装はRakudo - 言語的にはスクリプト言語であり, 漸進的型付き言語 - 動作環境は、独自のVMのMoarVM, JVM、一部JavaScript上で動作する ## 現在のPerl6 - 現在のバージョンは `6.d` - [ブラウザ上で実行可能な環境](https://perl6.github.io/6pad/)が存在する - [IDE](https://commaide.com/)が開発されている - WebApplicationFrameworkなども開発されており、 Perl5のモジュールを移行したものがいくつか存在する - 日本では趣味のプロダクト以外社会では使用されていない - 海外では実際に使われているケースも存在する - 処理速度では一部Perl5に勝っているが、それでも大分遅い ## [参考]Perl5のソースコード - Perl5時代 − スカラ、配列、ハッシュの3種類 - それぞれの変数への参照であるリファレンスが使用可能 ```perl use ustrict; use warnings; my $scalar_value = "hello!"; print "$scalar_value\n"; my @array = (1..10); print "$array[0]\n"; my %hash = ( this_is_key => "this_is_value"); print "$hash{this_is_key}\n"; my $hash_ref = \%hash; print "$hash_ref->{this_is_key}\n"; ``` ## Perl6のソースコード概要 - Perl5の文法とは比較的変更が多い - 雰囲気は似ている - 変数がオブジェクトと化した事により, 変数からsayメソッドを呼ぶことが可能 ``` my $str_value = 'hello world!'; $str_value.say; # hello world! ``` - Perl5と同様に,変数にはデフォルトでは型がないような振る舞いをする ``` my $sample_value = 'hello world!'; $sample_value.say; # hello world! $sample_value = '31'; $sample_value.say; # 31 say($sample_value * 3); ``` ## Perl6の言語的な特徴 - 漸進的型付き言語である為, 型を強制することも可能となる ``` my Int $int_value = 31; $int_value = "hello"; # Compile error! ``` ``` $ perl6 type_invalid.p6 Type check failed in assignment to $int_value; expected Int but got Str ("hello") in block at type_invalid.p6 line 4 ``` ## Perl6の言語的な特徴 - 型を独自に定義することも可能 - 入力の型によって実行する関数を変える事などができる ```perl6 my subset Fizz of Int where * %% 3; my subset Buzz of Int where * %% 5; my subset FizzBuzz of Int where Fizz&Buzz; my subset Number of Int where none Fizz|Buzz; proto sub fizzbuzz ($) { * } multi sub fizzbuzz (FizzBuzz) { "FuzzBuzz" } multi sub fizzbuzz (Fizz) { "Fizz" } multi sub fizzbuzz (Buzz) { "Buzz" } multi sub fizzbuzz (Number $number) { $number } fizzbuzz($_).say for 1..15; ``` - 型を利用したFizzBuzz ## スクリプト言語 - Perl6は現状コンパイルすることはできない - スクリプト言語の分類 - 現在広く使われているスクリプト言語(Perl,Python,Ruby...)などとPerl6の構成は類似している - 今回はPerl6の実装を追いながら、最近のスクリプト言語処理系の大まかな実装を理解する ## スクリプト言語処理系 - スクリプト言語は入力として与えられたソースコードを、 直接評価せずにバイトコードにコンパイルする形式が主流となっている - その為スクリプト言語の実装は大きく2つで構成されている - バイトコードに変換するフロントエンド部分 - バイトコードを解釈する仮想機械 ## Perl6以外のスクリプト言語 - 現在使われているプロセスVMは言語に組み込まれているものが多い - JVMやElixirなどのVMは複数の言語で使用されている - Java - JVM - Ruby - YARV - Python - PythonVM - Elixir - BEAM ## Perl6の処理系の構成 - Perl6の処理系で現在主流なものはRakudoと呼ばれる実装である(歴史上複数存在する) - Rakudoは3つのレイヤーから構成されている - Perl6インタプリタ - Perl6インタプリタを記述するPerl6のサブセットNQP - Perl6のバイトコードを解釈するMoarVM - Perl6/NQPがフロントエンドに相当し、MoarVMがバックエンドに相当する ## Rakudoの構成図 ![](fig/Rakudo_System_overview.png) (http://brrt-to-the-future.blogspot.com/2015/03/advancing-jit-compiler.html) ## Perl6とNQP - NQP(NotQuitPerl Perl) - Perl6のサブセット。Perl6っぽい言語 - Perl6、 NQP自体がNQPで記述されている - NQPもNQPで記述されている為、 セルフビルド(自分自身で自分自身をコンパイルする)を行う - NQPはPerl6の文法をベースにしているが、 制約がいくつか存在する - 元々はPerl6の主力実装がParrotだった時代に登場 - 文法がアップデートされており、当時の資料は古くなっている ``` my $value := "hello!"; say($value); ``` ## NQPスクリプト - 変数は束縛 `:=` を使う − 関数の間に空白を入れてはいけない - 再帰呼び出しを使うフィボナッチ数列 ``` #! nqp sub fib($n) { $n < 2 ?? $n !! fib($n-1) + fib($n - 2); } my $N := 29; my $z := fib($N); nqp::say("fib($N) = " ~ fib($N)); ``` ## NQPスクリプト(nまでの整数の和) ```perl6 sub add_test($n){ mu $sum := 0; while ( $n > 1) { $sum := $sum + $n; --$n; } return $sum; } say(add_test(10000)); ``` ## NQPとオペコード - NQPはPerl6の中で一番レイヤーが低い言語 - その為、 実行するVMのオペコード(処理単位)を使用することができる ## NQPとMoarVM - NQPそのものは実行することはできない - NQPの実行にはMoarVM/JVMが必要となる - NQPコンパイラが各VMに対応したバイトコードに変換する ## Perl6のVM - MoarVM, JVM , JavaScriptが選択可能 - メインで開発されているのはMoarVMであり、 他のVMは機能が実装されていないものが存在する - `rakudo-star` というPerl6のパッケージ環境では、 MoarVMがデフォルトでインストールされる ## MoarVM - C言語で記述されているPerl6専用の仮想機械 - レジスタマシン - 型情報を持つレジスタに対しての演算として処理される - Rubyなどはスタックマシンとして実装されている - LuaJITなどを利用したJITコンパイルなども可能 - Perl6やNQPは、MoarVMに対してライブラリなどを設定して起動する ## バイトコード - Perl6も、Rakudo/NQPはバイトコードに変換され、 バイトコードをVMが実行する - バイトコード実行部分は、 命令に対応するバイト列を読み込み、 解釈し、 次の命令を読み取ることを繰り返す