技術と飯とYAPC -- YAPC::Hakodate 2024 参戦記
着弾
コンフォートホテル函館というホテルに泊まりました!
なんと朝食つきです!
本編
U25支援企画
@notch_man さんが登壇するということで参加しました! Smartyのアップデートの話、噂には聞いていたけれど改めて聞くとすごいなあとなりました。
プロファイラ開発者と見る「推測するな、計測せよ」
バイナリを吐き出すプログラムではなくインタープリタを計測しようとすると、直接バイナリから計測できなくなるので大変そうだなと感じました。
以下めも。
計測を使って仮説検証する。
- 意識して理解すること
- 意識しているが理解していないこと
- 意識も理解もしていないこと
計測をするために重要なこと。
- ある層のことを精緻に知るためにより下の層に潜っていく。
- アプリケーション層の計測にはOSなどのより低層を見る必要がある
実世界におけるパフォーマンス改善
- 「なんか遅い」を主観で判断するお基準がブレる。システムの重要なパフォーマンスのレベルを定義する。
- 常に計測していることに価値がある -> NewRelic, DataDogなど
普通の Web エンジニアのための様相論理入門
CTLをテストフレームワークとして使う話。テストがしにくいなと思っていたものでもグラフの構造を使ってテストできるのであれば、やってみたいなと思いました。コンピュータサイエンスにおける様相論理という本を買ってみようと思います。
フロントエンドの現在地とこれから
フロントエンド書かないので、学習になった。Suspenseとか全然追えてないので勉強したい。
WebTransportは未来の技術?
HOL問題を解決する話とWebTransportの話
Web TransportはWeb Socketより性能が良いが、仕様が固まっていなかったり、一部で対応していないブラウザがあるとのことでした。Go用のライブラリ作成が卒業研究なので2月にはできるらしい。めでたい。
クレジットカードを製造する技術
クレジットカードの技術仕様やICチップの話。全然知らない世界で面白かった!秘密鍵の輸送方法が個人的に面白くて、分割して物理輸送するそうです。
Key Note
「アルバイトが書いたコードをバイトと呼びます」「入社日にKDDIに買収される」「ドキュメントのポインタでたらい回しにされて最初に戻る」「"""某社を経て"""」
磨かれた個人技による貢献、できるようになりてぇなあとなりました。
再発明から学習するの、本当にわかる...
表彰式
友人のうーたんさんが受賞されていました!おめでとうございます!!!
ごはん
HAKODATE BEER
@notch_man さん、 @live_in_2107 さんとビールを飲みに行きました!
https://www.hakodate-factory.com/beer/
チーズの揚げ物
まとめ
今年もYAPC最高でした!!! スタッフの皆さん、スポンサーの皆さん、スピーカーの皆さん、ありがとうございました!!!
カンファレンスに行ってきました
1日目は、仕事などの兼ね合いであまりいられなかったので2日目がメインでした。簡単な振り返り記事です。
Speech
Unlocking Python's Core Magic
Pythonに生えているAPIの力を引き出すためのテクニックをミニマムなコードとOSS(Airflow)のコードで解説する興味深い内容でした。
Slideは掲載されていないですが、気になった方は下記の内容を調べると良いと思います!
- dunder method
- getattr and PEP 562
- decorators
- functool.partial
- Iterable Protocol
Rustを活用したPythonライブラリの開発
maturinを使うといいぞ、ただ、パフォーマンスはCythonほどではないぞという内容だった。僕含め、会場にRustceanが多かったのとパフォーマンス計測がCythonに有利に見えたので、ちょっと質問時間ヒヤヒヤしてた。
個人的にはRustで書いたときに、Pythonのstubファイルが生成されるかと、Pythonの実行時の型チェックを自動で書いてくれる仕組みがないか気になった。質問した感じ、前者はあるが、後者はなさそう。
The Wheelhouse of Horrors
Pythonのwheel fileの話。普段あまり聞かないトピックだったので面白かった。so fileが別のso fileを参照して辛いので、外部ライブラリの依存を最小限に、必要ならwheelの中にという話が、他のシステムでもありそうだよなと思った。
Why Knowing Cython Helps in Understanding Python: A Deep Dive into Cython & PVM
Cythonの話を聞いた。速いという話はよく聞くけど、理由は知らなかったので勉強になった。コンパイル(.c
-> .o
-> .so
)とGILの脱出を見てそりゃCythonの実行速度が速くなる訳だ、と納得した。
Keynote
stringオブジェクトにメソッドがない時代があったなど、歴史の話が面白かった。
インタープリタが循環参照で死ぬという話もありました。数日前にRustのgenerics周りのcompilerの話を聞いていた身からすると、まあそうなるよねというお気持ちに..
作るの大変そうなソフトウェアでバグが出るのは定めなのかもしれないですね。
Booth
Stockmarkさんのブースで公開されていたナレッジグラフのRAGのdemoが凄すぎて、ナレッジグラフの本をすぐに読もうと思いました。見た感じ画像からRDF(Resource Description Framework) っぽいグラフを生成していたけれど、ソースが画像でなくとも動きそうな気がした。
JetBrainsさんのトートバッグおしゃれすぎて、早速導入しました。ほとんどのIDE触っていて、お世話になっているので身につけられるの嬉しすぎる。
CLI with Rustで幸せになる話、タイムゾーンを添えて
自己紹介
@shunsock
: GitHub・X- ファインディ株式会社 データソリューションチーム所属
- Rust歴、1年行かないくらい。趣味で書きまくって覚えた。
平和な日本の時間事情
日本はいいぞ、なんてったって、単一のタイムゾーンで夏時間が存在しないからね!
- 単一のタイムゾーン: 日本全土が同じ標準時(JST)を使用しており、国内に時差はない。
- 夏時間の不採用: 現在、日本ではサマータイム(夏時間)は採用されていない。そのため、年間を通じて時刻の変更はない。
※ 日本標準時(JST)は、日本国内で公式に採用されている標準時であり、協定世界時(UTC)に9時間を加えた時間。
UTCの魔の手がやってきた!!!
あるCloudサービスを使っていたときに、「時刻設定はUTCで指定できる」と言われた。
える、知っているか、UTCで指定できるということは、JSTでは指定できないんだよ。
手軽かつ安全にTimezoneの変換したい
Timezoneを使う機会はだいたい、コードの実行するタイミングを指定するタイミングでやってくる。時間を間違えると、他の更新日時の関係で動かないコードがあれば、大惨事に。
設定ミスはしたくないが、いちいちブラウザを開くのも面倒。
SystemのPythonに変更を加えたくない
「CLIで手軽に使えるものはないか」とChatGPTに聞いたら、Python製のソフトウェアをお勧めされた。しかし、globalでpipを使うと月日とともに崩壊する未来があるので、systemのPythonはできれば使いたくない。
とはいえ、それ以外の方法で作ると過剰な気がする。
※ Pythonにご飯を食べさせてもらっている立場なので文句を言うつもりはない
補足: Pythonの開発環境が年々良くなっている話(余ったら話す)
少し前まで、Docker経由が前提となりがちだったPythonだが、uv
によるプロジェクトの一元管理が可能になったため、docker経由しなくても十分開発できるようになりつつある。
uvとは、次世代Python Package Managerのryeの作者であるMitsuhiko氏とastral社がタッグを組んで開発をしているPython Projectのマネージャー。RustのCargoに近い。
uvでは以下の要素を管理できる。
- Python Interpreter (Pythonを解釈・実行するバイナリ)
- Python Module (⇒ RustのCrate)
- Pythonの周辺ツール ( ⇒ RustのClippy, rustfmtなど)
CLIはできればワンバイナリで入って欲しい
汎用言語のインタープリターが強力な手段であることは、理解しつつも、仕事で使うツールであれば、持ち運びのしやすいバイナリ形式、特に単一のバイナリ形式で欲しい。(雑に usr/local/bin/
に突っ込んだら動いてほしい)
せっかくなので作ることに。
補足: インタープリタ型言語の実行方法
Pythonなどのインタープリタ型の言語(特にASTを利用する言語)の場合、以下のような工程をインタープリタというソフトウェアが行い実行される。
- 字句解析
- 構文解析
- (オペコード変換)
- 実行・評価
※ 実際には、JITコンパイル(Just In Compile)というプログラムの実行時にバイトコードや中間言語をネイティブコード(機械語)に動的にコンパイル(変換)する技術があり、JavaScriptやRubyなど主要言語で採用されている。 ※ オペコードを生成しなくても動かすことは可能、例えば、ASTを巡回しながら順番に実行する方法がある。
Rustを採用した背景
Rustを使った背景としては以下の要素がある。
- 個人開発
- 単一のバイナリで動かせる
- CLIパーサーが使いやすい
- 自分が使える
個人開発
自分用のツールだったので、Rustを導入しやすかった。
チームで使うツールであれば、(よほどの理由がない限り)チームメイトが慣れている言語や、それに近い言語を選定する傾向があるので、ここは大きいと思う。
単一のバイナリで動かせる
持ち運びが便利なツールが欲しくて、自作した。つまり、簡単に使える単一バイナリのツールが欲しかった。
使いやすい入力パーサーがある
Rustには、使いやすいCLIのユーザー入力のパーサーがあり、開発がしやすい。
use clap::Parser; /// Simple program to greet a person #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { /// Name of the person to greet #[arg(short, long)] name: String, /// Number of times to greet #[arg(short, long, default_value_t = 1)] count: u8, } fn main() { let args = Args::parse(); for _ in 0..args.count { println!("Hello {}!", args.name); } }
https://github.com/clap-rs/clap
この件以来、RustでCLIを作ることが多くなったので、どう使うのがいいか色々検証している。最近の趣味のプロジェクト(AST巡回型言語)だと以下のようなReceiverを作ってwrapしている。
※ これが最適という主張ではないので注意
use clap::{Arg, ArgAction, ArgMatches, Command}; pub(super) struct Receiver {} impl Receiver { pub(super) fn new() -> Self { Receiver {} } pub(super) fn receive(&self) -> ArgMatches { let command: Command = Receiver::load_command_settings(); command.get_matches() } fn load_command_settings() -> Command { Command::new("rast") .about("AST Based Language") .author("shunsock") .version("0.1.0") .arg(Arg::new("expression").required(false).short('e')) .arg(Arg::new("file").required(false).short('f')) .arg( Arg::new("debug") .short('d') .long("debug") .action(ArgAction::SetTrue) .help("Enable debug mode"), ) } }
自分が使える
すぐに使いたかったので、普段書いている言語から選んだ。選択肢は3つで、気分で決めた。
- Golang
- 趣味で書いている。楽しい。
- 最近
http/template
でPolars Cheat Sheetのpdfの再実装をした。
- CSharp
- 趣味で書いている。楽しい。
dotnet publish
でバイナリ吐ける。
- Rust
- 趣味で書いている。楽しい。
- Rust Rover最高
開発秘話的な
サマータイム(DST: Daylight Saving Time)
GitHubでPublicに公開した後、RedditというSNSで意見を募った。インストールしてくれた人がいて、「コーナーケースでバグあったよ」と教えてくれた。
サマータイム(夏時間)とは、夏季の一定期間に時計を通常の標準時より1時間進める制度です。これにより、夕方の明るい時間を有効活用し、エネルギーの節約や生活の質の向上を図ることが目的とされています。
サマータイムの開始時(春)
- 時計を1時間進める: サマータイムが始まる日には、標準時よりも時計を1時間早める。たとえば、午前2時を午前3時に変更する。
- 調整のタイミング: 多くの国では、サマータイムの開始は3月の最終日曜日の深夜に行われる。
サマータイムの終了時(秋)
- 時計を1時間戻す: サマータイムが終了する日には、時計を1時間遅らせて標準時に戻す。たとえば、午前3時を午前2時に変更する。
- 調整のタイミング: 多くの国では、サマータイムの終了は10月または11月の最終日曜日の深夜に行われる。
サマータイム開始時(春に時計を1時間進めるとき)
- 存在しない時刻が発生。
- 例: 午前2時から午前3時に時計を1時間進める場合、午前2時から午前2時59分までの時刻が存在しなくなります。
- この間に予定されていたイベントや交通機関の時刻は、一時間飛ばされることになります。
サマータイム終了時(秋に時計を1時間戻すとき)
- 二回存在する時刻が発生。
- 例: 午前2時に時計を1時間戻す場合、午前1時から午前1時59分までの時刻が二度繰り返される。
- この時間帯に起きた出来事は、どちらの「午前1時」だったのかを明確にする必要がある。
サマータイム対応
タイムゾーンの変換は、ChronoとChrono Tzというライブラリを使っている。このライブラリでは、タイムゾーンの変換が一対一でないことを前提に、 Single
, Ambiguous
, None
という計算結果の値を用意している。
Timezone Translatorでもこの実装を採用し、エラーハンドリングを行っている
pub(crate) fn convert(&self) -> Result<DateTime<Tz>, TranslationError> { // Extract the time from the `time` field with `from_tz` field let mapped: MappedLocalTime<DateTime<Tz>> = self.from_tz.from_local_datetime(&self.time); match mapped { LocalResult::Single(time) => Ok(time.with_timezone(&self.to_tz)), LocalResult::Ambiguous(time_earliest, time_latest) => { Ok(select_time_with_ambiguous_time_strategy( self.ambiguous_time_strategy, self.to_tz, time_earliest, time_latest, )) } LocalResult::None => { let error = TranslationError::TranslationError { time: self.time, from_tz: self.from_tz, to_tz: self.to_tz, }; Err(error) } } }
タイムゾーンが入力されていないときの挙動
タイムゾーンの入力がめんどくさい人むけに時間の自動入力をサポートしています。主に自分。
pub(crate) fn command_provider() -> Command { let now: String = provide_local_timezone_string(); let now_str: &'static str = Box::leak(now.into_boxed_str()); Command::new("tzt - Timezone Translator") .version("0.3.0") .author("shunsock") .about("translate time from one timezone to another") .arg(time()) .arg(from(now_str)) .arg(to(now_str)) .arg(ambiguous_time_strategy()) }
変数を渡すことでデフォルトの値を設定している。
use clap::Arg; pub(crate) fn from(timezone: &'static str) -> Arg { Arg::new("from_timezone") .short('f') .long("from") .value_name("FROM_TIMEZONE") .help("The original timezone (e.g. America/New_York) @see https://docs.rs/chrono-tz/latest/chrono_tz/enum.Tz.html") .required(false) .default_value(timezone) }
実は、この機能気合いでやっていたりします。環境変数を見たり、システムのタイムゾーンのファイルを見に行ったりします。
pub(crate) fn provide_local_timezone_string() -> String { // read environment variable TZ let env_var_tz: Option<String> = EnvironmentVariableTzProvider::new(None).get_env_var_tz(); if let Some(env_var_tz) = env_var_tz { return env_var_tz; } // read /etc/localtime let tz_from_etc_localtime: Option<String> = get_system_timezone_from_etc_localtime(); if let Some(tz_from_etc_localtime) = tz_from_etc_localtime { return tz_from_etc_localtime; } // read /etc/timezone let tz_from_etc_timezone: Option<String> = get_system_timezone_from_etc_timezone(); if let Some(tz_from_etc_timezone) = tz_from_etc_timezone { return tz_from_etc_timezone; } let error_message = "System Timezone Not Found: Could not find local timezone. Please set TZ environment variable. "; panic!("{}", error_message); }
まとめ
参考にしている本とか
Rustのプログラミングは公式ドキュメントとこの本で学びました。
途中でプログラミング言語の話が出てきたので、こちらも載せておきます。
JavaとCですが、参考になると思います。
日曜大工で、インタープリタ作っている話
私は趣味でCLI職人をしているのですが、ある日気づきました。 「業務上、標準関数を自分でいじれるプログラミング言語、一つぐらいあったほうが便利じゃね?」
で、作りました。
let main: fn = (x: string): None { let message: string = "Hello, " + x; print(message); return none; }; main("Shunsock");
print関数も開発段階なので、まだ型名が出ています。味があっていいですね。
shunsuke.tsuchiya in ~/Hobby/rast on function λ rast -f tests/data/function.rast Str("Hello, Shunsock")
言語開発をしようとするとデバッグが面倒になってくるので、デバッグオプションを差し込んでいます。
shunsuke.tsuchiya in ~/Hobby/rast on function λ rast -f tests/data/function.rast -d Scanned Tokens: Let Identifier("main") Colon Fn Equal LeftParen Identifier("x") Colon StringType RightParen Colon NoneType LeftBrace Let Identifier("message") Colon StringType Equal StringLiteral("Hello, World!") Plus Identifier("x") Semicolon Print LeftParen Identifier("message") RightParen Semicolon Return NoneLiteral Semicolon RightBrace Semicolon Identifier("main") LeftParen StringLiteral("shunsock") RightParen Semicolon Eof AST is created: AST[0]: Statement(FunctionDeclaration(FunctionDeclarationNode { name: "main", params: [("x", "string")], return_type: "none", body: [Statement(VariableDeclaration(VariableDeclarationNode { name: "message", var_type: "string", value: BinaryOperation(BinaryOperationNode { left: Literal(LiteralNode { value: String("Hello, World!") }), operator: "+", right: Identifier(IdentifierNode { name: "x" }) }) })), Statement(Print(PrintNode { expression: Identifier(IdentifierNode { name: "message" }) })), Statement(Return(Literal(LiteralNode { value: None })))] })) AST[1]: Expression(FunctionCall(FunctionCallNode { name: "main", arguments: [Literal(LiteralNode { value: String("shunsock") })] })) Str("Hello, World!shunsock")
clapは優秀なAPIを提供してくれるのでこれだけでパースができます。(自分のCLIでは実際には複数ファイルでこれを書いている、詳細は下の方みてくれ)
let cmd = Command::new("rast") .about("AST Based Language") .author("shunsock") .version("0.1.0") .arg(Arg::new("expression").required(false).short('e')) .arg(Arg::new("file").required(false).short('f')) .arg( Arg::new("debug") .short('d') .long("debug") .action(ArgAction::SetTrue) .help("Enable debug mode"), ); let matches = cmd.get_matches(); let debug_mode: bool = matches.get_flag("debug");
明後日に同じような話をする予定ですが、ディレクトリはこんな感じに切るのがおすすめです。
src ├── main.rs # 本体 ├── rast_error.rs ├── receiver.rs # Clapのユーザー入力の実体を送ってくれるやつ ├── source_code_loader.rs # サービス ├── virtual_machine # サービス │ ├── ast.rs │ ├── evaluator │ │ ├── evaluation_error.rs │ │ ├── function_table.rs │ │ ├── symbol_table.rs │ │ └── value.rs │ ├── evaluator.rs │ ├── parser │ │ └── parser_error.rs │ ├── parser.rs │ ├── scanner │ │ └── scanner_error.rs │ ├── scanner.rs │ └── token.rs └── virtual_machine.rs 5 directories, 16 files
ここはウェブ開発とかだとModel, Router, Controller, Service, Repositoryなどで一段階切っちゃうパターンが多いかもですね。(どれを使うかは人や組織によるだろう) 今回の場合、ソースコードをOnelinerで入力するパターンとプログラムから読み込ませるパターンにデバッグオプションが付くだけで最終的にはどれもvirtual_machineというプログラムに行き着くため、サービス直置きって感じです。
とはいえ、インタープリタをそのまま一つのファイルに書くと世界が崩壊するので、feature in featureしています。具体的にはScannerとParserとEvaluatorです。それぞれ500 line - 1000 line位はあるので、もっと分割した方が良い説が濃厚かなという気持ちです。次の実装するとしたら標準関数なのですが、Evaluatorが相当膨れているので、その前にリファクタリング作業に追われることになるでしょう。今見るとASTはvirtual_machine直置きで、tokenがscannerなの謎ですね。Parserでも呼ばれるので場所変えようかな。
公開は当分先です。まだ、開発中ってことで。
ではまた。
転職しました
概要
8月16日付けでファインディ株式会社に入社しました。現職では、データエンジニアとして、CTO室データソリューションチームに所属いたします。
転職情報
期間
4月の半ばぐらいに転職の意思決定をして、5月末に終了しました。
軸
データに関わるエンジニアとして、チームで働くことのできる企業に行きたいと考えていました。また、組織横断的なチームに所属したいと考えていました。
軸ではない
逆に、下記の事項は気にしませんでした。
- 技術スタック: 好きな技術は「勝手に触ってしまう」ので気にしてもなあ
- 年収アップ: Roleが上がるタイミングではないので、気にしていませんでした。
経緯
いわゆるリファラルです。
- 技術系カンファレンスでCTO&DevRelチームとお好み焼きを食べた
- 友人が新卒で入社
- 転職しようかなと考えていた矢先、ファインディがデータエンジニアの募集をしていることを知り、友人に連絡
- カジュアル面談、選考
- 内定承諾
面接対策
Xで話題になっていたので、読みました。聞かれた部分は読む前から知っていたので、就活に寄与したかは謎。全く関係ないけれど、友人のソフトウェアの相談で、中盤から後半あたりに載っていた方法が役にたちました。趣味で読んでも面白いんじゃないかなと思います。
アレックス・シュウ."システム設計の面接試験".2023.ソシム
変わることと変わらないこと
所属は変わりますが、エンジニアとしてこれからも働きます。技術イベントが好きなので、変わらず、登壇・聴講に行く予定です。
ファインディはRubyでアプリケーションが動いているので、Rubyのイベントにも行ってみようかなーと思っています。
これからもよろしくお願いします!
自己紹介
かなり暗い話です.事実をまとめます.
4歳からクラシック音楽を子供の頃から学んでいたのですが,技術力が足りず,高二の秋で諦めました.最後の方は精神を病んでしまい,脅迫性障害になっていました.最近落ち着いたのですが,通院を6-7年しています.ちなみに,進学は現役で藝大行くことが条件でした.浪人前提だと思われがちですが,藝大は3割ぐらいが現役で入れます.
中高生の頃に染み付いてしまった夜型のせいで,家族と生活リズムがあまりにも合わず,互いに辛かったので,一人暮らしをしています.ただ,子供の頃に受けた恩を少しずつでも返したいなと思っていて,実家に差し入れ持って行ったりご飯誘ったりしています.この行動にはここ数年で,世話になった方が次々に亡くなってショックを受けた背景があります.
プログラミングはB2の時に始めました.マーケティング学科という名前の統計応用学科だったからか,理工系の教授に師事することになり,科学計算をするために学びました.子曰く,「プログラミングと大学数学は独学で学べ」らしいです.なので,プレゼンの論理の穴を突かれたことはあっても,何かを習ったという経験は実はなかったりします.
師というか,大学では特に以下のことを叩き込まれました.今思えば,貴重な学びですね.
- 何かを主張することは,精緻な理論を用いても極めて難しい.
- 自分の知識や技術は極めて限定的で,不明瞭なことや知らない分野に対してはわかりませんと答えることが誠実である.
- 反論に根拠を持って反駁できないのにも関わらず,自説の誤りを認めない人間は,専門的な議論ができないと解釈する.
数学も,※ 高校まではパズルみたいでつまらないなーと思っていたのですが,大学で基礎に触れて面白いなと思うようになり,本を漁るようになりました.分野で言うと統計的言語モデルに興味があります.最近は,数理力を上げるために「現代数理統計学の基礎」という本を周回演習しています.
※ 主観です.同様の理由で競プロなども続かなかったです …
いわゆるWebのプログラミングを始めたのは,Pythonを使って生活をハックしようと思ったことがきっかけでした.当時コロナ禍で,リアル麻雀が打てなくなったのでライブ感がなく寂しく感じていました.そこでライングループ用にWebhookを開発したりしました.そこから,インターンに行ったり,ハッカソンに出るようになって今に至ります.
統計計算がメインだったので,Web系に来てからは知識面で苦労しましたが,カンファレンスや勉強会が毎月開催される環境だったので,比較的容易にキャッチアップできました.YAPC::Kyoto 2023がきっかけでオフラインの会に出るようになったので,誘っていただいたuzullaさんとcatatsuyさんに感謝しています.
業務・趣味ではCLIを開発していることが多いです.趣味だとRustを書いている時間が長くなりましたが,仕事にするつもりはあまりなかったりします.最近,Rubyを始めました.Sorbetという静的解析ツールが快適で今のところ楽しいです.
振り返ると,今も昔も何か作るのが好きだったんですね.子供の頃は親のWindows Vistaのプリントミュージックで作編曲に没頭していましたし,今も相変わらず土日家に引きこもってプログラムを書いています.これからも楽しく何かに没頭して過ごしたいものです.