-
Notifications
You must be signed in to change notification settings - Fork 120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docs: "APIデザイン ガイドライン"を追加 #870
Conversation
docs/guide/dev/api-design.md
Outdated
* 「範囲」を表すデータ型が言語レベルである場合は、可能な限りそのデータ型を用いてAPIを構成するべきです。 | ||
* 例えばSwiftやRubyでは`Range`という名のデータ型を使って表現します。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これ、その言語のRangeがサポートしているものと、APIがサポートするrangeが異なる場合があってややこしいかもです!
例えばたしかPythonはrange(0, 3, -1)
とかにすると、逆向きの範囲を指定できます。たぶん[2, 1, 0]
。
あと飛び飛びの範囲も指定できます。range(0, 4, 2)
だとたぶん[0, 2]
。
このとき、例えばrender()
で期待されるレンダリング結果は何になるべきなのか考えれなそうです。
一言でいうと、range
は範囲だけを表してるものではないので、範囲だけを表したい場合にrangeを使うのは良くないのかもと・・・(今気づきました)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これの対象はSynthesizer::render
の引数ですね。なのでPythonだとおそらく、仮にstepに対応したとしてもrange
― というより多分slice
オブジェクトですが ― を引数に取るのはやめた方がよいという感じになると思います。
# こっちより多分、
synthesizer.render(audio_feature, slice(0, length, 10))
# こう
synthesizer.render(audio_feature, 0, length, 10)
多分正しいのはRust APIのrender
の引数をRange
にした上で、そのrender
を名指しして他のAPIもそうしろという感じになると思うのですが、今はまだ #867 でもそうなっていないわけでどうしたものか…という感じでいます。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
あ〜〜〜なるほどです、RustのRangeは本当に範囲で、かつend >= startなんですね!
たしかにすくなくともRustは使ってもよいのかも。
いやでもrender()はend=startのときにエラーを返す設計になるかもな気がします。
個人的には一旦何も書かず、render側でRangeを使おうってなったら書き足すのが良い気がしてます。
(Range使いたい寄りだと把握しました!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
なるほど。そうしましょう!
0832bdd
(#870)
(消したと同時にtodoを残しました)
render()はend=startのときにエラーを返す設計になるかもな気がします。
start > end
はともかくstart == end
だと空と解釈されると思います。他言語的にもそれが自然かと。
(実装レベルだと空テンソルをONNX Runtimeに渡したらどうなるのか私は知りませんが、別途チェックすればよさそう)
start > end
だと、この(↓)表とかを考えると慣習に反しうりそうなので補足は要りますね。
#867 (comment)
ちなみにRustでリテラルを使って20..10
とか書くと、deny
レベルで怒られます。
[追記] エラーのスクショがちょっと適切じゃなかったので差し替え
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
start
は含むけどend
は含まないの、普通の感覚で行くとものすごい分かりにくいということに気づきました 😇
Rangeだとかがそうなってるのは、「そう設計しておくと色々便利だから」以外に説明が思いつかず、APIで同じことをすると不慣れな人にとってはなぜそうなっているかわからない気がしました。。。
(例えばstart: 10・end: 10だったとき、10を含まないのが直感的じゃない)
開区間、あるいは閉区間になってることが分かりやすい用語を探しました。
採用するかは置いといて、min
とmax
が一番わかりやすそうではありました。(けど予約語なことも多そうなので迷いどころ)
from
とto
も良さそうだけど、小さい値がどちらかは分かりづらそう。
offset
とcount
(length
)も悪くなさそう。
https://chatgpt.com/share/673102c7-9858-8008-a5d1-fb36a5ac0382
これめちゃくちゃ厄介ですね。。。
RustはRangeで良いかもだけど、例えばC APIでstart・endにしてendは開区間にすると、まあ間違いなく閉区間だと思ってバグって使う人が出てくる気がする。。
まあrender()したものを聞いたときに、音声が不連続になるので違和感には気づけそうですが・・・・・・・。
追記:おそらく(offset, length)
が使いやすい&わかりやすい気がする。
RustはRangeでも良いかも。このときRange(offset, offset+length)
と書けるので相性も良い。
start=endのときの挙動は、サポートしてもしなくてもどちらでも良さそう。どちらにせよドキュメントに明記したい。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これ(↓)は以前私がした提案ですが、C APIとJava APIだけKotlinにならってbegin
/start
, end_exclusive
とし、他の言語では言語に合った名称 ― 例えばPythonではstart
, stop
― にしておけば十分なのではと思っています。
#854 (comment)
というのも
- プログラミングの経験がある人で
$[i, j)$ か$[i, j]$ かを気にせずにコードを書く人は多分ほぼいない - "start"や"end"といった語彙が言語の標準にある場合、その語彙はその言語において一意だとしてよいはず
- 例えばPythonで"start"と"stop"という言葉が出たら、それは
$[\texttt{start}, \texttt{stop})$ という区間を意味する。NumPyもそう - [追記] Rustでは"end"といってもinclusiveかexclusiveかは一意ではない(後述するが実は"start"も一意ではない)けど、
Range<_>
とRangeInclusive<_>
といったくくりのオブジェクトで取り回す文化であるため、問題ではない
- 例えばPythonで"start"と"stop"という言葉が出たら、それは
- [追記] もし言語レベルでの明確な単語が無く、また範囲データ型のようなものが無い場合でも、Kotlinにならって
end_exclusive
とでもすればよい - 1., 2., 3.を踏まえると、プログラミング初学者に対しての役割はドキュメントが持つべき
だと思った次第です。
参考:
VOICEVOX/voicevox_core#854 (comment)
Ruby, C++「begin...end
」
Python 「start:stop
」
Rust, Scala, C# 「start..end
」
Kotlin 「start..<endExclusive
」
Swift 「first..<last
」
Erlang 「lists::seq(From, To)
」
https://discord.com/channels/879570910208733277/893889888208977960/1296122640406417468
(offset, length)
ユーザーのニーズには多分合っていると思うのですが、今現在はみ出すような範囲指定を不正入力扱いすると決めているため、"end"側の処理については絶対位置を意識せざるを得なくなり不便なのでは、という気もします。
ちなみに豆知識その2
Rustでは
use std::ops::Bound;
let a = [1, 2, 3, 4, 5];
// {`Included(i)`, `Excluded(i)`, `Unbounded`}²で、3²=9通り
// [i, j)
assert_eq!([2, 3], a[(Bound::Included(1), Bound::Excluded(3))]);
assert_eq!([2, 3], a[1..3]); // ショートハンド
// [i, j]
assert_eq!([2, 3, 4], a[(Bound::Included(1), Bound::Included(3))]);
assert_eq!([2, 3, 4], a[1..=3]); // ショートハンド
// [i, 右端]
assert_eq!([2, 3, 4, 5], a[(Bound::Included(1), Bound::Unbounded)]);
assert_eq!([2, 3, 4, 5], a[1..]); // ショートハンド
// (i, j)
assert_eq!([3], a[(Bound::Excluded(1), Bound::Excluded(3))]);
// ショートハンドは無い
// (i, j]
assert_eq!([3, 4], a[(Bound::Excluded(1), Bound::Included(3))]);
// ショートハンドは無い
// (i, 右端]
assert_eq!([3, 4, 5], a[(Bound::Excluded(1), Bound::Unbounded)]);
// ショートハンドは無い
// [左端, j)
assert_eq!([1, 2, 3], a[(Bound::Unbounded, Bound::Excluded(3))]);
assert_eq!([1, 2, 3], a[..3]); // ショートハンド
// [左端, j]
assert_eq!([1, 2, 3, 4], a[(Bound::Unbounded, Bound::Included(3))]);
assert_eq!([1, 2, 3, 4], a[..=3]); // ショートハンド
// [左端, 右端]
assert_eq!([1, 2, 3, 4, 5], a[(Bound::Unbounded, Bound::Unbounded)]);
assert_eq!([1, 2, 3, 4, 5], a[..]); // ショートハンド
render
の話になってきたので。
CC: @Yosshi999
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
たぁしかにlengthにすると最後部分の計算が面倒ですね…。
(chagpt君も全く同じ意見だった https://chatgpt.com/share/67319e1d-53b0-8008-8a27-1ebec0b8cef3 )
少なくともmin,max指定はダメそうですね!!
例えば将来秒指定するAPIができた時に詰みそう。
lengthで範囲を指定し間違えるとエラーになってくれますが、endの解釈を間違えると変な音が出るだけなので、そういうものかと品質の誤解になりえるかも。
まーーーでもこれだけ考えて決定打が出ないということは、わりとどちらも一長一短で決まりな気がしますね!!
とりあえず今のところ、endにしてドキュメント書くでも、lengthにするでもどちらでも良さそう!!
また時間置きますかぁ。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!!
レビューリクエストありがとうございます!!
`Synthesizer::render`の引数の範囲指定部分を次のようにする。 Rust API: `range: Range<usize>` Python API: `start: int, stop: int` 以前の議論: #870 (comment)
内容
#632 に続く形で、独立した"APIデザイン ガイドライン"を追加する。
内容としてはとりあえず
Rust 以外の言語の API
として、newtype以外も言及したものを用意する。関連 Issue
Refs: #631, #854 (comment)
その他