YAPC::Kansai で Perl と Go のトークをしてきます!!
14:00 ~ 14:20 の時間に C 会場で 「Perl to Go」というタイトルでお話しします。内容としては、ある Perl モジュールを Go で書き直すときに、Perl の書き方をそのまま Go に移植したところ、信じられないくらい不便なコードになってしまったので、その辺の戒めを含めアウトプットしていきたいと思いました。
ちなみに大きなイベントでトークするのは今回が初めてなので、皆さん心温かく見守っていただけると嬉しいです🙏
是非来てください!!
Perl の warn は何をしているの?
最近 Go ばっかだったので、久しぶりに Perl を書いてると色々疑問が出てきました。その中の一つが「$SIG{__WARN__}
に代入されたサブルーチンが実行されるタイミング」でした。
__WARN__
とあるので、 warn
を実行した時がトリガーになって実行されるものだろうというのは予想できていましたが、僕が warn
の処理内容を内部で caller
のようなものを実行し、package 名、warn
を実行した行番号を取得して stderr へ出力しているものだと思っていました。そのため「stderr を通じて出力された時がトリガーとなって $SIG{__WARN__}
が呼び出される」と考えたのですが、Okinawa.pm の Slack で
というようなアドバイスをもらったので以下のようなコードを書いて試してみました。
use strict; use warnings; use Carp; BEGIN { $SIG{__WARN__} = sub { print "CALLED!!\n" }; } warn "First\n"; carp "Second\n"; print STDERR "Third\n";
結果は
CALLED!! CALLED!! Third
というような結果を得ることができました。carp
も内部では warn
を呼び出しているようなので、結果から warn
を実行したタイミングがトリガーになるということが分かりました!
…が今度は warn
って実際に何やってるのだろうという疑問が出てきたので、コードを大まかに追ってみることにしました。
これが多分 warn
のソースコード。この関数の中で vwarn
という関数を呼んでいますが、実際は Perl_vwarn のマクロ みたいですね!
ということは warn
の本体である Perl_vwarn
を読んでみると stderr へ書き出す前に invoke_exception_hook
という S_invoke_exception_hook
のマクロが呼び出されていることが分かりますね!
S_invoke_exception_hook のコードです。
STATIC bool S_invoke_exception_hook(pTHX_ SV *ex, bool warn) { HV *stash; GV *gv; CV *cv; SV **const hook = warn ? &PL_warnhook : &PL_diehook; /* sv_2cv might call Perl_croak() or Perl_warner() */ SV * const oldhook = *hook; if (!oldhook) return FALSE; ENTER; SAVESPTR(*hook); *hook = NULL; cv = sv_2cv(oldhook, &stash, &gv, 0); LEAVE; if (cv && !CvDEPTH(cv) && (CvROOT(cv) || CvXSUB(cv))) { dSP; SV *exarg; ENTER; save_re_context(); if (warn) { SAVESPTR(*hook); *hook = NULL; } exarg = newSVsv(ex); SvREADONLY_on(exarg); SAVEFREESV(exarg); PUSHSTACKi(warn ? PERLSI_WARNHOOK : PERLSI_DIEHOOK); PUSHMARK(SP); XPUSHs(exarg); PUTBACK; call_sv(MUTABLE_SV(cv), G_DISCARD); POPSTACK; LEAVE; return TRUE; } return FALSE; }
分からない部分多すぎて泣けてくる…
きっと SV **const hook = warn ? &PL_warnhook : &PL_diehook;
の部分で hook
変数に $SIG{__WARN__}
へ代入されたサブルーチンを渡していて、SV * const oldhook = *hook;
して cv = sv_2cv(oldhook, &stash, &gv, 0);
を行って最終的に cv
変数の中にサブルーチンが代入されるのだろうと。 call_sv(MUTABLE_SV(cv), G_DISCARD);
でそのサブルーチンが実行されているんだろうなと考えました。間違っていたら教えてください。
ちなみに $SIG{__WARN__}
部分のコードは Perl_magic_setsig
だと思います。
結論
warn
は stderr へ出力するだけの関数ではない。warn
は$SIG{__WARN__}
に代入されたサブルーチンを実行して stderr へ出力する。
…多分!!!!
Go で複数プロジェクトを同時に起動する Golet を作った
Golet can manage many services with goroutine from one golang program.
Go で複数コマンドを同時に実行したり、 Cron のようにあるタイミングで何か実行させたりなどを行うことができるものを作りました。
これは元々 Perl で Proclet という kazeburo さんが作成したものを基にして作ってたのがきっかけです。Proclet 本当に素晴らしいモジュールなので Perl 使ってる方は是非使ってみてください!!
そもそも何ができるのか
例えば、マイクロサービス的な言語別の複数プロジェクトを一つのサービスとして扱いたい、つまり複数プロジェクトを起動したい時に Golet を使うことができます。
どんなコードを書けばいいのかは Synopsis を見ると分かるはずです。
特徴
自動でサービスごとのポートの割り当てや cron で動かすタスクを指定することもできます。 github.com/robfig/cron のフォーマットを使って動かします。
動きはこんな感じ。
興味があれば使ってみてください。
約10年共に生活したカメが亡くなりました
昨日1月8日に小6の頃から生活を共にしてきたクサガメのカメ丸がお亡くなりになりました。
僕の家では2匹のカメを水槽別々で飼っていて、カメ丸はそのうちの一匹でした。(もう一匹は甲太郎です。)水槽を洗う時はよくベランダに野放しにしていると、室外機の下や、園芸用道具箱の隙間などによく入っていく子でした。つい最近、カメ丸の水槽につけているフィルターも新しい物へ変えたばっかりだったし、ついこの間まで水槽内を暴れまわるくらい元気だったので、突然の別れが本当に信じられませんでした。
亡くなったカメ丸は近くの公園にある森林のエリアで埋葬しました。
そして今日の午後に飼っていた水槽を片付けすることができました。
今の僕の気持ちとしては、なんで早く異変に気付いてやれなかったのか、一昨日からもう少し観察していれば変化に気づいていたのではないかなどの後悔があります。そして今までキツイことがある度に何度もカメ達に癒されてきたことか、一匹でも存在を失ったことが正直とても辛いです。
このブログを書いた目的としては、今までの思い出(めっちゃ沢山ある)を思い出しながら自分の気持ちを新たに切り替えていこうという気持ちで書きました。
そして残りのもう一匹である甲太郎をカメ丸の分、精一杯可愛がろうと思います。
カメ丸よ本当に今までありがとう。そしてお疲れ様でした。また会う日まで。
YAPC::Hokkaido へ参加してきた
この記事は 12/10 の Perl入学式 Advent Calendar 2016 も兼ねています。本当は沖縄のPerl入学式の様子を書こうと思ってましたが、今日が YAPC::Hokkaido だったので、どういう雰囲気だったのかを踏まえて書くことができると良いかなと思います。
僕が聞くことができたセッション、LT一覧です。
前夜祭
- JSON, JSON::PP, and more
- 楽器アプリ制作の裏側
- From Perl to Haskell
- Perl MongersのためのサーバーサイドSwift入門
- なるほどErlangプロセス
前夜祭はどんな感じだったかというと、乾杯した後LT大会が始まってからが盛り上がってきました。しかも本セッションで聴けてもいいのでは?といった内容だったので、来年のYAPCに参加したい方は是非前夜祭から行くことをお勧めします。
本セッション
- 奥 一穂さんの「HTTP/2の最適化について」
- papixさんの「APIをPerlで作る時に僕達が考えたこと 」
- charsbarさんの「2016年のPerl (Long Version)」
- Vickenty Fesunovさんの「Writing Perl extensions in Rust (English) 」
- Dan Kogaiさんの「Number Unlimited」
- kazeburoさんの「Site Reliability Engineeringの話」
- 高山裕司さんの「CMS と API の素敵な関係」
- 千葉 誠さんの「Vue.jsによるWebアプリケーション開発 」
- karupaneruraさんの「頼りがいのあるORM「Aniki」徹底解説!」
- masayoshiさんの「はてなのインフラ環境を自宅で再現する」
- Yappoさんの「LINE Bot on the Perl」
- Miyagawaさんの「Delivering a CDN」
一番感じたことは、YAPCはそれぞれのトークが部屋ごとに分かれており、「もう一人自分がいれば...!!!!」というくらい全てのセッションを聴きたかったです。 僕はあまりにも学べることがあったので、メモのためにツイートの数が増えてしまいました。
感想
僕は前夜祭からの参加することにしました。今回の YAPC に参加して感じたことは、トークの内容が少しついていけない部分があるなーと感じましたが、何が分からなかったという部分を覚えていれば、後で発表者に直接聞いてみるということができたので、そういうことをできるのが YAPC の凄さかなと思いました。
セッション後は懇親会が開催されました。懇親会では Perl 界の重鎮達と会話する機会があったので積極的に話を聞きに行きました。皆さん優しいのでちゃんと話を聞いてくれるし、話をしてくれますので、初心者でも積極的に話を聞きに行くことができればとても勉強になるだろうなーと感じました。
次はYAPC::Kansaiがあるのでこの記事を読んでくれている皆さん是非参加してみましょう!
cgo をちゃんと使ってみる!!
cgo を使ってみます
cgo って何?という時にここを読むと良さそうです!
コードを書いてみる
cgo では C.C言語の関数()
とすることで, C言語から関数を呼び出すことができます. 実際に簡単な print を行うコードを書いてみます. ちなみに OS は Mac OSX 10.11.6 です.
package main /* #include <stdio.h> void print(const char *str) { printf("%s", str); } */ import "C" func main() { str := C.CString("Hello, World\n") C.print(str) }
出力結果は
Hello, World
となります. 始め
C.printf(C.CString("Hello, World\n"))
という風に書いてましたが, unexpected type: ...
というエラーが出てコンパイルができませんでした. これは僕の予想ですが, printf(1)
の引数は次のようになっているからだと考えました.
int printf(const char * __restrict, ...)
事実はわからないので, 知ってる方教えて欲しいです.
C で得られた結果を Go で扱ってみる
package main /* package main /* #include <string.h> int num(int n) { return 10 + n; } float flt(float f) { return f + 20.5; } char *str(char *s) { return strcat(s, " Good!!"); } */ import "C" import "fmt" func main() { n := C.num(10) fmt.Println(n) f := C.flt(20.78) fmt.Println(f) s := C.str(C.CString("Condition is")) fmt.Println(C.GoString(s)) }
得られた出力結果です.
20 41.28 Condition is Good!!
結構簡単にやり取りできますが, C で扱う文字列と Go で扱う文字列は型が異なるため, C.GoString
を利用して C 側で得られた文字列の結果を Go の文字列へ変換してあげることがポイントです.
Go から C の Macro を呼び出してみる
Macro に文字列, をしていしてる場合は次のように Go の文字列として扱うことができるようです.
package main /* #define Hello "Hello" #define INT 10 #define CHR 'A' */ import "C" import "fmt" func main() { fmt.Println(C.Hello, C.INT, C.CHR) }
結果は次のようになります.
Hello 10 65
しかし, まだ対応してないのか #define FLOAT 1.234
のような Macro を呼び出そうとするとエラーになります.
C の errno を Go の error として扱う
cgo のドキュメントには次のような記述があります.
Any C function (even void functions) may be called in a multiple assignment context to retrieve both the return value (if any) and the C errno variable as an error (use _ to skip the result value if the function returns void).
試してみましょう.
package main /* #include <math.h> */ import "C" import ( "fmt" "log" ) func main() { n, err := C.sqrt(-1) //n, err := C.sqrt(4) if err != nil { log.Fatal(err.Error()) } fmt.Println(n) }
結果は
NaN
ちゃんとエラーを得ることができているみたいですね!
Go でかわいいコードを書きたいの♡
この記事は 12/3 の okinawago アドベントカレンダーの記事です。 サーモンを作った話をする予定でしたが全く進んでないので... かわいいコードを書いてみようと思います(´∀`艸)♡♡♡♡♡
かわいいの定義
「かわいい」にはいろいろ定義がありそうなのでここでは僕がかわいいと思ったことを可愛いということにしますね!!
Go 1.7.4 のシンタックス
Go 1.7.4 の構文の識別子 (identifier) は次のような条件になっています。
unicode.IsLetter(c) || c == '_' || unicode.IsDigit(c)
1.7.4 の識別子のソースコードです。
ちなみに、この記事を書いてる時点での開発中の最新版のソースコードと違いますが、どうやら最新版も同じ条件みたいです。
ここで unicode.IsLetter(r rune) bool
がどのような文字をカバーしているのか気になります。そこで unicode.IsLetter(r rune) bool
を使い true の時だけ出力するコードを書いてみましょう。
package main import ( "fmt" "unicode" ) func main() { for i := 0; i < 0xffff; i++ { if unicode.IsLetter(rune(i)) { fmt.Printf("%c\n", i) } } }
今回は 0x0000
から 0xffff
までを範囲としています。
このコードを実行した結果をファイルに書き出して、less(1) などで閲覧するのも有りでしょう。
日本語を使ったコードを書いてみる
それでは日本語を使ったコードを書いてみましょう。
package main import "fmt" type 性 int const ( 男 性 = iota 女 ) type 人 struct { 性別 性 名前 string 住んでる場所 string } func 生まれる(性別 性, 名前, 住んでる場所 string) *人 { return &人{ 性別: 性別, 名前: 名前, 住んでる場所: 住んでる場所, } } func main() { 瀧 := 生まれる(男, "瀧", "東京") 三葉 := 生まれる(女, "三葉", "糸守町") 入れ替わり(瀧, 三葉) fmt.Println(瀧.君の名前は()) fmt.Println(三葉.君の名前は()) } func (相手の 人) 君の名前は() string { return 相手の.名前 } func 入れ替わり(瀧, 三葉 *人) { これってもしかして, 私たち身体が := 入れ替わってるー(瀧, 三葉) *瀧, *三葉 = *これってもしかして, *私たち身体が } func 入れ替わってるー(瀧, 三葉 *人) (*人, *人) { return 三葉, 瀧 }
結果は
三葉 瀧
うん!入れ替わってますね!!
カゝ ゎ レヽ レヽ ] ─ ├″を書 、キ ま ι ょ ぅ ♡
それではかわいいコードを書いてみましょう。
package main import "fmt" func main() { ょιょιˊȎωȎˋノ("இдஇ") ㄘんㄘんぺㄋぺㄋ("。╹ω╹。") } func ょιょιˊȎωȎˋノ(இдஇ string) { fmt.Printf("ょιょιˊʘωʘˋノ(%s;)\n", இдஇ) } func ㄘんㄘんぺㄋぺㄋ(カゝぉ string) { fmt.Printf("ㄘんㄘんぺㄋぺㄋ(%s)\n", カゝぉ) }
結果は
ょιょιˊʘωʘˋノ(இдஇ;) ㄘんㄘんぺㄋぺㄋ(。╹ω╹。)
かわいい!!!!!