cockscomblog?

cockscomb on hatena blog

WWDC24予言

今年も書いておく。

AI

今のAppleに最も期待されているトピックであり、Google I/OMicrosoft Buildなどで競合他社が最も力を入れているのがAI。当然WWDC24でも、「AI」という語が飛び交うことになる。

「AI」という語を使い始めたApple

Appleは2024年5月のスペシャルイベントでLogic Pro*1とFinal Cut Pro*2のアップデートを発表したが、そこでは機械学習を活用した機能を「AI機能」と宣伝するようになった。これは例えば、Apple Watchのダブルタップジェスチャーでは「機械学習アルゴリズム」と表現していた*3のと比べると、わかりやすい変化である。

とはいえWWDC24では、AIの中でもとりわけLLMを含む生成AIについて発表されるだろう。ここで、Appleのプライバシー重視の姿勢とどう折り合いをつけるかというのが一つの焦点になる。

オンデバイスクラウドの分担

現在の高性能なLLMを、iPhone/iPad/Macなどのオンデバイスでそのまま動作させるのは、Appleをもってしても不可能だろう。基本的には規模と性能は相関関係にある。もちろんフットプリントを小さくするテクニックはあるし、ハイエンドのMacである程度のものを動かすのは可能だろうが、ごく一部の環境でしか利用できないのでは仕方ない。必然的に高性能のLLMをクラウドで動作させ、オンデバイスでは小規模な言語モデルでも精度の出るタスクを行わせることになる。

例えば、端末内のデータを高性能になったSiriが読み取って応答する、というようなRAGを応用した機能を考える。このとき、端末内のデータのEmbeddingを計算するのはオンデバイスのマルチモーダルなモデルが行い、端末内のベクトル検索エンジンに保存する。高性能になったSiriはクラウド上で動作し、必要に応じて端末内のデータを検索する。検索で類似度の高いと判定されたデータは、端末内で要約され、その結果がクラウドで処理されてSiriの応答になる。

つまり端末内のモデルはEmbeddingの計算と要約タスクだけを行う。こうすると、クラウドに送信されるのは応答に必要な最小限のデータに絞られ、プライバシー上の問題が軽減される。Appleは例えiCloudでデータを同期していても、データをエンド・ツー・エンドで暗号化してクラウド上では復元できないように扱うことが多いため、データ自体をオンデバイスで扱うのが自然だ。

このくらいの仕組みでも、例えばSafariの履歴をSiriで参照するようなことができるだろう。Embeddingや要約は比較的小規模なモデルでも精度が保たれる。

SDKから見たAI

他にもAIに関連した新機能が出てくるものと思われるが、基本的にはオンデバイスで処理するものが多いのではないか。開発者向けのSDKという観点から見ると、オンデバイスで動作するAI機能は、SDKとして公開しやすい。一方でクラウド上で動作する大規模なAIは、現在のWeatherKitがそうであるように、別途何らかの課金体系が定められる可能性がある。

visionOS

もう一つ、WWDC24で重要なトピックになるのは、visionOSのアップデートだろう。この真新しいプラットフォームは、まだ実験的な位置付けというのが実情だ。

アメリカ国外での発売

現在はまだApple Vision Proが米国でしか販売されていないため、visionOSも国際化が行われていない。しかし今年後半には米国外での販売が開始されることが事前にアナウンスされているため、WWDC24ではvisionOSの国際化が発表されるだろう。それがvisionOS 2を待たなければならないのか、あるいはvisionOS 1.2なのか、まだはっきりとしない。

機能の拡充

visionOSは、iOS/iPadOSをベースとしているだろうことから、初期のリリースから一定の品質、機能を持っている。それでも、例えばアプリ一覧を並び替えることができないとか、未完成さを感じさせる部分もある。visionOS 2ではそういったギャップが埋められていくはずだ。

実用上は、ウインドウマネジメントの進歩があると便利だろうと思う。空間の中で自由にウインドウを置けるのは、今ある大きなメリットだが、実際にやってみると、もう少し何か制約がある方が便利だろうと想像される。例えば、ウインドウを物理的な壁にスナップできれば、現実世界との認知的な不協和が起きにくくなるだろうと思う。

空間WidgetKit

本質的に、visionOSの空間コンピューティングはあまり拡張現実的ではない。現実を拡張するというよりは、単に現実の空間内でコンピューティングしている状態だ。これはもう少し機能が拡充されてほしい。Apple Vision Proをわざわざ装着しているからこそ得られる現実の拡張が必要だと思う。例えばWidgetKitが追加されて、壁にウィジェットを貼り付けておけるとか、それくらいでもいい。Apple Vision Proを着けていれば壁が情報のダッシュボードになる、というのは拡張的だと思う。

空間コンピューティング対応アプリ

空間コンピューティングを活かしたアプリが増えるような仕組みがあってもよい。例えばAppleKeynoteは、visionOS専用のリハーサルモードを持っていて、まるで壇上にいるような体験ができる。これは空間コンピューティングを活かした好例だ。しかしKeynoteの他のPagesやNumbersは、まだiPad版が動作するだけだ。Logic ProやFinal Cut Proのようなツールは、空間を活かしたUIを想像しやすいと思う。SwiftUIに空間を活用したUIを作りやすくするような改善が行われるとおもしろいが、想像し難いところでもある。

空間MapKit

同じ理由で、MapKitは空間を活かすのに向いている。そもそも地図が表す現実の世界は立体的だし、MapKitも特に米国内のいくつかの都市では非常にリアルな立体地図で描写できる。

端末間での連携

また端末間の連携についても機能の向上を期待したい。すでにMacの画面のミラーリング機能を備えているが、これをiPadにも拡張したり、あるいはMacの画面であっても単なるミラーリングではなく、Apple Vision Proでは3Dコンテンツを表示できるような、そういった拡張があると嬉しい。あるいは、テキストの入力を連携したiPhoneから行えるとか、そのような機能があってもいい。こういった端末間の連携はAppleの得意とするところでもあるはずだ。

開発ツール

Xcode

XcodeにもAI機能が搭載されることはあまり疑う余地がない。Xcode自体に、AppleがファインチューニングしたXcode AIが搭載される可能性もある。あるいは、Xcodeの拡張の仕組みが改善され、GitHub Copilotが自然に動作するようになるかもしれない。

XcodeについてはSwift Playgroundに導入されている新しいプロジェクト形式の方にも期待がある。

Swift

Swiftは今年秋のSwift 6で大きく変化する。Concurrencyに関連して、データ競合を避けるための言語上の機能がデフォルトで有効になり、データ競合の発生し得るパターンがコンパイラで検出されるようになる。もちろんコンパイラ自体の解析も新たな言語仕様でより親切になり、よりセマンティックに行われるようになる。Swift 6には他にも、ジェネリクスなどに関しての大きめのアップデートもあって、いい年になりそうだ。

SwiftUI

Swift Concurrencyといえば、SwiftUIのConcurrencyをサポートする部分はもう少し便利になってほしい。ReactにSuspenseがあるように、非同期に得られるデータに依存したViewが書きやすくなる仕掛けがあると便利そうだ。

iOS/iPadOS

iOSとパーソナライズ

最近のiOSでは、パーソナライズに関する機能が増えていっている。ロック画面の変化はわかりやすい例だ。WWDC24でもパーソナライズに関連した機能が拡充される可能性は十分にある。iOSがパーソナライズ機能を充実させていることの裏を返すと、要するにiOSが純粋に成熟しているということなのだろう。

iPadOSとプロフェッショナル

iPadOSについても、ライトウェイトなタブレットのOSとしては限りなく成熟している。プロフェッショナル向けのアプリの不足に対しては、Apple自身もLogic ProやFinal Cut Proを開発したし、AdobePhotoshopIllustratorをリリース済み、DaVinci ResolveもiPad向けにリリースされている。もちろん現実的には多くのプロフェッショナルがMacを手放せないにせよ、一定の成果が出ているということにはなる。

iPadOSでのソフトウェア開発

開発者としてはiPadで本格的なソフトウェア開発ができる未来を待ち遠しく思うが、そこはMacを使えばいいということのようにも思う。とはいえ例えばVirtualization.frameworkをiPadOSでも動作させるような方法で、iPadでソフトウェア開発する未来がひらけてほしいとは思う。macOSのVirtualization.frameworkはかなり進歩していて、macOS/Linuxを動作させられる。あるいはVirtualization.frameworkではなく、Linuxカーネルにあるようなコンテナ技術をDarwinにも搭載し、「Darwin Container」のようにしてもいい。サンドボックスと一定の自由を両立させることは不可能ではない。

サイドローディング

サイドローディングについては、2024年3月にEUの規制に合わせて大きく状況が動いた。とはいえあくまでもEUに限定された状況であることには変わりない。このシチュエーション自体は今後も続くのだろうと思われ、法制度で求められれば地域を限定して制限が緩和されるということになるのかと思う。

これはApp Storeのビジネス的側面を考えると、そういうものとしか言いようがない。Apple営利企業だ。といっても、例えばWebKit以外のブラウザエンジンについてはEUに限定しなくてもよいのではないかと思う。WebKitのシェアが落ちて、ブラウザエンジンの寡占化が進むことに対する懸念はあるが。


ということです。

Rust 1.75.0のasync fn in traits

Rustでツールを書こうとして、コンポーネントを差し替えられるようにtraitとして定義した。GUIプログラミングの習い性で、IOが発生するメソッドは非同期にしたいから、asyncキーワードをつける。ここでは、何か文字列を読み込む予定のLoader traitを定義する。

use std::error::Error;

trait Loader {
    async fn load(&self) -> Result<String, Box<dyn Error>>;
}

このコードを書き始めた時点で、このコードは有効ではなかった。Rustではtraitにasyncメソッドを持たせられなかったのだ。そこで、async-trait crateの出番となる。

use std::error::Error;

use async_trait::async_trait;

#[async_trait]
trait Loader {
    async fn load(&self) -> Result<String, Box<dyn Error>>;
}

これを使ってコードを書くと、こういう感じになった。

(Rust Playground)

Rust 1.75.0のasync fn in traits

2023年12月28日に、Rust 1.75.0がリリースされた。

このバージョンから、traitのメソッドをasyncにできるようになった。impl TraitTraitを実装したなんらかの型を表していて、traitのasync fn-> impl Futureの糖衣構文のような扱いになっている。

これを利用すると、async-trait crateを使わなくても同じように書けるはずなので、書き換えてみる。単に#[async_trait]を除去すると、次のようなエラーにぶつかる。

   |
24 |     loader: Box<dyn Loader>,
   |                 ^^^^^^^^^^ `Loader` cannot be made into an object
   |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>

うわーとなって、「Announcing `async fn` and return-position `impl Trait` in traits | Rust Blog」の記事をよく読み直してみると、制限にぶつかっていることがわかる。特に今回は Dynamic dispatch のところに注目する。

Traits that use -> impl Trait and async fn are not object-safe, which means they lack support for dynamic dispatch. We plan to provide utilities that enable dynamic dispatch in an upcoming version of the trait-variant crate.

動的ディスパッチというのは要するに仮想関数みたいなやつと同じで、コンパイル時に静的に型が決まらないようなケースはまだサポートされていないようだ。ここでさっきのコードを見直すと、確かに、Box<dyn Loader>などとやっている。いずれ公式のtrait-variant crateを使うと、なんらかいい感じにしてくれるようだが、現時点ではまだそういう機能がない。

ということでいったんasync-trait crateに戻ってもよいのだけど、そもそも今回は動的ディスパッチをやめても、いまのところ差し支えない。ChatGPTに「Rustでtraitオブジェクトに対するdynamic dispatchを避けるにはどうしたらよいですか」と聞いてみると、ジェネリクスを使えばいいとわかる(それはそう)。

use std::error::Error;

trait Loader {
    async fn load(&self) -> Result<String, Box<dyn Error>>;
}

struct Processor<L: Loader> {
    loader: L,
}

impl<L: Loader> Processor<L> {
    fn new(loader: L) -> Self {
        return Self { loader };
    }

    async fn process(&self) -> Result<(), Box<dyn Error>> {
        unimplemented!()
    }
}

これで無事にコンパイルできる。

(Rust Playground)

なるほどでした。

2023年の「散財 of the Year」

買ってよかった2023ということで2023年の「散財 of the Year」を発表します。

Best おもちゃ

今年のBestおもちゃはガンプラガンプラは小学生か中学生くらいの頃にいくつか作ったが、それ以来、20年以上ぶりにプラモデルを作った。

小学生になった息子と作ったら楽しかろうと思って、子供でも簡単そうなものと、大人でも楽しめそうなものを少しずつ買った。息子だけでなく4歳の娘もすっかり虜になり、3人でHGのガンプラを作ったりもしている。

自分でも、工具をいくらか買い揃えて、RGを2つとHGを2つ作った。ニッパーでパーツを切り離し、ヤスリでゲート跡を目立たなくして、スミ入れでディテールを足していく。この地味な工程を延々繰り返しながら、最近の出来事を思い返したりする。そういうことをしていると、何やらセラピーのように感じられる。

20年前と比べて今時のガンプラはかなり出来がよく、満足感も高い。細かな作業をしながらセルフリフレクションしたい皆さんにおすすめです。

Best コンピュータ

Bestコンピュータは、MacBook Pro (14-インチ, M3 Pro, Nov 2023)。M3は来年だと思っていたので、驚きつつ購入。スペースブラックがかっこいい。

2023年における計算機の性能を決めるパラメータは、製造プロセスへの依存度が高い。製造プロセスの微細化が進めば、回路規模を大きくしてもダイサイズを維持でき、消費電力も減らせる。ということでTSMCのN3目当てにM1 ProをM3 Proにした。実際、シングルコア性能が向上したためか、GUIがさらに滑らかになったのを感じられる。

Macの性能がいいと、作業へのためらいが減って、少し気楽になるのがよい。

Best ゲームハードウェア

Bestゲームハードウェアは、PlayStation VR 2と少し迷ったが、PlayStation Portalに決めた。

今年は大作ゲームを何本かクリアしていて、ほどほどにコンソールゲームで遊んでいる。PlayStation 5もNintendo Switchもそれぞれちゃんと遊んでいて、もちろんグラフィックスはPlayStation 5に分があるのだが、Nintendo Switchの気やすさの方が好ましく感じられる場面の方が多かった。というところに、Project QことPlayStation Portalが発表。

発売時に手に入れてちょこちょこ遊んでいるが、テレビの前に座らなくていい開放感で、ゲームの体験が大きく変化した。もちろんグラフィックスのよさは(HDRじゃないとか細かいことはあるけれど)折り紙つき。専用ハードウェアでないとできない体験でもあった。

Best 散財

さて、栄えあるBest散財は、3DプリンタBambu Lab P1Sだ。これを買ってから、CADで簡単なモデリングができるようになって、ちょっとしたものを出力している。使用頻度は高くないが、いつでも使える3Dプリンタがあるという事実それ自体に満足している。

モデル共有サイトで自作のモデルを公開してみているが、これまでに500回ダウンロードされ、出力した人がコメントを残してくれている。そういうのもかなり嬉しい。


2023年は円安、物価高で、生活のコストも上がった感があるが、振り返ってみればかなりエンジョイしている。2024年はApple Vision Proあたりでしっかり散財したい。


▶ 【PR】はてなブログ 「AIタイトルアシスト」リリース記念 特別お題キャンペーン
お題と新機能「AIタイトルアシスト」についてはこちら!
by はてなブログ

買ってよかった2023

SwiftにおけるTyped throwsの現在

現在Swift Evolutionで議論されているSE-0413 Typed throwsについて、Swiftの歴史を辿りながら紹介します。

この記事ははてなエンジニア Advent Calendar 2023の9日目の記事です。昨日は id:kouki_daniPadだけでアプリを作ってみるでした。ファスティング中の id:kouki_dan を関モバに誘ったのは私です。お誕生日おめでとうございました。

Swiftのエラーハンドリング

Swiftのエラーハンドリングでは、2015年6月のSwift 2.0のリリース以来、エラーに型がつかない。Errorプロトコルに準拠したなんらかの型が投げられるということだけ決まっていて、それが実際にどうであるかを確認するのは(あるいは確認しないのは)、呼び出し側に任されている。do文のcatch句にはパターンが書けるので、必要に応じてハンドリングできる。

do {
    let xmlDoc = try parse(myXMLData)
} catch let e as XMLParsingError {
    print("Parsing error: \(e.kind) [\(e.line):\(e.column)]")
} catch {
    print("Other error: \(error)")
}

実際にどういった型のエラーが起きるのかは、ドキュメンテーションでしか宣言できない。エラーのハンドリングが網羅的かどうかを機械的に検査することもできない。

Typed throwsに関する初期の議論

このことは度々議論の的となった。2015年12月にはすでに、当時のswift-evolutionメーリングリストで議論されている。Swiftを生み出したクリス・ラトナーは、typed throwsは良いが、Swift 3の resilience モデルまでは問題がある、と返信している。

動的にリンクされるライブラリがエラーをthrowする際に、ライブラリ側が変化してthrowするエラーが変わっても、呼び出し元からはそれを知ることができないから、なんらかの仕組みがないと型の安全性が壊れる、ということだ。

ちなみに当時 resilience モデルと言っていたものは、Swift 3では実現されない。Swift 5.0でのABIの安定化後に、Library Evolutionとして、2019年9月にリリースされたSwift 5.1から利用できるようになった。

エラーの型をパラメータに持つ型

Result

2018年11月にResultを標準ライブラリへ追加するプロポーザルがSwift Evolutionで起案され、1ヶ月後に承認される。そして2019年3月のSwift 5.0でリリースされた。

@frozen public enum Result<Success, Failure> where Failure : Error {
    case success(Success)
    case failure(Failure)
}

これはSwiftのエラーシステムが提供するthrowstrycatchとは全く違う方法でエラーハンドリングを行わせるもので、言語としての一貫性という意味では怪しいところがある。ただし当時の背景からすればこれは妥当で、まだSwift Concurrencyがなく、非同期処理はコールバックで表現されていたため、このようなものが求められていた。実際にサードパーティのResult型が広く使われてもいた。

C++の開発者であるビャーネ・ストラウストラップは「プログラミング言語C++ 第4版」の中で、標準ライブラリの役割のひとつに「ライブラリ間通信を実現するコンポーネントの集合」を挙げている。ライブラリ間でのやり取りに必要な汎用のコンテナ型を提供するのは、標準ライブラリの重要な役割である。したがってResultが標準ライブラリに追加されることには必然性があった。

そしてこのResult<Success, Failure>の型パラメータには、Errorプロトコルに制約されたFailureがある。他のプログラミング言語におけるEither型を考えればこれも妥当であるが、既存のエラーハンドリングモデルとはギャップがある。

Resultgetメソッドやイニシャライザによって、Swiftのエラーハンドリングシステムと相互運用できるようになっている。このときエラーの型はany Errorになる。

@frozen public enum Result<Success, Failure> where Failure : Error {
    @inlinable public func get() throws -> Success
}

extension Result where Failure == any Error {
    public init(catching body: () throws -> Success)
}

Swift ConcurrencyのTask

2021年9月リリースのSwift 5.5で、Swift Concurrencyとしてasync/awaitやActorなどが導入された。ここで導入されたTaskにもResultと同様にFailure型パラメータがある。

@frozen public struct Task<Success, Failure> : Sendable where Success : Sendable, Failure : Error {
}

これもResultに近い。

Primary Associated TypesとAsyncSequence

2022年9月にリリースされたSwift 5.7で、Primary Associated Typeという機能が追加された。標準ライブラリの多くのプロトコルにも設定されたため、この機能でsome Sequence<String>のように書ける。ところが、AsyncSequenceプロトコルにはPrimary Associated Typeが設定されなかった。

AsyncSequence and AsyncIteratorProtocol logically ought to have Element as their primary associated type. However, we have ongoing evolution discussions about adding a precise error type to these. If those discussions bear fruit, then it's possible we may want to also mark the potential new Error associated type as primary. To prevent source compatibility complications, adding primary associated types to these two protocols is deferred to a future proposal. — Primary Associated Types in the Standard Library

Swift Evolutionでは、エラーの型に関する議論が続いているから、とされた。このことでany AsyncSequence<String, any Error>とは書けない。

Typed throws

そして2023年8月に、Status check: Typed throwsが投稿される。9月にはSwift Language Steering GroupのDoug GregorがPitchを投稿し、11月、ついに正式なプロポーザルSE-0413 Typed throwsができた。

実際に試す

ここで実際に動作を試してみる。

最新のdo throws(ErrorType)の構文を試したいので、Swift Forumに投稿された最新のツールチェーンをダウンロードし、~/Library/Developer/Toolchains/に展開する。

Xcodeの場合

Xcodeなら「Xcode > Toolchains」からこれを選択。

あるいは「Manage Toolchains…」でもいい。

Terminalの場合

シェルではツールチェーンのBundle IDを調べてTOOLCHAINS環境変数に設定する。

$ ls ~/Library/Developer/Toolchains/ 
swift-PR-70182-969.xctoolchain

$ /usr/libexec/PlistBuddy -c "Print CFBundleIdentifier:" ~/Library/Developer/Toolchains/swift-PR-70182-969.xctoolchain/Info.plist
org.swift.pr.70182.969

$ export TOOLCHAINS=org.swift.pr.70182.969

$ swift --version                         
Apple Swift version 5.11-dev (LLVM e131e99f323910c, Swift 4d62b1f4e64aa28)
Target: arm64-apple-macosx14.0

実験的フラグの設定

また実験的フラグTypedThrowsを有効にする必要がある。Swift Packageなら、.enableExperimentalFeature("TypedThrows")とするのが簡単だ。

// swift-tools-version: 5.9

import PackageDescription

let package = Package(
    name: "TypedThrows",
    targets: [
        .executableTarget(
            name: "TypedThrows",
            swiftSettings: [
                .enableExperimentalFeature("TypedThrows"),
            ]
        ),
    ]
)

Typed throwsを見ていく

エラーの型を指定するにはthrowsの代わりにthrows(ErrorType)を書く。

enum CatError: Error {
    case sleeps
    case sitsAtATree
}

func callCat() throws(CatError) -> Cat {
    if Int.random(in: 0..<24) < 20 {
        throw .sleeps
    }
    return Cat(name: "Neko")
}

もし宣言したのと違う型のエラーをthrowしようとすれば、そこでコンパイルエラーになる。

func callCatBadly() throws(CatError) -> Cat {
    throw SimpleError(message: "sleeping") // error: Thrown expression type 'SimpleError' cannot be converted to error type 'CatError'
}

catch句では型推論される。

do {
    _ = try callCat()
} catch {
    print(error) // このerrorはCatError
}

ただし、doの中で複数の型のエラーが起きる場合は、any Errorに落ちる。

do throws(ErrorType)で明示的に発生していいエラーを限定できる。もしもそれ以外のエラーが発生するようであれば、そこでコンパイルエラーになる。

do throws(SimpleError) {
    _ = try callCat() // error: Thrown expression type 'CatError' cannot be converted to error type 'SimpleError'
} catch {
    print(error)
}

throws(any Error)throwsと同じ意味で、throws(Never)throwsじゃないのと同じ意味になる。

またエラー型を型パラメータにすることで、rethrowsを置き換えられる。

ということで、全体の規則は難しくない。

Typed throwsを使うべき場面は限定されている

プロポーザルに、Typed throwsを使っていいケースが紹介されている。普通はエラーを網羅的に場合分けしないので、any Errorである方がむしろいい。型があってもいい場面は次の通り。

  • モジュールやパッケージ内に閉じていて、常にエラー処理したい場合は、純粋に実装の詳細であり、もっともらしい
  • ジェネリックなコードで自分自身がエラーを発生させず、利用者が発生させたエラーをそのまま伝える場合
  • 制限された環境下で動作するか、あるいはメモリを割り当てできない場合で、かつ自分自身でしかエラーを作らないとき

1つ目のケースは、つまり外部との境界に表れないなら問題ないということだ。あとからエラーの種類が増えてもモジュール内に閉じているので、特に問題が起きない。

2つ目のケースは、rethrowsと同等の条件だ。これも型は外から与えられるので、実質的にモジュールに閉じる。

3つ目のケースは少し特殊で、組み込み環境のようなものが想定されている。

要するに、モジュールの境界ではまず型をつけない方がいい、ということが書かれている。Typed throwsが利用されすぎることが懸念されている。

Typed throwsの今後

現在のプロポーザルについて、おおよそ全体には好意的に受け止められている。このまま受理されれば、遅くとも来年秋のSwift 5.11頃にリリースされるのではないか。(互換性のためにSwift 6になると少し動作が変化する予定とされている。)

Typed throwsによってResultTaskなどとインピーダンスが揃い、使いやすくなる面が多いだろう。ただしAsyncSequenceにPrimary Associated Typesを設定するのはFuture directionsに示されている通り、for..inの調整も含めて別のプロポーザルを待つ必要がある。

またかねてから議論されていた、throws(FileSystemError | NetworkError)のように複数のエラー型を扱えるようにする話はいったん見送られ、Alternatives consideredに記載された。実質的に匿名enum(直和型と呼ばれることも)を追加することになるため、このプロポーザルのスコープから外されている。


ということで、関西モバイルアプリ研究会A #1で話したTyped throwsでした。

明日は id:papix です。

3DプリンタでMagSafe充電器スタンドをつくる

この秋にリリースされるiOS 17では、充電中のiPhoneに情報を一目でわかるように表示する「スタンバイ」機能が搭載されるそうだ。iPhone 14 Proの常時表示ディスプレイと組み合わせると便利そうだ。

これを活用するには充電中のiPhoneを一定の角度に保つ充電スタンドが必要になる。MagSafe充電器 ワイヤレスなら、充電器ごとに設定を記憶してくれるようだから、MagSafe充電器タイプが望ましい。市場には、MagSafe充電器が一体になったスタンドや、単体のMagSafe充電器と組み合わせて使う製品がある。今回は3Dプリンタを買ったことだから、メイカー精神を発揮してみる。

試作1号

MagSafe充電器の大きさをノギスで測ると、直径は55.9 mm、厚みは5.5 mmある。そこから例によって、Fusion 360モデリングする。MagSafe充電器が少し高い位置に一定の角度で固定されていれば用が足りるので、70度に傾けた枠に愚直に支柱をつける。iPhoneの分だけ重心が手前にくるから、台座も手前に伸ばしてある。

これを3Dプリンタで出力する。ツリー状のサポートをつけている。

一見するとうまくできているが、オーバーハング(せり出し)部分で精度が悪化し、荒れてしまった。内側もその影響を受けて、MagSafe充電器に干渉し、うまく収まらない。パーツを分割して出力するとよいのだろうか。

使用感も確かめてみると、最低限スタンドとしての役には立つ。iPhoneが浮かんで見えるのは格好がよい。ただ、支柱部分の剛性が足りていないのか、iPhoneの重みで僅かなたわみが生まれ、振動してしまう。iPhoneを操作するたびに振動するので、使い心地はよくない。

試作2号

全体的な剛性を確保するために、支柱で支える構造をやめた。ケーブルを裏側にまわすついでに、ひろく穴を空けてMagSafe充電器の充電中の熱が後ろへ逃げるようにしている。

3Dプリンタでの出力時には、MagSafe充電器まわりの精度が出やすいように、倒した状態にしている。

出力してみると、精度は十分で、剛性もある。使用感にも問題がない。

素材が軽いので、iPhoneを近づけると磁力でスタンドの方が動いてしまう。錘をつけると改善されるかもしれないが、困るわけではないので、このままでもよいだろうか。

感想

CADの操作に慣れて、少し複雑な形でもモデリングできるようになった。ただしCADでどんなに自由にかたちを作っても、それでうまくいくとは限らない。素材の特性や加工する装置にあわせて設計しなければ、まさに机上の空論だ。

今回は最低限度、MagSafe充電器スタンドとして使えるものができた。ヒンジで角度を調整できるとか、改良したい箇所もあるが、そもそもヒンジをどう作ったらいいのか、見当がつかない。いくらでも学ぶことがある。

3Dプリンタで何かを作るプロセスは、頭の中にあるものを現実で試し、失敗や成功の経験をするもので、刺激的でおもしろい。これは相当におもしろい。


3Dプリンタを買った

Bambu LabのP1Sという3Dプリンタを買った。少し前に出たばかりの機種で、同じメーカーのP1Pという機種にエンクロージャ(覆い)がついたようなモデルだ。

家庭用の3Dプリンタは安いものなら数万円で買えるが、これは12万円弱で、価格帯としてはミドルレンジにあたるのだろうか。会社の表彰制度で社長賞を頂戴して気が大きくなっていたので、ついに買った。何かを買うときに、少しいいものを選びがちな性分で、安物買いを恐れている。入念にインターネットで検索をして、FDM(熱溶解積層)法で、Core XY方式であり、エンクロージャで覆われている、不具合の少なそうな機種を選んだ。

3Dプリンタを選ぶのは相当に難しいことだ。何を基準に選んだらいいのかも知らないし、進歩が激しい分野だから、状況が変化しやすい。

もしも家電量販店で販売されているような製品であれば、家電量販店の売り場にしばらくいれば、だんだんと事情が掴めてくるものだ。商品のひとつひとつに主だった特徴が書かれていて、例えばパソコンなら、CPUが何で、メモリが何GBで、ストレージがどうで、インターフェースが……、というのがまとめられている。それがドラム式洗濯機であっても、乾燥機能がヒートポンプ式なのかヒーター式なのか、洗濯の容量、洗剤自動投入機能がついているか、一目でわかる。つまりそういったことが製品を差別化していて、それに気を配ればよい、ということだ。

そういうポイントを知らない状態で3Dプリンタを選ぶのは困難だから、かなりの時間をかけて、何となくの知識を得た。

この過程でわかったことだが、3Dプリンタを愛好する人たちの多くにはメイカー文化が根付いていて、3Dプリンタそれ自体もその対象となっている。つまり3Dプリンタそのものについて、自分たちで手を加えやすいものが好まれている。これにはフリーソフトウェア運動的な側面もあって、プリント可能な3Dプリンタのパーツのモデルが多く公開されている。

それはさておいて、何となくの知識でBambu Lab P1Sを注文し、3日後には届いた。

P1Sを設置するのは、付属する印刷物を読めば難しくない。Bambu Labが公開しているYouTubeの動画でも予習してあったので、特に苦も無く済んだ。電源が国内で一般的な2ピンではなく3ピンタイプなので、変換してやる必要はあるが、これも事前にアダプタを用意していたので問題ない。

設置後に早速、Benchyと呼ばれる船のモデルをプリントしてみた。3Dプリンタベンチマークとされる船は、だいたい20分弱できれいに出力された。これは3Dプリンタとしてはかなり早い部類で、Bambu Labのセールスポイントだ。

そこから、Autodesk Fusion 360の個人利用版を使って、CADを学んでいる。スケッチから立体を作るフローがわかってきて、単純な形状なら作れるようになった。今朝は毎月頼んでいるレターのスタンドをプリントしてみた。プリントにかかった時間は30分くらい。まあまあかな。

使い途の当てもなく3Dプリンタを買ったが、自分でモデリングしたかたちをプリントできたとき、えもいわれぬ感慨を得た。コンピュータの中にしか存在しなかったものが、実際に触れるものとしてできあがると、嬉しいものだ。ましてそれが家にいながらにしてとなれば格別である。

Apple Vision Pro所感

Apple Vision Proの発表を見ての感想です。

空間コンピュータ

予想通り、ハードウェア面でもソフトウェア面でも圧倒的な完成度の製品を出してきた、という印象だ。ディスプレイからオーディオ、センサーに至るまで、可能な限りが詰め込まれている。新たなカテゴリをプラットフォームとして切り拓く、硬い意志が感じられる。

製品に「Pro」と名付けつつも、同種の製品カテゴリではかなりコンシューマ向けに寄せられてもいる。映画とか、ゲームとか。この辺りはMacintoshを生み出したAppleらしいところでもある。とはいえ価格帯からは「Pro」カテゴリになるし、将来的により廉価なモデルを発売する余地を確保しているのだろう。

Appleは本日、デジタルコンテンツを現実の世界とシームレスに融合しながら、実世界や周囲の人とのつながりを保つことができる革新的な空間コンピュータ、Apple Vision Proを発表しました。

visionOSは、思っていたよりもずっとコンピューティングにフォーカスされていた印象がある。Apple自身も空間コンピューティングと表現しているが、道具として屋内で普通に使うものとして設計されていて、奇を衒ったところがない。極めてインドア的な、普段からコンピュータを四六時中使っているようなオフィスワーカー向けの製品のように感じられ、つまりこれはMacintoshなんだな、と理解した。

Apple Silicon

Apple Siliconもしっかりと活かされている。性能と効率のバランス、そして既存のアプリとの互換性を考えれば、M2チップが採用されたのは理解しやすい。空間コンピューティングで複数のアプリを同時に実行するには、最低限の性能かもしれない。できればM3を期待したかったところだが、コストや量産する上でのトレードオフがあるのだろう。

M2チップは単体で機能するための比類ないパフォーマンスを提供し、新たに開発されたR1チップは、12のカメラ、5つのセンサー、6つのマイクロフォンからの入力を処理し、コンテンツがユーザーの目の前に現れるような感覚を生み出します。

白眉はR1チップで、空間上にリアリティのあるユーザーインターフェースを描画するための専用チップになっている。R1に多くの処理を肩代わりさせられることで、M2はその性能のほとんどをコンピューティングに使えるのだろう。プレスリリースには次のように記載されていて、これは要するにフレームレートが90fpsということか。

R1は瞬きの8倍高速な12ミリ秒で新しいイメージをディスプレイにデータストリームとして伝送します。

VR的な感覚では、片目あたり4K以上とされる解像度だと、グラフィクスを処理するのに非常に大きな性能が必要に感じられる。しかし実際には、Apple Vision Proの空間コンピューティングでは、単にウインドウを描画する程度の性能でいいはずだ。このウインドウの描画を行うのがM2チップなんだろう。R1チップは描画されたウインドウを空間上にプロジェクションして、カメラの入力と合成する。こちらは片目あたり4Kのグラフィクスを扱うことになる。

ディスプレイ

micro-OLEDで2,300万ピクセルということで、例えば4,150×2,750ピクセルくらいのディスプレイが2枚くらいだろうか。

Appleシリコンのチップにもとづいて開発された画期的な超高解像度ディスプレイシステムを備えたApple Vision Proは、micro-OLEDテクノロジーにより、広色域とハイダイナミックレンジを備えた切手サイズの2つのディスプレイに、合計2,300万ものピクセルを詰め込んでいます。

これが実際にどれくらいきれいに見えるのかは、視野角と関係するから、この情報だけではわからない。例えばPS VR2の水平視野角は110度で、これと同じくらいだとすれば、角画素密度(ピクセル/度)はおおよそ38になり、PS VR2の2倍になる。これくらいの角画素密度があるとよほど小さな文字でなければ普通に読めると思う。

オーディオ

WWDC20で、iOS 14からAirPods Proなどで空間オーディオをサポートすることが発表された。それから3年、空間オーディオが利用できるハードウェアもソフトウェアも増え、そして今回Apple Vision Proでも空間オーディオが大きく取り上げられている。

Apple Vision Proでの体験の中核となるのは、サウンドがユーザーを取り巻く環境から聞こえるような感覚を生み出し、その空間に合わせてサウンドを調整する、先進的な空間オーディオシステムです。

Apple Vision Proの開発期間を考えれば、空間オーディオはそもそもApple Visionプラットフォームの副産物である可能性も考えられる。

空間コンピューティングというものを考えたときに、音がどこから聞こえるかは空間を認識する重要な手掛かりになる。

API

visionOSのアプリは、iOSやiPadOS向けのアプリがほとんどそのまま動くほか、UIKitやSwiftUIで開発できるようだ。UIKitが異なるフォームファクタのOSに使われるのはこれが初めてではなく、例えばtvOSでも利用されている。

SwiftUIはもともとAppleのプラットフォーム間でクロスプラットフォーム指向であり、visionOS向けアプリ開発に推奨されることに驚きはない。開発者にとっても扱いやすい。

総じて、visionOS向けのアプリ開発iOSアプリの開発と同じくらいには容易そうだ。既存のアプリも含めて、ローンチ時から多くのアプリが提供されるだろう。新規のプラットフォームで最初からiOSのエコシステムに相乗りできるのは恩恵が大きそうだ。特に昨今のiPadOSは、プロ向けのアプリを推進してきたところがあって、空間コンピューティングとは相性がよさそうだ。

ゲーミング

Apple Vision Proは、明確にVRのゲームプラットフォームではなさそうだ。ハンドコントローラがなく、ジェスチャのみでVRのゲームを遊ぶのはまだ難しいだろう。そもそもデモに登場したゲームは、PlayStationのコントローラを持って遊んでいる。


いったんこれくらいです。