VimでPerlを書くときにctagsを導入して使ってみた
コードジャンプを利用したい場合にctagsを導入するという方法がありますが、最近VimでPerlを書くときに導入してみたところ、非常に良い感じだったので、インストール方法と簡単な使い方を記しておきます。
インストールするものについて
ctagsの実装(?)にはいろいろ種類があるようですが、今も活発に開発が続いている universal-ctags
をインストールするのが良さそうです。
1. インストールする
僕の個人機のOSはUbuntu18.04なので、今回はgithub上にあるレポジトリからcloneしてきて手動でビルドしました。
$ git clone https://github.com/universal-ctags/ctags $ ./autogen.sh $ ./configure $ make $ make install
詳しいことが知りたい場合は Readme や docs/autotools.rst を見てください。 Max OSだとbrewからインストールもできるようです。
2.tagsファイルを生成する
エディタでコードジャンプできるようにするには、まず対象のコードに対してどのようなパターンの時にどのファイルのどの場所にジャンプするのか、みたいなことを定義したtagsファイルを生成する必要があります。
tagsファイルはctagsコマンドで生成できます。
今回はPerlのコードのみを対象にしているので、プロジェクトのルートディレクトリで以下のようなコマンドを実行しtagsファイルを生成します。
$ ctags -R --languages=Perl
実行すると、 tags というファイルが生成されているのが確認できます。
tagsファイルは直接プロジェクトに関係ないので、ついでに .gitignore
に追加しておくといいと思います。
注意点として、tagsファイルはコードに変更がある度に生成し直さないと正しくコードジャンプできなくなるので、コードに変更がある度に再生成する必要があります。
tagsファイルを再生成する方法はいろいろありますが、僕は git で commit した時にフック処理をさせるようにして、commitする度にtagsファイルの再生成を行うようにさせました。
Cartonを利用して開発している場合、 local/
以下に依存モジュールが入っていると思うので、依存モジュールの定義元にもコードジャンプできてより開発が捗ります。
3. Vimのキーバインドについて
- 定義元へのジャンプ :
<C - ]>
- 複数の候補がある場合の定義元へのジャンプ :
<gC - ]>
- 継承してoverrideしているようなモジュールが複数ある場合などに使います
- コードジャンプ前の位置に戻る :
<C - t>
他にもいろいろありますが、とりあえずこれらのキーバインドを覚えとくといいと思います
所感
Perlでコードを書いてると、どんな引数を渡せばよくわからなくて関数の定義元に飛びたい!、というような状況がよくあるのでとても便利です。
しかし、Mooseのシンタクスシュガーやkeywordプラグインを使っているようなモジュールには対応していないので、それらを使っている場合は別の方法でtagsファイルを生成する必要がありそうです。(CPANにそういうライブラリがあがってるっぽいのでそのうち試したい。)
Function::Parameters + Mouse::Util::TypeConstraints = パフォーマンス激強
概要
Perlのサブルーチンの引数バリデーションをするモジュールはいろいろありますが、 Function::Parameters が他の引数のバリデーションを行うモジュールと比べて高速にバリデーションできるという話を聞いて、であれば Mouse::Util::TypeConstraints と組み合わせて使えばとても良いパフォーマンスがでるのではと思い、実際にベンチマークをとってみました。
比較対象は以下のとおりです。
最近良く使われている印象があるモジュールと、何もチェックを行わない場合とで比較してみました。
Smart::Args
と Data::Validator
は、引数にハッシュを渡すほうがリストを渡すよりもパフォーマンスが良い(あるいはほとんど変わらない)ようなので、ハッシュを渡しています。
- 何もチェックしない場合
- Data::Validator 1.07
- Smart::Args 0.14
- Function::Parameters 2.001003 + Type::Tiny 1.004002
- Function::Parameters 2.001003 + Mouse::Util::TypeConstraints v2.5.4
ベンチマークスクリプト
use v5.28; use warnings; use utf8; use DDP; use Benchmark qw( timethese cmpthese ); use Smart::Args qw( args args_pos ); use Data::Validator; sub nomal { my ($id, $name, $pass) = @_; } sub smart_args { args my $id => 'Str', my $name => 'Str', my $pass => 'Str'; } sub data_validator { state $rule = Data::Validator->new( id => 'Str', name => 'Str', pass => 'Str', ); my $args = $rule->validate(@_); my ($id, $name, $pass) = @$args{qw/ id name pass /}; } { use Function::Parameters; use constant Str => Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint('Str'); fun function_parameters_mouse(Str $id, Str $name, Str $pass) { } use Types::Standard qw( Str ); fun function_parameters_types_standard(Str $id, Str $name, Str $pass) { } } my $args = { id => 'mp0liiu', name => 'Maybe_mp0liiu', pass => 'hogehoge!!!', }; my @args = @$args{qw/ id name pass /}; cmpthese( timethese( 100_0000, { nomal => sub { nomal(@args) }, smart_args => sub { smart_args($args) }, data_validator => sub { data_validator($args) }, function_parameters_mouse => sub { function_parameters_mouse(@args) }, function_parameters_types_standard => sub { function_parameters_types_standard(@args) }, } ) );
結果
Rate smart_args data_validator function_parameters_types_standard function_parameters_mouse nomal smart_args 23068/s -- -77% -93% -97% -99% data_validator 99404/s 331% -- -68% -86% -96% function_parameters_types_standard 313480/s 1259% 215% -- -56% -87% function_parameters_mouse 719424/s 3019% 624% 129% -- -69% nomal 2325581/s 9981% 2240% 642% 223% --
感想
Function::Parameters
と Mouse::Util::TypeConstraints
を併用した場合の速度がとても速くて驚きました。
誤差などあると思いますが、何もチェックしない関数の1/3程度の時間でちゃんと引数の個数や型をチェックをしてくれるってすごくないですか???
Function::Parameters
の書き方は微妙に気に入らないところもあるのですが、このパフォーマンスで引数の個数と型をチェックしてくれるのは非常に魅力的なので個人の開発では利用していきたいと思います。
ただ、型チェックオブジェクトを Types::Standard
みたいにクールにかけないので、 Mouse::Util::TypeConstraints
を使う場合も同じようにクールに書けると良さそうかなあ、、、
ドメイン駆動設計の実践 勉強会メモ
8/22に開催された【TECH x GAME COLLEGE #1】ドメイン駆動設計の実践 - connpassに参加した時のメモです。
とても勉強になる面白い話を聞かせていただきました。
アジェンダ
- DDDの基礎知識
- ゲーム開発への活用の可能性
- ゲーム開発に取り入れる時の障害
DDDの基礎知識
ソフトウェア設計の一般原則
- 関心の分離
- モジュール構造
全体を複数の関心事に分離し、それぞれの関心事を独立したモジュールとして開発する。
そしてそれらを組み合わせて全体を構築する。
DDDの特徴
DDDではどのように関心事を分離しモジュール構造を構築するか?
従来のソフトウェアとの関心事の違い
- DDD
- 従来の設計
- 入出力
- 主な関心事はUI, DB, 通信など
ゲーム分野におけるドメインロジックの構成要素
- ゲームを構成する概念(世界観、キャラクター設定、ストーリーに登場する言葉, etc)
- ゲームの状況を表現する値(金, HP, MP, Exp, SP, アイテム種類, 武器種類, 防具種類, etc)
- 戦闘開始状況の判定ルール, ダメージなどの計算ルール, 勝敗の判定ルール
これらを1つ1つの機能として整理してモジュール化する。
ドメインロジック以外の関心事
ビジュアルデザイン, SE, インタラクションデザインなど
これらはドメインロジックに従属する要素である。
DDDで設計されたソフトウェアはドメインロジックのまわりにこれらがぶら下がる感じで出来上がる。
ドメインロジックに焦点を合わせる理由
- ソフトウェアの複雑さの原因はドメインロジック(計算、判定ロジック)にある
- ドメインロジックと入出力が混在しているとドメインロジックの全体像や構造が見えてこない
- 入力の関心事を分離してドメインロジックだけを対象にするとドメインロジックの輪郭や構造がはっきりしてくる
- ドメインロジックの輪郭や構造がみえてくると計算、判定ロジックがはっきりしてくる
- ロジックの整理がしやすくなると入出力もシンプルになる
- この実感を感じられるようになるとDDDの恩恵がわかるようになる(入出力がシンプルになるから)
- 結果的にソフトウェア全体の見通しが良くなる
- どこに何があるかわかる
- コード変更による影響範囲がわかる
MVCモデルのソフトウェアのモデル部分をビジネスロジックと入出力処理に分離するイメージといえそう。
ゲーム開発への活用の可能性
ゲーム開発でのメリット
- 計算ロジック、判定ロジックの記述が整理できる
- 同一ロジックの重複記述の防止
- ロジックの組み合わせ方の見通しをよくできる
- 世界観、キャラクター設定、シナリオ構成と計算ロジック、判定ロジックの記述を直接的に関係づける
- 全体が整合する
- 逆に言いうと整合が取れていないとビジネスロジックを考えなおしてコンセプト(=世界観)などに合わせていくべき
- 変更の対象箇所、影響範囲がわかりやすい
- 全体が整合する
- 入出力処理の記述がシンプル
- 計算や判定の記述をドメイン層に外だし出来る
- 一番実感しやすい
OOPでモジュール化するとは?
- ソフトウェアのモジュール
- コードを扱いやすい塊にグループ化して、グループごとに別ファイルで記述する
- モジュール間の依存関係を少なくする
- モジュール化のアプローチ
- 入出力の単位ごと
- 計算する値の種類ごと
入出力単位でモジュール化する方法
- 入力を起点に、出力するための処理手順の単位でファイルを分ける
- 機能分割し機能ごとに入力と出力を定義する
- 出力に必要な一連の処理を時系列に記述する(手続き)
- (Stringクラス の length と substr するのに時間は関係ないよね)
計算する値の種類ごとにモジュール化する方法
- 計算や判定に登場する値の種類を見つける
- レベル, HP, MP ...
- 武器種類 ...
- ダメージの判定結果、勝敗判定結果
- 計算する値の種類ごとに必要な計算(演算)を整理する
値の種類は多くて計算の種類は少ない
OOPのモジュール化の考え方とやり方を学ぶには?
- 値オブジェクト, 区分オブジェクト, コレクションオブジェクト
- 現場で役立つシステム設計の原則(登壇者の著書), オブジェクト指向入門とか参考になるよって言っていた
インクリメンタルに開発するとは?
- 最初から良い設計はでいない
- 開発を始めるときはゲームに対する知識が貧弱
- 最初に書いたコードはあとからもっと良い書き方が見つかる
- 最初の要求はあいまいで時間とともに具体的、詳細になる
- 要求は時間とともに変化する
- フィードバックの反映
- 周りの環境の変化
- 複雑な構造に対して最初から格闘していてもつくれない
- 基本的な値の種類を見つける(例えば、HPクラスだけ最初に作ってみるとか)
- 値と値を組み合わせたロジックを置く場所(第3のクラスを見つける)
- 定番の答えはない, 開発してるゲームに合わせて探す
例えばRPGのようなゲームを作っているとどんな値の種類が見つかるか?
- バトル型
- 進行中の戦闘状態を表す状態を集めた型
- ターン型オブジェクトの履歴を持つ
- 戦闘結果を判定するロジックを持つ
- バトルコンテキスト型
- 戦闘開始時の状態を表現する型
- 戦闘やターンの計算、判定に必要な基本情報を表現した値の集合
- ターン型
- 戦闘のターンごとの状況を表す値を集めた型
- 前のターンの結果を計算して次のターンを返す
- 次のターンから戦闘結果を判定する
- ポイント型
- HP, MP, SPなどの共通の定義
- アイテム型
- 食料、薬、巻物などのアイテム共通の定義
これらはあくまで例えばの話で、例えばアイテムに関しては特に全アイテム共通のアイテム型なんか持つ必要がなくて、 食料、薬、巻物別々に定義や所持枠を作るという方法もある。
ゲーム開発に取り入れる時の障害
どんな障害が起こるか
- そうはいっても実際にソフトウェアを開発していると入出力の処理を重視しがちになる
- UI命, 通信が〜, DBが〜
- フレームワーク
- 開発プロセス
- 例えば見た目をすぐに修正して欲しいということがあったとして、 ビューモデルだけ変えるか, ビジネスモデルも変えるか・・・みたいなことが起きる
障害に対するいくつかのアプローチ
- 少数精鋭のチームで徹底してDDDで開発してみる
- 学習するための実験プロジェクトをやってみるなど
- 効果は非常に高いけど難しい
- 従来のやり方に少しずつ
- 数値の計算ロジックを独自型にwrapして計算を行う
- HPの単純な足し算、引き算のロジックをHP型にする、など
- 判定結果をboolean型で表現することをやめ、独自に定義したenum型などを使って判定を行う
- コレクションの操作を独自型にwrapして行う
- 数値の計算ロジックを独自型にwrapして計算を行う
実際にコードを書いてその結果を確認しながら議論し、決めていくことが一番大事!
質問やその他メモ
- DDDでビジネスロジックを書いていたら固有名詞のクラスとかが生まれてきて気持ち悪い
- ビジネスロジックでの計算のために使われているならいいと思いますよ的な
- (けっこう難しいお話をされていたので多分こんな感じのことを言っていたと思うけどよく覚えていない。。)
- 企画の人が完全に要件を定義していない。どうしよう?
- 集約がむずかしくてよくわからない
- エヴァンス本での集約のトランザクション整合性とはDBのトランザクションの話をしているわけではない
- 複数のドメインモデルの間で変更の同期が必要なもののこと
- 例えば商品の値段が上がれば請求書の値段もあがるみたいな(これはちょっと不自然なのでたとえとして悪いけど)
ソースコードからビルドしたPerlをplenvで管理する
独自にコンパイルされたperlや自分で少しソースコードをいじったperlもplenvで管理したいなーと思ったのでやってみました。
基本的にrbenvなどと同じようにできるようです。
手順
1. Perlのソースコードを入手する
2. コンパイルする
取得してきたPerlのソースコードの中に ./Configure
というファイルがあります。
このファイルを実行することでMakefileが生成されます。
plenv管理下のperlは通常 $HOME/.plenv/versions/
以下にあり、その中にあるディレクトリの一覧が plenv versions
で表示されるようになっています。
なので、以下のようにplenv管理下のperlがあるディレクトリの中にインストールされるようにオプションを指定してMakefileを生成します。
(${version_name}
が plenv versions
で表示される名前になるので適宜設定します。)
./Configure -Dprefix=$HOME/.plenv/versions/${version_name} -de -Dversiononly -Dusedevel -A'eval:scriptdir=$HOME/.plenv/versions/${version_name}/bin'
Makefileの生成が終わったらコンパイル、テスト、インストールを実行します。
make
make test
make install
3. 使う
インストールが終わったら plenv versions
で先ほどインストールしたperlが表示されているはずです。
無事インストールが完了していれば plenv local
などで設定して使うことができます。
参考にしたの
あとは Perl-Build
のコードを見てみたり
UNIVERSAL::DOESについてのメモ
Perlのオブジェクトに $object->DOES('SomeRole')
みたいな、ロールを消費したかどうかを判定できる(?)メソッドがあるのですが、それについていろいろ知見を得たのでまとめます。
- Perl自体にロールを消費したかどうかを判定する機能はない
- そもそもあまり使われていない
UNIVERSAL::can
で判定したほうが確実 (変なDOES
の実装だときちんと判定してくれない場合もありうるので・・・)- 大半のrole判定がほしい実装はMooseを使うのがベストプラクティスになってしまってそういう場面でのDOESの出番が無かった
これらに関してはid:karupanerura氏にいろいろ教えていただきました。
ありがとうございました。
YAPC::Okinawaにいってきました
けっこう遅くなりましたが、3/3〜3/5の間YAPC::Okinawa 2018 ONNASONに参加するため沖縄に行っていたのでその感想を書きます。
前後に予定が入っていたり、沖縄に行くのはなかなかしんどいのであまり参加に乗り気でなかったのですが、学生は宿泊代と交通費を負担していただけるということで参加することにしました。
学生交通費支援をしてくださった企業の皆様、ありがとうございました!
前夜祭
本祭の日は朝早く出発しなければなかったので前日から沖縄入りしついでに前夜祭に参加してきました。
バーでいろんな方とゆるーく食事をとりながらLTを聞くという感じでした。
本祭
見たトーク
萬國之津梁
charsbarさんによるトークで、氏が参加したPerl関係のイベントやそこでどんなことをしたかについて話されていました。
GraphQL をプロダクション導入した結果
GraphQLがどういうものなのか少し気になっていたので聞くことに。
とりあえずリクエストのパラメーターがちゃんと定義されていて型安全に扱える点がよさそうな感じでした。
GraphQLを使ったアプリの実装や運用についてかなり詳しく話されていたので、参考にして試してみようかと思います。
そろそろPerlでのHTTP/2について触れたい
HTTP2ってHTTP1.1と違って何ができるのか、どんなことが変わるのかといったことやPerlでHTTP2を利用するためのライブラリなどが紹介されていました。
(前半の内容は前にやっていて僕が見ていたかったトークの内容とかぶっていたそうですが、僕は前のトークを見ていなかったのでありがたかったです・・・)
HTTP2でコンテンツを配信するサーバーの実装は並行処理がふえたりと今までとかなり雰囲気が違ってきてきそう、
とても丁寧に解説されていたので、これを気にHTTP2を触ってみようかと思います。
しかしサーバープッシュはHTTP2も含めていろいろな方法が使えるようになっているようですが、どれを使うのがいいんでしょうかね。
High (Availability|Performance) WebSocket for Perl Real-Time Application
Webアプリのサーバープッシュの部分だけ別のサーバーでやるという発想で作られたWebSocketとHTTPの相互変換プロキシサーバkuiperbeltについてのトークでした。
これを使えばLLでもWebSocketを使ったアプリをだいぶいい感じにかけるんではないでしょうか
新しい概念から作っていてもうなんかすげえって感じでした。
ベストトーク賞もこれに投票しました。
2018年春のPerl
Perl5, Perl6の最新情報についてのトークでした。
5, 6月ごろにでるであろうPerl5.28での変更点とかPerl6がどんなふうになっているかは興味があったので見れてよかったです。
Perl5.28での変更点で気になったのはハッシュがkey/valueのスライスでdeleteできるようになることと正規表現文法が拡張されることでした。
他にもスマートマッチやgiven-when文などを実用的にするための変更が入る予定だったそうですが、一部のモジュールが壊れたので没になったそうです。。
ずっとexperimentalなままで早く外れないかなーと思っていただけに残念でした。
やはり後方互換性を維持しながら言語の開発を進めることって大変なんですね。
Perl6はぼちぼちって感じでした。
Inlineモジュールの世界
Inline系モジュールがどのように動いているのかがわかり面白かったです。
メリットやデメリットもまとめられていて、導入時の判断基準にできそうでした。
やはりランタイムエラーが起きた時にPerl側で捕捉できないというのがつらそうで、組み込む言語側でなんとかするしかないのかなあと思いました。
Perlコーディングテクニック2018
最近のPerlの便利な機能やモジュールの紹介をされているトークでした。
個人的に気になったのはModule::Functions, Importer, Module::Findあたりでしょうか。便利そうなのでどんどん使っていきたいと思います。
最近のPerl関連の情報ってあまり多くないのでありがたかったですね。
Perl6のエコシステム
最近のPerl6のエコシステムについてのお話でした。
最初はgithubで配布していましたが、最近はCPANでやるようになっているようです。
また、インストーラーもpandaからzefになっていて、いろいろ変わっているなーと感じました。
mi6というオーサリングツールもあって、簡単にモジュールを挙げられるようになっているそうです。
zefの実装がなかなかいい感じだということで(workaroudも多いそうですがw)参考にしていきたいと思います。
懇親会
懇親会に参加するのは初めてで、いろんな方とお話出来てよかったです。
Mouseの不具合(?)の話をskajiさんとお話できたのは良かったです、なかなか解決は難しそうですがp-r送ろうと思います。
その後
翌日は飛行機が出るまで少し時間があったので首里城に行っていました。
独特の雰囲気や眺めがとても素晴らしかったです。
DBIx::Schema::DSLで外部キー制約を定義するときにトリガを指定する方法
こんな感じででできます
foreign_key user_id => ( user => 'id', on_delete => 'CASCADE', ):
ドキュメントでは
foreign_key($columns :(Str|ArrayRef), $foreign_table :Str, $foreign_columns :(Str|ArrayRef) )
という風に明記されていませんが、追加の引数はそのままSQL::Translator::Schema::Table::add_constraintに渡されるので上記の方法で追加できます。
そもそもトリガはあまり使うべきではないのかもしれませんが...
他にも渡したいオプションがあればSQL::Translatorのドキュメントを読むとどんなことができるかわかります。