iOSDC Japan 2023 day2でLT登壇します #iosdc

すでに始まってしまったiOSDC Japan 2023。今年ははてな勢は4人でお邪魔しております。自分も2019年以来、4年振りのオフライン参加をとても楽しんでいます!

X(Twitter)ではお知らせしていたものの、ブログには書いていなかったので改めて共有ですが、最終日の明日9/3 (日) day2 ラストのLT大会にて登壇させていただきます!(最後から4番目)

fortee.jp

Renovate芸人・またiOSDC JapanのLT芸人として頑張りたいと思います⚡️

カンファレンスの最後までワイワイ楽しみましょう!😆

UISearchBarのデフォルトの背景色は `UIColor.tertiarySystemFill`

Use system fill colors for items situated on top of an existing background color. System fill colors incorporate transparency to allow the background color to show through.

Use this color to fill large shapes, such as input fields, search bars, or buttons.

と説明されているとおりだった。

ライトでの cgColor の出力はこんな感じ:

<CGColor 0x60000260e1c0> [<CGColorSpace 0x60000260cf00> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1; extended range)] ( 0.462745 0.462745 0.501961 0.12 )

XcodeGenのpackagesをRenovateで更新するプリセットを用意しました

ikesyo.hatenablog.com

この時の発表でXcodeGenの packagesをRenovateのregex managerで更新する方法を紹介してから、複数回言及・参照されることがありました。

これはこの方法を一般化しておいた方がよかろう、と思って hatena/renovate-config にXcodeGen用のプリセットを用意してみました。XcodeGenxcodegen ディレクトリにファイルを分割しているケースにも対応しています。

fileMatch が合致する場合はこのプリセットをextendsするだけで活用できるので、どうぞご利用ください。

{
  "extends": ["github>hatena/renovate-config:xcodegenSwiftPackages.json5"]
}

あわせて読みたい

Mojoのパラメータ(コンパイル時メタプログラミング)を使ったFizzBuzzを書いてみる

Mojoが発表されて早速盛り上がっていますね。

LLVMやSwiftの作者であるChris Lattner率いるチームが開発した新しい言語ということで、Swift好きな自分としてももちろん興味があります。現時点ではPlaygroundにアクセスするのにウェイトリストに並ぶ必要がありますが、自分もアクセスできるようになったので早速ちょっと遊んでみます。

Mojoにはコンパイルメタプログラミングを可能とするパラメータという機能があって、これを遊ぶにのにFizzBuzzを書いてみましょう。

早速コードを書いてみるとこんな感じです:

fn parameterizedFizzBuzz[n: Int, counter: Int]():
    @parameter
    if counter > n:
        return
    else:
        if counter % 15 == 0:
            print("FizzBuzz")
        elif counter % 3 == 0:
            print("Fizz")
        elif counter % 5 == 0:
            print("Buzz")
        else:
            print(counter)
        parameterizedFizzBuzz[n, counter + 1]()

通常の引数(argument)は () に引数リストを定義しますが、パラメータ(parameter)は [] に定義します。また @parameter if を使うことで、コンパイル時にif文を実行することができます。

This makes use of the @parameter if feature, which is an if statement that runs at compile time. It requires that its condition be a valid parameter expression, and ensures that only the live branch of the if is compiled into the program.

これを実際に実行してみると正しく動作します。

parameterizedFizzBuzz[60, 1]()

ちなみに2023年5月7日現在のPlayground環境のトップレベルコードとしては60回が限界でした。61にすると elaborator expansion is 129 levels deep - infinite recursion? というエラーになりました。

error: Expression [6]:21:1: no viable expansions found
fn __lldb_expr__(__mojo_repl_arg&: __mojo_repl_context__):
^

Expression [6]:23:28:   call expansion failed - no concrete specializations
    __mojo_repl_expr_impl__(__mojo_repl_arg)
                           ^

Expression [6]:34:26:     call expansion failed - no concrete specializations
  __mojo_repl_expr_body__()
                         ^

Expression [6]:31:33:       call expansion failed - no concrete specializations
    parameterizedFizzBuzz[61, 1]()
                                ^

Expression [6]:18:46:         call expansion failed - no concrete specializations
        parameterizedFizzBuzz[n, counter + 1]()
                                             ^

Expression [6]:5:1:                                                                                                                                 elaborator expansion is 129 levels deep - infinite recursion?
fn parameterizedFizzBuzz[n: Int, counter: Int]():
^

この elaborator というのは、ロードマップの「Protocols / Traits」というセクションで少し触れられていました。コンパイルのパイプライン中でパラメータ化されたコードをインスタンス化する役割のようですね。ここの再帰に制限があるのは納得できます。

Unlike C++, Mojo does not “instantiate templates” in its parser. Instead, it has a separate phase that works later in the compilation pipeline (the “Elaborator”) that instantiates parametric code, which is aware of autotuning and caching. This means that the parser has to perform full type checking and IR generation without instantiating algorithms.

パラメータではなく引数を使った場合は1万回や10万回でも問題なかったので、普通に末尾再帰最適化もされているということでいいのかな:

fn fizzBuzz(n: Int, counter: Int):
    if counter > n:
        return
    else:
        if counter % 15 == 0:
            print("FizzBuzz")
        elif counter % 3 == 0:
            print("Fizz")
        elif counter % 5 == 0:
            print("Buzz")
        else:
            print(counter)
        fizzBuzz(n, counter + 1)

ということでMojoのパラメータで少し遊んでみました。

Mojoは現時点ではPlaygroundが公開されているだけで、手元で動かすこともできませんし、オープンソースでもありませんが、すでにGitHubリポジトリは用意されてバグレポートやフィーチャーリクエストも受け付けています。

正式リリースまではクローズドな形で開発が進み、OSS化まではしばらく時間が掛かりそうではありますが、ロードマップやChangelogもチェックしながら今後を楽しみにしていきたいですね。

なぜ人はソースコードの複数行コメントで文が分かれているのに、改行を文区切りとしてしまって句点(。)を使わないのか?

改行されているときに、文が終わっている時もあれば、文が続いている時もあって、これは前の行からの続きなのか、別の文なのかを毎回意識する必要があって読みにくいなと思ってしまうパーソンです。皆さんはいかがですか?

YAPC::Kyoto 2023に参加した&GitHub Sponsorsに登録した

yapcjapan.org

京都、それも自宅から徒歩圏の会場で開催されるということもあり、去る3月19日に初めてYAPCに参加してきました。自分はPerlは(ほぼ)書いたことがないけど、YAPCPerlに関係あるものないものも何でもありなカンファレンスという感じがして、お祭り感があってよかった。

聞いたトークはどれも面白かったんですが、OSS好きパーソンな自分としては

の3つが響いてよかった。その勢いもあって、前から長らくやろうと思ってやっていなかったGitHub Sponsorsの登録作業についに手を付けました。

ここ半年以上はOSS活動がほとんど止まっていたけれど、これを機にまたいい感じにやっていきたいところ。宣伝になりますが、ikesyoのOSS活動を応援してくれる方がいればよろしくお願いします 🙏


閑話休題。ラストの id:onishi さんによるキーノートもとてもエモくてよかったですね……大西さんの元で自分もまだまだはてなで頑張っていこう。

オフラインでのカンファレンス参加自体がiOSDC Japan 2019以来だったのでとても久しぶりな感覚で、楽しさを思い出しました。次回のYAPCは広島で開催予定らしいので、次も遊びに行きたくなってきました。今度は何かしら喋れると楽しそうでもあり、CfPも出してみたい。

スピーカー・スタッフ・スポンサー・参加者・その他関係者の方々、楽しませてもらってありがとうございました!

『Production Ready GraphQL』の「Anemic GraphQL」

この記事は はてなエンジニア Advent Calendar 2022 の2023年1月3日の記事です。

昨日は id:onkストーリー性のあるプレゼン - id:onk のはてなブログ でした。


『Production Ready GraphQL』といえば Production Ready GraphQLはGraphQLを採用するなら必ず読んでおきたい良書 などでも紹介されていますが、GraphQL APIを設計・開発・利用するなら大変参考になる良書ですね。はてな社内でもGraphQL開発における前提本ですね、などと言われている模様です。

この中で好きな箇所の1つが「Specific or Generic」という節の「Anemic GraphQL」という項です。たくさん引用するわけにもいかないので冒頭だけ見ますと、

Anemic GraphQL is something I stole from the Anemic Domain Model, a pattern popularized by the great Martin Fowler. Anemic GraphQL means designing the schemas purely as dumb bags of data rather than designing them thinking of actions, use cases, or functionality. This is best expressed by an example:

type Discount {
  amount: Money!
}
type Product {
  price: Money!
  discounts: [Discount!]!
}

というスキーマ・型について、割引や税額を考慮した合計金額を表示することを考えるという例です。クライアントサイドで price から discounts の合計額を引いて totalPrice を算出することはできるが、Producttaxes が増えたりすると誤った計算になってしまうのでクライアントサイドの計算コードの修正が必要になる。クライアントサイドで合計金額を表示するというユースケースに応じて ProducttotalPrice フィールドを追加しようね、というような話。

type Product {
   price: Money!
   discounts: [Discount!]!
   taxes: Money!
   totalPrice: Money!
 }

こうすると Product のフィールドが増えたり合計金額の計算ロジックが変わっても、totalPrice フィールドを表示していれば良いままで変更に強くなる。

かのマーティン・ファウラー氏が命名したドメインモデル貧血症を引き合いに出していますが、GraphQLのスキーマ・型も単なるデータの入れ物に留まらず、ドメインユースケースに応じたフィールドを設計・公開するのが望ましいですねというお話でした。


メルペイのvvakameさんによる次の記事でも、同じような(と id:ikesyo が認識している)ことが言及されています。

engineering.mercari.com

Componentを構成するとき、1つのUI要素を表示するために複数のGraphQLのフィールドを組み合わせる必要がある場合、その処理をバックエンド側で肩代わりするcomputed fieldを導入することを検討してください。
たとえばメルカリの出品物に何個のコメントがついているかのバッジをUIに表示したいとします。しかしGraphQLスキーマにコメント数のフィールドが存在しない場合、クライアント側でコメントを全件取得し、その数をカウントするコードを書く必要があります。これは不便です。
クライアント側は不要なフィールドの取得をして、面倒なページングの処理も必要になります。バックエンド側もいくつかの不便を強いられます。個別のキャッシュが難しくなること、UIに対して過剰な本来必要な分以上のデータが取得されてしまうこと、これによりbackend主導の改善がより困難になることです。
バックエンド側はGraphQLのフィールドが要求されていることはわかっても、それがUI上でどのように使われているかは分析できません。どういったcomputed fieldが必要であるかは、基本的にはクライアント側のリクエストベースで検討されるべきです。
computed fieldを定義することをためらわないこと。どういうcomputed fieldを作るべきかは、バックエンドエンジニアが考えることは難しいでしょう。だから、クライアントエンジニアから相談があった場合、快く応じましょう。それが複雑な計算が要求されるとしても、必要ならばやるべきです。
なぜならば、バックエンド側が実装を拒んだ場合、単にクライアント側で同等の処理が実装されるだけだからです。しかも、それがどんなに重たい処理であろうとも、たった1つのUIを表示するために大量のリソースを消費するものだとしても、それを最適化する余地はあなたの手から離れてしまいます。computed fieldとして実装してあげれば、その処理が重かったらmemcachedに突っ込むなりなんなり、あなたが自由にチューニングすることができます。

こちらの記事もとても参考になる・自分達でも実践していることが多く、おすすめです。

あわせて読みたい

developer.hatenastaff.com