Guild icon
swift-developers-japan
開発環境, ライブラリ / combine
Avatar
Combine の foo.eraseToAnyPublisher() って AnyPublisher(foo) するのと何か違うんでしょうか?
8:25 AM
eraseToAnySequence とかないし、不思議な感じの API だと思って。
Avatar
Kishikawa Katsumi 4/16/2020 8:26 AM
メソッドチェーンでかけるのがいいんじゃないですか?
Avatar
omochimetaru 4/16/2020 8:26 AM
同じ意味でメソッドチェーンできるようにしたのかなと思ってたけどソースはなし
Avatar
その理屈なら eraseToAnySequence もほしくないですか?
Avatar
omochimetaru 4/16/2020 8:26 AM
AnySequenceにしたい事自体そんなに無い
Avatar
Open source implementation of Apple's Combine framework for processing values over time. - broadwaylamb/OpenCombine
Avatar
omochimetaru 4/16/2020 8:27 AM
CombineはSwiftUIと同じように
8:27 AM
オペレータがどんどん型パラメータにエンコードされるから
Avatar
Kishikawa Katsumi 4/16/2020 8:27 AM
一緒なんじゃないかとは思います。
Avatar
omochimetaru 4/16/2020 8:27 AM
型消去したいモチベーションが高い
8:27 AM
普通のSequenceはmapとかしてもそうならないから、低い。
Avatar
オペレータがどんどん型パラメータにエンコードされるから
なるほど。
Avatar
Kishikawa Katsumi 4/16/2020 8:28 AM
map().map().eraseToAnyPublisher() と書きたい、AnyPublisher(...map().map()) は見にくい、のかなと。 AnySequenceはよくわからないです。必要ならあってもいいと思います。ただ、これは利便性の問題なので一貫性とかはどうでもいいかと。 (edited)
Avatar
頻度の問題はあるにしても、 Array でも AnySequence(array.map {}.map {}) より array.map {}.map {}.eraseToAnySequence() と書きたいと。 (edited)
Avatar
Kishikawa Katsumi 4/16/2020 8:31 AM
あってもいいと思いますね。ただ、Publisherはほぼ絶対AnyPublisherにするしかなかろう、、、ですけど、
Avatar
omochimetaru 4/16/2020 8:31 AM
まあそうですね。だからKotlinには .let { ... } があるんだと思うし。
Avatar
Kishikawa Katsumi 4/16/2020 8:31 AM
array.map {}.map {}.eraseToAnySequence() これはだいたい変換後の型が欲しいんじゃないかな。そうでもない?
Avatar
array.lazy.map {}.map {}.eraseToAnySequence() とかはありそう?
Avatar
Kishikawa Katsumi 4/16/2020 8:32 AM
ありだと思います。
Avatar
omochimetaru 4/16/2020 8:32 AM
そうですね。
8:32 AM
lazyとzipは型が育つ
8:33 AM
lazy付いてるとmapもそうだっけ
Avatar
array.lazy.map {}.map {}.to(AnySequence.self) とか書ける方がいいのかな。
Avatar
omochimetaru 4/16/2020 8:36 AM
う〜ん。
8:36 AM
Swiftは型変換は基本的にinitで包むんですよね (edited)
Avatar
でもニーズとしてはメソッドチェーンの中で変換したいことがあると。 (edited)
Avatar
omochimetaru 4/16/2020 8:36 AM
.description は例外的で、 他の言語だと toInt() になりそうなやつが Int(x)
Avatar
なので、イニシャライザをメソッドチェーンの途中に持っていける方法があるといいのかもしれないなと。
8:37 AM
#combine じゃなくて #swift っぽくなってきた・・・。
Avatar
omochimetaru 4/16/2020 8:37 AM
func =><X, R>(x: X, f: (X) -> R) -> R { f(x) }
Avatar
演算子も考えたんだけど、メソッドチェーンの途中だと使いづらいなと。
8:41 AM
AnyPublisher については、 Opaque Result Type を早く associatedtype 指定できるようになることが望まれるなぁ。
8:41 AM
SwiftUI はたまたまパラメータがないから今の Opaque Result Type でなんとかなってるだけで。
Avatar
@Published って、 @Published var foo: Foo なら CurrentValueSubject を公開するのと、 @Published private(set) var foo: Foo なら内部で保持した CurrentValueSubjecteraseToAnyPublisher() で公開するのと、できること的には変わらないという理解で正しいですか?もちろん型の違いや実装の違いはあるし、 $foofoo.value を使わないといけないというのはあるにしても。
Avatar
Kishikawa Katsumi 4/20/2020 3:58 PM
だいたい一緒じゃないですか? @Published(lldb) po viewController.thumbnailCollectionView.$images ▿ Publisher ▿ subject : <CurrentValueSubject<Array<PlayLogUploadParameter>, Never>: 0x6070000a3cc0> (lldb) CurrentValueSubject使われてるみたいですし。
3:58 PM
Errorの扱いがあるのは違うところかもしれないです。
Avatar
ありがとうございます。
3:59 PM
Property Wrapper の仕様から仕方ないんですが、 CurrentValueSubject@Published に寄せられても PassThroughSubject が寄せられないのが型がそろわなくて気持ち悪いんですが、仕方ないですよね? (edited)
Avatar
Kishikawa Katsumi 4/20/2020 4:01 PM
仕方ないかどうかはわからないですが(なんか良い方法はあるかも)私は@PublishedとPassThroughSubjectを両方使い分けてますね。
Avatar
はい、 @PublishedPassThroughSubject を使い分けるんですが、 CurrentValueSubjectPassThroughSubject の使い分けなら両方とも Subject として揃ってるのに、前者だけ @Published に置き換えたときに気持ち悪さがあるなと。
4:03 PM
あ、 FailureNever のときの話です。
Avatar
Kishikawa Katsumi 4/20/2020 4:04 PM
じきにPropertyWrapperの書き方が増えるかもしれませんね。 (edited)
Avatar
でも PassThroughSubject は現在の値があるわけではないので、 Property Wrapper にできなくないですか?
Avatar
Kishikawa Katsumi 4/20/2020 4:05 PM
私も @PublishedPassThroughSubject に書き換えるとき(そこそこある)は、気持ち悪さはないですけど、なんかそれっぽい書き方はないかなと思います。
4:06 PM
でも PassThroughSubject は現在の値が
無理ですかね。
Avatar
Foo<T> にして T は保持しないとかで作れないかなと考えたんですけど
Avatar
Kishikawa Katsumi 4/20/2020 4:07 PM
なんかbutton.$onTapみたいな感じに受け取り側は書きたい。
Avatar
たとえば↓みないなのがあったとして、 class Person { var firstName: String var familyName: String var fullName { "\(firstName) \(familyName)" } }
4:09 PM
ちがうか、
4:10 PM
fullName が現在の値があるから @Published でいいな。 (edited)
4:11 PM
まさに「 button.$onTap みたいな感じに」書きたいんですよね。
4:12 PM
性質が異なるものだから仕方ないのかな・・・。
Avatar
OutputFooFailureFooErrorPublisher を、 OutputResult<Foo, FooError>FailureNeverPublisher に一発で変換できるメソッドってありますか?もしくは、一番簡単と思われる方法は何でしょうか?
Avatar
flatMap して assertNoFailure ?
Avatar
flatMap じゃなくて flatMapError がほしくないですか?
5:10 AM
あー、 Future で考えてたけど、一般的な Publisher で考えるとやりたいことが変な気がしてきました。
5:11 AM
いやでも replaceError(with:) があるんだから、代わりに (Error) -> Output を受け取るものがあってもいいのか。
Avatar
こういう感じですか? .map { Result<Foo, FooError>.success($0) }.catch { Just(.failure(...)) }
Avatar
あ、それでいけますね。ありがとうございます。
😆 1
Avatar
GitHub Gist: instantly share code, notes, and snippets.
Avatar
ら、MarinがCombineCocoaを教えてくれた https://twitter.com/icanzilb/status/1266012241003982851?s=21
@d_date @lyumotech You might like: https://t.co/8rDoEa4Qcx :)
Avatar
combineのdebounceというかschedulerって粒度が時間でしか指定できない...?
Avatar
自己解決というかできなそうで、自分でそういうschedulerを作る必要があるっぽい
7:13 AM
いろいろ痒いところに手が届かない感が1.0感ある (edited)
Avatar
Combine の Future って、非同期処理のコールバック代わりに使おうとすると、いちいち cancellable をどこかに保持しておかないといけなくて面倒じゃないですか?キャンセルしないとき。
3:20 AM
↓こういうのがあれば便利じゃないかと思ったんですが、どうでしょうか? extension Future { public func get(_ body: @escaping (Result<Output, Failure>) -> Void) { let keep: Keep<AnyCancellable> = .init() keep.value = sink(receiveCompletion: { completion in switch completion { case .finished: break case .failure(let error): body(.failure(error)) } keep.value = nil }, receiveValue: { output in body(.success(output)) }) } } private class Keep<Value> { var value: Value? }
3:23 AM
↓使うとき。 let future = asyncFoo() future.get { foo in // cancellable は内部的に保持される // ... }
Avatar
.recieveはダメなんでしたっけ
Avatar
お、 receive 使ったことなかったです( receive(on:) しか)。 (edited)
Avatar
Kishikawa Katsumi 7/7/2020 3:24 AM
storeかな
Avatar
CombineのFutureは顧客の本当にほしかったものじゃなかった感がすごいある。べつにこれはこれでいいんだけど。 (edited)
Avatar
store しても結局保持するための入れ物が必要じゃないですか?
Avatar
procotol Future { associatedtype T; func respond(_: (T) -> Void) }
3:27 AM
これがほしかった
Avatar
Kishikawa Katsumi 7/7/2020 3:27 AM
1ビューコントローラに1つ、みたいな感じでcancellablesを持っています。
Avatar
非同期コールバックが返ってくるまでの間、自動的に参照しておいて、返ってきたら解放してほしいという感じです。 (edited)
Avatar
それでno futureの場合は解放してほしい
Avatar
cancellables を持てるケースはいいんですけど、持てないケースが辛くて・・・。
3:28 AM
それでno futureの場合は解放してほしい
no future の場合ってどうやってわかるんですか? failure ではなく?
(edited)
Avatar
それは実装しだいかな?そういう概念があればいいって言う感じ
Avatar
no future が検出できるケースと failure で complete するケースって同じになりませんか?
3:30 AM
あ、 no future は値が返ってこないという意味で解釈してます。
Avatar
completeすらしないで消えるんじゃないかな?
3:30 AM
なにもおこらない
3:31 AM
futureがreponderをぜんぶ知ってるケースならfutureがno futureになったらfutureが消える時が終わる時だから
3:32 AM
たぶん↑のKeepの実装がそれになってると思う (edited)
Avatar
ああ、なるほど、 Future を返す API 側で返ってこないことを通知するんじゃなくて、受けてがいなくなったらってことですね。 (edited)
Avatar
Kishikawa Katsumi 7/7/2020 3:34 AM
cancelablesを持てない場合は上の実装はいい感じだと思います。
3:35 AM
持てる場合は、今のところ普通に書いてもらったほうがトータルで良いと思います。 (リクエストが飛んでない時にソースコードを追いかけないとわからない)
Avatar
基本 cancellable を使って、それが難しいケースでコールバックと同じように気軽に使うには get みたいなのがあると便利だと思うんですよね。
3:36 AM
持てる場合は、今のところ普通に書いてもらったほうがトータルで良いと思います。
同意です。 コールバックで書いてた箇所を Future にしようとして、 cancellable 保持を考えるときついなということがあったので、そういうときに。
Avatar
Kishikawa Katsumi 7/7/2020 3:36 AM
標準のAPIでも終わるまでは自分で自分を、とかデリゲートを強参照する、みたいなのはあったし。
Avatar
多分一般的?なPromiseの実装とかはそうなってるんじゃないかなー
Avatar
はい、僕が昔実装した Promise はそうしていました。それならコールバックの置き換えが簡単なんですが、 Future だと cancellable 保持できないケースがきつくて。 (edited)
Avatar
わかる
Avatar
受け手がいなくなったら解放は、 cancellables 使ってるとできるけど、↑の get の実装だと、残っちゃいますね・・・。
Avatar
それはなんていうか思想の違いなきが。combineは受け手ドリブンだから。 (edited)
3:39 AM
のこっちゃっても、no futureにはならない、futureの寿命がながくないってわかってるなら
3:39 AM
問題ない気がする
Avatar
ん?? no future は受け手がいなくなったときではなく、やっぱり値が返ってこないときの話ですか?
3:40 AM
futureがreponderをぜんぶ知ってるケースならfutureがno futureになったらfutureが消える時が終わる時だから
ああ、これを誤読してたかも。
3:41 AM
Future を返す API 側が値が返ってこないことを知ったらそれを通知して解放するってことですよね。
Avatar
そう。でもそれがなくても、まあ長寿命なfutureって少ない気がしてるので
3:42 AM
それでいいんじゃないかなあ。
Avatar
まあ、今の Future でそれやるなら、値が返ってこないことがわかったら、値が返って来ないよという Error で failure にする感じですかね。 completion は流れちゃいますが、一応エラーの種類で UncompletedError だったら無視とか。
3:44 AM
そういう方法なら↑の get でも解放されるはず。
Avatar
これ系は、なんかcombineっぽくないってのが問題なきがする。
3:46 AM
わかって使う分には問題ないんだろうけれど、combineとしてこれが良い実装です、とはならなさそう?というか
3:47 AM
なんか雰囲気だけど。
Avatar
「これ系」は no future をエラーにする場合ですか?それとも get
3:48 AM
cancellableが保持されちゃう系? (edited)
Avatar
そうですねー。まあ get は使い手側の問題だから、使い手が理解した上で導入って感じでしょうか。
Avatar
そうだと思う
Avatar
コールバックだったものをすべて Future にしちゃうと使いづらくなりかねなかったけど、使い手が get みたいなのを導入すれば不便にはならないよと。
Avatar
質問失礼します。 なんか combineLatest の元に .subscribe(on:)を噛ませたら流れこなくなります。どうしてなのでしょう?🤔 Publisherのsubscribeの起きるタイミングの問題とかですか var subscriptions = [AnyCancellable]() Just(2) .combineLatest(Just(1)) .sink( receiveCompletion: { print("works fine", $0) }, receiveValue: { print("value", $0) }) .store(in: &subscriptions) Just(2) .subscribe(on: DispatchQueue.main) .combineLatest(Just(1)) .sink( receiveCompletion: { print("you won't see me", $0) }, receiveValue: { print("never", $0) }) .store(in: &subscriptions) Just(2) .combineLatest(Just(1).subscribe(on: DispatchQueue.main)) .sink( receiveCompletion: { print("you won't see me", $0) }, receiveValue: { print("never", $0) }) .store(in: &subscriptions)
Avatar
もしこれがコマンドラインで実行して終了するプログラムなら、メインスレッドで実行されているので DispatchQueue.main だとプログラム終了まで結果が返ってこないと思います。
11:06 AM
combineLatest に関係なく↓だと返ってきません。 import Combine import Dispatch Just(2) .subscribe(on: DispatchQueue.main) .sink( receiveCompletion: { print("completion:", $0) semaphore.signal() }, receiveValue: { print("value:", $0) }) .store(in: &subscriptions) semaphore.wait() (edited)
11:07 AM
↓だと返ってきます。 import Combine import Dispatch Just(2) .subscribe(on: DispatchQueue.global()) .sink( receiveCompletion: { print("completion:", $0) semaphore.signal() }, receiveValue: { print("value:", $0) }) .store(in: &subscriptions)
11:07 AM
しかし、 combineLatest を入れると DispatchQueue.global() でも返ってきません。
11:08 AM
combineLatest についてはまだ調べられてません。
Avatar
そうですね。playgroundで試してました。どのDispatchQueueとか関係なさそうです。直sinkしても特に問題ないです Just(2) をDeffered Futureに変えて検証してみてたけど、subscrbeされてそう。謎です。
Avatar
🍫 CombineCocoa 0.2.0 is finally out and wow is it jam packed with goodies! 💪Thanks to the wonderful @_disho, @icanzilb, @ronksa and @weswickwire for their contributions to this release 👉https://t.co/rNqhBs8mrn #iOS #Combine #AppleCombine #Swift #OpenSource #OSS
12:35 PM
ShaiさんRxSwiftの次のリーダー募集してたし、多分もうCombineに移るんやろうなあ
Avatar
RxSwiftやってるのはザハさんじゃないの?
Avatar
あ、ざはさんだったのか。Shaiさんがissue立てて募集してたのでつい
Avatar
最近はザハさんじゃなくてこの人がリードとってたね
Avatar
omochimetaru 9/27/2020 1:39 PM
なるほど
Avatar
Combineにも tryFlatMap が無いし、 SwiftNIO (EventLoopFuture) にも tryFlatMap が無いんだけど、
2:46 AM
サーバサイドでゴリゴリロジック書いてると結構普通に欲しい場面多いんだけど
2:47 AM
提供されていない深い理由あるんだろうか
2:47 AM
RxSwiftなら普通にできたのに。
Avatar
Publisher のエラーで扱えってこととか?
Avatar
それは throwしないでストリームにリフトして、 flatMap に寄せるってことですよね
Avatar
うん。
Avatar
それだと、throwsな同期関数を2つ使って値を用意してから非同期な関数を呼ぶみたいな手順のときに、
2:50 AM
2つの関数をそれぞれリフトするんで、flatMapがネストしちゃうか、途中の値をタプルとかで流しながらtryMapチェーンしないといけなくて、
2:51 AM
もしtryFlatMapがあった場合と比べると結構コードが膨らんじゃうんですよね
Avatar
tryFlatMap がほしくなるのは同意なんだけど、用意されてないのはエラーをどちらで扱うかというのがややこしいからじゃないかなぁ。
2:53 AM
Result にも用意されてない。
2:53 AM
ResulttryMap すらない)
Avatar
それでいうとCombineにtryMapがあるのは変じゃないです?エラーの型付かないし。
Avatar
Result.publisherが使えるから、flatMap { Result.init { try ... }.publisher } にするのだと思う
2:55 AM
mapだとこの方法が使えないのでtryMapになる、のかな
Avatar
型がつかないのはそもそもthrowsがuntypedだからいいのかな
Avatar
せっかく do 記法の世界に行ってたのにモナドの世界に戻ってくる辛さはあるなぁ・・・。
Avatar
@tarunon それだと2つあるときにResultの中にまとめてタプルにするとか、Publisherのチェーンが2段になるかで、tryFlatMapより辛い
2:57 AM
@koher そうなんですよね、async/awaitも見えてきてるのに、
2:57 AM
ここでモナド脳鍛える意味あんのかなと思って
Avatar
うーーーーん
2:58 AM
tryFlatMapって構造が
Avatar
まあでも async/await が来ても Publisher から解放されるわけじゃないから。
2:58 AM
単純な非同期は最近コールバックに寄せてる。多分 async/await へのマイグレーションツールが用意されそうだから。
Avatar
Publisherが継続モナドとエラーの入子だとしたときに
Avatar
Combine.Futureのときはasync/awaitで書きたいはず・・・
Avatar
Result<継続<Result<T>>>になるからキモいっていうのはわかるんだよな
Avatar
SwiftNIOのtryMapの名前は flatMapThrowing で
2:59 AM
flatMapのオーバーロード的な名前になっていて、それはわかる
Avatar
Combine.Futuremap しただけで Future でなくなる罠が😂
Avatar
え、マジ?
Avatar
なんかその辺は型定義をめっちゃ頑張れば
3:00 AM
現行でもなんとかなるんですけど、なってない
Avatar
mapしても理論上はFutureのままですよね
Avatar
そのはず
Avatar
うーん、それはキツすぎ
Avatar
Publisher.Mapが、ベースがFutureLikeなときはFutureLikeにするっていう
3:01 AM
拡張を手前で書けばやりたいこと自体はできるようになる
Avatar
ちなみに、CombineにはswithToLatestがあるので
3:02 AM
tryMapの中でPublisher返しちゃって即座にswitchTpLatestすれば
3:02 AM
tryFlatMapとして機能するんだけど
Avatar
つらすぎて前に Future を維持する futureMap 作った。 https://github.com/koher/future-map/blob/main/Sources/FutureMap/FutureMap.swift
Avatar
(そこまでやるなら拡張したほうがいいか?)
3:02 AM
SwiftNIOにはflatten(swithToLatest)がみあたらねえんだよな
Avatar
(個人的にはtryFlatMapが必要になるのは設計上のミスな気はするけど)欲しいなら良いんじゃないかな (edited)
3:09 AM
これはなんでかというと、さっきも書いたようにthrowsかつPublisherな関数は外側のErrorをPublisherの内部に持ってしまった方が良いので
3:09 AM
async throwsが常にFuture<Result<T>>になって欲しいのと同じ
3:10 AM
2回以上tryする場合もPublisher1個作ってまとめるのが良いのだと思う (edited)
Avatar
throwsかつPublisherな関数は外側のErrorをPublisherの内部に持ってしまった方が良い
これはそうだねえ
3:11 AM
tryMapに渡してるクロージャは、同期性の観点で型が強まってるから許されるけど
3:12 AM
うーんまあそうか、 () throws -> Publisher<> が型として良くない合成だから、
3:12 AM
tryFlatMap のクロージャの型に望ましくない型が出てこないようにしてるというのは納得できる理由だな
Avatar
@koher futureMapだと元々のMapの機能とか失っちゃってかなり勿体無いので protocol FutureLike { associatedtype Output associatedtype Failure: Error func asFuture() -> Future<Output, Failure> } こういうの作って extension Publishers.Map: FutureLike where Upstream: FutureLike { func asFuture() -> Future<Output, Upstream.Failure> { Future(self) } } こうした方が良いですね
3:53 AM
MapやTryMapはoverloadが充実していて、実行時にエコな仕組みがあるので(これはRxSwiftにもある仕組み) @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension Publishers.Map { public func map<T>(_ transform: @escaping (Output) -> T) -> Publishers.Map<Upstream, T> public func tryMap<T>(_ transform: @escaping (Output) throws -> T) -> Publishers.TryMap<Upstream, T> } @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension Publishers.TryMap { public func map<T>(_ transform: @escaping (Output) -> T) -> Publishers.TryMap<Upstream, T> public func tryMap<T>(_ transform: @escaping (Output) throws -> T) -> Publishers.TryMap<Upstream, T> } (edited)
Avatar
なるほど。ありがとうございます。
Avatar
SwiftNIO (EventLoopFuture) にも tryFlatMap が無いんだけど、
これSwiftNIOだと新しいEventLoopFutureを作るのにEventLoopを引き回さないといけないのと組み合わさって結構しんどいですね。
Avatar
実行時にエコな仕組み
これの Future 版を用意してくれたらそもそも困らなかった気が・・・。
Avatar
そもそもFutureがstructになっちゃってる時点でまあなんか
4:01 AM
protocolになっていてその実装、という扱いなら全部エコになっていたと思います
Avatar
え、 Future って class じゃないんですか??
Avatar
あ、classでした
4:01 AM
正しくは具体型になっちゃってる時点でアレ
4:02 AM
あー、、ううーーん??
4:02 AM
subclassとしてMapとかを内包した型を出すつもりだからclassなのか?参照持ってるからだよな
Avatar
お、よかった。 https://developer.apple.com/documentation/combine/future 代入したときとかどうなるんだろうとびっくりしました😅 (edited)
4:03 AM
struct だと結果を書き込めないように思います。
Avatar
Storeがstructな時点でなんでもアリですよ
4:04 AM
ガワだけstructとか普通にあっちゃう (edited)
Avatar
Store って store(in:) する先の話ですか? (edited)
4:07 AM
Map とかのこと?
4:08 AM
Map とかが struct なのはおかしくないように思います。参照型の let しか持たない structclass と同じだし、そうするとヒープに乗るオブジェクトを一つ減らせるので。
4:10 AM
struct なら型がネストしてお化けになっても最適化でないのと同じにできるし、将来的には some Publisher として扱って、 SwiftUI の View と似た感じになると思います。 (edited)
4:13 AM
futureMapMap 等のエコなオーバーロードと同じ位置づけな気もしてきたんですがどうでしょう?
Avatar
SwiftUI.Storeですね、Publishers.Mapとかは別にいい
4:14 AM
futureMap は Map 等のエコなオーバーロードと同じ位置づけな気もしてきたんですがどうでしょう?
これは微妙で、futureMapを使うとPublishers.Mapとしての機能を失うので
(edited)
4:14 AM
.futureMap(hoge).futureMap(fuga)がかなりキツいことになります
Avatar
@State のこと? < Store
Avatar
あーーー
4:14 AM
Stateでした
👌 1
4:14 AM
すみません
Avatar
内部で関数合成できないってことか < Publishers.Mapとしての機能を失う
Avatar
そうですね、それをするにはPublishers.Mapが型として顕現してないといけないので
4:17 AM
例えばもっとちゃんとやるならFuture.mapをoverloadしてFutureを返す関数にする(但し帰るFutureはFutureのサブクラスでMappedFutureになっている)とかでしょうか
4:18 AM
標準でそうなっていればこの手の拡張は不要ですし、理想系だと思います
Avatar
Future の実装者であれば、 isKnownUniquelyReferenced ならネストせずに自身を改造してフラットなまま返す map とか作れそうですね・・・。
4:23 AM
FutureProtocol があって、 some FutureProtocol が使えて、かつ Map: FutureProtocol where Upstream: FutureProtocol とかが一番 Swifty かもしれませんね。遠い道のりですが・・・。
Avatar
EventLoopを引き回さないといけない
これ自体はEventLoopFutureを返す関数はEventLoopを引数に取るように書くだけだからあまり苦痛には感じてない スレッドコンテキストが注入されてると考えると当然の設計だと思うし
4:25 AM
ただ複数の依存を受ける関数が、例えばDatabaseとRequestが別のEventLoopに所属してた時の関数の振る舞いとかは考えてられないので揃ってる前提でやってる・・・
4:30 AM
async/awaitが来た時にその手のコンテキスト抽象が陽に扱えなくなるのはちょっと気になってるけど
4:31 AM
昔フォーラムに書いたらJoe Groffが考えてるよって返事くれたからスレッドローカルストレージのasync context版のものがありそう
Avatar
Shohei Yamamoto 10/19/2020 1:53 PM
はじめまして、質問失礼いたします。 Sequenceの各要素を返すようなoperatorはあるのでしょうか? このコードがある時、 Just([1,2,3]) .sink(receiveValue: { elements in elements.forEach { element in print(element) } }) こちらのように修正したいです。 Just([1,2,3]) // do something for each的な何か .sink(receiveValue: { element in print(element) }) 入れ子を1つでもなくした方が読みやすいだろうという目的です。よろしくお願いいたします。
Avatar
@Shohei Yamamoto ↓みたいにできます。 [1, 2, 3].publisher.sink { element in print(element) }
Avatar
Shohei Yamamoto 10/19/2020 1:57 PM
@koher 返信ありがとうございます!
1:59 PM
背景うまく説明できておらずすみません。 実は元はこのような感じでして、配列でまとまってきてしまうため困っております var cancellable = URLSession.shared .dataTaskPublisher(for: qiitaURL) .tryMap { element -> Data in guard let httpResponse = element.response as? HTTPURLResponse, httpResponse.statusCode == 200 else { throw URLError(.badServerResponse) } return element.data } .decode(type: [LGTM].self, decoder: JSONDecoder()) .sink(receiveCompletion: { print("complete! \($0)") }, receiveValue: { lgtms in lgtms.forEach { lgtm in print(lgtm) } })
Avatar
.sink の直前に .flatMap { lgtms in lgtms.publisher } みたいなのを挟んでフラットにするのはいかがでしょう? (edited)
Avatar
Shohei Yamamoto 10/19/2020 2:05 PM
@koher 行けました!ありがとうございます!
🙂 1
Avatar
ObservableObjectobjectWillChange ってなんで did じゃなくて will なんだろうと思って調べてたら、中の人によるこんなツイートを見つけました。 https://twitter.com/luka_bernardi/status/1151633281982406656
@twostraws I do agree it might not map directly to the typical completion block mental model, but you can think of it as the begin of an update transaction. We made the change because it allows us to do a better job in coalescing updates; especially in the presence of animations.
3:50 AM
このスレッド見てたらリンクされてた。 https://forums.swift.org/t/combine-observableobject-in-uikit/28433
I began messing with combine in one of my personal projects and I am trying to use combine to allow my controllers and views to subscribe to different values in my app state. I created an app state that has @Published properties, and that’s great, I can subscribe to changes on those properties, but in the case of a controller wanting to subscri...
Avatar
最初didで実装されてたときに、これだと上手く行かないな〜willじゃないとな〜みたいなのを考えた記憶があります
👀 1
3:51 AM
まだBindable(だったっけ?)だったころは、didChangeだったんですけど
3:52 AM
そうするとrunloopでアニメーション処理しましょうねって言うときにdidだといちいち発火させないといけなくて非効率だなぁみたいなことを
3:53 AM
考えた気がする。willならフラグを立てていってrunloopでフラグ見てアニメーション開始っていう実装がイメージできる。didは無理ポ
Avatar
omochimetaru 3/10/2021 3:53 AM
beta2でいきなりwillに変わった
Avatar
まさに↓ですね。
We made the change because it allows us to do a better job in coalescing updates; especially in the presence of animations.
Avatar
そうそう
Avatar
その頃開発から離れてたからあまり経緯を追えてない・・・。
Avatar
willならsetNeedsLayoutを仕込んでrunloop側でlayoutIfNeededの実装パターンに相当する仕組みが作れるんですけど (edited)
3:55 AM
didだとlayoutにしかならない、とかそんな感じ
👍 1
Avatar
omochimetaru 3/10/2021 4:01 AM
setNeedsLayoutの喩えわかりやすいな
Avatar
1回玩具作ったからこの辺は理解が深い (edited)
Avatar
omochimetaru 3/10/2021 4:03 AM
coalescing updatesという言い方は覚えておこう
Avatar
ObservableObject の実装をするときに、 objectWillChange を経由して View に反映したいようなものは @Published でいいんですが、アニメーションやエフェクトの発火(たとえば画面を光らせるとか)については Publisher として公開しておいて、それを直接 subscribe する形にしておきたいことがあると思います。 そのようなときに、外部に send を公開したくないので↓のように書いているんですが、 final class Foo: ObservableObject { private let _flashScreen: PassthroughSubject<Void, Never> = .init() let flashScreen: AnyPublisher<Void, Never> { _flashScreen.eraseToAnyPublisher() } ... } PassthroughSubjectAnyPublisher を二重に宣言するのがボイラープレート的で嫌なんですが、もっと良い方法はありますか? send の公開を気にせずに PassthroughSubject を公開するという手もありますが・・・。
Avatar
omochimetaru 8/24/2021 2:49 AM
似たような話をRxSwiftで体験した事があるな
Avatar
そういうPropertyWrapperを用意してましたね
Avatar
omochimetaru 8/24/2021 2:49 AM
頻出だったので、 書き込めるオブジェクトと読み込み用のオブジェクトをペアにした型を自前で定義して、少し楽にしました
2:50 AM
今だとペアの型じゃなくて PropertyWrapper で実装する手もあるかも かぶった
Avatar
社内チャットに書くか迷ってこっちに書いたら社内からばかり返事が来たw
omomuki 2
😮 1
2:59 AM
ペアにしても Property Wrapper にしても、内からは見えて外からは見えなくすることってできますか? private(set) をうまく使うとか?ペアの片方を private で保持するのはできるけど、それだと PassthroughSubject 形式とあまり変わらないような・・・。
Avatar
@propertyWrapper struct PrivateSubject<T, E: Swift.Error> { var wrappedValue: AnyPublisher<T, E> { projectedValue.eraseToAnyPublisher() } let projectedValue = PassthroughSubject<T, E>() } final class Foo: ObservableObject { @PrivateSubject var flashScreen: AnyPublisher<Void, Never> func foo() { $flashScreen.send() } } こういう感じのを考えてました
3:01 AM
一応これでもFoo を直接しってる人からは触れてしまうか・・
3:02 AM
protocolとかで var flashScreen: AnyPublisher<...> {get} の粒度までしか知らない人に対しては隠蔽できます
Avatar
なるほど。究極的に触れてしまうのは防げないけど、 Property Wrapper のラップされている型として使っている限りでは触れないと。 (edited)
3:03 AM
それで十分な気もするなぁ。ありがとうございます。
Avatar
コンテキストに沿うかわかりませんが、View.onChange(of:perform:) もいいかもしれません 🤔
Avatar
Avatar
nanasi
コンテキストに沿うかわかりませんが、View.onChange(of:perform:) もいいかもしれません 🤔
ありがとうございます。何かのエフェクトをトリガーしたいとかなので、今回のケースでは onChange はちょっと合わないかもですね。たとえば Bool にして true にしても、もう一度そのエフェクトを実行したいときはどうなるのかとなってしまいますし。 Int にしてインクリメントしていくことはできますが、 AnyPublisher<Void, Never> に値を流して onReceive で実行する方が素直なのかなと。
Avatar
APIClient で Combine.Publisher を使った I/F を作ろうとしてるんですが、みなさん Error はどうやって伝えてるんですかね?ベストプラクティスとかあれば教えて欲しいです! 普通に AnyPublisher<Response, Error> などで返してしまうと一度通信失敗とかで途絶えると動かなくなってしまうのかなと思っていて。 Result なり、Rx の materialize ぽいものなりで <Value, Never> に置き換えるのが良いんですかねー。 https://stackoverflow.com/questions/64444966/swift-combine-sink-stops-receiving-values-after-first-error https://www.apeth.com/UnderstandingCombine/operators/operatorsTransformersBlockers/operatorsflatmap.html https://github.com/CombineCommunity/CombineExt#materialize
Im moving my project to Combine from RxSwift I have a logic where I want publisher to emit event every time I click button. Acrually clicking button executed pushMe.send() pushMe .print...
CombineExt provides a collection of operators, publishers and utilities for Combine, that are not provided by Apple themselves, but are common in other Reactive Frameworks and standards. - GitHub -...
Avatar
APIClientだと、reqとresは1:1だと思うので、Publisherの型ではなくFutureを返す関数として設計すると上手く行きそうな気がします。 そのあと繰り返し利用などするService側での利用があれば、都度materializeなどすれば大丈夫かな? (edited)
11:06 AM
RACのAction相当のものがあれば、それが一番使いやすい気がしますね
Avatar
Avatar
tarunon
APIClientだと、reqとresは1:1だと思うので、Publisherの型ではなくFutureを返す関数として設計すると上手く行きそうな気がします。 そのあと繰り返し利用などするService側での利用があれば、都度materializeなどすれば大丈夫かな? (edited)
ありがとうございます!名前こそ APIClient ですが内部にロジックもあったりで Service に近いですねー。繰り返し利用が想定されるので、やはり materialize 的なのが定石ですかねー
Avatar
シンプルにFutureが露出してあって、後の加工はユースケースに合わせるのが良さそうな気がしました。 Futureから諸々への加工は1stepなんですけど、逆はそうじゃない感じがする (edited)
Avatar
materialize使わなくても大抵はcatch で川を殺さずにリトライなりエラー処理なりできると思います
Avatar
後々async/awaitへ乗り換える事も加味すると、尚更接合点はFutureにしておいた方が良さそう (edited)
Avatar
FeatureってFutureのことを指していますか?
🤕 1
Avatar
それだわ、すみません (edited)
🙆‍♂️ 1
Avatar
Avatar
Iceman
materialize使わなくても大抵はcatch で川を殺さずにリトライなりエラー処理なりできると思います
都度 catch なり replaceErrorは良さそうかなと思ってました!それを APIClient なり Service を利用する側で書くイメージですよね
Avatar
Futureだと retry では適切にリトライできないのでちょっと工夫が必要そうですね
Avatar
Avatar
jarinosuke
都度 catch なり replaceErrorは良さそうかなと思ってました!それを APIClient なり Service を利用する側で書くイメージですよね
そうですね。利用側でリトライ回数に制限をつけて、最悪ケース時にエラーを表示するなりフォールバック値に置き換える、という感じになると思います
👍 1
Avatar
materialize も良いんですが、あんまり Rx の文脈を持ってきてしまうの懸念があってできればプレーンに行きたいなという気持ちがあります
Avatar
async/awaitにしないのはiOSのボトムの制約ですかね?
t_sorena 1
Avatar
Deffered { apiClient,foo() }.retry(3) とかやる感じかな
Avatar
Avatar
tarunon
async/awaitにしないのはiOSのボトムの制約ですかね?
おっしゃる通りですね。iOS 14 サポートなので…backport がそろそろ来れば良いんですがまだですよね…? https://github.com/apple/swift/pull/39051
Introduce an additional build product to build-script to build back-deployable concurrency libraries. These libraries would need to be embedded in apps deployed prior to macOS 12/iOS 15 to support ...
Avatar
出来るかどうかからのチャレンジなので、ボトム14ならそっち上げる方が早そうな気がしますね
👍 1
Avatar
Yoshikuni Kato 11/5/2021 11:41 AM
こんにちは、Combineについての質問をさせてください。 Combineで、複数の値を送るPublisherって、どうやって作っていますか? 例えば、コールバック関数が複数回呼ばれる場合(WebSocketでメッセージを受信する等)の処理をPublisherにラップしたい場合、どう実装できるのでしょうか? webSocketClient.receiveMessage { message, error in // this closure is called multiple times, everytime a message comes ... } 他のFRPライブラリ、例えばReactiveSwiftだと、SignalProducer.init(_ startHandler:)、RxSwiftだとObservable.create と、普通にinitializerでかけるのですが、Combineで相当するものが見つからず、Combineユーザーの方がどう対応しているのか知りたいです。 TCA (The Composable Architecture)の Effect.run はそのようなユースケースにも使えて便利なのですが、内部実装はカスタムのPublisherとSubscriberを作っているようで、このようにカスタムのPublisherを作って対応しているのでしょうか? 割とよくあるシナリオな割にうまく書けなくて、Combine使っている方がどう処理しているのか気になっています。よろしくお願いします〜。 (edited)
Avatar
Kishikawa Katsumi 11/5/2021 12:36 PM
var onMassage = PassthroughSubject<String, Error>() みたいにするのが一番簡単なコールバックの代替だと思います。 @Published でも似たような感じです。
12:37 PM
もうちょっと複雑なデータ構造を扱いたくなったらObservableObjectにするとなのかな。
12:38 PM
上の例、onMassageにエラーもくるのはちょっとおかしいかな。
Avatar
Yoshikuni Kato 11/5/2021 2:09 PM
ありがとうございます。 Subject等の状態を導入すればできそう、というのはイメージがつきます。 ただ、Rx等だと、例えば以下の処理のように、監視の終了などを含め、1つのObservable内にencapsulateできるのが扱いやすくて便利だなぁと思っているのですが、CombineだとこのようにPublisher内に閉じ込めることは難しい(あまりやらない?)のでしょうか? let observable = Observable<String>.create { observer in let token = webSocketClient.receiveMessage { message in observer.on(.next(message)) } return Disposables.create { // close connection when disposed token.invalidate() } }
Avatar
Kishikawa Katsumi 11/5/2021 2:31 PM
ああ、なるほど。質問の意味を理解しました。 Observable.createと同等のものがCombineにないので上で書かれてるようにPublisherでそういうのを拡張する、ってことになると思います。
2:32 PM
たぶんRxっぽくやりたい人はそこそこいて、そういう拡張を書いているんだと思います。
Avatar
Yoshikuni Kato 11/5/2021 3:11 PM
分かりました。少し調べていた感じだと、単純なextension等では実現できなくて、実現しているもの(CombineExtやTCA等)では独自Subscriptionを作る方法で実現しているようで、私も何度かトライしたことがあるのですが、少し難易度が高かったので、どのように回避しているのか、気になっていたところでした。 (edited)
3:13 PM
他の質問サイト等でも、Subject等の状態を導入して実現している、もしくは(自作は結構大変なので)CombineExtを導入しているという回答をもらったので、そのぐらいしか手がなさそうですね。
3:13 PM
ありがとうございます!
Avatar
最近Combine使い始めたんですけど、コードのいたる所にeraseToAnyPublisher()やsetFailureType(to: Error.self)が出てきて 見通しが悪いんですが、なんともならないんでしょうか? import Combine func f1() -> AnyPublisher<Int, Error> { Just(1) .setFailureType(to: Error.self) .eraseToAnyPublisher() } // 本当はこう書きたい func f2() -> Publisher<Int, Error> { Just(1) }
Avatar
Avatar
We
最近Combine使い始めたんですけど、コードのいたる所にeraseToAnyPublisher()やsetFailureType(to: Error.self)が出てきて 見通しが悪いんですが、なんともならないんでしょうか? import Combine func f1() -> AnyPublisher<Int, Error> { Just(1) .setFailureType(to: Error.self) .eraseToAnyPublisher() } // 本当はこう書きたい func f2() -> Publisher<Int, Error> { Just(1) }
このケースは、 AnyPublisher<Int, Never> ではダメですか?
Avatar
ORTがもうちょいなんとかなればこの辺りも全部ORTで書けるようになる気はする 今はムリだけど
Avatar
omochimetaru 12/7/2021 3:36 AM
今こんなんやってるけど、これは駄目なんよな https://forums.swift.org/t/se-0328-structural-opaque-result-types/53248
Hello, Swift community. The review of SE-0328: Structural opaque result types begins now and runs through November 11, 2021. Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager. If you do email...
Avatar
eraseToAnyPublisher は将来的には↓のようにできるようになると思います。 func f1() -> some Publisher<.Output = Int, .Failure = Never> { Just(1) }
3:37 AM
This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - swift-evolution/0328-structural-opaque-result-types.md at main · apple/swift-evolution
Avatar
omochimetaru 12/7/2021 3:37 AM
あ、0328のFuture Directionsに言及あるんすね。
Avatar
.Failure: Error も書けるのであれば
Avatar
えーと一応ですね
Avatar
func f1() -> some Publisher<.Output = Int, .Failure: Error> { Just(1) }
3:37 AM
にできるかも?
3:38 AM
いや、 some Error でいいのか?
Avatar
あんまりオススメしないですが、「変数宣言なら左辺の型を省略できる」という事象を利用して
Avatar
func f1() -> some Publisher<.Output = Int, .Failure = some Error> { Just(1) }
3:38 AM
Opaque Type のネストってできるんだっけ?
Avatar
型宣言事態を無くすことで擬似的に問題を解決することは出来る
3:39 AM
AnyPublisherの利用を強く勧めますが…
Avatar
Avatar
koher
Opaque Type のネストってできるんだっけ?
omochimetaru 12/7/2021 3:40 AM
言及されてるの見たことないなあ
Avatar
ORTネストはできません
Avatar
omochimetaru 12/7/2021 3:40 AM
そもそも Failure: Error だから、そこ宣言しなくていいのでは? (edited)
3:40 AM
some Publisher<.Output = Int> とおなじに見える (edited)
Avatar
そうか、それでいいのか。
Avatar
let f1 = { return Just(1).setFailureType(to: Error.self) }
😮 1
3:41 AM
こういうことは出来る。
Avatar
現状だと、↓とか作っておくと楽そう。 extension Just { static func anyPublisher<T>(_ value: T) -> AnyPublisher<T, Error> { Just(value) .setFailureType(to: Error.self) .eraseToAnyPublisher() } } Just.anyPublisher(1)
3:44 AM
いずれにせよ、 eraseToAnyPublisher は Opaque Result Type が完成するまでの間に合わせという認識です。将来的には不要になるはず。そして、現状では必要かと。 // 将来的にできるようになる予定の理想形 func f1() -> some Publisher<.Output = Int> { Just(1) }
Avatar
AnyPublisherは型消去の話なので将来的に無くすことは出来ますが、setFailureTypeは型を指定する話なので消すのは無理筋っぽい
3:45 AM
ただ、Neverに対してのサポートは元々手厚いので、そもそもErrorを付与する必要は特にないかも (edited)
Avatar
↑でおもちが言ってるように、 PublisherFailure: Error だから(そして Never: Error だから) (edited)
Avatar
omochimetaru 12/7/2021 3:45 AM
some Publisher<Int> になる(なってしまう)可能性もちょっとある・・・ https://forums.swift.org/t/pitch-light-weight-same-type-constraint-syntax/52889 (edited)
Avatar
何も指定しなければそのまま Failure の詳細を隠蔽して使えるのでは?
Avatar
Avatar
omochimetaru
some Publisher<Int> になる(なってしまう)可能性もちょっとある・・・ https://forums.swift.org/t/pitch-light-weight-same-type-constraint-syntax/52889 (edited)
これだと Output だけ埋めるとか Failure だけ埋めるとかできなさそう。
Avatar
omochimetaru 12/7/2021 3:47 AM
https://forums.swift.org/t/pitch-introduce-existential-any/53520#future-directions-11 ↑anyのスレでもlight weight same typeに言及がある (edited)
Avatar
Avatar
koher
これだと Output だけ埋めるとか Failure だけ埋めるとかできなさそう。
omochimetaru 12/7/2021 3:47 AM
<.Failure = FooError> の記法と共存はできますね。
Avatar
Protocol-orientedを考えたら現実的じゃなさそうだけどなぁ。
3:48 AM
Collection とかいくつの associatedtype があることか。
Avatar
omochimetaru 12/7/2021 3:49 AM
でも型推論系のxedinさんとhollyさんが言及してるからなあ
Avatar
Avatar
omochimetaru
言及されてるの見たことないなあ
似てると言及されているRustのimpl Traitではネストができますね。 https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=600dc7df3eda1063c6b78c246d412d61
👀 1
Avatar
実現は可能だろうけど、現実的に使い物になるのかな?
Avatar
omochimetaru 12/7/2021 3:49 AM
僕はこれ自体の実用性よりジェネリックprotocolが将来実装されたときに構文が衝突する問題のほうが大きいと思う。 (edited)
Avatar
もし some Foo<A, B, C> が前から順番に適用されるとして、 protocol Fooassociatedtype の宣言順とかわかんなくない?
3:50 AM
Collection くらいよく使うやつでも、最初は Element だろうなくらいはわかるけど、 2 番目が何かわからない。
3:50 AM
まあ、最初だけわかれば十分使えるのかもしれないけど・・・。
Avatar
omochimetaru 12/7/2021 3:51 AM
To accommodate this intuition, we propose to allow protocols to express primary associated type(s) in the same way they are declared in a generic type:
protocol Collection<Element> { associatedtype Index: Comparable ... }
Avatar
SubSequenceIndexIterator のどれが先かとかはまったくわからない。
3:51 AM
なるほど?
Avatar
omochimetaru 12/7/2021 3:52 AM
associatedtypeじゃなくて特別な場所に書くように変えるらしい
Avatar
これは誤解を招きそうな構文だ・・・
Avatar
generic protocol詰みそう
t_sorena 1
3:52 AM
流石に突っ込み入ってないの?
Avatar
omochimetaru 12/7/2021 3:53 AM
わからん。ピッチだからまだチェックしてないけど書き込みは80あるよ
Avatar
まあなんか、上に書けるようにしても良いとは思うけど
3:53 AM
下にもassoctypeであることを明記する、ならまぁ良いかなって感じはする
Avatar
Avatar
koher
このケースは、 AnyPublisher<Int, Never> ではダメですか?
返信ありがとうございます。実際はもう少し複雑でNeverだとうまくいきません。。 func f1() -> AnyPublisher<Int, Error> { if 何か条件1 { return Just(1) .setFailureType(to: Error.self) .eraseToAnyPublisher() } if 何か条件2 { return Just(2) .setFailureType(to: Error.self) .eraseToAnyPublisher() } return Fail(error: someError) .eraseToAnyPublisher() }
Avatar
Avatar
We
返信ありがとうございます。実際はもう少し複雑でNeverだとうまくいきません。。 func f1() -> AnyPublisher<Int, Error> { if 何か条件1 { return Just(1) .setFailureType(to: Error.self) .eraseToAnyPublisher() } if 何か条件2 { return Just(2) .setFailureType(to: Error.self) .eraseToAnyPublisher() } return Fail(error: someError) .eraseToAnyPublisher() }
これはCombineというか雪だるま型特有の問題ですねぇ
Avatar
Avatar
We
返信ありがとうございます。実際はもう少し複雑でNeverだとうまくいきません。。 func f1() -> AnyPublisher<Int, Error> { if 何か条件1 { return Just(1) .setFailureType(to: Error.self) .eraseToAnyPublisher() } if 何か条件2 { return Just(2) .setFailureType(to: Error.self) .eraseToAnyPublisher() } return Fail(error: someError) .eraseToAnyPublisher() }
なるほど。このケースだと ORT が完成してもダメそうですね。
Avatar
ResultBuilderを使ってif式をPublishers.Eitherに自前定義するみたいなことをやると
3:55 AM
シュッとかけるようになります
Avatar
Existential Type が完成したら、↓でしょうか。現状ではどうしようもなさそうです。 func f1() -> any Publisher<.Output == Int> { if 何か条件1 { return Just(1) } if 何か条件2 { return Just(2) } return Fail(error: someError) }
3:56 AM
どうしようもないというのは、もちろん tarunon さんが言うようにしたり、上に書いたような anyPublisher を生やすとかはできますが、本質的に AnyPublisher<Int, Error> に変換するのは欠かせないという意味です。
Avatar
Introduce if/switch/for expression in swift. Contribute to tarunon/Builder development by creating an account on GitHub.
Contribute to sidepelican/PublisherBuilder development by creating an account on GitHub.
3:57 AM
この辺でガチャガチャやってるworkaroundのどれかは刺さるかもしれない
Avatar
Avatar
koher
Existential Type が完成したら、↓でしょうか。現状ではどうしようもなさそうです。 func f1() -> any Publisher<.Output == Int> { if 何か条件1 { return Just(1) } if 何か条件2 { return Just(2) } return Fail(error: someError) }
omochimetaru 12/7/2021 3:57 AM
ああそうか、any の existentialへは暗黙にアップキャストできるので、いいですね。 (edited)
Avatar
AnyPublisherとどっちが楽?と言うところではあるけど
Avatar
Avatar
tarunon
AnyPublisherとどっちが楽?と言うところではあるけど
eraseToAnyPublisher が必要ないのでコードが簡潔になるのと、 Type Erasure の諸々のオーバーヘッドを考えると、直接の Existential の方がマシじゃないですか?
Avatar
このコメントはif式でガチャガチャやる方の話
Avatar
ああ、コメントの並び順で誤解しました😅 (edited)
Avatar
直Existentialも関数呼び出しのオーバーヘッドはそこそこあった気がするけど
Avatar
Type Erasure も結局中で動的ディスパッチしまくるから Existential Container のオーバーヘッドの方がマシそうと思ったけど、 Type Erasure の場合はクラスの継承で済ませられるからマシだったりするのかなぁ。構造の複雑さを考えると Type Erasure 遅そうだけど・・・。
Avatar
tryでその辺話したんですがあの時はどうだったかな
4:14 AM
そもGenericsも理想の静的呼び出しになることは珍しくて、かなりのケースで動的ディスパッチに落ちてるので、
4:15 AM
あんまり気にしなくても良いのかもしれません
4:15 AM
理想の静的呼び出しへの最適化が出来るなら、if式で雪だるま型をさらに包むのが一番良さそうではあります
Avatar
let box: AnySequenceBox<Element> = SequenceBox<[Element]>(base) みたいになったとして、 box から SequenceBox のメソッドを呼び出すところは動的ディスパッチだけどクラスの継承で vtable で済ませられて、 SequenceBoxbase のメソッドを呼ぶところが specialize されるなら、そこに Existential Contiainer のオーバーヘッドはなくなる。
4:16 AM
↑の specialize がされないなら、 vtable 引いた上で Existential Container のオーバーヘッドも乗って来るから相当遅そう。
Avatar
なるほど勉強になります。Existential Typeとか、全然知りませんでした。 some Publisher見て、なるほど〜確かにsome Viewって書くなと思いましたが、そううまくもいかないんですね。笑 現状だと糖衣構文で凌ぐのがよさそうですね。 このライブラリも、とてもおもしろいですね! https://github.com/sidepelican/PublisherBuilder flatMapでいちいち戻り値書くのめんどくさいなーと思ってました。 みなさんありがとうございました!
Contribute to sidepelican/PublisherBuilder development by creating an account on GitHub.
👍 1
Exported 359 message(s)
Timezone: UTC+0