Rakugan が断る書き方
方言は部分集合で、その境目に出会う場所が rakugan check です。
アプリを読み、受け取れない書き方があれば、ファイルと行と桁、その行そのもの、そして代わりの書き方を示します。
ファイルを先に読むのは perl なので、perl が撥ねた書き方が翻訳器に届くことはありません。
check はビルドの前にもゲートの前にも走ります。
コンパイラもウィンドウも要らず、言うことがなければ何も出力しません。
下の 51 個には、それを起こすファイルと、出力されるべき文面が、test/refuse/ にそのまま置いてあります。
tools/gate_all.sh がそれを回すので、断りの文面が黙って変わることはありません。
このページも、その同じファイルから引いています。
クラス
要素と型の名前と empty は import で入ってきますが、Perl の import はパッケージごとに効きます。
test/refuse/class_pragma.pl:3:11: Rakugan cannot take this — `class App` needs `use Rakugan;` as its first line: a Perl import is per package, and the elements, `empty` and the type names have to be in this one
class App {
^
フィールドの型は初期値から読むので、初期値がなければ読むものがありません。
test/refuse/field_initializer.pl:5:11: Rakugan cannot take this — a field needs an initializer (`= 0`, `= ""`, `= empty(Str)`) — that is where its type comes from
field $n;
^
アプリ自身のフィールドはアプリだけのものです。 外から渡すものも、外から読むものもありません。
test/refuse/field_attribute.pl:5:14: Rakugan cannot take this — a field takes no attributes here (`:param`, `:reader`); its type is read from the initializer
field $n :param = 0;
^
メソッドの引数の型はどこからも読めないので、メソッドに書いて示します。
test/refuse/method_without_sig.pl:7:12: Rakugan cannot take this — a method with parameters says what they are: `method add :Sig(Int) ($by) { ... }`
method add ($by) {
^
ファイルを先に読むのは perl で、そこでは裸の s は置換の始まりです。
test/refuse/quote_method.pl:7:12: Rakugan cannot take this — a method named `s` reads as a regular expression to the parser; pick another name
method s { $n += 1 }
^
ファイルの最上位はアプリを作る場所であって、状態を置く二つ目の場所ではありません。
test/refuse/top_statement.pl:3:1: Rakugan cannot take this — a declaration at the top of the file is a hash of keywords (`my %PILL = (...)`), a name for a literal (`my $WIDTH = 120;`) or the app itself (`my $app = Counter->new;`)
my @greetings = ("hello", "goodbye");
^
メソッドを持つクラスの中では、フィールドは名前で触ります。 同じクラスの別のメソッドをそこから呼ぶ形は、まだ運びません。
test/refuse/self_in_class.pl:6:28: Rakugan cannot take this — `$self` inside `Node`: a method reaches its own fields by name, and another method of the same class is not called from here yet
method grow { $n += 1; $self->grow }
^
フィールドは値なしの new から始まり、何も動く前に型が決まります。
値は ADJUST かハンドラで入れます。
test/refuse/new_with_values_in_field.pl:11:19: Rakugan cannot take this — a field starts as `Node->new` with no values; give it values in `ADJUST`, or make it in a handler
field $node = Node->new(n => 3);
^
コンパイルした実行はすべてのフィールドに、その名前の読み手と書き手を与えます。 メソッドはその名前を取れません。
test/refuse/method_named_like_field.pl:6:12: Rakugan cannot take this — `set_n` is the name the compiled run gives `n`'s own writer; put `:writer` on the field, or name the method for what it does
method set_n :Sig(Int) ($v) { $n = $v }
^
型
空で始まる入れ物には、型を読むものが入っていません。
test/refuse/empty_list.pl:5:20: Rakugan cannot take this — a list that starts empty says what it will hold: `field @items = empty(Str);`
field @items = ();
^
リストは一つの型です。 コンパイルした実行がそう持つからです。
test/refuse/mixed_list.pl:5:24: Rakugan cannot take this — a list holds one type: this one started with Int and this is String
field @items = (1, "two");
^
キーワードの型は表が決めます。 エンジンの両側が数えているのはその表です。
test/refuse/wrong_type.pl:7:30: Rakugan cannot take this — `size =>` takes a number (got String)
return text("hello", size => "large");
^
文面はその要素が取るものを並べるので、正しい名前はその場で分かります。
test/refuse/unknown_keyword.pl:7:30: Rakugan cannot take this — `text` has no `weight =>`; it takes `a11y_label`, `align`, `animate`, `background`, `bold`, `border_color`, `border_radius`, `border_width`, `col_span`, `color`, `disabled`, `easing`, `enter`, `exit`, `grow`, `height`, `italic`, `max_lines`, `max_width`, `min_width`, `mono`, `padding`, `role`, `row_span`, `size`, `theme`, `tooltip`, `underline`, `width`, `wrap`
return text("hello", weight => 700);
^
数や文字列の真偽は方言に入っていません。
条件は Bool です。
test/refuse/truthiness.pl:9:35: Rakugan cannot take this — a condition is a bool (got Int); Perl's truthiness of a number or a string is not in the translator — compare it (`!= 0`, `ne ""`)
push @cells, text("some") if $n;
^
文字列を数として読むことは、perl なら黙ってしますが、ここでは書いて示します。
test/refuse/string_and_number.pl:8:19: Rakugan cannot take this — `+` needs a number on both sides (got String and Int); `0 + $s` reads a string as a number, the way perl does
$n = "10" + 5;
^
perl はそこで文字を数えますが、コンパイルした実行にその数え方は入っていません。
test/refuse/string_increment.pl:8:13: Rakugan cannot take this — `$tag` holds a String, and Perl's `++` on a string counts letters (`"az"++` is `"ba"`), which the compiled run does not do; join or replace the string instead
$tag++;
^
フィールドの型は初期値から読みますが、undef だけでは型が決まりません。maybe(Int) が何を持ちうるかを言います。
test/refuse/undef_alone.pl:5:18: Rakugan cannot take this — `undef` alone says nothing about the type; say what this may hold: `maybe(Int)`, `maybe(Str)`
field $sel = undef;
^
なにもないものには文面がありません。
そのとき何を出すかをアプリが言うか、defined の中で値を読みます。
test/refuse/maybe_in_text.pl:8:25: Rakugan cannot take this — this may be nothing, and nothing has no text; say what to print then: `$x // "-"`, or read it inside `if (defined $x)`
method go { $note = "sel=$sel" }
^
なにもないもののメンバーは、perl が実行時に die する場所です。
if (defined $x) の中なら、どちらの実行でもオブジェクトがそこにあります。
test/refuse/maybe_member.pl:14:25: Rakugan cannot take this — `$root` may be nothing; read it inside `if (defined $root)`, where it is the object
method go { $note = $root->label }
^
その if の分岐が、値を値として読む場所です。
while や && にはそのような分岐がありません。
test/refuse/defined_in_while.pl:9:9: Rakugan cannot take this — `defined` here is the whole condition of an `if` or `unless`, and its branch reads the value; it does not go in a `while`, a `grep`, or beside `&&`
while (defined $sel) { $n += 1; $sel = undef }
^
分岐の中では、その名前は値の写しです。 そこで書けば、片方の実行では写しが、もう片方ではフィールドが変わります。
test/refuse/write_inside_defined.pl:11:13: Rakugan cannot take this — `$sel` is read as the value it holds inside `if (defined $sel)`; write it outside that block
$sel = undef;
^
perl は定数をパッケージごとに持つので、二つのクラスが同じ名前を使えます。 ただし、同じ値を指しているときだけです。
test/refuse/constant_twice.pl:6:18: Rakugan cannot take this — `LIMIT` is declared twice, and not the same way
use constant LIMIT => 10;
^
perl はその数を小数のある数に育てますが、方言が持つのは 64 bit までです。 書き下した二つの数はビルドの前に計算し、そこで断ります。
test/refuse/literal_overflow.pl:8:34: Rakugan cannot take this — this comes to 18446744073709551616, and a whole number here holds 64 bits — perl would grow it into a number with a fraction, which the compiled run cannot follow; write it with a `.0` to mean that number
$n = 4611686018427387904 * 4;
^
ビューとハンドラ
同じ画面を二度組み立てたら同じ画面になる必要があるので、組み立てるときは読むだけです。
test/refuse/view_calls_method.pl:13:21: Rakugan cannot take this — `bumped` touches the app's state, and building a view only reads; give it what it needs as parameters, or read a field
return text("n=@{[ $self->bumped ]}");
^
行は自分の番号を読みます。 それ以外は、確かめられる場所で計算します。
test/refuse/negative_index.pl:9:21: Rakugan cannot take this — an index a view cannot prove is not negative; a row reads its own index, and anything else is worked out in a handler
return text("at: $items[$at]");
^
ハンドラは、その出来事が運ぶものだけを受け取って呼ばれます。
test/refuse/handler_arity.pl:8:45: Rakugan cannot take this — this handler is called with nothing; drop the parameter
return button("go", on_click => sub ($x) { $n += 1 });
^
鍵がないときに何が起きるかで両方の実行が一致する必要があるので、答えをアプリが決めます。
test/refuse/bare_hash_read.pl:9:26: Rakugan cannot take this — a hash may not have that key, so say what to answer when it does not: `$prices{$k} // 0`
$picked = $prices{"apple"};
^
ハッシュの順序は perl を起動するたびに変わります。 画面がそれに依存するわけにはいきません。
test/refuse/unsorted_keys.pl:10:20: Rakugan cannot take this — a hash hands `keys` back in the order perl happens to hold it, which is a different order every time perl starts; write `sort keys %h`
for my $k (keys %prices) {
^
同じ画面を二度組み立てたら同じ画面になる必要がありますが、組み立てながら引いた数は二度目には別の数です。
test/refuse/rand_in_view.pl:10:28: Rakugan cannot take this — a view calls what cannot change; draw the number in a handler and keep it in a field
return column(text("roll: @{[ int(rand(6)) ]}"), button("go", on_click => sub { $self->go }));
^
コンパイルした実行に perl がないこと
コンパイルしたアプリが書くのは画面で、ゲートが木を読むのもそこからです。
test/refuse/say_to_stdout.pl:8:9: Rakugan cannot take this — a compiled app writes its screen, not its standard output; `warn` goes to standard error
say "n is $n";
^
配ったアプリはコンパイラを積んでいません。
test/refuse/string_eval.pl:8:14: Rakugan cannot take this — a string `eval` compiles Perl while the app runs, and a shipped app carries no compiler; catch a failure with `try` / `catch`
$n = eval "1 + 1";
^
パターンは翻訳のときにコンパイルするので、そのときにはもうファイルに書かれていなければなりません。
test/refuse/pattern_built.pl:9:27: Rakugan cannot take this — a pattern here is written out; one built while the app runs would have to be compiled by something the shipped app does not carry
$found = "abc" =~ /$needle/;
^
コードを走らせるパターンには perl が要りますが、コンパイルした実行に perl はありません。
test/refuse/pattern_code.pl:8:27: Rakugan cannot take this — a pattern that runs code (`(?{ … })`) is perl's own; the compiled run has no perl in it
$found = "abc" =~ /a(?{ print "hi" })b/;
^
同じ理由です。 置換の中身は、アプリが動いている最中に走る perl になります。
test/refuse/substitute_eval.pl:8:18: Rakugan cannot take this — a replacement here is text, with `$1` … `$9` for what the pattern caught; `/e` runs perl and the compiled run has none
$line =~ s/(\d+)/$1 + 1/e;
^
その if の外では、どこかで最後に成功した一致が残したものになります。
test/refuse/capture_unguarded.pl:10:16: Rakugan cannot take this — what a pattern caught is read where the match is known to have happened: inside the `if` that made it
$got = $1;
^
コンパイルした実行は失敗した文でハンドラを止めるので、どちらの道でも必ず動く場所がありません。
test/refuse/try_finally.pl:9:50: Rakugan cannot take this — `finally` runs after either path, and the compiled run has no unwinding to hang it on; write the line after the `try`
try { $n = 1 } catch ($e) { $note = $e } finally { $n = 2 }
^
ループの中では失敗ごとに catch が走り、ループは続くことになります。
ループの中に try を置けば、そのあと何をするかを言えます。
test/refuse/try_loop.pl:12:44: Rakugan cannot take this — a `try` does not reach into a loop yet; put the `try` inside the loop, around the line that can fail
for my $x (@xs) { $total += $x / $n }
^
メソッドは一度だけコンパイルされ、外の try はその中まで届きません。
中に置けば、失敗しうる行はすぐそこにあります。
test/refuse/try_method.pl:12:22: Rakugan cannot take this — `halve` can fail — it divides, takes a root, writes `die` or calls the library — and a `try` here does not reach into it yet; put the `try` inside `halve`, around the line that can fail
try { $self->halve } catch ($e) { $note = $e }
^
perl はその行を走らせないので、コンパイルした実行も走らせてはなりません。 動かないコードを読ませないために断ります。
test/refuse/after_die.pl:9:9: Rakugan cannot take this — nothing after `die` runs; drop these lines, or put the `die` under an `if`
$n = 1;
^
perl はその行をすぐに走らせ、コンパイルした実行は処理が終わってから走らせます。
task の前に置けば、どちらもすぐに走らせます。
test/refuse/after_task.pl:10:9: Rakugan cannot take this — `task` is the last thing a handler does: the compiled run reaches these lines when the work is done, and perl reaches them at once; write them before the `task`
$status = "working";
^
何も種を蒔かないと、perl は時計とプロセスから自分で種を取ります。 だから種のないアプリは、どちらの実行でも起動ごとに別の数列を引きます。
test/refuse/rand_unseeded.pl:7:26: Rakugan cannot take this — `rand` in an app that never calls `srand` draws a different sequence every time it starts, in both runs; seed it once (`srand(42);`) and the two runs draw the same numbers
method go { $n = int(rand(10)) }
^
同じ理由です。
引数のない srand() は、自分で種を選びます。
test/refuse/srand_bare.pl:8:9: Rakugan cannot take this — `srand` with nothing picks a seed of its own, a different one in each run; write the seed: `srand(42)`
srand();
^
perl は負の指数に小数を、そうでなければ整数を答えますが、型はどちらか一方です。
test/refuse/pow_variable.pl:9:16: Rakugan cannot take this — a whole number to a power that is not written out may come out a fraction, and the compiled run has to know; write `2.0 ** $n` for a fraction, or `int(2.0 ** $n)`
$n = 2 ** $n;
^
翻訳器にまだ双子のない Perl の関数は、Perl 自身のものだと言います。 代わりがあれば、それも言います。
test/refuse/sleep_builtin.pl:9:9: Rakugan cannot take this — `sleep` is Perl's own, and not in the translator yet; a handler that waits freezes the window; `task(sub { ... }, on_done => ...)` does the waiting elsewhere
sleep(1);
^
配るアプリは一つのファイルで、自分のモジュールはそれと一緒に翻訳されなければなりません。
test/refuse/module_call.pl:11:14: Rakugan cannot take this — `My::Counter::next` comes from a module of your own, and a module does not reach the compiled run yet: an app is one file, and the modules the translator knows are List::Util's and POSIX's
$n = My::Counter::next();
^
eval { … } は try と catch の古い綴りで、方言は新しいほうを受け取ります。
test/refuse/eval_block.pl:9:9: Rakugan cannot take this — `eval { ... }` catches a failure; write it as perl 5.40 does: `try { ... } catch ($e) { ... }`
eval { $n = 1 };
^
変数に持ったサブルーチンはクロージャで、コンパイルした実行にその形はありません。 アプリのメソッドを呼びます。
test/refuse/sub_value.pl:9:17: Rakugan cannot take this — a sub written here has no shape in the compiled run; write a method of the app and call it
my $f = sub { 1 };
^
ラベルつきのループへの飛び越しは、コンパイルした実行に形がありません。 内側のループが立てるフラグで同じことができます。
test/refuse/label_loop.pl:9:9: Rakugan cannot take this — a loop with a label, and `next LABEL` / `last LABEL`, are not carried yet; a flag the inner loop sets and the outer loop reads does the same
OUTER: for my $i (1 .. 3) {
^
state 変数はメソッドが自分のために持つフィールドで、アプリにはそのためのフィールドがあります。
test/refuse/state_var.pl:9:9: Rakugan cannot take this — `state` is Perl's own, and not in the translator yet; a field of the app holds what a `state` variable would
state $calls = 0;
^
一歩ずつ歩く一致は、どこまで来たかを perl 自身が覚えています。 先に全部取っても同じ並びです。
test/refuse/match_in_loop.pl:10:9: Rakugan cannot take this — a match in a condition is asked once; to walk every match, take them all first: `my @found = ($s =~ /(\d)/g);` and loop over `@found`
while ($t =~ /(\d)/g) { $n += 1 }
^
名前は、一致したと分かってから と から取ります。
それは if の中です。