私が歌川です

@utgwkk が書いている

関ケ原Ruby会議01に参加した #sekigahara01

regional.rubykaigi.org

参加しました。もうじき1ヶ月が経とうとしていますが、記事に残しておかないと何かを失ってしまいそうなので書き残します。

武将ネームは藤原歌川筑前守私伽歌川出寿喜喜です。いちおう自分のルーツなどを思い出した上で考えて提出しました。個人スポンサーの特典としては過去イチで難しかったです。

最後まで一貫して全力の悪ふざけが行われていたので、気持ちよくノることができました。「合戦」のネタバレも一切なく、正確には話しぶりからなんらかのアクティビティがあることまでは想像していましたが、玉入れを最後にやったのっていつ以来なんでしょうか。たぶん二度と見られないであろう光景をいくつも見ることができたと思います。

関ケ原古戦場記念館にも行きました。関ヶ原の戦いについては日本史の授業ぐらいの知識しかなく、小早川秀秋が裏切ったこと・東軍が勝利したこと しか知らなかったのですが、より深く背景を知れたと思います。記念館のチケットに武将のイラストが描いてあったのですが、この人が関ヶ原の戦いにおいてどのような役目を果たしたのか、全て理解することができました。関ケ原Ruby会議01がなかったら一生ぼんやりとした知識のままだったと思います。

BEER&CRAFTさんには連日お世話になりました。

関ケ原で撮影したいちばんいい写真で記事を締めようと思います。

決着


関ケ原Ruby会議の次回、はもうないと思うけど、次は関西Ruby会議09で会いましょう。

胃カメラ

人間ドックでバリウムを飲んだ結果、胃炎の疑いがあるので胃カメラを飲むことになったので、飲んできた。

前日

ふつうに飲み会が入っていたので、21時までは食べ飲みしつつ21時を過ぎたら絶食開始した。水しか飲んでないので退店する頃にはすっかり酔いが覚めていて意識もはっきりしており、久しぶりに酔っ払いを冷静に眺める機会ができたと思う。

当日

5時半に起きてシャワーを浴びてから検診会場に行った。

「胃カメラを飲みに行く」と言ったが、肝機能についても要検査と出ていたのであった。まずは血液検査から。看護師さんが3人集まってきて左腕の血管を探す局面もあったが、それ以外は針を刺されてもとくに苦しみがない側の人間なのでつつがなく終わった。

薄いポカリスエットみたいな液体を飲んでちょっと待機してから、本題の胃カメラに挑む。まずは喉の麻酔ということで、喉にスプレーを吹きかけられる。痺れてきておもしろい。なんか体勢の取り方が難しすぎるのでレクチャーされまくっていた。

口元にトレーとかガーゼとかが用意されまくったのち、ついに胃カメラが入ってきた。HDMIケーブルぐらいの太さのケーブルを経口摂取するのである。苦しい。喉の麻酔は喉の痛みを緩和してくれるが、喉を貫いているものの異物感を取り除いてくれることはついになかった。飲み込んでしまいたいが当然そんなことはできないし、できた場合は更なる問題が発生して検査どころではなくなる。呼吸はできるものの、タイミングをうまく取らないと息ができなくて詰まりそうになる。案内してくれるのに従ってゆっくり呼吸することでなんとか平穏を保っている。平穏というのは言葉の綾であり、実際には終わりの中にかすかな希望を見出しているのに過ぎない。だんだん、これは恐怖感との勝負であり、無の感情になって終わりを待つのがコツなんだろうな、ということが分かってきたがもう終盤である。ゲップはできるだけ我慢してくださいね、と言われるけど、満足な呼吸もできない状況でそこまで気を払うのは不可能。

何分か分からないけど、ついに検査が終わって胃カメラが抜かれた。喉を物体が貫いていない状態がこんなに心地よいとは知らなかった。やったことないからそうだろう、としか言えない。写真を見せてもらい、やはり胃炎なのでピロリ菌の検査が要りますね的なことを言われ、追加で血液を抜かれる可能性が浮上してくる。自分が血液検査に対して負の感情を持たないタイプでよかったと思う。「大丈夫ですか?」「ええ、なんとか」というやり取りののち、クタクタになった状態で検査室を後にした。結局、血液の量が足りていたので検査項目を増やしてもらうだけで済み、血液を追加で抜かれることはなかった。

ラーメンを食べてから帰宅して仕事した。

感想

初めての胃の検査がバリウムでよかったと思う。逃れようのない身体の苦痛はただただ通り過ぎるのを待つしかない。なんかもうちょっと人道的な感じにならないか。

slogでエラーをちょっとリッチにログ出力するためのライブラリ slogerr をリリースした

このたび、slogerrというライブラリをリリースしました。

github.com

モチベーション

uber-go/zapの Error 関数のように、エラー型に付加情報があればついでにログ出力のattributeに含めてくれるものが欲しくて作りました。

slogでエラーを出力する場合、大抵は slog.String("error", err.Error()) のようにエラーを文字列化するか slog.Any("error", err) のようにany型として取り扱うか、の2択だと思います。エラーを文字列化する場合、エラー型が %+v によるフォーマットに対応している場合でも、シンプルなエラーメッセージしかログに残りません。any型として扱う場合も同様です。

使い方

slogerrが提供する関数は2つだけです。

func Error(err error) slog.Attr

func NamedError(key string, err error) slog.Attr

以下のように、ログ出力時のattributeとして slogerr.Error(...) 関数の呼び出し結果を渡してあげればよいです。

slog.ErrorContext("failed", slogerr.Error(err))
// => {"msg":"request failed","error":{"msg":"something went wrong"}}

普通に使う範囲であれば、ログのattribute名を決めなくてよいとか、出力されるのが単なる文字列からオブジェクトに変わったとか、それぐらいでは? と思われるかもしれません。

slogerrが本領を発揮するのは、スタックトレース情報つきのエラー*1や、errors.Join 関数で複数のエラーがjoinしたようなエラーを出力するときです。verbose フィールドにスタックトレースを含むエラーメッセージが表示されたり、causes フィールドにjoinされた複数のエラーが個別に表示されたりするようになります。

{"msg":"request failed","error":{"msg":"something went wrong","verbose":"something went wrong\nmain.main()\n\t/app/main.go:42"}}
{"msg":"request failed","error":{"msg":"db error\ntimeout","causes":{"0":{"msg":"db error"},"1":{"msg":"timeout"}}}}

(2026/6/12 0:34 追記) 渡したエラーが slog.LogValuer を実装している場合は (つまり、元々slogでログ出力するように作られている場合は) msg verbose causes のようなフィールドを追加するのではなく、元々のエラーが返すattributesを出力するようにしました。

(追記ここまで)

実装

APIインタフェースはuber-go/zapを大いに参考にしました。

ほぼ全ての実装をClaude Codeにやってもらいました。zapの Error NamedError のようなインタフェースをslogでも使えるようにしたい、アイデアを大いに参考にしているので著作権表記を入れておいて*2、ぐらいしか指示していなかったと思います。あとはベンチマークを書いてもらい、アロケーションを減らしてほしいと頼んだぐらい?

エラーにスタックトレースが乗っていたら実際に抽出する、みたいなことも検討したけど、大抵のエラーライブラリは %+v によるフォーマットに対応しているのでこれでいいじゃん、と見切りをつけました。

おわりに

どうぞご利用ください。早速、自分がメンテナンスしているOSSに導入してみています。

github.com

github.com

*1:大抵 %+v によるフォーマットをサポートしている

*2:こういう対応で必要十分なのかはそこまで自信がない、何も書かないより遥かにマシだと思っているけど、自信のある方がいたら教えてください

*3:さすがにもう古い気はする

Kyoto.go #65 に参加した & 登壇した #kyotogo

Kyoto.go に参加しました。今回は会場提供を担当しました。

kyotogo.connpass.com

もともとは DynamoDB localのDescribeTable APIのレスポンスを補完するプロキシを書いた - 私が歌川です などでGoに関する発表ネタが貯まってきたのでそろそろやりたいな~と思い、Kyoto.go の運営メンバーの方にXでメンションしていたのですが、いろいろあり会場提供もやる形になりました。会場提供側として不慣れな点もあったと思うのですが、無事に開催できてよかったです。

ネタが貯まってきたといいつつ、Go 1.27で導入されるメソッドのジェネリクス対応のことが気になってきたので、当初予定していた内容から変えて発表しました。Goのジェネリクス導入以前・Go 1.18で導入されたジェネリクスとその限界・Go 1.27で導入予定のメソッドのジェネリクスで広がる可能性、という流れで過去・現在・未来の話をしました。

speakerdeck.com

Go 1.26までのジェネリクスでは、以下のコードのような独自型のメソッド定義に対して型引数を渡すことはできませんした (文法的には書けるがコンパイルエラーになる)。同等のことを実現するには、メソッドではなく関数を経由する必要がありました。Go 1.27からはこの制約が撤廃される予定です。

type List[T any] []T

func (l List[T]) Map[U any](f func(v T) U) List[U] {
  // snip
}

ただし、以下のようにinterfaceのメソッドをジェネリクスにすることは引き続きできません。

type Iface interface {
  Method[T any]() T
}

発表中では、メソッドのジェネリクス対応によってどのようなコードが書けるか、このように書けると夢が広がりそうだね、というトピックを中心に取り上げていました。発表後の雑談タイムで、*Rand 型のメソッドで乱数を生成するときに、整数の型をジェネリクスで指定できるようにするproposalがある、という話を聞いて、地に足の付いた・正当進化らしい用法についても知れてよかったです。発表すると知見を広められるだけではなく、知見を得ることもできるという感じですね。

github.com

雑談タイムのどこかで、リフレクションを使う実装を学ぶにあたって、どういうライブラリのコードを読むとよいか、という質問があったと思います。だいたいがstructのフィールドに対する操作だと思うので、まずは以下あたりから読むとよいと思います。github.com/caarlos0/envについては、ジェネリクスとリフレクションを組み合わせた実装もあります。


Goでツールを作った話やWASM、tsgo、登壇に向けた話など、いろいろ聞けてよかったです。身近に普段使っているプログラミング言語のコミュニティがあると何かと便利なので、今後も盛り上げていきたいですね。

TOPIK 4級に合格した

4月に受験したTOPIK IIの結果が出ました。163/300点で4級*1に合格しました。

一般的でない個人的な話題や親しみのある抽象的・社会的な話題について話し、学業・業務、公的な対人関係や社会活動に必要なコミュニケーションを遂行できる。具体的で論理的な意見を比較的明確に聞き取り、話すことができ、詳細な情報が含まれる文章や推論的理解が必要な文章を大体読んだり書いたりできる。複雑な文構造を持つ2~3段落の文章を理解し、作成できる。

韓国語能力試験TOPIK | 試験概要

TOPIK Iのときの様子はこちらの記事にあります。

blog.utgw.net

内訳・振り返り

全体

リスニング・リーディングで50点ぐらいは取れてるだろうから、ライティングが30点ぐらい取れたら3級合格なので、それぐらいあるといいな〜と思いながら試験会場を後にしました。蓋を開けてみたら思ったよりも点数が取れており、4級に合格できていて嬉しいけど、もっと対策をちゃんとしないとなーという反省もあります。

듣기 (リスニング)

58/100点でした。

過去問を解いたときにも思ったのですが、前半は音声が1回しか読み上げられないので、けっこうきついですね。聞き逃すとリカバリーできなくなるので、先に問題文と選択肢を読んでおいて、どこがポイントになりそうか予想しながら聞くことを意識していました。音声のスピード自体はTOPIK Iほどではないけどゆっくりなので、文の構造さえ分かればついて行けると思います。

쓰기 (ライティング)

41/100点でした。

ライティングは得点の予想が全くつきませんでした。リスニング・リーディングなら答えが4択だし、ある程度手応えの予想はできると思います。採点基準は探せば見つかると思うけど、自分の書いた文章の点数の予想がつかないまま試験を終えました。後述するように、ライティングの対策本を読むぐらいしかやっていなかったです。

3級・4級目標なら前半の短文補充問題を取りこぼさないようにしよう、ということをよく言われると思うのですが、短文補充が分からなくて、なんなら空欄提出していた問題もありました。長文作文であれば、図表の要素と問題文中の語彙を組み合わせることで論をある程度展開できるので、そちらに注力していました。それでも手でハングルを書く練習が足りていなかったので、文字数が足りていなかったり、書き上げたあとに単語を挿入したい箇所に気づいたりしていました。

읽기 (リーディング)

64/100点でした。

前半は過去問を解いたときの感触のまま、知らない文法や語彙があるのは仕方ないとして、知っているところに集中して解くことができました。やはり文章を読むスピードが足りておらず、後半の読解問題は時間が足りなくて、15問ぐらいは適当にマークしていたと思います。語彙や文法のカバレッジを上げつつ、いわゆる「試験の外国語の読み方」の勘を取り戻していかないと厳しいな〜。

対策

実は、TOPIK IIの試験対策はあんまりできていないまま当日を迎えていました。参考書をいくつか買ってみたもののパラパラ目を通したぐらいしかしておらず、前日にリスニング・リーディングの過去問を通しで解いてみたのと、ライティングの対策本を当日の試験前に読んで雰囲気をつかんでいたぐらいです。作文のルールやコツをひと通り知っておくことが合格に効いたのかもしれません。

相変わらずDuolingoの継続日数は伸ばしていたけど、ややマンネリ化していて継続することだけをゴールにしている感じがあります。

こういう受験記ってだいたい試験対策の情報が求められていると思うけど、なにも参考にならない気がする!! そういうのはほかの人の記事を読んでください。

今後

今回のTOPIK IIに合格していなかったら10月の試験を受けようと思っていたのですが、4級に合格したのでいったん小休止かなーと考えています。高い点数を取ることはもちろん目指せるとよさそうだけど、文法・語彙の学習に集中したり、リスニング・スピーキングを鍛えて生の韓国語をなるだけ聞き取れるようにしたりとか、方向性を変えるのがよいのかもしれません。

韓国に行くたびに、ちょっとずつ聞き取れる・話せる言葉が増えていることは実感しつつも、知らない語彙が多くて話しかけられてもキョトンとしているかPapagoを取り出すしかできない場面がまだ多いです。

相変わらず、ブルアカやトリッカルの二次創作を読みあさったり、たまに渡韓したりしていると思いますので、よろしくお願いします。

*1:TOPIKは級の数字が大きいほど難しい。1級が最も簡単で、6級が最も難しい

眼科

blog.utgw.net

眼圧検査 右目だけ2回検査することに 右目見えにくいとかありますか? って聞かれたけどなにも心あたりがない

人間ドック - 私が歌川です

言われて見ると、片目だけでものを見たときに見えやすさが違うな、というのが気になってきたので眼科に行きました。

  • 遠くに赤い気球が浮いてる画像って実在したんですね
  • 右目
    • 遠視
    • 白内障
  • 左目
    • 近視
  • どちらの目も視力は1.0以上ある
  • 緊急で治療しないとまずい状態でもないので経過観察
    • 半年後、秋ぐらいにまた来てください

健康診断の視力検査だけだと気づけないことがいっぱいありますね。身体は無限の資源じゃないので大切に使おう、ということを実感する機会が増えています。みなさまも気になることがあったら近くの病院に行くといいと思います。

suzuri.jp

TSKaigi 2026に参加した #tskaigi

TSKaigi 2026に両日参加しました。聞いたトークのメモはCosenseにまとめてあります。

scrapbox.io

発表資料は TSKaigi 2026 発表資料まとめ にまとまっているので、こちらも見てみるとよいと思います。

話題

Branded Types

TypeScriptは構造的部分型を採用しており、型同士は名前ではなく構造によって区別されます*1type UserId = stringtype BlogId = string はどちらも同じ構造を持つため、UserIdBlogId は可換となります。

プリミティブ型に別名をつけて区別するためのテクニックとしてBranded Types (ブランド型) があります。典型的な実装としては以下のようになるでしょう。万が一 UserIdBlogId を取り違えても、型検査でエラーになるので実行前に気づくことができます。

declare const brand: unique symbol;

type Branded<T, Brand extends string> = T & {[brand]: Brand};

type UserId = Branded<string, 'UserId'>;
type BlogId = Branded<string, 'BlogId'>;

function getUser(id: UserId) {
  // ...
}
getUser('blog' as BlogId); // コンパイルエラー

今年のTSKaigiの発表を俯瞰してみると、Branded Typesをテーマとした発表が多かったような印象があります。個人的には、Branded Typesは知っているテクニックだったので新しさを見いだしづらいと思っていたけど、これは想定聴衆と自分がずれているということかもしれません。あとは「Branded Types + 活用事例」という構成だと、活用事例のほうだけ知りたいというのもあったかも。

クラス・デコレータ

クラスの継承を型注釈で表明する、みたいなことは誰か検討しなかったのかな? たぶん検討されているような気がする。

class Klass {}

// 注意: このプログラムは構文エラーになると思う
function takeKlassInstance(x: instanceof Klass) {
  // ...
}

function キーワードを使って関数を定義すると this が変わることについて、暗黙の第1引数が文脈に応じて渡されていると考えると急に腑に落ちました。とはいえ難しいのでできるだけ this に依存したコードを書くのを回避したいところでもありそう。

デコレータについて、使うと嬉しい場面があることは理解したけど、個人的にはだいたいなんでも関数で済ませるようになっているので、関数でよくない? と思っていました。後追いで標準化するにあたってエコシステムとのギャップが生じているのがむずかしそう。

いつテストを書くか

docs.google.com

lacolacoさんのトークが一番よかったというか、「TypeScriptに関連する発表」という型にとらわれず、考える切り口を示唆してくれるのがよかったと思います。これはTypeScriptにとどまらず「ソフトウェア」を書きたいなら一度発表資料を読んでみるべきです。

「TypeScriptコミュニティ」という括りについて思うこと

この件についてちょっと考えていたけど、TypeScriptを含めたJavaScript/Node.js/Webフロントエンド全般に関するコミュニティ*2が既に身近にあって、「TypeScriptコミュニティ」を分けて語るモチベーションに自分が共感できていないのだろう、と思うことにしました。すでにTypeScriptの言語機能や型の話がもう行われているし、コンパイラの内部実装や型の話を突き詰めることは誰も妨げないでしょう。TypeScriptに関する発見や困ったことがあれば近くの会でまとめて話したり、話を聞きに行ったりする、ぐらいでゆるくやっています。

話題をTypeScriptについて絞ることの作用というかトレードオフがたぶんあって、よりdeep diveした話題が増えるとか、似たようなテーマで切り口を工夫するみたいな感じになるのか、いろいろありそうな気はします。話題が広がりやすい形になっているといいな、と思います。

現状あんまりいい日本語が思いついていないので、気になる方は飲み会とかで自分を捕まえて聞き出してください。

プロポーザル

「フルスタックTypeScriptをチームとして開発できるようにやったこと」についてのプロポーザルを提出したのですが、落選しました。


運営の皆様、登壇者の皆様、今回のTSKaigiで話してくださった皆様ありがとうございました。

思い出したら追記します。

時間のないサイト運営者リング