君はあのコンマ演算子を知っているか?

僕は知らなかったです。

Perlで遭遇

次のようなPerlのコードを読んでいるときに思ってなかった挙動をした。

my $x = ('a', 'b');
$x  # => ??

始め以下のような想定をして $x には 'a' が入るのだと思っていた。

  1. 左辺がスカラコンテキストなので、右辺もスカラコンテキストになる
  2. 配列の区切りの最初の値が入り $x には 'a' が入る
  3. 'b' はどの変数にも入らず捨てられる

しかし実行してみると $x には 'b' が入る。

my $x = ('a', 'b');
$x  # => 'b'

これはどういうことかと思い調べてみると、どうやらコンマ演算子というものがあるらしい。perldocを引いてみると

perldoc.jp

二項演算子の "," はコンマ演算子です。 スカラコンテキストではその左引数を評価し、その値を捨てて、 それから右引数を評価し、その値を返します。 これはちょうど、C のコンマ演算子と同じです。 リストコンテキストでは、これは単にリスト引数の区切り文字で、 双方の引数をそのリストに挿入する働きがあります。 これらの引数も左から右に評価されます。

そんなのあるの……。つまりさっきのコードは以下のような流れで 'b' が入る。

  1. 左辺がスカラコンテキストなので、右辺もスカラコンテキストになる
  2. 'a', 'b',二項演算子コンマ演算子として働く
  3. そのため 'a' を評価し値を捨て、 'b' を評価し値を返す
  4. そして $x には 'b' が入る

という流れ。

恥ずかしながら2年弱Perlを書いててコンマ演算子のことは初めて知った……。あまりこういうコードを書く機会は無いだろうけどミスってこのようなコードを書いてしまった時は意図しないハマりポイントになりそうだなと思った。

ちょっと待てよ……

Perlのハマりポイントを知ったところで、さっきのperldocの文を思い出してみると……。

これはちょうど、C のコンマ演算子と同じです。

ということはC言語やそれ以外の言語にもあるということではないだろうか?そう思って雑にググってみるとWikipediaがヒットした。

コンマ演算子 - Wikipedia

CやC++をめきめき書いたことは無いのでなるほど〜といった感想程度だったけど、次のコード例はちょっと面白い。

int a=1, b=2, c=3, i;   // このコンマは演算子としてではなくセパレータとして作用する
i = (a += 2, a + b);    // aに2を加算した後、a+b = 3+2 をiへ代入する                                    ... a=3, b=2, c=3, i=5

ちょっとしたコード短縮テクニックなのかなと思ったけど、Twitterで調べてみたら定期的にツイートがある。CやC++ということは競技プログラミングの界隈だと普通に知られたテクニックだったりするのだろうか?

コンマ演算子 - Twitter Search

お前もか〜〜

見つけてしまった瞬間「マジかよ……」と思ってしまったのだけれ、JavaScriptにもコンマ演算子は存在していた。雰囲気は同じ感じで詳細はMDNを見ればよさそう。

developer.mozilla.org

Minifierとかでコードが圧縮された後だとこんな感じのコードが生成されているのかもしれないなと思った。

終わりに

知らない演算子を発見するの結構新鮮な気分だった。PerlJavaScriptも仕事で書いているけど、言語の仕様を上から下までさらって覚えるということはしたことないので他にもこういった発見は今後もあるんだろうなと思った。 (あとJavaScriptオチ担当として優秀な気がする)

IntelliJでPerlの変数宣言が複数続いた際に=の位置を揃える設定

普段Perlを書くときにIntelliJPerl pluginを使って書いているのだが、便利なことにIntelliJReformat Code時にPerl::Tidyを使ってReformatしてくれるらしい。

しかしReformatしてみたところ、変数宣言複数続いたときに = イコールの位置を揃えてくれないので、どうしたものかと困っていた。

例えば以下のような変数宣言のときに

my $short = 'short variable';
my $long_long_long = 'long variable';

= イコールの位置が揃うようにReformatしてほしい。

my $short     = 'short variable';
my $long_long = 'long variable';

あれこれ見てみたらプラグインPerl::Tidyの設定が悪いわけではなく、IntelliJ側のCode Styleの設定のせいではないかと疑ってみた。試しに設定を探してみたところどんぴしゃで、思い通りにReformatされるようになった。 設定方法は以下の通り。

  • Preferences -> Editor -> Code Style -> Perl5 にいく
  • Wrapping, Braces & Alignments のタブを選択
  • Assingment expressionAlignmentDon't align から On consecutive line に変更

これで上記のフォーマットが実現出来た。 設定方法に関してはIntelliJのバージョンによって設定までのとどりつき方が変わることもありそう。

IntelliJを使ってPerlアプリケーションの開発をする

これは、はてなエンジニア Advent Calendar 2018 23日目の記事です。

昨日は id:tkzwtks さんで AWS Step Functionsを利用してAWS BatchやFargateのタスクを起動する でした。

こんにちは。普段はマンガチームでGigaViewerの開発をしています、id:miki_bene といいます。マンガチームやGigaViewerについてはこちらの記事などをどうぞ。 仕事ではPerlを使ってアプリケーション開発をしており、Perlを書くのにVimEmacsのようなテキストエディタ拡張機能を入れて書くのではなくIntelliJを使って開発をしています。始めはEmacsを使って開発をしていましたが、IntelliJを使うようになって感じ始めた便利さやIDEを使うメリットをご紹介します。

何故IntelliJ?

まず、IntelliJを使うようになったきっかけを説明します。Perlを書き始めて最初の内はEmacsに拡張を入れて書いていましたが、

  • バッファ内の文字列や拡張で定義されているトークンの補完でしか対応していないため、十分な補完を行うことが難しい
  • gtagsなどを使ったタグジャンプで定義ジャンプを行うため、時々不正確なジャンプをしてしまう

といった悩みを感じていました。Perlを本格的に書き始める前はScalaIntelliJを使って書いていたので、プログラミング言語の差だけでなく補完やコードジャンプ・リファクタリングなどといったコードを書く際の体験の差が明確にあったのも理由です。 そんな中ふと、「最近はPHPRubyPythonのような動的型付けの言語でもIntelliJベースのIDEがあるのだから、Perlにもあるのではないか」と思ったのがきっかけです。そうして見つけたのがIntelliJPerlプラグインです。

メリット

ここからはIntelliJを使ってPerlを書くことのメリットについて紹介します。

まず上げたいこととして、当たり前なことなのですがIntelliJの機能に乗っかることが出来ることですは最大のメリットだと思います。ざっと上げるだけでも

などがあります。IntelliJの静的解析の機能を使って実現されているので、正確な補完や定義ジャンプ、リファクタリングが行えます。これはかなりの体験の改善で、悩みに上げていた正確さを改善するだけでなく、GUIベースで操作出来ることの簡単さや高機能なリファクタリングツールを手に入れることが出来ました。

またPerlプラグイン独自の機能としてアノテーションがあります。プラグインPerlの構文に沿うコードは解析でき補完も効きますが、メソッド定義時にSmart::Argsなどを用いてPerlの一般的なメソッド定義に沿わなくなった場合は補完が効きません。例えば以下の2つのメソッド定義の場合、 Smart::Args を用いたfind_by_id_with_smart_args は補完されません。

package My::Package;
use Smart::Args qw(args);

# 解析可能で My::Package->find_by_id() の呼び出しが補完可能
sub find_by_id_with_normal {
    my ($class, $id) = @_;
    ...
}

# 解析不可能で My::Package->find_by_id() の呼び出しが補完できない
sub find_by_id_with_smart_args {
    args my $class => 'ClassName',
         my $id    => 'Int',
         ;
    ...
}

その様な場合は #@method というコメントをメソッド定義の直前に書いて、アノテーションとしてプラグイン側に伝えます。そうすることで、Smart::Args を使った場合でも補完されるようになります。

# アノテーションコメントを付けることで、補完できるようになる
#@method
sub find_by_id_with_smart_args {
    args my $class => 'ClassName',
         my $id    => 'Int',
         ;
    ...
}

Perlの解析の難しさをアノテーションを使って補っている形のため若干の面倒さはありますが、アノテーションに続けてメソッド自体のコメントを書くということを半強制できているため、副次的なメリットもあるなと感じています。

まだ不便な所

IntelliJを使ったメリットを紹介していきましたが、まだまだ解消できていないものもあります。

補完やコードジャンプといった機能は静的解析によって受けている恩恵のため、通常の構文とは異なるSmart::ArgsModule::Functionsといった動的に解決するようなモジュールの利用した場合は、補完や定義ジャンプなどが行えません。当然文字列結合をしたクラス読み込みやメソッド呼び出しなどのメタプログラミングを行っている部分も同様です。 またチーム内で自分しかIntelliJを使っていないことで起こることもあります。全てのメソッドにアノテーションが付いているわけでは無いため、多くのメソッドは補完がされないといったことや、逆にチームメンバーからは僕だけ不思議なコメントを付けているように見えて、レビューで指摘されたりといったこともあります。

こういった課題は新たに Smart::ArgsModule::Functions の対応プラグインを開発したり、他のエディタでもアノテーションを意味のあるようにする拡張を作ったりということで解決できますが、かなりの労力がかかり道程はまだまだ遠そうです。

終わりに

ここまでで、IntelliJを使ってPerlアプリケーションの開発をすることについてご紹介しました。チームでは今のところマイノリティのため100%快適というわけにはいきませんが、Emacsの拡張をあれこれ試していた時期と比べるとかなり改善しました。ここではコーディング時のことを中心に書きましたが、IntelliJIDEA 2018.3からGitHubのPullRequestをIntelliJから見れる機能が加わりました。

IntelliJ IDEA 2018.3 EAP: GitHub pull requests and more – IntelliJ IDEA Blog | JetBrains

最近レビューの際はこの機能を使うようにしていて、IntelliJのDiffの見やすさとエディタとの統合に驚いているところです。これは始めは考えていなかったメリットでした。これまでIntelliJのコードを書くといった部分しか使ってきませんでしたが、様々なツールの統合といったIDEの本領を発揮させていきたいと思います。

明日は id:alpicola さんです。