dein vimでプラグインをupgradeする

何かしらのbrewの更新でしくったのか。vimを起動しようとするとfugitive.vimがエラーするようになった。

/Users/anatofuz/.cache/dein/.cache/.vimrc/.dein/plugin/fugitive.vim の処理中にエラーが検出されました:
行  471: E1208: -complete used without -nargs行  472: E1208: -complete used without -nargs行  476: E1208: -complete used without -nargs行  479: E1208: -complete used without -nargs続けるにはENTERを押すかコマンドを入力してください

fugtive.vimはgitをvimから操作するプラグイン。実はあまり使っていない。

github.com

プラグインでなくてvim本体の問題かと思って、vim自体をupgradeしたけれど状況変わらず。 う〜んと思っていたところ、vimプラグイン管理で使っているdeinでupgradeすればいけるのではと思いやってみた。 upgrade自体は死ぬほど簡単で、vimを開いてコマンドモードでcall dein#update()を実行するといい。

upgradeしたところ無事に起動出来た。fugitiveの最新版で治った説もあるけれど、upgrade時に依存ライブラリが解決されたとかな気がする。

PerlのXSをlldbでデバッグする

興味が湧いたのでPerlのC拡張であるXS言語で書かれているList::Utilを読んでいるのだけど、C言語チックなものを読むときはやはりデバッガが使いたい。 XSも結局C言語なのでgdb/lldbでデバッグできないかなと思ってググったところ、できそうなエントリを見つけたので試していく。

stackoverflow.com

XSのデバッグビルド

C言語デバッグにはCで書かれたコードにデバッグオプションを付けてビルドする必要がある。 XSも同様だと思われるので、何かしらの方法でデバッグビルドする必要がある。

XSモジュールに限らずPerlモジュールのビルドは、Makefile.PLかBuild.PLのどちらかのビルドツールがよく使われている。 Makefile.PLは名前の通りMakefileを生成してくれる君で、Build.PLはModule::Buildを前提としたシステムになっている。 詳細はモダンPerlの世界へようこそ 第23回 Module::Build:MakeMakerの後継者を目指してが詳しい。

今回は読みたいコードがList::Utilで、List::UtilはMakefile.PLを使っていたのでこちらの雰囲気を見る。

最終的にMakefileを生成しにいくのはExtUtils::MakeMakerが提供しているWriteMakefileを実行するタイミングである。 List::UtilのMakefile.PLの場合は一番最後の行でこのように実行している。

WriteMakefile(%params);

この引数%paramsにはビルドに関する様々な情報を入れる事ができる。 ExUtils::MakeMakerのドキュメントを見ると、XSの最適化の度合いを調整出来るOPTIMIZEがオプションとして存在している。 ドキュメントを見る限りCの最適化オプションをそのまま渡せるので、デバッグしたかったら-O0 -gあたりを渡せばよいだろう。

ということでList::Utilのparamsを作っている箇所の一番下にしれっとOPTIMIZEを指定する。

my %params = (
  NAME         => q[List::Util],
  ABSTRACT     => q[Common Scalar and List utility subroutines],
  AUTHOR       => q[Graham Barr <gbarr@cpan.org>],
  DEFINE       => $defines,
  DISTNAME     => q[Scalar-List-Utils],
  VERSION_FROM => 'lib/List/Util.pm',
  OPTIMIZE    => '-g -O0',

準備は整ったのでビルドしていく。まずはMakefileを生成する必要があるので、Makefile.PLを実行する。

❯ perl Makefile.PL
Checking if your kit is complete...
Looks good
Generating a Unix-style Makefile
Writing Makefile for List::Util
Writing MYMETA.yml and MYMETA.json

無事Makefileができたので素朴にmakeする

❯ make
cp lib/Scalar/Util.pm blib/lib/Scalar/Util.pm
cp lib/List/Util.pm blib/lib/List/Util.pm
cp lib/Sub/Util.pm blib/lib/Sub/Util.pm
cp neko.pl blib/lib/List/neko.pl
cp lib/List/Util/XS.pm blib/lib/List/Util/XS.pm
Running Mkbootstrap for Util ()
chmod 644 "Util.bs"
"/Users/anatofuz/.plenv/versions/5.32.0/bin/perl5.32.0" -MExtUtils::Command::MM -e 'cp_nonempty' -- Util.bs blib/arch/auto/List/Util/Util.bs 644
"/Users/anatofuz/.plenv/versions/5.32.0/bin/perl5.32.0" "/Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/5.32.0/ExtUtils/xsubpp"  -typemap '/Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/5.32.0/ExtUtils/typemap'  ListUtil.xs > ListUtil.xsc
mv ListUtil.xsc ListUtil.c
cc -c   -fno-common -DPERL_DARWIN -mmacosx-version-min=11.2 -fno-strict-aliasing -pipe -fstack-protector-strong -DPERL_USE_SAFE_PUTENV -g -O0   -DVERSION=\"1.56\" -DXS_VERSION=\"1.56\"  "-I/Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/5.32.0/darwin-2level/CORE"  -DPERL_EXT -DUSE_PPPORT_H ListUtil.c
rm -f blib/arch/auto/List/Util/Util.bundle
cc  -mmacosx-version-min=11.2 -bundle -undefined dynamic_lookup -fstack-protector-strong  ListUtil.o  -o blib/arch/auto/List/Util/Util.bundle  \
              \

chmod 755 blib/arch/auto/List/Util/Util.bundle
Manifying 4 pod documents

無事makeが実行できた。 ちなみにmake cleanとするとMakefileごとビルドしたものを消してくれるので便利。

デバッグビルドしたXSをPerlから呼び出す

ビルドするともとのxsのコードを純粋なC言語*1に変換されたものなどが生成される。 実際にPerlインタプリタが読みこめるファイル形式のものはblib以下に書き出される。

blibディレクトリはMakeMakerがビルドしたファイルが含まれている。 List::Utilの場合は次のようなファイルが含まれている。

❯ tree blib
blib
├── arch
│   └── auto
│       └── List
│           └── Util
│               └── Util.bundle
├── bin
├── lib
│   ├── List
│   │   ├── Util
│   │   │   └── XS.pm
│   │   └── Util.pm
│   ├── Scalar
│   │   └── Util.pm
│   ├── Sub
│   │   └── Util.pm
│   └── auto
│       └── List
│           └── Util
├── man1
├── man3
│   ├── List::Util.3
│   ├── List::Util::XS.3
│   ├── Scalar::Util.3
│   └── Sub::Util.3
└── script

16 directories, 9 files

ちなみにUtil.bundleの場合はバイナリファイルだったりする。

普通のPerlのライブラリの場合はlib以下のものを@INCに入れれば実行できた。 似た感じでblib以下のものをPerlに教えてあげれば自分でビルドしたXSをPerlインタプリタから呼び出す事ができる。

blibをPerlスクリプトから呼び出すにはblibモジュールを使う方法もあるけれど、勝手にカレントのblibディレクトリ以下を読み込んでくれるExtUtils::testlibを使うのが便利。

というわけで自分でビルドしたList::Utilを呼び出すサンプルコードは次のようになる。今回はめんどくさいのでビルドしたディレクトリ上でやっている。

use strict;
use warnings;
use ExtUtils::testlib;
use List::Util qw/all/;

my $hoge = [1,2,3];

if (all { $_ } @$hoge) {
    print "hello!\n";
}

xsのデバッグ実行

さて例題も書けたのでデバッグしていこう。 一点ポイントとなるのは、このblibを実行できるのは、XSをビルドする際に利用したPerlじゃなくと実行ができない。

今回では上のログにある通り/Users/anatofuz/.plenv/versions/5.32.0/bin/perl5.32.0が実行したPerlのバイナリである。 これ以外のPerlのバイナリ、例えば/usr/bin/perlで実行すると以下のような感じでモジュールロードに失敗する。

❯ /usr/bin/perl neko.pl
Can't load '/Users/anatofuz/src/github.com/Dual-Life/Scalar-List-Utils/blib/arch/auto/List/Util/Util.bundle' for module List::Util: dlopen(/Users/anatofuz/src/github.com/Dual-Life/Scalar-List-Utils/blib/arch/auto/List/Util/Util.bundle, 0x0001): symbol '_PL_DBsub' not found, expected in flat namespace by '/Users/anatofuz/src/github.com/Dual-Life/Scalar-List-Utils/blib/arch/auto/List/Util/Util.bundle' at /System/Library/Perl/5.30/darwin-thread-multi-2level/DynaLoader.pm line 197.
 at neko.pl line 4.
Compilation failed in require at neko.pl line 4.
BEGIN failed--compilation aborted at neko.pl line 4.

ビルド時に使ったPerlだと問題なく実行できる。

❯ /Users/anatofuz/.plenv/versions/5.32.0/bin/perl5.32.0 neko.pl
hello!

ではデバッガをかけて実行してみる。今回はlldbでやる。

❯ lldb -- /Users/anatofuz/.plenv/versions/5.32.0/bin/perl neko.pl
(lldb) target create "/Users/anatofuz/.plenv/versions/5.32.0/bin/perl"
Current executable set to '/Users/anatofuz/.plenv/versions/5.32.0/bin/perl' (arm64).
(lldb) settings set -- target.run-args  "neko.pl"
(lldb)

今回デバッグしたいxsのファイルはListUtil.xsで、使っているallのコードを見てみたい。 allは実はanyのエイリアスで、any関数は702行目から本体がある

というわけでListUtil.xsの702行目にbreak pointを貼ってみる。

(lldb) b ListUtil.xs:702
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.

そして実行すると見事に止まる。

(lldb) process launch
Process 69006 launched: '/Users/anatofuz/.plenv/versions/5.32.0/bin/perl' (arm64)
1 location added to breakpoint 1
Process 69006 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x00000001004ae5a0 Util.bundle`XS_List__Util_any(cv=0x00000001010fd810) at ListUtil.xs:702:22
   699  PROTOTYPE: &@
   700  PPCODE:
   701  {
-> 702      int ret_true = !(ix & 2); /* return true at end of loop for none/all; false for any/notall */
   703      int invert   =  (ix & 1); /* invert block test for all/notall */
   704      GV *gv;
   705      HV *stash;
Target 0: (perl) stopped.

これで止まるとこっちのものなので、あとはnextしたり値をprintしたりして確かめていくと普通に読んでいける。 読み方としてはPerlインタプリタ読んでるのと同じなので、SVの中身に思いを馳せる感じになっていくと思う。

というわけでXSも実はlldbで読める!!! 読んでいくぞ!!!

*1:といってもPerlのマクロがバチバチにあたっている

JSのPromiseとJQueryPromiseを雰囲気で理解した

雰囲気で理解したのでメモです。

JSの世界だとPromiseがあって、基本的に失敗/成功で処理を切り分けていく事ができる。 よく使われる方法だと、非同期処理を同期的に使いたいときに使われる。 例えばAというエンドポイントにアクセスした後に取得した情報で、Bエンドポイントにアクセスする必要がある場合、何も考えずにjsで書いてしまうとAの処理が終わる前にBにアクセスに行く可能性がある。

Promiseを使うとAが終わるまでBが処理されないので同期的に使うことが可能。

ちなみにJQueryの世界にはJQuery Promiseというものがある。JSのnativeなPromiseとの使い分けはこのStackOverflowが詳しい。

stackoverflow.com

要約するとPromiseサポートしていないブラウザでPromise使いたい場合はJQueryPromiseを使おうという世界観。 JQuery使っていないけど昔のブラウザでもPromise使いたいんじゃというときはcore-jsなどを使うとよしなに変換してくれるらしい。

ネイティブのPromiseだと最近はもうasync functionの内部処理か、複数一気動かすみたいなときにPromise.allを打つくらしか手で実行することはない気がする。 反面JQueryPromiseを使う場合、async functionに対応していないブラウザで動かすのが目標にある場合があるので、async functionみたいなリッチな構文を使わずに気合でPromiseを処理する必要がある。

JQueryPromiseを使う場合はDefferedオブジェクトのAPIを利用することになる。 ちょっとわかり付いらいのだけど、Promiseのresolve/rejectはPromiseに生えていそうなのだけど違っていて、Promiseオブジェクトを作ることができるDefferedオブジェクト側のAPIとして定義されている。 Defferedオブジェクトのresolve/rejectを実行することで、ネイティブのresolve/reject処理を実現している世界観。

対してPromiseがresolve/rejectした場合に処理を分岐させる、ネイティブのthenとerror処理に相当するのは、Promiseオブジェクトのthenとfailを呼ぶことで実現している。 分岐させたい処理を発生させる関数がJQueryPromiseを返すので、ネイティブのPromiseチェーンのような書き心地で書く事ができる。 わかりやすいのだとJQuery.ajaxはJQueryPromiseを返すのでそこで分岐させることが可能。

JQueryPromiseがRejectedになった場合、fail処理を受け持つのは、ネイティブのPromiseと同様に一番近いRejectハンドリング部分が受け取る。 ネイティブのPromiseの場合はcatchが受け取っていたが、JQueryPromiseの場合はfailが相当する。resolve/rejectどっちでも処理を継続させたい場合はalwaysを指定する。

const defer = $.Deferred();

ちなみにTypeScriptを使っているケースで、resolve時に値を返したい場合は、返り値の型を<>の中に指定する。

const defer = $.Deferred<HTMLHtmlElement>();

テストを書くときに強制的にPromiseをrejectしたい!!!と言うときはどうするかというと、強制的に$.Deferred().reject()を呼び出すとrejectされる。 当然resolveを強制させたいときも$.Deferred().resolve()するとよい。

例えば、sinonを使ってmockしたメソッドが返すJQueryPromiseをrejectした状態にするには次のように書く。

const stubFunction = sinon.stub(object, 'stubFunction');
stubFunction.returns($.Deferred().reject());

こうするとstubFunctionrejectされた際の挙動を取るので、例えばエラーメッセージの内容を検証したい場合は次のように続けられる。 この例ではrejectされたらfailに処理が落ちるので、errorにエラーメッセージが代入されて検証可能になる。

let error : any;
const expectError = new Error("エラー");

object.stubFunction().fail((_error) => {
   error = _error;
});

assert.equal(error.message, expectError.message, 'rejectするとエラーが帰ってくる');

こうみるとJQueryでPromiseをするには$.Deferred()を取り回せばええやんという感じになる。 とはいえぐぐると出てくるJSのPromiseの使い方とは別なので慣れるまでは大変そう。。。

参考

jsprimer.net

azu.github.io

developer.mozilla.org

perlのmapは2引数渡せる

というわけで驚きの事実です。この2行は同じhashrefを返します。

my $hoge = [map { id => $_, piyo => "bar" }, @hoge];
my $hoge = [map { {id => $_, piyo => "bar"} } @hoge];

これはmapの後ろの{はmapの開始地点の意味と、hashrefのコンストラクタとしての意味の{があります。 通常のmapの使い方だと、{をそれぞれの意味で解釈できるように2つ置く必要があります。

my $hoge = [map { {id => $_, piyo => "bar"} } @hoge];

これとは逆に{1つだとmapのhashrefのコンストラクタという意味になり、後ろにLISTが必要となります。 ドキュメントを見るとこうあります。

map BLOCK LIST
map EXPR,LIST

perldoc.jp

というわけでこうかくとhashrefがなんと手に入ります。,がポイント。

my $hoge = [map { id => $_, piyo => "bar" }, @hoge];

とはいえこの書き方はperltidyがフォーマットしてくれないのでやめたほうが良いでしょう。2引数は渡さないのが良さそう。

M1 macにGoogle日本語入力入れた

最近M1 mac使っていて、IMEmacOS標準の日本語入力を使っている。*1

標準搭載されているだけあって別に問題はないのだけれど、個人的に顔文字をIMEで出力したい。 例えば「ぶわっ」で変換したら「(`;ω;´)」が出てほしい。

辞書登録する手もあるけれど、使い慣れていたGoogle日本語入力のインストールを試した。 Google日本語入力自体はx86バージョンしか出ていないので、そのままでは動かないが、Rosetta2さえいれておけば普通にインストールできる。 30分くらい使っているけれど今の所問題はない。

とはいえ以前x86アーキテクチャmacで使っていたときは、変換候補の履歴を消すたびにIMEがフリーズするみたいな状態になったので、今回はどうなるかは微妙な気持ちになっている。ネイティブ対応のいいIMEがあったらそっちに切り替えたい。ATOK?

*1:ことえりを使っているって当初書いたけどことえりではすでにないらしい

プログラミング言語Raku(旧:Perl6)のオンリーカンファレンスが開催されるらしい

インターネット見ていたら発見してしまったのですが、ついにプログラミング言語Raku(旧 Perl6)のオンリーカンファレンスが開催されるようです。

conf.raku.org

今までのRakuはPerlのカンファレンスであるYAPCやPerlConでPerl5とセットで話される機会がほとんどでしたが、ついに初めて単独カンファレンスとなります。 今のところはトークはLTしか応募されていないようですので、みなさん応募しましょう!!(??)

Online, August 7, 2021

とのことです。 以前のThe Perl and Raku Conference 2020 cloudの際はEDTでスケジュールされていたので、日本からリアルタイムでみる場合は夜になるかと思います。


参加チケットは無料ですが、個人で寄付をすることが可能です。

面白いのはTシャツを発送することができないので、Tシャツの素材が配布されており、各自でプリントアウトしてくれ!!!という新スタイルで運用しています。僕もTシャツほしい。

f:id:anatofuz:20210516191406p:plain

ちなみに The Perl and Raku Conference 2021 cloudもやるっぽい。

w3schoolsのjQueryの練習問題解いた

jQuery全然知らないのでちょっと触ってみようと思っていた。 webサイトエイヤで構築するのも良さそうだったけど、軽い練習問題みたいなのをとりあえずガンガン進めてみたかったので、適当にググって出てきたサイトの問題を解いた

w3schoolsのチュートリアルの問題なので、まずはチュートリアルを上から眺めた。 eventの種類とか完全にわかっていなかったので、なるほどねと言いながらみていた。 jsなので自分のブラウザでも動くし、ワンクリックでどうこう動いて体験がいい。 www.w3schools.com

問題自体は穴埋め形式なので、問題文とサンプルコードを見て当てはめていく感じ。

www.w3schools.com

実際に解いた(問題として提供されていた)のは次のもの

  • Selectors
  • Events
  • Hide and Show
  • Fade
  • Slide
  • Animate
  • Stop
  • Get
  • Set
  • Add
  • Remove
  • CSS Classes
  • CSS
  • Dimensions

Ajaxチュートリアルにはあったのだけど問題にはなかったのでペラHTML作って練習するかーみたいな感じ。 jQuery完全に理解した!というわけではないのだけど、コードを読めるレベルの知見はある程度出たかなという気がする。 次は軽くなんか例題を書いて動かしてみる予定。