Masteries

技術的なことや仕事に関することを書いていきます.

Syntax::Keyword::TryとPerlのキーワードプラグイン (その2)

id:papix です. この記事は, Perl Advent Calendar 2019の24日目の記事です. 昨日はmp0liiuさんの「Perlのスタックトレースを見やすく扱う方法」でした.

qiita.com

Syntax::Keyword::Try

さて, Perl Advent Calendar 2019の2日目に, Syntax::Keyword::Tryの紹介をしました.

papix.hatenablog.com

そしてその最後に, このような予告をしていました.

「キーワードプラグインを使ったモジュールの実装について」をご紹介させてもらえればと思います!!!!!!

...が, 当初予定していた16日では準備が間に合わず, 他の話題でお茶を濁したりしました.

papix.hatenablog.com

このエントリでは, 今度こそSyntax::Keyword::Tryを題材に, キーワードプラグインの実装について触れていきたいと思います. ...が, 後述しますが, この記事単体では到底説明を終われそうにありません!!!! 今後, 数記事に分けて紹介していきますのでその点ご了承ください...

諸注意

  • papixのXS力はゴミです
  • 以下の記述は, 実際のコードやPerlの各種ドキュメントを読みながら, 推測(自信がない状態)で書いています
  • この記事で紹介しているSyntax::Keyword::Tryの実装やXSの知識が正しい保証は出来ません

Syntax::Keyword::Tryのソースコード

さて, いよいよSyntax::Keyword::Tryを通じて, Perlのキーワードプラグインについて, その実装について触れていきましょう. 今回は, 現時点での最新版である0.11のソースコードを題材にします.

metacpan.org

さあ, まずはlib/Syntax/Keyword/Try.pm を見ていきましょう.

...import, import_intoという, パッケージをuseするときに使われる関数を除けば, このファイルで実行している処理はたったこれだけです:

require XSLoader;
XSLoader::load( __PACKAGE__, $VERSION );

XSLoader. というわけで, ここからはXSの世界へと突入していきます.

XSとは?

そもそもXSとは何か. XSは, PerlとC言語を紐付けるための言語... という表現が一番わかり易いのではないかと思います. C言語で書かれたプログラムをPerlの世界から呼ぶためには, XSという言語で紐付けてあげる必要がある, という感じです.

日本語の資料で言えば, 下記の「CによるPerl拡張入門(α)」が有用です.

xsubtut.github.io

Try.xsを読み解く

というわけで, Syntax::Keyword::TryのコアにあたるXSで記述されたlib/Syntax/Keyword/Try.xsを見ていきましょう.

...そもそも, XSの世界でキーワードプラグインを有効にするにはどうすればいいのでしょうか? いろいろ調べていくと, perlapiの「Global Variables」の節に, 次のような記載があります.

PL_keyword_plugin NOTE: this function is experimental and may change or be removed without notice.

これっぽいですね. PL_keyword_pluginというグローバル変数は, キーワードプラグインを扱う関数を指す関数ポインタとのこと. つまりここに適当な関数ポインタ(キーワードプラグイン用の)をセットすると, Perlはそれを使ってよしなにキーワードプラグインを有効にしてくれそうです. そして, ここで指す関数は, 以下のように宣言されている必要があると書かれています.

 int keyword_plugin_function(pTHX_
        char *keyword_ptr, STRLEN keyword_len,
        OP **op_ptr)

これをもとに, Try.xsを見ていくと, まず static void S_wrap_keyword_plugin(pTHX_ Perl_keyword_plugin_t func, Perl_keyword_plugin_t *var) という関数の中でグローバル変数 PL_keyword_plugin に対し, 引数 func を代入していることがわかります.

更にこれは, define wrap_keyword_plugin(func, var) S_wrap_keyword_plugin(aTHX_ func, var) というマクロが定義されていて, wrap_keyword_plugin は Try.xs の末尾に, 次のような形で呼び出されています.

BOOT:
    (中略)
wrap_keyword_plugin(&my_keyword_plugin, &next_keyword_plugin);

このコードは, BOOT: から始まるBOOTキーワードの中にあります. BOOT以下に書かれたコードは, このXSを使ったモジュールを実行する際, 最初に実行されるブートストラップのコードです.

さて. ここで &my_keyword_plugin は関数 static int my_keyword_plugin(pTHX_ char *kw, STRLEN kwlen, OP **op) のアドレス(これは, 先に紹介した PL_keyword_plugin に代入可能な形で宣言されています), そして &next_keyword_plugin は定数 next_keyword_plugin, つまり static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **); のアドレスになります. これを踏まえて, 改めて関数 S_wrap_keyword_plugin (マクロである wrap_keyword_plugin を展開した先)を見てみましょう.

/* papix注釈  
 * func = &my_keyword_plugin, *var = &next_keyword_plugin
 */ 
static void S_wrap_keyword_plugin(pTHX_ Perl_keyword_plugin_t func, Perl_keyword_plugin_t *var)
{
  /* BOOT can potentially race with other threads (RT123547) */
 
  /* Perl doesn't really provide us a nice mutex for doing this so this is the
   * best we can find. See also
   *   https://rt.perl.org/Public/Bug/Display.html?id=132413
   */
  if(*var)
    return;
 
  OP_CHECK_MUTEX_LOCK;
  if(!*var) {
    *var = PL_keyword_plugin;
    PL_keyword_plugin = func;
  }
 
  OP_CHECK_MUTEX_UNLOCK;
}

重要なのは以下のコードです.

  if(!*var) {
    *var = PL_keyword_plugin;
    PL_keyword_plugin = func;
  }

つまり, 既にグローバル変数 PL_keyword_plugin にあるキーワードプラグイン用の関数を, *var すなわち定数 next_keyword_plugin に退避して, 改めて PL_keyword_pluginfunc, すなわちSyntax::Keyword::Tryを提供するための関数, my_keyword_plugin をセットしている訳です. PL_keyword_plugin がグローバル変数なら, キーワードプラグインを読み込むために上書きされていくはずで, どうやって複数のキーワードプラグインを実現するのだろう...? と思っていましたが, こういう実装になっている... のだと思います. 多分.

そして改めて関数 my_keyword_plugin を見てみると,

static int my_keyword_plugin(pTHX_ char *kw, STRLEN kwlen, OP **op)
{
  HV *hints;
  if(PL_parser && PL_parser->error_count)
    return (*next_keyword_plugin)(aTHX_ kw, kwlen, op);
 
  if(!(hints = GvHV(PL_hintgv)))
    return (*next_keyword_plugin)(aTHX_ kw, kwlen, op);
 
  if(kwlen == 3 && strEQ(kw, "try") &&
      hv_fetchs(hints, "Syntax::Keyword::Try/try", 0))
    return try_keyword(aTHX_ op);
 
  return (*next_keyword_plugin)(aTHX_ kw, kwlen, op);
}

...となっています. Syntax::Keyword::Tryのためのコードは, ここから改めてtry_keywordという関数を呼び出して実行しており, エラーが発生している場合や, Syntax::Keyword::Tryのコードが無事処理に成功した時は, (*next_keyword_plugin)(aTHX_ kw, kwlen, op); として, 先程退避した(別の)キーワードプラグインを実行する... という構成になっているようです.

今回のまとめ

XS, 本当に難しいですね... 一応大学時代, 修士論文はXSのモジュールを書いて卒業したのですが, その時の知見もほぼ失われ, 改めてイチから咀嚼しながらこの記事を書きました. かなり憶測というか, 「実際のコードやperldocの記載を見るとこうではないか...?」と, 自信のないまま書いているので, 間違いなどは多々あるかもしれません. その時は, Twitterやコメントなどで教えて頂けると幸いです.

XS, そしてキーワードプラグインは興味深い内容なので, これからも少しずつ読み解いていきたいと思います. 次は, 実際にSyntax::Keyword::Tryのための処理が書かれている... であろう, try_keyword についてコードを読んでいこうと思います.

明日のPerl Advent Calendar 2019の担当は sy250f さんです. 宜しくおねがいします.

最近の読書法

この記事は, 「はてなエンジニア Advent Calendar 2019」の21日目の記事です.

qiita.com

昨日は id:dekokun さんの「Kinesis Data Streams + Lambdaが詰まった時の対処法」でした.

dekotech.dekokun.info

最近の読書法

「読書習慣が定着しないな...」という課題を延々と持ち続けていて, 積読本も無限に溜まっていたのですが, 最近徐々に消化が進んできました. うまくサイクルが回り始めた理由を考えていて, 幾つか気付いたことを書き残しておこうと思います. 結論としては, 本を読むにあたって現在は以下の3点に気をつけています:

  • 少しでもいいので読む
  • 理解できなくてもひとまず読む
  • とはいえ, グッと来なかったら読み飛ばす

少しでもいいので読む

既に読書の習慣が定着しているならともかく, そうでないのなら, まずは「定期的に本を読む」ようになるのが大事だと思いました.

読書の習慣がなかった頃は, 面白そうな本をとりあえず買うものの, 読書の習慣が定着していないので当然のように積まれていって, だからこそ気持ちが高まってきた時に「消化するぞ! たくさん読むぞ!!!」と思うものの, その気持が逆に読書のハードルを自分で高めてしまい(折角読むのだからたくさん読もう, 折角読むのだから理解しよう, 等...), 結果として挫折する... といった悪循環を, 延々と繰り返してしまっていました.

この辺りは, 今年「小さな習慣」を読んだ影響があって, 毎日少しずつでもいいので, なるべくコンスタントに読書をしていく... という習慣を試みていて, それが徐々に定着してきているように感じます. とにかく, 本を読むハードルを意図的に, どんどん下げていっています.

papix.hatenablog.com

直近だと,

  • Kindleで, 1日2ページは読むようにする
  • 飲みすぎたり疲れた日は休んでも良い, が翌日は2倍読む(2倍と言っても4ページ)

...という感じでやっています. Kindleで2ページ程度であれば, どう転んでも10分もあれば読み切れるので, 出勤時間にサクッと読んでしまえますし, 仕事で多少疲れた時でも寝る前にベッドの中でなんとか読了できる量だと思います(そして, 本当に疲れている時は前述のように諦めてもよく, 代わりに翌日2倍読む).

これの良い所は, 「とりあえず2ページで...」という気持ちで本を読み始めると, 時と場合によってはだんだん面白くなってきて, 結果として2ページ以上読み進めている... ということが結構ある点です. 読書に対するハードルを, 極限まで下げているはずなのに, 結果としては気合いを入れて本を呼んでいた時よりもたくさんの分量を, コンスタントに読み進めることができるようになったと思います.

理解できなくてもひとまず読み進める

読書の習慣が定着しない頃, 何かのきっかけで本を読むか! となった時, 先にも述べたように, 「せっかく読むのだから, 全部理解しよう...!」と意気込みすぎた事が多かったように思います. 結果としてページは進まず, 「うーん, うーん...」と悩んでいるうちに時間が過ぎ去ってしまい, 更にそのせいか「また続きを読もう!」という気持ちになれない... という, 悪循環を起こしていました.

これもまた, 読書のハードルを下げる観点の1つではあるのですが, まずはとりあえず「どんな事が書かれているかわかればいいや」という気持ちで本と向き合うようにしています. サクサク読んでいって, 興味深いと思ったところだけしっかり読む, という雰囲気です. 気持ちとしては, 「じっくり読もうとして進まないより, とりあえず1冊読み切った方がまだ有益」と思うことにしています.

...とはいえ, そういう読み方をしていても, 1冊あたり(分量によりますが)どれだけ少なくても2〜3ポイントくらいは学びというか, 新しい視点が得られるものだなあと思っています. また, 何より「この本には, こういう事が書いてある」というインデックスを作ることが出来るのが有益で, まずはそこを目指す程度で十分ではないか, と思うようにしています. そうすれば, ある意味で本を「外部記憶」にできる訳で, いつか将来, 「これ, あの本に書いてあったな...!」というタイミングがあれば, また本を開いて, 今度は必要なところを理解出来るまで読み込む... という使い方が出来るはずです.

課題としては, 本のジャンルによってはこういう読み方が明らかに不向きなことがある(例えばプログラミング言語の解説/入門書などはそういうジャンルと思います)というところで, そこについては今後の課題としたいと思います.

とはいえ, グッと来なかったら読み飛ばす

先に, 「理解できなくてもひとまず読み進める」と書きましたが, とはいえ読んでいて「グッと来ない」と思った所は, 思い切って読み飛ばすようにしています. 「せっかく読書するのだし, 全部読むようにしよう!」と思っていた時期もあったのですが, そのような理由で無理やり文言と向き合っても頭に入ってきませんし, そういう状態で本を読んでも得るものはほとんどない... という結論に至りました.

なので, 最近は「この辺りはあまりグッと来ないな...」と思ったら, 各節のタイトル等をサッと眺めて読み飛ばすようにしています. 「各節のタイトルをさっと眺めておく」というのは大事だと思っていて, 先に「本のインデックスを作って外部記憶にする」という話をしましたが, そのためにもそれくらいは読んでおく必要があると思っています.

まとめ

とにかく, まず本を読む習慣を定着させたい! と思うのであれば, ただひたすらに「本を読むハードルを下げる」必要があると思っています. ここで紹介した3つのポイントは, それを実現するために自分が実施していることです.

かつての自分は, 「本をたくさん読み, そして多くのことを理解したい」と思っていました. しかし, 二兎を追う者は一兎をも得ず, という諺があるように, (少なくとも自分には)その2つをシュッと両立することはできませんでした...

なので, 最近はこうした施策によって本を読むハードルを下げ, 読書習慣を定着させ, その次のフェイズとして, 読解力や本から得るものを増やしていく... という取り組む方が良さそうという結論に至っています. 皆さんはどのようにして「本」と向き合っていますか? この機会に言語化してみると得るものが多いかもしれません.

明日の担当は id:side_tana さんです. 宜しくおねがいします.

チームでPerlを書く時に考えていること

id:papix です. この記事は, Perl Advent Calendar 2019の16日目の記事です. 昨日は bayashi_net さんの「今年書いた何か Github Actions」でした.

bayashi.net

さて, 今日は前回2日目に書いたキーワードプラグインについて, 実際の実装方法について解説する... と予告していましたが, 師走っているのと, XSについて記憶がかなり失われていることなどから, 準備に時間が足りませんでした. すいません...

papix.hatenablog.com

代わりに, 「チームでPerlを書く時に考えていること」というタイトルで, 自分自身が業務の中で, チームメンバーと一緒にPerlのプロダクトを開発していく中で, 気をつけていること, 考えていることについて綴ろうと思います.

注意

  • これはあくまで id:papix 個人の意見, 見解, 感想です
  • チームでPerlを書くにあたって, 必ず下記の点について気をつけないといけない, 守らないといけない! という訳ではありません

チームでPerlを書く時に考えていること

変数は宣言だけしない

変数を宣言するときに, 条件によって初期値を切り替えたい, と思うことがあると思います. そういう時の書き方として...

my $var;
if ($cond) {
    $var = 1;
} else {
    $var = 2;
}

このように書くことも出来ます. ただ最近, 上記のコードのように, 初期値の代入がない, ただの変数の宣言を見るとドキッとするようになりました. 理由はなんだろう? と考えると, 宣言した変数が一瞬でもundefである状態が怖いのかな? と思っています. その直後で正しく初期化しなければ, 意図しない挙動になってしまいそう... と思っているのかもしれません.

例えば, チームでプロダクトを開発していて, 様々な事情があって, my $var;という変数の宣言と, その変数の初期化の処理の間に, 更に別の処理が入っていって... と複雑化していくことはない... とは言い切れないと思います.

my $var;

... 他のいろいろなコード ...

# ようやくここで $var を初期化する
if ($cond) {
    $var = 1;
} else {
    $var = 2;
}

こういう時に, もし間違って ... 他のいろいろなコード ... の中で $var を使ってしまうと, 当然初期化の処理が実行されていないので意図しない挙動になります. 一方で, $var は変数として宣言されているので構文としては正しく, 結果としては「なぜundefなんだろう...?」と悩むことになります.

...というわけで, 最近は変数の宣言と初期値の代入は必ず同時にするように心がけています. 例えば, 先の例であれば次のように書きます:

my $var = $cond ? 1 : 0;

条件が複雑な場合は, 次のようにdoを使ったりもします. doの場合, スコープが区切られるので初期値を生成するために一時的に変数を宣言する, といったこともできます.

my $var = do {
   my $cond = ...;
   if ($cond == 1) {
      'a';
   } elsif ($cond == 2 ) {
      'b';
   } else {
      'c';
   }
};

あるいは, 初期値を生成するためのメソッドを用意するのも良いでしょう:

my $var = _prepare_var(...);

importするメソッドは明記するようにする

Perlには, import/exportという仕組みがあります. 例えば, 次のようなSampleパッケージがあるとしましょう. このコードでは, Exporterモジュールを使って, func_a, func_bをエクスポートしています.

package Sample;

use Exporter 'import';
our @EXPORT = qw(func_a func_b);

sub func_a { ... }
sub func_b { ... }

1;

こうすることで, our @EXPORT に含まれる名前のメソッドは, そのパッケージをインポートするだけで自動的に利用出来るようになります.

use Sample;

func_a(); # => Sample#func_a が呼べる
Sample::func_a(); # import/exportの仕組みを使わないなら, こう書かないといけない

func_b(); # => Sample#func_b も呼べる

import/exportの仕組みはとても便利です. ただ, チームでPerlを開発していて, 次のようなコードに手を加えることになったら... どう思いますか?

use Sample1;
use Sample2;
use Sample3;
use Sample4;
use Sample5;

func_a();

「...func_aってどこでexportされているんだ?」と困惑してしまいそうです. そこで, チームでPerlを書くような場合は, importするメソッドを次のように明記するようにしています.

use Sample1 qw(func_a);
use Sample2;
use Sample3;
use Sample4;
use Sample5;

func_a();

このコードのように, useでパッケージをimportする際, 文字列で関数名を渡すと, その名前の関数だけをインポートすることができます. こうすれば, 「なるほど, func_aSample1にあるんだな!」と一目瞭然ですね.

use Sample1 qw();

func_a(); # => 呼べない
Sample1::func_a(); # => 呼べる

ちなみに, 上記のコードのように, use Sample1 qw(); としてモジュールを呼び出すと, 全ての関数をインポートしないように出来ます. この場合, パッケージ名を付与して関数を呼び出してあげることになります

さいごに

チームでPerlを書く時に気をつけていることをご紹介しました. Perlは超手軽につかえて便利なプログラミング言語ですが, とはいえチーム開発というシーンにおいてはその手軽さに甘えた時に落とし穴にハマる, みたいなこともあるので, 意識するべきところを意識して書いていきたいものです. ところでこのエントリは時間の制約上かなりシュッと書いたので, もっといろいろ気をつけポイントはあると思うので, 「○○○はどうですか?」とか, 「✕✕✕も罠になるので気をつけると良さそう」といった感想をお待ちしています.

Perl Advent Calendar 2019, 明日の担当は, doikojiさんです.

VSCodeで開かれたシェルであることを環境変数から知る方法

結論から述べると,

qiita.com

こちらのQiitaエントリの, 統合ターミナルかその他ターミナルかを判別するの項を読みましょう.


「VSCodeで開いたシェルだけ○○をする」といったことを実現しようと思った時, Googleなどで検索すると VSCODE_PID 環境変数で識別するとよい... という情報を得ることができたのですが, VSCode 1.26より VSCODE_PID 環境変数はセットされなくなったので, 上記エントリにもあるように, TERM_PROGRAM 環境変数で識別するようにしましょう.

# VSCodeで開かれたシェル
$ echo $TERM_PROGRAM
vscode

# 例: iTerm2で開かれたシェル
$ echo $TERM_PROGRAM
iTerm.app

...といった形で, どのプログラムでシェルを開いたか? という情報が TERM_PROGRAM 環境変数に入っているようです. これを知るまで数時間潰してしまったので, 備忘録のために書き記しておきます.

Syntax::Keyword::TryとPerlのキーワードプラグイン (その1)

id:papix です. この記事は, Perl Advent Calendar 2019の2日目の記事です. 昨日は, id:karupanerura さんの「2019年の最先端のPerl開発ボイラープレート」でした.

qiita.com

今日は, 昨日のエントリでも触れられていたSyntax::Keyword::Tryについて, さっくりとアレコレ綴りたいと思います.

Perlと例外処理

Perlで例外処理をするにあたって, 例えばJavaのようなtrycatchといった仕組みは言語仕様として提供されていません. そのため, Perl Monger達はtry/catchのような機能を提供するモジュールを用意して対処してきました. その中でも著名なものの1つは, Try::Tinyでしょう.

metacpan.org

Try::Tinyを使えば, 例外(die)が起こるコードについて, 次のように書くことができます.

use Try::Tiny;

sub run {
    try {
        die "!!!";
    } catch {
        warn "recovery";
    };
}

run();

tryで囲まれたコードで例外が発生した場合, Try::Tinyはそれをよしなにcatchしてくれて, catchで囲まれたコードを実行してくれる訳です.

Try::Tinyの罠

さて, Try::Tinyには, 何だかんだで誰もが1度はハマる罠が存在します. それが次のコードです.

use Try::Tiny;

sub run {
    try {
        # ... 例外が起こるかもしれない処理 ...
    } catch {
        warn "recovery";
        return 1;
    };

    # ... 何かしらの処理 ...

    return 2;
}

my $result = run();
print "$result\n";

このコードを実行したとき, print "$result\n"; は何と表示されるでしょうか? コードを書いた人の意図としては,

  • tryで囲まれたコードで例外が発生すれば, catch で囲まれたコードが実行され, 結果としてrunは1を返す
  • tryで囲まれたコードで例外が発生しなければ, # ... 何かしらの処理 ... の部分にある何かしらのロジックが実行され, 結果としてrunは2を返す

...と考えていそうです. しかしながら, 例えば # ... 例外が起こるかもしれない処理 ... を, 実際に例外が起こるようなコード(例えばdie("!!!"))にしたとしても, このコードの実行結果(= print "$result\n";の出力)は, 2になります.

これは何故でしょうか? 結論から言えば, Try::TinyはPerlの既存の仕組み(プロトタイプなど)を利用して, 強引にtry/catch風の構文を再現しているから... ということになります. 実は, 先のコードは下記のコードと等価なのです.

    try (
        sub {
            # ... 例外が起こるかもしれない処理 ...
        },
        catch (
            sub {
                warn "recovery";
                return 1;
            },
        ),
    );

そのため, return 1はTry::Tinyが提供するcatchという関数の第一引数に渡るサブルーチンリファレンスの返り値となり, 最終的にこの値はTry::Tinyが提供するtryという関数の返り値となるわけです.

また, Try::Tinyの場合, 最後のコードブロックに;をつける必要があるというのも, よくある罠と言えるでしょう.

use Try::Tiny;

sub run {
    try {
        # ... 例外が起こるかもしれない処理 ...
    } catch {
        warn "recovery";
        return 1;
    } # ← ここに ; が必要!!!

    # ... 何かしらの処理 ...

    return 2;
}

Syntax::Keyword::Try

さて, そういったTry::Tinyの問題を解決する, Syntax::Keyword::Tryというモジュールがあります.

metacpan.org

このモジュールを使うと, Perlにおける例外処理を次のように実装することができます:

use Syntax::Keyword::Try;

sub run {
    try {
        # ... 例外が起こるかもしれない処理 ...
    } catch {
        warn "recovery";
        return 1;
    }

    # ... 何かしらの処理 ...

    return 2;
}

my $result = run();
print "$result\n";

末尾の;が不要だったり, # ... 例外が起こるかもしれない処理 ...で例外が起おきた時も, 関数runは当初意図していた通り, 1を返します(当然, 例外が発生しなければ return 2 によって2を返す). Syntax::Keyword::Tryには, Try::Tinyに潜む罠が存在しないのです.

「キーワードプラグイン」

さて, このモジュールは, Perlの"キーワードプラグイン"という仕組みを使って実装されています. キーワードプラグインはPerl 5.12(2010年リリース)から実装された機能で, Perlのパーサーにフックを設定し, 構文を拡張できるようになっています.

Syntax::Keyword::Try以外にも, キーワードプラグインを利用したモジュールは公開されていて, 例えばFunction::Parameters(関数の定義をより良い形で記述できる), Keyword::Boolean(true/falseを提供する), Switch::Plain(switch構文を提供する)などがあります.

metacpan.org

metacpan.org

metacpan.org

id:charsbar さんが2016年に参加した, London Perl Workshop 2016で, Syntax::Keyword::Tryを開発した, Paul Evans氏の発表があったそうですが,

Perlをもっとよくしていくには、公開されている機能をギリギリまで使い倒して、できる人たちに「そんな無茶なやり方よりもっといい方法がある」と言わせるといい

charsbar.hatenadiary.org

...と仰っていたそうです. キーワードプラグインによって今のPerlを拡張し, 日々の開発に役立て, それを通して未来のPerlに貢献する... というのは, とてもおもしろそうですね.

次回予告

...というわけで, キーワードプラグインの実装までご紹介出来ればよかったのですが, ちょっと準備に時間が足りず... 今回はここまでとさせてください. 改めて, Perl Advent Calendar 2019の16日目にて, 「キーワードプラグインを使ったモジュールの実装について」をご紹介させてもらえればと思います!!!!!!

Perl Advent Calendar 2019, 明日の担当は, take_3さんです.