今更ではありますが、Swift の API Design Guidelinesを訳してみました。
基本
- 利用者にとっての明確さがもっとも重要な目標です。メソッドやプロパティを宣言するのは一度だけですが、繰り返し 使われます。API を利用するコードが明確で簡潔になるように API を設計しましょう。設計を評価するには宣言を読むだけでは不十分です。API が明瞭なものであることを確かめるため常に利用例を書いてみて検証しましょう。
- 簡潔さより明確さが重要です。Swift のコードはコンパクトに書くことができますが、出来る限り少ない文字数で出来る限り短いコードを書けるようにすることが 目標ではありません。Swiftにおける簡潔さとは強い型システムとボイラープレートを自然と減らす言語機能の副作用としてもたらされるものです。
- 全ての宣言にドキュメンテーションコメントを書きましょう。ドキュメントを書くことによって得られる深い洞察が設計に対して重要な影響を与えることがあります。先延ばしにしてはいけません。(もし設計している API の機能をシンプルな言葉でうまく説明できないとしたら誤った設計をしているかもしれません。)
- Swift の markdown 記法 を使いましょう。
- 宣言の概要説明から始めましょう。 宣言と概要からその API を完全に理解できることがよくあります。
/// Returns a "view" of `self` containing the same elements in /// reverse order func reversed() -> ReverseCollection
- 概要に集中しましょう。もっとも重要な部分です。素晴らしいドキュメンテーションコメントを書くには、優れた概要より大事なものはありません。
- 可能なら単文を使い、ピリオドで終わりましょう。完全な文を使ってはいけません。
- 関数やメソッドは何を行い、何を 返す のか記述しましょう。効果がないことと戻り値が
Void
であることについては省略します。/// Inserts `newHead` at the beginning of `self`. mutating func prepend(_ newHead: Int) /// Returns a `List` containing `head` followed by the elements /// of `self`. func prepending(_ head: Element) -> List /// Removes and returns the first element of `self` if non-empty; /// returns `nil` otherwise. mutating func popFirst() -> Element?
注意: 上の
popFirst
のような一部の場合には複数の文をセミコロンで分けて書きます。
- 添字は何にアクセスするのか記述しましょう。
/// Accesses the `index`th element. subscript(index: Int) -> Element { get set }
- イニシャライザは何を作るのか記述しましょう。
/// Creates an instance containing `n` repetitions of `x`. init(count n: Int, repeatedElement x: Element)
- その他の全ての宣言には宣言するものが何であるのか記述しましょう。
/// A collection that supports equally efficient insertion/removal /// at any position. struct List { /// The element at the beginning of `self`, or `nil` if self is /// empty. var first: Element? ...
- 必要であれば、ひとつ以上の段落と箇条書きを続けます。段落は空白行で分け、完全な文を使います。
/// Writes the textual representation of each ← Summary /// element of `items` to the standard output. /// ← Blank line /// The textual representation for each item `x` ← Additional discussion /// is generated by the expression `String(x)`. /// /// - Parameter separator: text to be printed ⎫ /// between items. ⎟ /// - Parameter terminator: text to be printed ⎬ Parameters section /// at the end. ⎟ /// ⎭ /// - Note: To print without a trailing ⎫ /// newline, pass `terminator: ""` ⎟ /// ⎬ Symbol commands /// - SeeAlso: `CustomDebugStringConvertible`, ⎟ /// `CustomStringConvertible`, `debugPrint`. ⎭ public func print( _ items: Any..., separator: String = " ", terminator: String = "\n")
- 利用できる場合は常に認めれたシンボルドキュメンテーションマークアップを使い概要以上の情報を加えましょう。1
- シンボルコマンドシンタックス付きの認められた箇条書きの項目について知り、利用しましょう。Xcode のような人気の開発ツールは次のキーワードで始まる箇条書きの項目に対して特別な扱いをします:
Attention, Author, Authors, Bug, Complexity, Copyright, Date, Experiment, Important
, Invariant, Note, Parameter, Parameters, Postcondition, Precondition, Remark, Requires, Returns, SeeAlso, Since, Throws, Todo, Version, Warning
名前
明確な利用を促しましょう
- その名前が利用される部分を読む人にとっての曖昧さをなくすために必要な言葉を全て盛り込みましょう。
例として、コレクションの指定された箇所の要素を削除するメソッドについて考えてみましょう。
// ✅ 良い例 extension List { public mutating func remove(at position: Index) -> Element } employees.remove(at: x)
もしメソッドシグネチャから
at
という単語を省略していたら、このメソッドはx
を削除する要素の位置を示すために使うのではなく、x
に等しい要素を探して削除するのだと利用者に伝わってしまったかもしれません。// ⛔ 悪い例 employees.remove(x) // unclear: are we removing x?
- 必要ない言葉は省きましょう。名前の中の全ての言葉はその名前が利用される箇所で意味のある情報を伝えるものであるべきです。
意図を明確にしたり意味の違いを明らかにするためにはより多くの言葉が必要かもしれません。しかしコードを読む人が既に持っている情報を含む冗長なものは省略するべきです。特に、型情報を 単に繰り返しているだけ の言葉は省略するべきです。
// ⛔ 悪い例 public mutating func removeElement(_ member: Element) -> Element? allViews.removeElement(cancelButton)
このケースでは、
Element
という言葉は呼び出される箇所において意味のあるものを何も付け加えていません。この API は次のようにした方がよいでしょう:// ✅ 良い例 public mutating func remove(_ member: Element) -> Element? allViews.remove(cancelButton) // clearer
場合によっては曖昧さを避けるために型情報を繰り返すことが必要な場合もあります。しかし一般的にはパラメータの型より 役割 を表す言葉を使う方がよいです。詳しくは次の項目を見ましょう。
- 型の束縛よりも役割によって変数、パラメータ、連想型の名前をつけましょう。
// ⛔ 悪い例 var string = "Hello" protocol ViewController { associatedtype ViewType : View } class ProductionLine { func restock(from widgetFactory: WidgetFactory) }
このように型名を本来の目的とは異なる目的で使用すると明確さ、表現の向上に失敗します。そうするのではなく、役割を表現する名前を選ぶようにしましょう。
// ✅ 良い例 var greeting = "Hello" protocol ViewController { associatedtype ContentView : View } class ProductionLine { func restock(from supplier: WidgetFactory) }
もし連想型がその型を束縛しているプロトコルと強く結びついており、プロトコル名が役割になっているという場合には連想型の名前に
Type
をつけることで衝突を防ぎます。protocol Sequence { associatedtype IteratorType : Iterator }
- パラメータの役割を明確にするため乏しい型情報を補いましょう。
パラメータの型が
NSObject
,Any
,AnyObject
やInt
,String
等の基本的な型である場合、型情報と使用する際のコンテキストから意図を充分に伝えられないかもしれません。この例では宣言は明確ですが、使用する箇所は不明確です。// ⛔ 悪い例 func add(_ observer: NSObject, for keyPath: String) grid.add(self, for: graphics) // vague
明確さを取り戻すため型情報が乏しいパラメータの前にはその役割を説明する名詞をつけましょう:
// ✅ 良い例 func addObserver(_ observer: NSObject, forKeyPath path: String) grid.addObserver(self, forKeyPath: graphics) // clear
流暢に使えるよう努力しましょう
- メソッドや関数を使用する箇所が文法的に正しい英語の表現となるような名前を選びましょう。
// ✅ 良い例 x.insert(y, at: z) // “x, insert y at z” x.subViews(havingColor: y) // “x's subviews having color y” x.capitalizingNouns() // “x, capitalizing nouns”
// ⛔ 悪い例 x.insert(y, position: z) x.subViews(color: y) x.nounCapitalize()
第一引数、または第二引数より後の引数が呼び出しにとっての中心的な役割を持たない場合、それらが流暢さを損なってしまうことは許容されます。
AudioUnit.instantiate( with: description, options: [.inProcess], completionHandler: stopProgressBar)
-
ファクトリーメソッドは
make
で始めましょう。例:x.makeIterator()
-
イニシャライザとファクトリーメソッドの呼び出しは第一引数を含まないフレーズとなるべきです。例:
x.makeWidget(cogCount: 47)
例えばこれらの呼び出しが意味しているフレーズには第一引数が含まれていません。
// ✅ 良い例 let foreground = Color(red: 32, green: 64, blue: 128) let newPart = factory.makeWidget(gears: 42, spindles: 14)
次の例では API の作者は第一引数までを英文法的に繋げようとしました。
// ⛔ 悪い例 let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128) let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)
実際問題としてこのガイドラインと引数ラベルに関するガンドラインによって値を保つ型変換を行う呼び出し以外は第一引数に引数ラベルをつけるということになります。
let rgbForeground = RGBColor(cmykForeground)
- 副作用によって関数とメソッドの名前をつけましょう。
- 副作用がないものは名詞句として読めるべきです。例:
x.distance(to: y)
,i.successor()
.- 副作用があるものは命令形の動詞句として読めるべきです。例:
print(x)
,x.sort()
,x.append(y)
.- 一貫性をもってインスタンスの内容を変更するメソッド/しないメソッドのペアに名前をつけましょう。インスタンスの内容を更新するメソッドはセマンティクスは似ているもののその場でインスタンスの値を変更するのではなく、新しい値を返す変種をもつことがよくあります。
- 操作が動詞で自然に説明できる場合は、その命令形をインスタンスの内容を更新するメソッドに使い、"ed" または "ing" をつけた名前を対応するインスタンスの内容を更新しないメソッドに使います。
インスタンスの内容を更新するメソッド インスタンスの内容を更新しないメソッド x.sort()
z = x.sorted()
x.append(y)
z = z.appending(y)
- インスタンスの内容を更新しないメソッドには動詞の過去分詞(通常 "ed" がついたもの)を使って名前を選びましょう。
/// Reverses `self` in-place. mutating func reverse() /// Returns a reversed copy of `self`. func reversed() -> Self ... x.reverse() let y = x.reversed()
- 動詞が直接目的語を持つため "ed" をつけるのが文法的に正しくない場合、動詞の現在分詞を使用し "ing" をつけてインスタンスの内容を更新しないメソッドに名前をつけましょう。
/// Strips all the newlines from `self` mutating func stripNewlines() /// Returns a copy of `self` with all the newlines stripped. func strippingNewlines() -> String ... s.stripNewlines() let oneLine = t.strippingNewlines()
- 操作が名詞で自然に説明できる場合はインスタンスの内容を更新しないメソッドにその名詞を使い、対応するインスタンスの内容を変更するメソッドには頭に "form" をつけた名前をつけましょう。
インスタンスの内容を更新しないメソッド インスタンスの内容を更新するメソッド x = y.union()
y.formUnion()
j = c.successor(i)
c.formSuccessor(&i)
- インスタンスの内容を更新しないBool 値を返すメソッドとプロパティはレシーバに関する表明として読めるべきです。例:
x.isEmpty
,line1.intersects(line2)
-
何であるのか を記述するプロトコルは名詞として読めるべきです。(例:
Collection
) -
能力 を記述するプロトコルは接尾語 able, ible, ing を使用して名前をつけるべきです。(例:
Equatable
,ProgressReporting
) - その他の型、プロパティ、変数、定数の名前は名詞として読めるべきです。
専門用語は上手に使いましょう
技術的専門用語: (名詞)特定の分野または専門的職業における正確で特化された意味を持つ単語、または成句。
- 同じような意味を持つ、より一般的な単語で充分に通じるならあまり知られていない専門用語を使わないようにしましょう。「皮膚」で通じるなら「上皮」と言ってはいけません。技術的専門用語は不可欠なコミュニケーションツールですが、使わなければ重要な意味合いが失われてしまう場合に限って使うべきです。
- 本当に技術的専門用語を使う場合は確立された意味から外れないようにしましょう。
一般的な言葉ではなく技術的専門用語を使う唯一の理由は、それを使わないと曖昧になったりはっきりしなくなったりするものを 正確に 表現するためです。そのため API では専門用語は広く受け入れられている意味に厳密に沿うように使うべきです。
- 専門家を驚かせないようにしましょう: もし専門用語に新しい意味を発明したとしたら、既にその言葉に慣れ親しんでいる人を驚かせ、おそらく怒らせることになるでしょう。
- 初心者を混乱させないようにしましょう: 専門用語を学ぼうとする人は Web で検索して伝統的な意味を見つけます。
- 略語は避けましょう。略語、特に一般的ではないものは、実質的に技術的専門用語です。なぜなら理解できるかどうかは略されていない形式に正しく翻訳できるかどうかに拠るためです。
使用する略語で伝えようとしている意味は Web で検索すれば簡単に見つかるべきです。
- 前例を採用しましょう。既存の文化に対する適応を犠牲にしてまで完全な初心者に合わせて専門用語を使うのはやめましょう。
初心者は
List
の方が意味を取りやすいかもしれませんが、連続したデータ構造はList
のような単純な用語よりもArray
と名付ける方がよいです。配列は現代のコンピューティングの基礎であるため、全てのプログラマーは配列とは何か知っています。もしくはすぐに習います。全てのプログラマーが慣れ親しんでいる用語を使いましょう。そうすれば彼/彼女たちの Web 検索と質問に応えられるでしょう。数学のような特定のプログラミングの分野では
verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)
といった説明的なフレーズよりsin(x)
といった広く認められている用語が好ましいです。このケースでは先例が略語は避けようというガイドラインに勝っています: 完全な単語はsine
ですが、"sin(x)"は何十年ものあいだプログラマーにとって一般的でした。そして数学者にとっては数世紀にわたってそうでした。
慣習
一般的な慣習
- 全ての O(1) でない計算型プロパティには計算量についてドキュメントに書きましょう。往々にしてプログラマーはプロパティアクセスは大した計算を引き起こさないと想定しています。心理的にはストアドプロパティだからです。その想定を破るかもしれないときは彼/彼女に警告を発するようにしましょう。
- 自由関数2よりメソッドとプロパティを選びましょう。自由関数は特別な場合にのみ使用されます:
1. 自明な
self
がない場合:min(x, y, z)
2. 束縛されていない一般的な関数の場合:
print(x)
3. 関数の文法が確立された分野の表記の一部である場合:
sin(x)
-
大文字/小文字の慣習に従いましょう。型とプロトコルの名前は
UppserCamelCase
にします。その他の全てのものはlowerCamelCase
にします。
アメリカ英語で全て大文字で表記される頭字語は大文字/小文字の慣習に従って全部を大文字または全部を小文字にするべきです。
var utf8Bytes: [UTF8.CodeUnit] var isRepresentableAsASCII = true var userSMTPServer: SecureSMTPServer
他の頭字語は通常の単語として扱うべきです。
var radarDetector: RadarScanner var enjoysScubaDiving = true
- 基本的に同じ意味を保つ場合や全く異なった分野で使用されるものである場合、メソッドは基部に共通する名前を持つことができます。
例えば次の例は本質的には同じことを行うメソッドであるため推奨されます。
// ✅ 良い例 extension Shape { /// Returns `true` iff `other` is within the area of `self`. func contains(_ other: Point) -> Bool { ... } /// Returns `true` iff `other` is entirely within the area of `self`. func contains(_ other: Shape) -> Bool { ... } /// Returns `true` iff `other` is within the area of `self`. func contains(_ other: LineSegment) -> Bool { ... } }
幾何学的な型とコレクションは別の分野のものであるため、この例も同じプログラムの中にあって構いません。
// ✅ 良い例 extension Collection where Element : Equatable { /// Returns `true` iff `self` contains an element equal to /// `sought`. func contains(_ sought: Element) -> Bool { ... } }
しかし、これらの
index
メソッドは異なるセマンティクスを持つため、異なる名前を持つべきです。// ⛔ 悪い例 extension Database { /// Rebuilds the database's search index func index() { ... } /// Returns the `n`th row in the given table. func index(_ n: Int, inTable: TableID) -> TableRow { ... } }
最後に、型推論に曖昧さを引き起こすため「戻り値の型によるオーバーロード」は避けましょう。
// ⛔ 悪い例 extension Box { /// Returns the `Int` stored in `self`, if any, and /// `nil` otherwise. func value() -> Int? { ... } /// Returns the `String` stored in `self`, if any, and /// `nil` otherwise. func value() -> String? { ... } }
パラメータ
func move(from start: Point, to end: Point)
- ドキュメントの役割を果たすパラメータ名を選びましょう。パラメータ名は関数やメソッドを使用する箇所には現れませんが、それでも重要な説明的役割を果たします。
ドキュメントを読みやすくする名前を選びましょう。例えば、次の例では名前によってドキュメントが自然に読めるようになります。
// ✅ 良い例 /// Return an `Array` containing the elements of `self` /// that satisfy `predicate`. func filter(_ predicate: (Element) -> Bool) -> [Generator.Element] /// Replace the given `subRange` of elements with `newElements`. mutating func replaceRange(_ subRange: Range, with newElements: [E])
しかし、次の例では名前によってドキュメントがひどくて英文法として間違ったものになります。
// ⛔ 悪い例 /// Return an `Array` containing the elements of `self` /// that satisfy `includedInResult`. func filter(_ includedInResult: (Element) -> Bool) -> [Generator.Element] /// Replace the range of elements indicated by `r` with /// the contents of `with`. mutating func replaceRange(_ r: Range, with: [E])
- 一般的な使い方を単純にできる場合は、デフォルトパラメータを利用しましょう。よく使う値がひとつだけあるようなパラメータはデフォルトパラメータを使う候補になります。
デフォルトパラメータは重要ではない情報を隠すので可読性が向上します。例えば:
// ⛔ 悪い例 let order = lastName.compare( royalFamilyName, options: [], range: nil, locale: nil)
はもっと簡単にできます。
// ✅ 良い例 let order = lastName.compare(royalFamilyName)
デフォルトパラメータをメソッドファミリに使用するのは通常望ましいことです。API を理解しようとする人の負荷を下げることができるからです。
// ✅ 良い例 extension String { /// ...description... public func compare( _ other: String, options: CompareOptions = [], range: Range? = nil, locale: Locale? = nil ) -> Ordering }
上の例は単純ではないかもしれませんが、次の例よりはかなりシンプルです。
// ⛔ 悪い例 extension String { /// ...description 1... public func compare(_ other: String) -> Ordering /// ...description 2... public func compare(_ other: String, options: CompareOptions) -> Ordering /// ...description 3... public func compare( _ other: String, options: CompareOptions, range: Range) -> Ordering /// ...description 4... public func compare( _ other: String, options: StringCompareOptions, range: Range, locale: Locale) -> Ordering }
メソッドファミリの全てのメンバそれぞれにドキュメントが必要で、利用者はそれらを個別に理解しなければなりません。どれを使うか決めるには全て理解する必要があります。ときどき驚くような関係 - 例えば、
foo(bar: nil)
とfoo()
は常に同じものとは限らないなど - もあるため、これはほとんど同じものの僅かな違いを探しだすようなうんざりする作業になります。デフォルトパラメータ付きのメソッドをひとつだけ使うようにするとプログラマの体験ははるかに優れたものになります。
- デフォルト値付きのパラメータはパラメータリストの後ろの方に並べましょう。通常、デフォルト値なしのパラメータの方がメソッドのセマンティクスにおいて欠かせないものです。またデフォルト値なしのパラメータによってメソッドが呼び出しの先頭が同じパターンになります。
引数ラベル
func move(from start: Point, to end: Point)
x.move(from: x, to: y)
-
引数を区別する意味がない場合は、全てのラベルを省略しましょう。例:
min(number1, number2)
,zip(sequence1, sequence2)
-
値を保つ型変換をするイニシャライザでは最初の引数ベルを省略しましょう。例:
Int64(someUInt32)
最初の引数は常に変換元であるべきです。
extension String { // Convert `x` into its textual representation in the given radix init(_ x: BigInt, radix: Int = 10) ← Note the initial underscore } text = "The value is: " text += String(veryLargeNumber) text += " and in hexadecimal, it's" text += String(veryLargeNumber, radix: 16)
しかし、値の定義域が狭くなる型変換では狭くなることを説明するラベルが推奨されます。
extension UInt32 { /// Creates an instance having the specified `value`. init(_ value: Int16) // ← Widening, so no label /// Creates an instance having the lowest 32 bits of `source`. init(truncating source: UInt64) /// Creates an instance having the nearest representable /// approximation of `valueToApproximate`. init(saturating valueToApproximate: UInt64) }
値を保つ型変換はモニック射です。すなわち、変換元の値の違いは全て変換後の値の違いとなります。例えば、
Int8
からInt64
への変換は値を保つ型変換で、全ての異なるInt8
の値は異なるInt64
の値に変換されます。しかし、逆方向の変換は値を保つことができません:Int64
はInt8
で表すことができる値より多くの値とることができるからです。注意: 元の値を得ることができるかどうかということと値を保つ型変換かどうかということはは無関係です。
-
最初の引数が前置詞句の一部である場合は引数ラベルをつけましょう。通常引数ラベルは前置詞で始まるべきです。例:
x.removeBoxes(havingLength: 12)
最初のふたつの引数が一つの抽象概念の一部を表している場合は例外です。
// ⛔ 悪い例 a.move(toX: b, y: c) a.fade(fromRed: b, green: c, blue: d)
そのような場合、前置詞の 後から 引数ラベルを始めましょう。抽象概念を明確にしておくためです。
// ✅ 良い例 a.moveTo(x: b, y: c) a.fadeFrom(red: b, green: c, blue: d)
-
それ以外の場合、第一引数が英文的なフレーズの一部であるならラベルを省略し、先行する語を名前の基部につけましょう。すなわち:
x.addSubview(y)
このガイドラインは第一引数が英文的なフレーズの一部を構成して いない 場合はラベルのままにしておくべきだということも意味しています。
// ✅ 良い例 view.dismiss(animated: false) let text = words.split(maxSplits: 12) let studentsByName = students.sorted(isOrderedBefore: Student.namePrecedes)
フレーズが正しい意味を伝えることが大切だということに注意してください。次の例は英文法的には正しいかもしれませんが、間違ったことを表しているでしょう。
// ⛔ 悪い例 view.dismiss(false) // Don't dismiss? Dismiss a Bool? words.split(12) // Split the number 12?
- その他の全ての引数にラベルをつけましょう。
特別な場合についての指示
- API の一部になっている場合は、クロージャパラメータとタプルのメンバにラベルを付けましょう。
これらの名前には説明する力があります。ドキュメンテーションコメントから名前によって言及でき、タプルのメンバに意味でアクセスすることができるようになります。
/// Ensure that we hold uniquely-referenced storage for at least /// `requestedCapacity` elements. /// /// If more storage is needed, `allocate` is called with /// `byteCount` equal to the number of maximally-aligned /// bytes to allocate. /// /// - Returns: /// - reallocated: `true` iff a new block of memory /// was allocated. /// - capacityChanged: `true` iff `capacity` was updated. mutating func ensureUniqueStorage( minimumCapacity requestedCapacity: Int, allocate: (byteCount: Int) -> UnsafePointer<Void> ) -> (reallocated: Bool, capacityChanged: Bool)
クロージャで使われた場合、技術的には引数ラベルですが、パラメータ名のように名前をつけ、ドキュメンテーションコメントから言及してください。関数本体の中のそのクロージャを呼び出しは、第一引数を名前の基部に含まない関数の呼び出しと同じように読めるからです。
allocate(byteCount: newCount * elementSize)
- あいまいなオーバーロードを避けるため型制約がないポリモーフィズム(すなわち:
Any
,AnyObject
, 型制約のないジェネリックパラメータ)については特に注意しましょう。
例として、次のオーバーロードについて考えてみましょう:
// ⛔ 悪い例 struct Array { /// Inserts `newElement` at `self.endIndex`. public mutating func append(_ newElement: Element) /// Inserts the contents of `newElements`, in order, at /// `self.endIndex`. public mutating func append(_ newElements: S) where S.Generator.Element == Element }
これらのメソッドはセマンティクス上のファミリを構成します。はじめは引数の型は明らかに異なるように見えます。しかし、
Element
がAny
の場合、単一の要素と要素のシーケンスの型が同じになります。// ⛔ 悪い例 var values: [Any] = [1, "a"] values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]?
この曖昧さを取り除くにはふたつ目のオーバーロードをより明確にします。
// ⛔ 悪い例 struct Array { /// Inserts `newElement` at `self.endIndex`. public mutating func append(_ newElement: Element) /// Inserts the contents of `newElements`, in order, at /// `self.endIndex`. public mutating func append(contentsOf newElements: S) where S.Generator.Element == Element }
新しい名前がドキュメンテーションコメントに以前よりもあっている点に注目してください。このケースでは実際にドキュメンテーションコメントのおかげで API の作者が問題に目を向けるようになりました。