Skip to content

Instantly share code, notes, and snippets.

@koher
Last active Sep 18, 2021
Embed
What would you like to do?

----- March 28th, 2016 -----

koher [11:39 AM] joined #discussion, and invited @omochimetaru. Also, @hkato193 joined, @shingt joined, @norio_nomura joined.

koher [11:42 AM]

Yeah, we extensively discussed adding a Result type internally, but ultimately couldn't justify it. The only real use case we could see in the wild was for threading errors through CPS-inversion-style abstractions like async promises, something we hope to provide proper language support for. More generally, expressing effects as monadic values is a pretty awful abstraction; aside from polluting the Internet with an endless deluge of unhelpful tutorials, they also don't compose cleanly, they impose nesting where is desired—you have to pick between Result<Async<T>> and Async<Result<T>>, or build ResultT<AsyncT<Identity>><T> out of monad transformers—and they don't do the natural thing when used with other higher-order abstractions—if you're mapping a throws function over a collection, you probably want to propagate that error like rethrows does, not end up with a collection of Result<T>. I'd rather see us adopt an extensible algebraic effects system, something like http://www.eff-lang.org, which provides a framework for throws, async and other control flow effects to be cleanly composed and abstracted over. I see throws as the first seed of that. -- Joe Groff https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160314/012545.html

ikesyo [11:42 AM] joined #discussion

koher [11:43 AM] これを読む限りどうもコアチームはモナド的なエラーハンドリングを目指してなさそうなので、最近少し Result と距離を置いた方がいいんじゃないかなぁという気がしてるという話です。

[11:44] Result がたくさんでてきたら Haskell の do 記法みたいなのがないと flatMap チェーンじゃ辛いねという話があって、

[11:44] アプリカティブはみんなそれでエラーハンドリングしてるイメージがわかないし、ということがあって、 (edited)

[11:45] try! Swift では Resultdo, try, catch を融合できる方向がいいんじゃないかって話をしたけど、

[11:46] ( Error Handling Rationale and Proposal にも Result が必要だって書いてあったし、 swift-evolution でも typed throws の話に決着がついたら Result について話しそうな気配があったけど)

dictav [11:47 AM] joined #discussion. Also, @huin joined.

koher [11:48 AM] そっちの方向にいかなさそうなら色んなものを Result まみれにしておくよりも、今は throws で書いておいた方がいいのかなぁと。

[11:48] Eff がわからないからあまり Joe Groff の言ってる方法のイメージがわかないですが。

[11:49] で、じゃあ今の段階で throws で非同期処理どうすんの?って話があって、

[11:50]

func download(url: NSURL, completion: Result<NSData, NetworkError> -> ()) {  }

[11:50] の代わりに

[11:51]

func download(url: NSURL, completion: (() throws -> NSData) -> ()) {  }

[11:51] のように書くのはどうかなぁという話をしてました。

econa77 [11:52 AM] joined #discussion

koher [11:54 AM] @omochimetaru 的には、 () throws -> NSData は参照透過であることが型で保証されてないから

[11:55] 型付けが弱くなっちゃってる部分が微妙だと。

ooba [11:55 AM] joined #discussion

koher [11:56 AM] 他の案として、 JS の Promisethen みたいに成功と失敗の二つのコールバックを渡せる方法が挙がったけど、成功と失敗をうまく統合して処理できなから微妙な気がします。

omochimetaru [11:59 AM] 参照透過性の論点を具体的な例で言い換えると、そのcompletionで受け取るクロージャを、例えば2回3回呼び出したら毎回同じ結果になるのかとか、それを呼び出す事がdownloader側に対する通知の機能(例えば、クリーンナップタスクが起動するとか)を含むのではないか、ってライブラリユーザーが無駄に混乱するという事です。 (edited)

koher [12:00 PM] 昔話してた、副作用を持たないことを保証する型みたいなのがあるといいね。

omochimetaru [12:01 PM] なるほど

koher [12:01 PM] 副作用を持つ関数は副作用を持たない型には代入できないけど逆はできる。

[12:01] クラスも、イミュータブル→ミュータブルは代入できるけど逆はできない。

[12:02] Swift が出た時は mutating がそれか?!と思ったけどちょっと違った。

omochimetaru [12:02 PM] 仮に参照等価でも、NSDataの異なるインスタンスが毎回生成されるか、同じインスタンスが毎回返されるかの挙動の違いは起こりうる?

[12:02] イミュータブルだから実質問題にはならないかな

koher [12:03 PM] 参照透過なら同じインスタンスが返るんじゃない?

[12:03] そうとは限らないか?

omochimetaru [12:03 PM] 例えば内部でchar * をキャプチャしたクロージャになってれば

[12:03] あ、それだとメモリ管理的に破綻しそうだが

koher [12:03 PM] まあでも、同じインスタンスかどうかは別に問題じゃないと思う。 (edited)

[12:05] うーん、結構いいと思うんだけどなぁ。

func download(url: NSURL, completion: (() throws -> NSData) -> ()) {  }

omochimetaru [12:06 PM] Resultで来たほうが嬉しいですよ

koher [12:08 PM] 前は throws きらいだったけど、

[12:08] do, try, catch って結局 Haskell の do 構文だよなって認識になってから

[12:09] 積極的に使ったらいいんじゃないかって気分。

toshi0383 [12:11 PM] joined #discussion

koher [12:11 PM] Result だと複数まとめて処理するのは? flatMap のネスト?アプリカティブ? dematerialize

omochimetaru [12:12 PM] それこそライブラリのユーザーサイドだから

[12:13] 好きな書き方

[12:13] アプリ全体で統一すればいいけど

koher [12:13 PM] どれも辛くないかなぁ。

omochimetaru [12:13 PM] throws派の人もdematerializeすれば

[12:13] 1手でtry-catchスタイルに持っていけるから

[12:13] まずはResultで渡ってくるのが便利だと思う

[12:14] 複数まとめてって、何が複数の話ですか?

koher [12:14 PM] 複数の Result をアンラップしたいときとか。

[12:14] たとえば JSON をデコードしてて結果が Result で大量に返ってきたときに、

[12:15] 非同期から話ずれちゃってるか。

omochimetaru [12:15 PM] そのケースはthrowsのほうが便利かもと思いつつある

koher [12:15 PM] そもそも同期なら throws でいいんだけど。

[12:16] で、 Result の先行きも怪しくなってきたし、 (edited)

omochimetaru [12:16 PM] それはある

koher [12:16 PM] throws 便利という認識に変わってきたし

[12:16] なら全部 throws に寄せちゃいたいなぁという中で

[12:17] 非同期はどうしよう?って話なんよね。

omochimetaru [12:17 PM] さっきのケースで

[12:17] ()で呼び出してT | Errorになるのと

[12:17] .demateiralize()で呼び出してT | Errorになるの

[12:18] 10文字ぐらいしか変わらない代わりに

[12:18] 型的に、2者択一の値であるという確信が得られる

[12:18] JoeGroffの最初のやつって、非同期に言及してないような?

koher [12:19 PM] うーん、コールバックで (() throws -> NSData) -> ()() throws -> NSData が副作用を持つなんて、考えづらいと思うけどなぁ。

omochimetaru [12:19 PM] 違うのか、非同期の部分もコールバックスタイルじゃなくてPromiseにするからカオスになっていくって話なのかな

koher [12:19 PM] というか、 Error Handling Rationale で非同期とかで Result が要るね、って書かれていて

[12:20] それを引用して Resultthrows を融合しようよって話をしたら

[12:20] Result やめたよ、って。

[12:21] 確かに Foo<Bar<T>> なのか Bar<Foo<T>> なのかの問題はあって、

[12:21] これの扱いは面倒くさいから、

[12:22]

which provides a framework for throws, async and other control flow effects to be cleanly composed and abstracted over.

[12:22] これが本当ならそっちの方がいい気がする。

[12:22] イメージわいてないけど。

omochimetaru [12:26 PM] Effをやった範囲のイメージだと

[12:26] コードが自動的に継続渡しになって、その継続を受ける方の実装が外で書けるから

[12:27] tryしながら正常系を書いていって、エラーだったら脱出する制御と

[12:27] awaitしながら非同期呼び出しを書いていって、見かけ上シーケンシャルなコードが

[12:27] どっちも、tryとかawaitとかの目印が無くて

[12:27] 素直な正常系だけを書くことができて

[12:28] みたいな感じだったけど

[12:28] 高度な例は解読できなかったから組み合わせる場合にどうなってくるとかはよくわからない

koher [12:28 PM] そもそも今の do には戻り値がないからそこが問題になることはないのか。

[12:29] 例えば Swift に async, await が追加されたとしても、

[12:29]

do {
  let foo = try makeFoo()
  let bar = await makeBar()
  
} catch _ {
  
}

[12:30] としても、結果を受ける部分がないから Result<Promise<T>> なのか Promise<Result<T>> なのかみたいな問題が起こり得ない?

omochimetaru [12:31 PM] うおどうなんだそれ

[12:32] え、でもそれだと

koher [12:32 PM] ↓これにしたら、 foo で失敗したら同期的に、 baz に失敗したら非同期的に catch に入る?

do {
  let foo = try makeFoo()
  let bar = await makeBar()
  let baz = try makeBaz()
  
} catch _ {
  
}

omochimetaru [12:33 PM] その例でmakeFooとmakeBarの返り値のあり得る型ってなんですか?

[12:33] makeFooは Result or Result<Promise> ?

[12:33] あ、違うか、throwsだから・・・

koher [12:33 PM]

func makeFoo() throws -> Foo
func makeBar() async -> Bar
func makeBaz() throws -> Baz

(edited)

norio_nomura [12:34 PM] C# がそんな仕組みだった気がする。

omochimetaru [12:34 PM] C#のasync/awaitは例外仕様とくっついてて、awaitした時に例外飛ぶとcatchできた気がしますね。

[12:35] makeBar() async -> Bar ; makeBar() async throws -> Bar

[12:35] で、どっちでもいいようにできるのか。

koher [12:35 PM] うん。そんな感じの async/await が Swift に馴染みそう。

335g [12:36 PM] joined #discussion

koher [12:36 PM] 同期的にも非同期的にも catch に飛んできたらややこしいことにならないかな・・・。

omochimetaru [12:36 PM] たしかに入れ子問題はない・・・?

norio_nomura [12:36 PM] https://msdn.microsoft.com/ja-jp/library/0yd65esw.aspx

koher [12:38 PM] @norio_nomura: 確かに catch に非同期的に飛んでそうですね。

[12:39] これって await 前に例外発生した場合も同じ catch に同期的に飛ぶんですよね?

omochimetaru [12:39 PM] C#の場合try-catchをするメソッド自体がasync関数になるから、同期と非同期の混乱はないってことですかね

koher [12:39 PM] それは今のところ Swift もそうじゃない?

[12:40] catch 相当の wait みたいなのを作って

[12:40] 非同期→同期を実現しないと

[12:40] 外側の関数は必ず async じゃないといけなくなる。

omochimetaru [12:40 PM] C#はまさにそうですね、というかWinRTかな?

[12:41] 同期的な待機ができないように徹底されてた

[12:41] async修飾がどんどん上まで伝搬します Javaのthrowsみたいに

koher [12:41 PM]

do {
  let a = await asyncGetInt()
  let b = await asyncGetInt()
  let sum = a + b
  
} wait

[12:41] こんなことができてもいい気がする。

omochimetaru [12:43 PM] C#で

[12:43] エラーが非同期に戻るときは

[12:43] asyncになってるってことですかね?

tarunon [12:43 PM] joined #discussion

omochimetaru [12:44 PM] asyncがあるのにわざわざコールバック受けるシグネチャにするメリット無いしそうだよな

[12:44] てことは、そもそもの議論は、async/awaitが入れば解決して、コールバックが無くなる

[12:44] ってのが将来的な方向性の見込みで

[12:44] それを見据えた一時的なAPIをどうするか

koher [12:45 PM] async/await はまだわからないんじゃない? Swift 3.0 以降だろうし。

omochimetaru [12:45 PM] 来てほしいなあ

norio_nomura [12:45 PM] 言語レベルでマルチスレッドに対応するってことですよね?

[12:45] くるかなあ?

omochimetaru [12:45 PM] マルチスレッドとは限らないような?

koher [12:45 PM]

Concurrency: Swift 3.0 relies entirely on platform concurrency primitives (libdispatch, Foundation, pthreads, etc.) for concurrency. Language support for concurrency is an often-requested and potentially high-value feature, but is too large to be in scope for Swift 3.0.

akio0911b [12:46 PM] joined #discussion

omochimetaru [12:46 PM] 内部的にはPromiseへの変換で、awaitの発火はGUIとかのコールバックと同じだから、メインスレで走行します

[12:47] 言語仕様からスレッディング仕様を規定せずにスケジューラーとかだけでプラットフォーム固有実装でasync/awaitが実現できるかどうかよくわからない

koher [12:48 PM] PromiseK で Promise 自体を実現することはマルチスレッド関係なくできたよ。ロックだけは必要だったけど。

[12:48] だから、 async/await もできるかも?

omochimetaru [12:49 PM] 3.0にconcurrency入らない事が確定してる中でasync/awaitが来るかどうかw

[12:49] でも逆に言うと

[12:50] 3.0でasync/await入らないなら、その世代でのコールバックでエラー戻す推奨パターンはどうなるの問題は残ってるわけか

koher [12:50 PM] うん。 3.0 では来ないと思う。

[12:50]

The only real use case we could see in the wild was for threading errors through CPS-inversion-style abstractions like async promises, something we hope to provide proper language support for.

[12:51] これからすると3.0以降では検討されそう?

omochimetaru [12:51 PM] 一個思ったんですけど

[12:51] async/awaitが、エラー(throws)仕様を含む、非同期仕様になるとして

[12:52] 現実のプログラマとして、Rx派、RAC派の人たちが居て

[12:52] あれは、エラーと例外と、さらに、ストリーム(Tがn個返る)っていう機能まで持ったモナド(?)

norio_nomura [12:52 PM] C# の async/await のフローはここが参考になる? https://msdn.microsoft.com/ja-jp/library/hh191443.aspx#Anchor_2 >見てる人。

omochimetaru [12:52 PM] に寄せていっちゃうわけで

[12:53] throwsもasyncも使わず、全部Observableにしとけばいいやってなりそうだけど

[12:53] それもasyncみたいに演算子化/言語機能化

[12:53] できうるのかな。

[12:54] throwsの良いところとしてはthrows -> () でもキャッチ強制ができてるという点があるので、それができたらよりRxも捗ると思うんだけど。

[12:54] error_unused_resultは実用的には役立つけど、結果を使いつつもエラーの事忘れてるパターンはありえるし。

koher [12:55 PM] 言語として目指す方向と違う方向のスタイルのライブラリやそのユーザーが発展すること自体はしかたないんじゃないかな。

omochimetaru [12:55 PM] そもそもRxがC#のMVVMと同じルーツなのだっけ?

koher [12:55 PM] 多様性としても悪いことはないと思うし。

[12:56] でも、言語としての方向性はそういう人たちの存在とは独立に、あるべき方向性を目指すんじゃない?

[12:56] Rxもマイクロソフト系じゃないっけ?

norio_nomura [12:56 PM] Rx.NET ですかね?

[12:57] おっと、web としてはこっち http://reactivex.io

koher [12:58 PM]

あれは、エラーと例外と、さらに、ストリーム(Tがn個返る)っていう機能まで持ったモナド(?) なんでもできるモナド的な方向性は筋が悪いと思う。

[12:58] モナドでなくてもだけど。

[12:58] エラーが発生しないかもしれないものも必ずエラー処理できる(もしくは強制する)ってことだし。

omochimetaru [12:59 PM] RxだとストリームじゃなくてEventで渡す可能性もあるかも。

koher [12:59 PM] 強制したら無駄だし、強制しなかったら安全じゃないし。

omochimetaru [12:59 PM] まあ確かに

koher [12:59 PM] ばらばらの機能として提供してて、好きに組み合わせられるのがいいんじゃないかな。

omochimetaru [12:59 PM] @norio_nomura: なんか、WinRT案件をちょっとやったときに

[1:00] @norio_nomura: Observable的な型がちらほら見かけて

koher [1:00 PM] リンクありがとうございます。 > @norio_nomura さん

omochimetaru [1:00 PM] あれがRxと繋がるものだったのか、なんだったのかなーと・・・

[1:01]

ばらばらの機能として提供してて、好きに組み合わせられる throws無しasyncもありえるってことですか?

koher [1:02 PM] うーん、あってもいい気がする。

omochimetaru [1:02 PM] func nya() async throws -> String ; エラーがありうる非同期メソッド func wan() async -> String ; エラーがありえない非同期メソッド

[1:02]

func nya() async throws -> String ; エラーがありうる非同期メソッド
func wan() async -> String ; エラーがありえない非同期メソッド
do { 
  let x = await nya()
  let y = await wan()
} catch {

}

(edited)

[1:02] nyaの方のawaitは暗黙にtryも書かれている感じ?

koher [1:03 PM] 論理的にエラーが発生しない非同期処理(N秒待つだけ)でエラーが発生した場合は Universal error として扱うとか。

[1:03] throws は Recoverable error のためのものだから、 Universal error はスルー。

omochimetaru [1:04 PM] クリックされたら戻るasync関数とかを想定すればありえますね

koher [1:04 PM] ↓こうなんじゃない?

func nya() async throws -> String ; エラーがありうる非同期メソッド
func wan() async -> String ; エラーがありえない非同期メソッド
do { 
  let x = await try nya()
  let y = await wan()
} catch {

}

omochimetaru [1:05 PM] そうすると気になるのが

func piyo() throws async -> Stringlet z = try await piyo()

になるのかどうかで・・・

koher [1:05 PM] 順番変えても等価じゃない?

[1:05] さっきから思ってるんだけど、 Union type が Foo|ErrorA|ErrorB == Foo|ErrorB|ErrorA なのに

omochimetaru [1:05 PM] そんなきもする

koher [1:06 PM] モナドだと Either<Foo, Either<ErrorA, ErrorB>> != Either<Foo, Either<ErrorB, ErrorA>> になっちゃうように

[1:07] モナドだと Result<Promise<T>> なのか Promise<Result<T>> なのかが問題になるけど

[1:07] throwsasync だと throws async なのか async throws なのか、 try await なのか await try なのかが問題にならないとするとおもしろい。

omochimetaru [1:08 PM] たしかに。

tarunon [1:08 PM] Result<Promise<T>>Promise<Result<T>>はそもそも意味が違うような

koher [1:08 PM] そうですね。意味は違いますね。

[1:09] 現実的には Promise<Result<T>> しか要らなさそう?

[1:09] そうとも限らないか。

[1:10] 入れ子の順序にこだわる必要のない(少ない)ものについてもそれを扱わないといけないところがモナドの辛さなのかなと。

omochimetaru [1:10 PM] Result<Promise>はPromiseさえ取り出せた場合には失敗しない非同期タスクになるから、 失敗の確認が同期的に(タスク発火前に)確定できるけど

koher [1:11 PM] そりゃそうなんだけど

omochimetaru [1:11 PM] Tを取り出すのに非同期タスクの発火が必要な以上、非同期にエラーが戻ってくるとしてもあまり困らないような気もするな。

[1:11] どうなんだろう、それって、なんでもできるモナドに押し付ければいい発想な気もして

koher [1:11 PM] その場合って Result の中に Promise 入れなきゃいけない意味があんまない気がする。

[1:11] まず分岐してから非同期処理投げればいいわけで。

[1:12] 自分が関数書くときに Result<Promise<T>> を作りたいユースケースが思いつかないような?

omochimetaru [1:13 PM] たとえば

[1:13] 引数でURLの文字列をうけて

[1:13] ダウンロード結果のNSDataを返すようなパターンで

[1:13] 引数のURLがhttp:// みたいな (edited)

[1:13] !!

tarunon [1:13 PM] w

omochimetaru [1:13 PM] ただしい文字列じゃなかったら。

koher [1:14 PM] 同様に、 (…) throws async -> Foo は必ず非同期でいい気がする。

[1:14] その場合って

tarunon [1:14 PM] 事前にチェックするか、Errorが起きた場合も非同期として扱っても差し支えない

omochimetaru [1:14 PM] ↑の例は実際RACで作ってたときあったけどSignalProducerに全部押し付けました・・・

koher [1:14 PM] 非同期処理でエラーが発生するケースもあるわけで

[1:14] モナド的には

tarunon [1:14 PM] Result<Promise<Result<T>>>

koher [1:14 PM] Result<Promise<Result<T>>

tarunon [1:14 PM] 発狂待ったなしですね

omochimetaru [1:14 PM] Result<Promise<Result>

koher [1:14 PM] みんな同じことをw

omochimetaru [1:14 PM] ww

[1:14] いや、書いてみたかった

tarunon [1:15 PM] まさにそのパターンこの間書いたんですよね

omochimetaru [1:15 PM] mjsk (edited)

koher [1:15 PM] で、多分これを Promise<Result<T>> として返す関数にするだろうから

tarunon [1:15 PM] というか

koher [1:16 PM] throws async -> TPromise<Result<T>> で良さそうな気が。

tarunon [1:16 PM] Observableも実体はPromise<Result>ですし

[1:16] 実体というか実際というか

[1:16] 多分そこはそれで良くって (edited)

[1:17] なんで、上にあったように、swift doがHaskellのdo記法相当のものなら

[1:18] RACとかRxでやってるモナドに詰め込んだ世界が、全部doの中に降りてくる?みたいなかんじ

[1:18] になると良いのかな

koher [1:18 PM] それで、個々が区別されていて、自由に組み合わせられるとかがいいですね。

[1:19] throws/try/catch, async/await/wait 以外に何があるかわからないですけど・・・。

[1:19] モナドごとにキーワード用意するわけにいかないですしね。

[1:19] キーワード用意したものについては組み合わせた時の入れ子順を規定しないといけないから

[1:20] どっちの順番も必要とかなるとうまくいかないし、

omochimetaru [1:20 PM] Optionalモナド?

koher [1:20 PM] 三つ以上の順番も考えだすと辛そう・・・。

[1:21] うん、だから OptionalResult に統合して、 Resultthrows を統合して

omochimetaru [1:21 PM] Resultと同じでtryで剥がせればいい説

koher [1:21 PM] ってのを思い描いたんだけど、 Result は却下されたので・・・。

[1:21] うん、それはまだ提案しようと思ってるんだけど、

omochimetaru [1:22 PM] なんか非同期とエラーみたいに決定的に違うやつが思いつかないんですよね

koher [1:22 PM] nil だった場合は NilError みたいなものになるべきなのか、 catch しない場合は throws 付ける代わりに Optional を返すようにするのかで

[1:22] 話が違ってきて、

[1:23] 後者の場合は throws の世界と混ざっちゃってコード読みづらそう。

[1:23] throws と混ぜて使ったらどうなるの?って話もあるし。

[1:25] 複数まとめて処理するのが辛い問題は Result だけでなく Optional にもあって、 Optional binding でもある程度はできるけどどうしてもできないケースもあるし、

[1:25] やっぱり do, try, catch できたらいいと思う。

[1:26]

do {
  let sum: Int = (try Int(str1)) + (try Int(str2))
} catch _ {
  ...
}

tarunon [1:27 PM] Optionalに、 throws -> Wrapped なアンラップ用のオペレータがあれば解決する気はする

[1:27] Result よろしく

omochimetaru [1:27 PM] そこだけ

Int(str1).flatMap { x in Int(str2).map { y in x + y} }

するのはやっぱ無いっすね

[1:28] @tarunon: 同じ事を考えて、!がそれだったらよかったのではとちょっと 思った

[1:28] !はやっぱりそうじゃないね、っていう話をkoherとしたんですけど。

tarunon [1:29 PM] try! Int(str)!で良いじゃんとか思ったり

omochimetaru [1:29 PM] !をT or crashから throws -> Tに変えるとそうなりますよね (edited)

koher [1:30 PM] でも ! は失敗したときには死を覚悟っていう意味で統一されてるから

[1:30] ! でエラーが飛んでハンドリングは良くないと思うんですよね。

tarunon [1:31 PM] まあそれはそれとして、間にオペレータもう一個足すのは

[1:31] 現実的な解法な気はするんですよね

[1:31] というか

omochimetaru [1:32 PM] まあ個人的には .value でも .unwrap() でもいいけど・・・ (edited)

koher [1:32 PM] .valuethrows できないから

tarunon [1:32 PM] それですね、別に無理に記号使わなくても

koher [1:32 PM] func value() throws -> T かなぁ。

omochimetaru [1:33 PM] OptionalはResultの小さい版なのに、Resultはそもままtryできて、よりメジャーでとっつきやすいはずのOptionalは、一手間かかるってのは気持ち悪さはある

koher [1:33 PM] でもその .value() が辛くなるから直接 try でアンラップできたらいいねという話な気が。

[1:33] 2.2 時点の現実的な案としてはありだと思うけど。 (edited)

[1:34]

do {
  let sum: Int = (try Int(str1).value()) + (try Int(str2).value())
} catch _ {
  
}

(edited)

[1:34] 長い・・・。

[1:35] SwiftyJSON でデコードしてイニシャライザに渡すときとか発狂しそう・・・。

tarunon [1:35 PM] .value()Int() の外側な気がする

koher [1:35 PM] あ、本当ですね。

omochimetaru [1:35 PM] 逆にResultに対するOptionalの非型化を func eat() may -> T

[1:37] Resultがthrowsによって不要になるならOptionalなんて要らなかった・・・?ってことになる・・・?

koher [1:37 PM]

enum Result<T, E: ErrorType> {  }
struct NilError: ErrorType {}
typealias Optional<T> = Result<T, NilError>

omochimetaru [1:37 PM] プロパティとかあるから流石に無茶か

koher [1:37 PM] これがいいと思うんだけどなぁ。

omochimetaru [1:37 PM] それは超思います

tarunon [1:37 PM] 上級者向け過ぎる感w

omochimetaru [1:37 PM] 型パラ付きエイリアス来たし。

koher [1:38 PM] しかも、 NilError が 0 バイトだから

[1:38] sizeof(Optional) もこれまで通り。

[1:38] それで、 typed throws が導入されて一つだけエラー型指定できるようになって、 throws E -> T-> Result<T, E> と等価になって、 (edited)

[1:39] だと、 OptionalResulttry でアンラップできる。

[1:40] でも Result の道は閉ざされたから、やっぱり Result 入れようってことにならないと無理。

omochimetaru [1:40 PM] そうですね。

koher [1:42 PM] !!

[1:42] apple/swift-evolution#68 GitHub Allow Type Annotation on Throws, Take 2 by owensd · Pull Request #68 · apple/swift-evolution · GitHub This is a revised proposal for typed throws based on the feedback from the mailing list, and based on the request from Doug to flush out the specifics more fully.

[1:42] これどうすんだろうって思ってたら、一端リジェクトされてる。

[1:42] 3.0ではやらないって。

[1:42] Typed throws

[1:43] 3 ヶ月間放置されてたのに。

inamiy [1:44 PM] joined #discussion

koher [1:44 PM] ということはしばらくは Untyped Throws が継続するのは決定ですね。

omochimetaru [1:46 PM] https://github.com/owensd/swift-evolution/blob/090f40de5065a6592d2cf5a78f395257d6f60809/proposals/allow-type-annotations-on-throw.md ふむ・・・ GitHub owensd/swift-evolution Contribute to swift-evolution development by creating an account on GitHub.

koher [1:47 PM]

so it could come into Swift in a later release without compromising the design

omochimetaru [1:47 PM]

Aren't we just creating Java checked-exceptions, which we all know are terrible?

No. The primary reason is that a function can only return a single error-type. 

[1:47] なるほど

kozyty [2:07 PM] joined #discussion

inamiy [2:13 PM] func nya() async throws 複数のモナド構造が絡み合ってくると、記述がかなり苦しそうですね。。

[2:15] https://speakerdeck.com/inamiy/swift-2-error-handling-vs-result-t-e 昔、tryがこうあってほしい的な話をしたので、参考になれば Speaker Deck Swift 2 Error Handling vs Result Swift 2 (& LLDB) シンポジウム http://realm.connpass.com/event/16556/ (65kB)

tarunon [2:21 PM] うーん

[2:22] do構文自体はそこら辺フラットに出来るのが理想な気はするので

[2:23] async throwsの記述の順番に関わらず、

[2:23] do { ... } await { ... } catch { ... } とか出来ると良いのかなとか思いました

koher [2:27 PM] @inamiy: リンクありがとうございます!これとてもよく整理されてますね。僕が try! Swift で話す必要なかったのではw

[2:28] Joe Groff の↓はいかがですか?

they also don't compose cleanly, they impose nesting where is desired—you have to pick between Result<Async<T>> and Async<Result<T>>, or build ResultT<AsyncT<Identity>><T> out of monad transformers—and they don't do the natural thing when used with other higher-order abstractions—if you're mapping a throws function over a collection, you probably want to propagate that error like rethrows does, not end up with a collection of Result<T>. I'd rather see us adopt an extensible algebraic effects system, something like http://www.eff-lang.org, which provides a framework for throws, async and other control flow effects to be cleanly composed and abstracted over. I see throws as the first seed of that.

inamiy [2:30 PM] 👆 を前にメーリングリストで読んで嘆いた人ですw

koher [2:30 PM] w

[2:31] ↑で話してたみたいに throwsasync をモナドにマッピングせずに導入する方式なら入れ子問題が起こらないので、それもありな気がしています。入れ子の外と内を気にしなくていい組み合わせなら、ですが。

[2:32] 僕はそれほど Haskell を使ったことはないんですが、 Haskell とかだとこの入れ子の順序は sequence とかで当然操作するものと捉えられてるんでしょうか?それともやっかいなもの?

omochimetaru [2:33 PM] inamiyさんのスライドを読んでいる途中ですが、() throws -> Tがでてきた。。

koher [2:34 PM] うん。それも含めて整理されてた。

inamiy [2:34 PM] 型のネスト構造も含めて、きちんと定義することが本当の意味でのtype safeだと思うので、

[2:35] 「入れ子の外と内を気にしなくていい組み合わせなら」 こうしてしまうと、型安全を放棄していることになってしまうかなと思います。今のthrowing funcのように

[2:37] Resultなら、flatMapErrorをさせるのが、よりtype safeな設計で、させずにカジュアルに書けるが ネストなしの設計

koher [2:39 PM] 同種のモナドならいいですが、↑で話題になってた Result<Promise<Result<T>>> とか辛くないですか?

inamiy [2:40 PM] Swift 3はカジュアルさを目指しているところがあるので、type safetyは二の次なのかなぁという印象です。

[2:40] Result<Promise<Result>> 全然ありです!

koher [2:41 PM] do 記法さえあれば辛くないってことですか? Swift だと辛いですよね?

tarunon [2:41 PM] throwing func とか async funcを言語仕様の領域に持っていくことで、型の問題から切り離そうとしているようにも感じる

[2:42] モナドのステップをすっ飛ばして、直接do記法に持ってってる感じでしょうか

inamiy [2:42 PM] 型理論は詳しくないですけど、「型のネスト」と「型安全」は表裏一体じゃないかと思います。

koher [2:43 PM] たしかに Result<Promise<Result<T>>>Promise<Result<T>> は意味が異なるので

omochimetaru [2:43 PM] Rustのtry!マクロ展開はドキュメント読んだときヒェッとなりました

koher [2:43 PM] それを厳密に区別したい場合にそれらが区別できるというのは重要なのかもしれませんが、

[2:44] 一方で意味のないネストもあると思うんですよね。

[2:44] A|B|C を表したいだけなのに Either<A, Either<B, C>>Either<Either<A, B>, C> も書けるとか。

[2:45]

モナドのステップをすっ飛ばして、直接do記法に持ってってる感じでしょうか 今の Swift はそんな感じですよね。

[2:46] @inamiy さんや僕はそれをモナドと関連付けることを考えていたけど、今の方向性は @tarunon さんの言う

throwing func とか async funcを言語仕様の領域に持っていくことで、型の問題から切り離そうとしているようにも感じる のように見えますよね。

inamiy [2:47 PM] A|B|C`` これは |` のassociativity/precedence次第で片方に決まりますね。

koher [2:48 PM] あ、えっと A|B|C は Ceylon とかの Union type 的な意図でした。 A|B|C == A|C|B

[2:48] それを意図したいだけなのに表現の仕方がいろいろあって、その表現の自由度は不要じゃないかという話でした。

inamiy [2:50 PM] Union type 面白そうですね

[2:50] (ただ、A|B|C == A|C|B は型的にイコールに本来ならない気もしますが)

koher [2:50 PM] @inamiy: ↓だと Result<Promise<Result<Promise<Result<Foo>>>>> も当然って感じですか?

let foo = do {
  let a = try 
  let b = await 
  let c = try 
  let d = try 
  let e = await 
  let f = try 
  Foo(a, b, c, d, e, f)
}

tarunon [2:50 PM] 鼻水出たw

[2:51] コンパイラに取って幸せな世界は、どっちなんだろう

koher [2:51 PM] Union type だと A|A == A, A|B|B|C == A|B|C ですね。

inamiy [2:51 PM] 型は深いw

[2:52] 上のコードは、必要があって、その型でtype safeにしたいならそうする方向かなという気がします (edited)

koher [2:53 PM] ただ、 99% のユースケースでは Promise<Result<Foo>> がほしいだけだと思うんですよねぇ・・・。

tarunon [2:55 PM] awaitになって言語仕様に組み込まれたところで

[2:55] 型安全にしたい人はそれをラップしたPromise作って使うだけのような気もする

inamiy [2:55 PM] Result<Promise<Result<Promise<Result<Foo>>>>> から flattenして Promise<Result<Foo>> を作ることは可能なので、その変換ロジックを言語内でどう定義するかによりそうです。 ただ、 Result<Promise<Result<Foo>>> を作りたい場合もきっとある。

omochimetaru [2:56 PM] なるほど

inamiy [2:57 PM] 僕は言語内で勝手にその変換処理を何らかのロジックでやってしまうより、自分で好きなように作りたい派ですね。

yuta-t [3:00 PM] joined #discussion

omochimetaru [3:03 PM] swiftだと参照カウンタが言語に組み込まれてるけど、 C++だと生ポ、スマポ(カウンタがポインタ側/カウンタがオブジェクト側)とか戦略が選べるのを連想しました

[3:04] 他の選択肢もあるところを切り捨てて言語仕様に昇格していく過程で言語の個性が出てきますよね

[3:06] ハスケルのdo構文はユーザからの拡張性を残す形で特殊だ

inamiy [3:08 PM] ですね。SwiftにHaskell doがあれば、throws / async記法も実は要らないかもしれない、と考えてます。

koher [3:09 PM] 実際かなり似てますからね。

[3:10] 戻り値がないのは異なりますが、 try<- と読み替えればとてもよく似ています。

[3:10] do ってキーワードもあえて Haskell に似せている気もします。 (edited)

omochimetaru [3:12 PM] EffってHaskellのdoをより発展させた機能といえるんですかね

[3:14] http://www.eff-lang.org/try/ Exampleの Overview of syntax Printing to the standard output は理解できたんですが、 Non-Determinismが意味不明でした。

koher [3:14 PM] Eff の詳細がわかってないから何も言えないけど、もしさっきまで話してたみたいな throwsasync をフラットに解決するんだとしたら @inamiy さんの言っているネストによる表現力は失われてしまっていそう。

omochimetaru [3:14 PM] doの中だと<-がflatMapに変換されるっていう機能より、Effのhandlerのほうが、もっと柔軟な気がする。

[3:17] @koher ミソはcps変換で、変換されたクロージャ自体は素直な変換っぽいから、Eff自体が勝手に型潰すとかそういうのは無さそう (edited)

koher [3:19 PM] @omochimetaru: そうなのか。今度 Eff 勉強してみます。

daybysay [3:47 PM] joined #discussion. Also, @kishikawakatsumi joined, @rizumita joined, @niwatako joined, @hoshi-takanori joined, @chocoyama joined, @hiroraba joined, @yashigani joined, @roworks joined along with some others.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment