func hoge<Foo: Protocol>(foo: Foo)
と、fucn (foo: Protocol)
だとstruct API { private init() { } struct Request { private init() { } struct GetDayOfWeekStories: APIRequest {
(edited)case
なし enum
だとどうやってもインスタンス化できないので、 private init 書く必要ないから いいんじゃないかと思ってます。private init() { fatalError() }
くらいにした方がいいかなと思ってました)/// Namespace for diagnostics enum diag {} extension diag { // メンバー群 ...
実際にはすべてのメンバーを extension に実装することにより、 case
を書けなくするということをやっています。 (edited)??
って、もしかしてコンパイルすごい遅いんでしょうか func apiErrorWithPromise(_ errorMsg: String? = nil, _ httpNumber: Int? = nil) -> Promise<Void>{ let errorMessage = "エラーメッセージ:\n"+(errorMsg ?? "不明なエラー(iMast)")+" ("+String(httpNumber ?? -1)+")\n\nエラーメッセージに従っても解決しない場合は、アプリを再起動してみてください。" return alertWithPromise( title: "APIエラー", message: errorMessage ) }
var errorMessage:String = "" errorMessage = "エラーメッセージ:\n" errorMessage += errorMsg ?? "不明なエラー(iMast)" errorMessage += " (" errorMessage += String(httpNumber ?? -1) errorMessage += ")\n\nエラーメッセージに従っても解決しない場合は、アプリを再起動してみてください。"
のようにしたら早くなりました、ありがとうございます\()
どうも個人的に読みにくいんですよねlet errorMessage
に +で連結して代入 let errorMsg2 = errorMsg ?? "不明なエラー(iMast)" let httpNumber2 = httpNumber ?? -1
String.localizedStringWithFormat("", a, b)
) 今回の場合、これだけで速くなりそう。 (edited) typealias DataSearchFlags : oneof { None, Backward, Anchored }
[default: ]
を指定するのは、値の存在が保証されてない場面では??dict[key]! += 1
ってできたっけ?dict[key] += 1
ができるかどうかはわからないけど、 それができないからといってdict[key, default: never()] += 1
こう?dict[key, default: undefined()] += 1
var x: [String: Int] = [:] x["aaa"] = 3 x["aaa"]! += 1 print(x["aaa"])
Optional(4)
!
演算子はオペランドが左辺値の場合、左辺値のままにしてくれるのか。dict[key]! += 1
で良いってことになるのか・・・?dict[key] ?? defaultValue += 1
dict[key] ||= 3
rubyだとこういうのよく見る (edited)func doubleQuestion( left: (read: ()->T?, write: (T)->Void), right: (read: ()->T, write: (T)->Void) ) -> (read: () -> T, write: (T) -> Void)
(edited)Neverがボトムになれba まどろっこしい関数用意しなくて良くなるんだがな
これおもしろいですね。dict[key, default: fatalError()] += 1
(edited)Never
がボトムタイプでない現状だと、 func never<T>(_ message: String = "") -> T { fatalError(message) }
みたいなのがいると。Never
がボトムタイプにならないのって誰も Proposal 書かないから?An uninhabited type can be seen as a subtype of any other type ... This can be considered as a separate proposal.
enum Never {}
がボトムってどうなの?とか、他の enum Foo {}
はどうなるの?とか、色々ややこしいから後回しにされてるのでは。+1
みたいなリプライあるけどwsomeFunc(AnyProtocol, parameter...)
的なのを考えてたんですが型で判別できるから、ありかどうかで迷ってる感じですdownload
は Alamofire.download
ですよね 正式にはSwift.min
ですdownload
でアクセスできるので 結局そこがどうなのかって話なわけですよねcom.omochimetaru.Alamofire.download
とかなってシンドイけど Alamofire.download
ならまあ別に平気だし、だいたいは引数の型でも区別可能だし。download
が出てきてごちゃごちゃしてきたときはfunc alamofireDownload() { .. }
とかを自作してラップするのが良いんじゃないでしょうかimport ... as ...
的なやつですかprotocol APIRequestable { func load<R: Request>(request: R) -> R.Response }
って感じですかね?requests<R: Request>(adapter: RequestAdapter=DefaultRequestAdapter()) -> (request: R) -> R.Response
みたいにしようかで悩んでました (edited)swiftc
が生成したhello_world.bc
のターゲットがx86_64-apple-macosx10.9
になってるからダメだよ、となってる。swiftc
でtarget
がasmjs-unknown-emscripten
な.bc
を生成できないとダメなんじゃないかな。emscripten
のissueには「asmjs-unknown-emscripten
はWebAssembly
(wasm32
)とほぼ同じだよね?」って話してる人がいて、同じ人がSwiftをWebAssemblyへクロスコンパイルする話題をswift-devに投げてる。 https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20170626/004860.html (edited)Promise
みたいに非同期処理とエラー処理をまとめて扱うような設計が嫌いで、非同期処理なら非同期処理、エラー処理ならエラー処理と責務を分解しておくべきだと考えて、エラー処理を含まない Promise
↓を作ってました。 https://github.com/koher/PromiseK/tree/dev-3.0Promise
と Result
が対応してるなと思って、それらを throws
, try
と (edited)async
, await
に対応させれば、自由にまぜて使えるよなぁと考えたものです。Promise
にエラー処理を担当させちゃうと、 catch
忘れがおこっちゃうんですよねぇ。Promise
と Result
を一緒に使うとモナドパズルになっちゃうんですよねぇ。Result<Promise<Result<Foo>>>
を Promise<Result<Foo>>
にしたいとか。Array<Promise<Result<Foo>>>
を Primise<Result<Array<Foo>>>
にしたいとか。let a: Promise<Int> = ... let b: Promise<() throws -> Int> = a.map { try foo($0) }
みたいに、 map
等の中で throw
される場合は戻り値の型パラメータを () throws -> Foo
に変換する API を考えてます。async
, await
ができれば無用の長物ですが。typealias Promise<Value> = (@escaping (Value) -> ()) -> ()
とする Promisure (= Promise + Closure) というライブラリも作ってましたw// Wrapping let a: Promise<Int> = { $0(3) } // like `Promise.resolve(3)` in JS // Promisifying a callback let b: Promise<Int> = promise { resolve in // Gets 5 after 1.0 seconds DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { resolve({ $0(5) }) } } // Operators let sum: Promise<Int> = a >>- { a in b >>- { b in { $0(a + b) } } } // flatMap let square: Promise<Int> = { $0 * $0 } <^> a // map let vector: Promise<Vector2> = curry(Vector2.init) <^> { $0(2.0) } <*> { $0(3.0) } // apply
Observable
にエラーが組み込まれてるのも僕は好みじゃないんですが、どうにかならないものでしょうか。throws
, try
みたいな特殊構文用意するよりも、モナドに対して do
記法用意するとかの方が汎用性はありますよねぇ。Result
と Promise
なら Promise
が外側にいてほしいとか、モナド感での precedence みたいなものが考えられる気がして、Monad
型に対して演算子みたいな precedence
を指定できてsort
関数みたいに、それを使って入れ子を関数が解決する。let a: Result<Promise<Int>> = ... let b: Promise<Result<Int>> = magic(a) do { let c: Int <-- b // 多段アンラップ let d: Result<Int> <- b // 一段アンラップ }
try
とかキーワード足さなくても自作モナドを組み込みモナドと同じように便利に使える。then
が map
と flatMap
になってるんですけど、ネストしてくると読みづらいんですよねぇ・・・。Result
を作らなかったから thorws
, try
を別の型でラップしたい場合に Promise<() throws -> Int>
とかなるのはちょっとつらいですよね。Observable
とかでやろうとしたらできないですよね?=>
レベルだと演算子の方が可読性高そうだけど。Any
でもダメで、任意の型パラじゃないといけないから辛い・・・extension Any { func `let`<T>(_ f: (Any) -> (T)) -> T { ... } }
Any
になっちゃってSelf
にする extension
ってできるんだっけ?public func =><T, U>(lhs: T, rhs: (T) throws -> U) rethrows -> U { return try rhs(lhs) }
class Animal {} class Cat : Animal { func nya() { print("nyaa") } } extension Animal { func exec(f: (Self) -> Void) { f(self) } } let cat = Cat() cat.exec { $0.nya() } error: TempGround.playground:11:12: error: value of type 'Animal' has no member 'nya' cat.exec { $0.nya() } ^~ ~~~
extension SomeProtocol where Self: SomeClass {}
(edited)extension Any where Self: SomeClass {}
protocol Anything {} extension Anything { func `let`<T>(_ f: (Self) throws -> T) rethrows -> T { return try f(self) } } extension Double: Anything {} (42.0).`let` { $0 + 1.0 / $0 }
protocol Proto {} class Cat : Proto { func nya() { print("nyaa") } } extension Proto { func exec2(f: (Self) -> Void) { f(self) } } let cat = Cat() cat.exec2 { $0.nya() }
=>
の議論からするとそれは問題ないのでは=>
の実装をオペレータからメソッドにできるかどうかだけを今考えていてAny
が nominal でないってことだけなわけだ。Self
を f
に渡す形で実装はできる。Any
じゃなくてLettable
という protocol
のメソッドとして作って.let
を使いたい型については extension Cat : Lettable {}
=>
の方がいいと思う。<$>
は順番が逆転しちゃう問題もありますしねぇ・・・Promise
のチェーンはpromise.flatMap { ... }.flatMap { ... }.flatMap { ... }
primise >>- { ... } >>- { ... } >>- { ... }
=>
は標準ライブラリに入れてほしい・・・。r'''
出せなかったし嘘だと思うr'''
は正規表現じゃなくて生マルチライン文字列Swift >>-
で検索したら、一つ目は https://www.apple.com/jp/swift/ だけど、二つ目は typelift/Operadics で、三つ目は thoughtbot/Runes だった。<^>
も駄目っぽいですよ>>=
は重複してるから >>-
に、 <$>
は言語仕様上の制約で <^>
になってる。haskell <$>
は https://stackoverflow.com/questions/37286376/what-does-mean-in-haskell が出たからEither<T, Either<U, V>>
public enum Either2<A, B> { case a(A) case b(B) } public protocol Either2Convertible { associatedtype CaseA associatedtype CaseB var asEither: Either2<CaseA, CaseB> { get } } extension Either2: Either2Convertible { public var asEither: Either2 { return self } }
enum
の抽象化ってことは、 Either2<Int, String>
と Either2<String, Int>
は区別したいってことですよね?Enum2
, Enum2Convertible
は良さそうEnum1
と Enum0
もいらないですか? Box
と Never
Enum3
とかだと CaseA
より Case1
とかの方がよさそうenum Enum2<T0, T1> { case Case0(T0) case Case1(T1) }
EnumN
とか?Houyhnhnm 音節Hou・yhn・hnm 発音記号/hwínəm|húːɪ‐/ 名詞可算名詞 フーイナム 《Swift 作 Gulliver's Travels (ガリバー旅行記)の中の人間的な理性が徹底している馬; 人間の形をした Yahooを支配する》. [馬の鳴き声からのSwiftの造語]
swift package init
したときに面倒$ swift package init --help OVERVIEW: Initialize a new package OPTIONS: --type empty|library|executable|system-module
Yahoo!の名前の由来は英語の「Yet Another Hierarchical Officious Oracle」(さらにもう一つの階層的でお節介な神託)の略だといわれている[57]。また、ファイロとヤンは自分たちのことを「ならずもの」だと考えているので、「粗野な人」という意味がある「Yahoo」(『ガリヴァー旅行記』に登場する野獣の名前が由来)という言葉を選んだと主張している[10]。さらに感嘆符が付いていることに関しては「ヤッホー!」「やったー!」を意味する英語の感動詞「yahoo」と掛けているとも考えられる。
EnumNConvertible
を EnumN
に convert するんじゃないんですか?CustomStringConvertible
とかを考えても EnumNConvertible
とかならおかしくない気も。$ gyb -D Key=Value
でパラメータ渡せるから、できるよpublic func animatedItems<S: AnimatableSectionModelType, O: ObservableType, C0: Reusable, C1: Reusable, C2: Reusable, C3: Reusable, C4: Reusable>(for type0: C0.Type, _ type1: C1.Type, _ type2: C2.Type, _ type3: C3.Type, _ type4: C4.Type) -> (O) -> Disposable where C0: UICollectionViewCell , C1: UICollectionViewCell , C2: UICollectionViewCell , C3: UICollectionViewCell , C4: UICollectionViewCell , S.Item: Enum5Convertible , S.Item.T0 == C0.Dependency , S.Item.T1 == C1.Dependency , S.Item.T2 == C2.Dependency , S.Item.T3 == C3.Dependency , S.Item.T4 == C4.Dependency , O.E == [S] {
使う側がこんな感じの地獄じみた形相になってるから、任意個数サポートできるのはもはや必須の感じがするdataSourceObservable .bind(to: tableView.rx.animatedItems(for: MyCell1.self, MyCell2.self)) .disposed(by: disposeBag)
curry
は昔ジェネリック引数14個(?)でコンパイル終わらなくなる問題がありましたが大丈夫そうですか?--line-directive=
入れないと ###sourceLocation
が各行入るからなんなのかと思った!!
オペレータ。機能は !
と同じ。Codable
を適用すると、encode/decodeを実現するための実装をコンパイラーが生...struct ObservableA<T> { func subscribe(onNext:(T) -> () = { _ in }) { } } ObservableA<(Int, Int)>().subscribe(onNext: { (a, b) in // ok }) struct ObservableB<T> { func subscribe(onNext:((T) -> ())? = nil) { } } ObservableB<(Int, Int)>().subscribe(onNext: { (a, b) in // 死ーん })
[omochi@omochi-iMac bin]$ pwd /Users/omochi/work/swift-source/build/Ninja-RelWithDebInfoAssert/swift-macosx-x86_64/bin [omochi@omochi-iMac bin]$ ./swift --version Swift version 4.0-dev (LLVM a15decabe3, Clang 60a12bf739, Swift fcd389b17e) Target: x86_64-apple-macosx10.9
XCTest
にXFAIL
欲しいlit
はテキストファイルならなんでも良いので、.md
にテストを書いたりできるのが面白いと思った。 https://github.com/apple/swift-integration-tests/blob/master/swift-package-init-exec.mdlit.cfg
で指定すればですね。 https://github.com/apple/swift-integration-tests/blob/master/lit.cfg#L56XCTAssertEqualWithAcuracy
はフィールドごとに確認するので増えるほど書くのがきつくなる stcuct Hoge { val a: Float } let x = Hoge(a: 0) let y = Hoge(a: 0) XCTAssertEqualWithAcuracy(x, y, accuracy: 1e-10)
かといって関数に切り出すとXCodeで停止地点が分かりづらくなる。 XCTAsserEqual
はEquatable
を実装するだけで使えるので 同様にNealyEquatable
みたいなプロトコルを切ってXCTestがそれを使うXCTAssertEqualWithAcuracy
を用意してくれるのがベストかな。swift test
とかで結構密接に結びついてるのでこちらからどうこうもしにくい気が。かといって関数に切り出すとXCodeで停止地点が分かりづらくなる。
切り出した関数に…file: StaticString = #file, line: UInt = #line)
とかつけると良いのでは。QuickSpec
が(間接的に) XCTestCase
を継承してるのか。 https://github.com/Quick/Quick/blob/cd89737d3d5306124e91c357b66cde274a3269b4/Sources/Quick/QuickSpec.swift#L13swift test
もできるのかな?swift test
Package.swift
はdependenciesがなんか複雑になってますね……Quick
を使いたいからじゃない? < 複雑XCTestCase
を継承してる https://github.com/Quick/Quick/blob/cd89737d3d5306124e91c357b66cde274a3269b4/Sources/QuickSpecBase/include/QuickSpecBase.h#L8testDependencies
がなかったので、テストの時だけ使う依存を実現してる。testDependencies
見た気がしてたんだけど使えなくなってるのか・・・Remove testDependencies from PackageDescription This feature was supposed to support dependencies only for the root package but at some point it stopped working however the API still remained open. This patch removes the public API. This is a valid and desired feature which is supposed to come back after it goes through proper review on swift evolution.
https://github.com/apple/swift-package-manager/commit/34b7826cb586b0769ea5f60a7718d7de599ce27fPackage.swift
を用意してたけど、環境変数で切り替えられることをikesyoさんに伝えたら使ってくれた。 (edited)Package
の dependencies
がテストからも使えるのなんか変な気も。> SWIFTPM_TEST_Carthage ああこれはSPMが定義してくれるのか。
Makefileで実行するときに設定してます。 https://github.com/Carthage/Carthage/blob/master/Makefile#L82Package.swift
がサンドボックス内で解釈される際に、SwiftPMがあえて環境変数を引き継ぐ様にしてるので、あながち間違いでもない。$ which -a sandbox-exec /usr/bin/sandbox-exec
DESCRIPTION The sandbox-exec command is DEPRECATED. Developers who wish to sandbox an app should instead adopt the App Sandbox feature described in the App Sandbox Design Guide.
sandbox-exec
を使う様にしたコミットには環境変数がらみが特に書かれてないぽいから「環境変数をあえて渡す」というのは、チャットの中だけの話だったかも。#line
を使うってこれのことですか。 http://masilotti.com/xctest-helpers/ そもそもrecordFailure
の存在を知らなくてログに行番号出るだけじゃ分かりにくいとか思っていました。XCTAssert*()
シリーズにも渡せます。XCTAssert*()
シリーズに渡せるってのは何でしょう? recordFailure
がXCTestCase
のメソッドなのでXCTAssert*()
のようにトップレベルのを定義するにはデフォルト引数で呼び出し元のself
を渡すとかするのかなぁと思ったのですが。self
を渡す話をよく覚えていない……XCTestCase
は引数にないのでどこから渡っているのか謎。 別の仕組みになっているんだろうか。recordFailure
はXCTestCase
のメソッドなんですよ。 で、XCTAssert*()
はトップレベルなのでrecordFailure
が使えない(はず)XCTestCase
のextensionにすればいいんですが XCTAssert*()
と同名を用いると例のバグ?でトップレベルのほうが見えなくなる。 (edited)recordFailure()
ではなく XCTFail()
を使ってました。 https://github.com/realm/SwiftLint/blob/master/Tests/SwiftLintFrameworkTests/IntegrationTests.swift#L35recordFailure
を使うんじゃなくて内部でfile, line指定付きのXCTAssert*()
を使えってことですね。 間違った方向に進んでました。enum Foo { static var foo: Foo { fatalError() } } func bar(_ foo: @autoclosure () -> Foo, ....) { ... } bar(.foo, ....)
これが可能になるFoo.foo
が実際には生成されないのがポイント?delegate.scrollView(scrollView, .didScroll)
とかにいいかも (edited)struct Foo {} let foo = Foo() func bar(foo: Foo, ....) { ... } bar(foo, ....)
そもそもこれじゃ駄目な理由は?struct Foo { static var foo = Foo() }
ならまあ差はそれほど無いのかもしれない。.foo
で書けるねstruct BarOverloads { static let foo = BarFoo() static let poo = BarPoo() } func bar(_ overload: BarFoo, ...) func bar(_ overload: BarPoo, ...) bar(.foo, ...) // できない bar(.poo, ...)
(edited)struct Bar { struct Foo { static let foo = Foo() } struct Poo { static let poo = Poo() } } func bar(_ overload: Bar.Foo, ...) func bar(_ overload: Bar.Poo, ...) bar(.foo, ...) bar(.poo, ...)
(edited)\
を使う感じで。 https://github.com/apple/swift-evolution/blob/master/proposals/0182-newline-escape-in-strings.md (edited)let s = """ ... In Windows you have paths like C:\ ... """
\
を \\
に直さないといけないけど、新使用だとコンパイルエラーにならないから問題だと。特に """
だからといって文字列コピペしてきたときとかに問題起こりそう。c:\news
とかもそうなんで。ちょっと弱い。Optionalのclosureでtupleが分解できない問題治してもらえた。はやいw
swift-4.0-DEVELOPMENT-SNAPSHOT-2017-07-11-a
以降で直ってるぽいですね。struct
でもサイズが大きいと勝手に最適化されたりするんでしょうか?どこかで触れられてましたっけ? https://developer.apple.com/videos/play/wwdc2017/402/std::string
は確か COW だったけど COW 止めたんよね?Array
等の場合はマルチスレッドをサポートしないことで問題を回避している??var a = [2, 3, 5, 7] // スレッドA a[2] = 4 print(a) // スレッドB a[3] = 8 print(a)
(edited)startThread( closure: () -> Void )
var a = [2, 3, 5, 7] a[2] = 4 startThread { a[3] = 8 }
func startThread(f: @escaping () -> Void) { f() } func main() { var a = [2, 3, 5] a[0] = 88 startThread { a[1] = 99 } print(a) // [88, 99, 5] } main()
struct
である Array
に包まれてるからvar a = 2 let foo: () -> () = { a += 1 } foo() print(a) // 3
@escaping
が@nonescaping
なときはキャプチャされていてもvar a = 42 if random() < 0.0001 { foo = { print(a) } }
foo
がエスケープしてないからならない (edited)a
はスタック上には無いと思いますvar foo: () -> () = {} func bar() { var a = 42 if random() < 0.0001 { foo = { print(a) } } } for _ in 1...10000000 { bar() }
bar
のコールのほとんどではスタックで済ませられるはずだけど、a[0] = 99
に、バッファストレージインスタンスの参照カウンタを見て、1ならそのまま書き換える、2以上ならディープコピーする、だから・・・a
のスコープ自体がヒープに確保されてるから a
のバッファへの参照カウントは常に 1 になりそう。 var a = [2, 3, 5, 7] // スレッドA startThread { a[2] = 4 print(a) } // スレッドB startThread { a[3] = 8 print(a) }
a
の実体が1つだけあってa[2] = 4
とかを、ユーザ側で ロックしないとバグりそう?a
の実体が消えて、その先のストレージも消える。Array
がスレッドアンセーフなのと、 COW がスレッドセーフかどうかと、参照カウンタがスレッドセーフなことは独立な気がする。Array
がスレッドアンセーフな時点で Array
の COW もスレッドセーフである必要はない?それとも、 Array がスレッドアンセーフな時点で Array の COW もスレッドセーフである必要はない?
これがYESだと思います。Array
自体がスレッドアンセーフな時点で、 ArrayのCOWだけのスレッドセーフティーを考える意味がなくて、それもアンセーフArray
のCOWに同期のオーバーヘッドはないと。std::string
自体はスレッドセーフじゃないと思うんだよなあArray
をロックして使ってれっば問題は起こらない気がする。std::string
が COW でなくなったのは別の理由なのかな?std::string
と COW だった頃の std::string
はまた別かもしれない・・・std::thread
が入るときにこの規格がちゃんと整理されたって話がこの記事に同時に書いてあるのでstd::string
としては、2つに見えてるんだ。ユーザには。std::string
一個単位で readonly safe ってことしか聞いてないから (というかコピーをまたいだグループでロックを取るなんで不可能)Array
の COW はスレッドアンセーフでいい話と矛盾してない?var a = [2, 3, 5] // スレッドA a[2] = 4 // スレッドB var b = a b[2] = 999
a
と b
で同期とるとか無理でしょってことか。var a = [2, 3, 5] // スレッドA startThread { a[2] = 4 } // スレッドB startThread { var b = a b[2] = 999 }
Array
自体はスレッドアンセーフでも COW はスレッドセーフでないといけないってこと?a[2] = 4
と var b = a
の2行を、ユーザがロックすべきだと思う。b = a
の代入の最中に、 a[2] = 4
の処理が走り始めて内部状態が壊れる可能性があるはず。a[2] = 4
が書き込み操作で、 b = a
の右辺はreadだから、競合してるのでロックが要るって事になるはず (edited)int
の書き込みは atomic で同時なんてないんじゃないの? Q4. 基本型intはスレッドセーフですか? A4. 「同時アクセスが全て読み込み操作であれば安全」というスレッドセーフ性レベルが保証される。
http://yohhoy.hatenablog.jp/entry/2013/12/15/204116// 同一変数に対して片方が変更操作 int y = 2; void th1() { int r2 = y; // NG: データ競合 } void th2() { y = 42; // NG: データ競合 }
th1
と th2
の中でロックしてても不定じゃない?struct Counter { var count = 0 mutating func countUp() { count += 1 } mutating func reset() { count = 0 } }
(edited)struct
の場合CoWで実体分かれて問題ないんではreset
の write の方struct
だからといって CoW は自動的には起こりません。// 同一変数に対して片方が変更操作 int y = 2; void th1() { int r2 = y; // NG: データ競合 } void th2() { y = 42; // NG: データ競合 }
int y = 2; void th1() { y = 99; } void th2() { y = 42; }
の代入が atomic なら↑も OK となりそうだけど・・・。int
の代入は atomic なのかint
の代入がatomicでないから、ってことやんね?疑問は二つで、 - C++ において int の代入は atomic なのか - atomic だと仮定して↑はスレッドセーフなのか
int
にたいしてどれが適用されるかよくわからない。long
と double
以外のプリミティブや参照の読み書きは atomic っぽいから、 int
を同時に読み書きすること自体には問題なさそう。 For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write. Writes and reads of volatile long and double values are always atomic. Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.htmlint
の読み書きが atomic かどうかを調べて atomic だと知って安心した記憶があるんだけど、 Java の話だったのかなぁ。その感覚で int
の読み書きを atomic だと思っていたから↓がおかしいんじゃないかと思ったけど、 // 同一変数に対して片方が変更操作 int y = 2; void th1() { int r2 = y; // NG: データ競合 } void th2() { y = 42; // NG: データ競合 }
C++ では規格上 atomic でないからたとえそれを atomic に行う CPU 向けにコンパイルしたとしてもどのように最適化されるかわからず未定義動作なので↑は正しい、と。var a = 3 var b = 2 a = 4 b = 1 print("a=\(a), b=\(b)")
(edited)Int
等の代入や、参照型のアドレスの代入が atomic なのか気になる。a = 3
や b = 2
の時間帯は削除されるんじゃないかなあ。Int
やアドレスの代入が atomic でないなら、結構気を付けてプログラム書かないといけないなぁInt
の read / write ですら atomic ではない?.swift
か。Int
じゃ駄目 (edited)Int
であってもlet a = _stdlib_AtomicInt(42)
let a = _stdlib_AtomicInt(42) print(a.load()) // 42 a.store(999) print(a.load()) // 999
_stdlib_AtomicInt
とかも atomic であるところまでの保証であって「「Intの代入がatomic」だとしても」は同じじゃないの?var x = 3 x = 2
func a() -> Int { var a = 3 a = 2 return a }
func a
の例で言うなら、これってfunc a() -> Int { return 2 }
と最適化されるかもしれないfunc a(x: inout Int) -> Int { x = 3 x = 2 return x }
(edited)func a(x: inout Int) -> Int { return 2 }
(edited)std::atomic<int>
に対してはstore
命令の削除も禁止されるはず。x
→ a
だったら inout
だから x
への代入は必須じゃない?func a(x: inout Int) -> Int { x = 2 return 2 }
(edited)x = 3
の瞬間が削除されていることとreturn x
じゃなくて return 2
になっていることがポイントx=3
が観測できないし、 どんだけ x
に書き込んでも、 固定で 2
が返ってくるx = 3
が削除されるのは問題になりうる。a
が atomic な挙動をしているだけだと思うんだけど、 (edited)int data = 0; volatile int flag = 0; // 生産側スレッド void producer_thread() { data = 42; // [A] flag = 1; // [B] ??? } // 消費側スレッド void consumer_thread() { while ( !flag ) {} // [C] assert(data == 42); // [D] ??? }
(edited)func a() { lock.sync { atomicFoo(42) } } func b() { lock.sync { atomicFoo(999) } }
の lock
はいらないよね?ということでdata
を読んだ時に、0か42以外の中途半端にビットが書き換わった変な数字になったり、CPUがぶっ壊れたりしない、としてもfunc a() { lock.sync { atomicFoo(42) } } func b() { lock.sync { atomicFoo(999) } }
これ自体はlockはいらない。Int
を同時に更新する(だけ)みたいなのでも同期が必要となると想定していないケースが多そうということです。lock
が不要だったらうれしい。 var x: Int = 42 func a() { lock.sync { x = 999 } } func b() { lock.sync { x = 888 } }
(edited)let x = LargeSizeValue() f1(x) f2(x) f3(x)
let
だからそのまま渡せるのかfunc consume1(_ b: BigStruct) { consume2(b) } func consume2(_ b: BigStruct) { consume3(b) } func consume3(_ b: BigStruct) { consume4(b) } func consume4(_ b: BigStruct) { print(b) }
極端だけどこういう場合。 (edited)func consume1(_ b: P) { consume2(b) } func consume2(_ b: P) { consume3(b) } func consume3(_ b: P) { consume4(b) } func consume4(_ b: P) { print(b) } extension BigStruct: P {}
こうしたほうが速そう。結局のところ Swift4 の Existential CoW はヒープに 25 バイト以上のデカい構造体を alloc/initialize/dealloc するより参照カウンタ操作の方が速いってだけなのかな?
は Swift 3 vs Swift 4 の話ではない??func consume1<T: P>(_ b: T) { consume2(b) } func consume2<T: P>(_ b: T) { consume3(b) } func consume3<T: P>(_ b: T) { consume4(b) } func consume4<T: P>(_ b: T) { print(b) } extension BigStruct: P {}
// 1.swift protocol Hogeable { func hoge() -> Int } struct HogeCat: Hogeable { func hoge() -> Int { return 1111 } } func callHogeableHoge(hogeable: Hogeable) -> Int { return hogeable.hoge() } func callCatHoge(cat: HogeCat) -> Int { return callHogeableHoge(cat) } print(callCatHoge(HogeCat()))
callHogeableHoge
nofunc foo(_ a: P, _ b: P) { ... } extension Int: P {} extension String: P {} foo(2, 3) // スペシャライズされる foo(2, "abc") // スペシャライズされない
@_specialize
付けなくてもスペシャライズって起こるんだっけ?func consume1(_ b: P) { consume2(b) } func consume2(_ b: P) { consume3(b) } func consume3(_ b: P) { consume4(b) } func consume4(_ b: P) { print(b) } extension BigStruct: P {} こうしたほうが速そう。
↑これでいけそうですfunc consume1(_ b: shared BigStruct) { }
void consume1(const BigStruct & b)
と同じ意味func consume1(_ b: inout BigStruct) { consume2(&b) } func consume2(_ b: inout BigStruct) { consume3(&b) } func consume3(_ b: inout BigStruct) { consume4(&b) } func consume4(_ b: inout BigStruct) { print(b) } extension BigStruct: P {}
(edited)var x = BigStruct() compose(consume1(&x), consume1(&x))
std::atomic
で実現されてました。 struct HeapObject に フィールド InlineRefCounts refCounts がある。 https://github.com/apple/swift/blob/b7d78853112c1279fc7bc5b85853779040f13703/stdlib/public/SwiftShims/HeapObject.h InlineRefCounts は RefCounts<InlineRefCountBits> のエイリアス class RefCounts<T> は フィールド std::atomic<T> を持ってる。 デクリメント処理はなんかいろいろあるけど doDecrement
が基本的なやつっぽくて、 分岐もいろいろ複雑なんだけどstd::atomicのcompare_exchange_weakを呼び出してたりする。 InlineRefCountBits は RefCountBitsT<RefCountIsInline> のエイリアス RefCountIsInline は true な定数 RefCountBitsTはカウンタに加えて動作に関するビットが5こぐらいくっついたよくわからんやつ。 https://github.com/apple/swift/blob/b7d78853112c1279fc7bc5b85853779040f13703/stdlib/public/SwiftShims/RefCount.hthe-sharing-economy
wSwift Static Analysis@Apple Cupertino, CA
https://twitter.com/CodaFi_Math@CMU 2019
どういうこと?未来人??val
( Swift の let
に相当)で interface
のプロパティ宣言して、具象クラス側で Computed property にできるよね・・・。mutating
を一つももたないクラスはイミュータブルクラスになる。 mutable class Foo { func bar() -> Bar {} mutating func baz() -> Baz {} }
(edited)mutating
を作ろうとするとコンパイルエラー。mutating
みたときは、これはもしかして?と思ったけどちょっと違った。けど、結局値型中心で考える方がセンスがいいと思う。pure
について言及されてて、 D 言語はよく知らないですが、、 pure
な方がデフォルトであるのがいいと思うのと、これを型にも広げられないんだったら効果半減だと思います。let
なプロパティを Computed Property としてオーバーライドできないという制約はあるから、「もともと let な stored property だったものを、後から computed property の getter に差し替えても」動くとはいえ意図的に区別して制約が課されてるんじゃないかな?interface A { val value: Int } class A1: A { override val value: Int get() = A1.getCurrent() companion object { var value: Int = 0 fun getCurrent(): Int { value += 1 return value } } } fun main(args : Array<String>) { val a1 = A1() println(a1.value) println(a1.value) }
let
in protocols https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170619/037676.htmlprotocol
で nested type 作れないのって何か理由があるんですっけ?名前空間的に、その protocol
でしか使わない型をネストしたい・・・。protocol Foo { var bar: Bar { get } struct Bar { ... } }
みたいな (edited)protocol
の内側に static func
をかいたとき、それは、 Foo.hoge()
と呼び出せるわけじゃなくてFoo
に関連したいくつかのパラメータをまとめた Bar
を作りたいけど、 Bar
は Foo
から独立したら意味を持たない型なのでネストしたいって感じです。struct Bar<F: Foo>
を作るのが筋良さそうprotocol Foo { typealias Bar = FooBar var a: Bar { get } } struct FooBar { } struct FooImpl: Foo { var a: Foo.Bar }
Foo
の異なる具象型間で Bar
をやりとりできないですよね?_FooBar
にしておけばありかもしれないなぁ。 1> protocol Foo { 2. typealias Bar = FooBar 3. var a: Bar { get } 4. } 5. 6. struct FooBar { 7. 8. } 9> let bar: Foo.Bar = Foo.Bar() error: repl.swift:9:27: error: cannot call value of non-function type 'Foo.Bar' (aka 'FooBar') let bar: Foo.Bar = Foo.Bar() ~~~~~~~^~
typealias
はデフォルトではないのでは?associatedtype
にしてデフォルト設定するんじゃないっけ? =
で指定しておいた型になるんじゃなかったっけstruct FooBar {} protocol Foo { associatedtype Bar } extension Foo { typealias Bar = FooBar }
struct FooBar {} protocol Foo { typealias Bar = FooBar }
struct P0Param {} protocol P0 { typealias Param = P0Param var a: Param { get set } } var a = P0.Param() print(a)
1> protocol Foo { 2. typealias Bar = FooBar 3. var a: Bar { get } 4. } 5. 6. struct FooBar { 7. 8. } 9> let bar: Foo.Bar = Foo.Bar()
typealias
in protocol
swift-4.0-branch-07-11-2017
https://github.com/apple/swift/tree/swift-4.0-branch-07-11-2017 みたいなブランチからビルドされてたぽいだけど、beta 5はどこからビルドされているのだろう? (edited)swift-4.0-branch-07-11-2017
みたいなスナップショットが出ると、discordで報告していて、swift-4.0-DEVELOPMENT-SNAPSHOT-2017-08-02-a
以降 swift-4.0-DEVELOPMENT-SNAPSHOT-2017-08-04-a
より前 ぽい。 (edited)Copy-On-Write (🐮) types like Array and String are sharable
リファレンスカウントとCOWはスレッドセーフだけど Array
そのものの操作はスレッドセーフではない?async
, await
出す前に来てしまった・・・。Concurrency is a broad and sweeping concept that can cover a wide range of topics. To help scope this down a bit, here are some non-goals for this proposal: - We are focusing on task based concurrency, not data parallelism. This is why we focus on GCD and threads as the baseline, while completely ignoring SIMD vectorization, data parallel for loops, etc. - In the systems programming context, it is important for Swift developers to have low-level opt-in access to something like the C or C++ memory consistency model. This is definitely interesting to push forward, but is orthogonal to this work. - We are not discussing APIs to improve existing concurrency patterns (e.g. atomic integers, better GCD APIs, etc).
UIImage(named:)
が挙げられてるんだけど、このレベルの API から非同期化するつもりなのかな? C# の世界みたいになりそう。async
が良さそう。throws
って言ってるのと同じ気がする。can of worms
って出てきてなんだ?と思ったら↓のような意味の表現らしい。 UIImage(named:)
にまで踏み込むなら、 pure Swift UI ライブラリもワンチャンあるかも?you should only have to teach your types how to serialize/🐟 themselves
fish
なんだよなあSpeaking of reliable systems, introducing an actor model is a good opportunity and excuse to introduce a mechanism for handling and partially recovering from runtime failures (like failed force-unwrap operations, out-of-bounds array accesses, etc). We explore several options that are possible to implement and make a recommendation that we think will be a good for for UI and server applications.
async
/ await
まで来た。async
と、呼び出しの await は (Int) -> Int // #1: Normal function (Int) throws -> Int // #2: Throwing function (Int) async -> Int // #3: Asynchronous function (Int) async throws -> Int // #4: Asynchronous function, can also throw.
reasync
がなさそうだから swift-evolution に書いておくか。await!
もほしいな。!
を使うのが良いのか自信がない。 actor TableModel { let mainActor : TheMainActor var theList : [String] = [] { didSet { mainActor.updateTableView(theList) } } init(mainActor: TheMainActor) { self.mainActor = mainActor } // this checks to see if all the entries in the list are capitalized: // if so, it capitalize the string before returning it to encourage // capitalization consistency in the list. func prettify(_ x : String) -> String { // ... details omitted, it just pokes theList directly ... } actor func add(entry: String) { theList.append(prettify(entry)) } }
reliable actor Notifier { ... } reliable actor class Notifier { ... }
distributed actor MyDistributedCache { ... } distributed actor class MyDistributedCache { ... }
func queueHopping() async -> Void { doSomeStuff() await DispatchQueue.main.syncCoroutine() doSomeStuffOnMainThread() await backgroundQueue.asyncCoroutine() doSomeStuffInBackground() }
/// Begins an asynchronous coroutine, transferring control to `body` until it /// either suspends itself for the first time with `suspendAsync` or completes, /// at which point `beginAsync` returns. If the async process completes by /// throwing an error before suspending itself, `beginAsync` rethrows the error. func beginAsync(_ body: () async throws -> Void) rethrows -> Void
↑の戻り値ってなんで T
じゃないの?@IBAction func buttonDidClick(sender:AnyObject) { // 1 beginAsync { // 2 let image = await processImage() imageView.image = image } // 3 }
rethrows
はどこで活躍するの?If the async process completes by throwing an error before suspending itself, `beginAsync` rethrows the error.
suspendAsync
は func suspendAsync<T>( _ body: (_ continuation: @escaping (T) throws -> ()) -> () ) async rethrows -> T
じゃないんだ??throws
の位置がおかしいかfunc suspendAsync<T>( _ body: (_ continuation: @escaping (T) -> ()) throws -> () ) async rethrows -> T
// Legacy callback-based API func getStuff(completion: (Stuff) -> Void) { ... } // Swift wrapper func getStuff() async -> Stuff { return await suspendAsync { continuation in getStuff(completion: continuation) } }
asynchronize
が間違ってるっぽいな。func suspendAsync<T>( _ body: (_ continuation: @escaping (T) -> ()) -> ()) async -> T func asynchronize(_ operation: ((T) throws -> ()) -> ()) async rethrows -> T // ラベル等を消すと func suspendAsync<T>( _ body: ((T) -> ()) -> ()) async -> T
(edited)(T) throws -> ()
があるけど、() throws -> T
じゃないと。func asynchronize(_ operation: ((() throws -> T) -> ()) -> ()) async rethrows -> T
func suspendAsync<T>( _ body: (_ continuation: @escaping (() throws -> T) -> ()) -> ()) async -> T
return await suspendAsync { continuation, error in awaiters.append({ switch $0 { case .error(let e): error(e) case .value(let v): continuation(v) } }) }
が return await suspendAsync { continuation in awaiters.append({ switch $0 { case .error(let e): continuation { throw e } case .value(let v): continuation { v } } }) }
になる。pass
みたい。async
await
じゃなくて yields
と yield
にするって選択肢についても書かれてますねasync
/ await
は必ずしも意味が正しくないんよね。async(nonthrowing)
を導入するasync(nonthrowing)
より async nonthrows
とかの方がいいな。 nonthrows
は英語的におかしそうだけど。nothorw
があるよthrows
がよくなかった気がするな。 Java のせいだけど。throwing func
とか async func
とかの方が英語的によかったかも。 (edited)nonthrowing func
だったら nonmutating func
とかとも整合するし。do { let a = await foo() let b = await bar(a) ... } wait
reasync
await!
もいいけど、まとめてブロックも必要かと。Blocking calls Affordances could be added to better call blocking APIs from async functions and to hard wait for an async function to complete. There are significant tradeoffs and wide open design space to explore here, and none of it is necessary for the base proposal.
let a = await foo() let b = await bar()
のときに foo
と bar
を並列に走らせるのか直列に走らせるのかはどうだろう?func processImageData1a() async -> Image { let dataResource = Future { await loadWebResource("dataprofile.txt") } let imageResource = Future { await loadWebResource("imagedata.dat") } // ... other stuff can go here to cover load latency... let imageTmp = await decodeImage(dataResource.get(), imageResource.get()) let imageResult = await dewarpAndCleanupImage(imageTmp) return imageResult }
beginAsync
していてget() async throws -> T
なのでasync/awai
に続いて Typed throws も。 Error
が existential だからパフォーマンスが、ってことが書かれていて興味深い。One thing that I’m personally very concerned about is in the systems programming domain. Systems code is sort of the classic example of code that is low-level enough and finely specified enough that there are lots of knowable things, including the failure modes. Beyond expressivity though, our current model involves boxing thrown values into an Error existential, something that forces an implicit memory allocation when the value is large. Unless this is fixed, I’m very concerned that we’ll end up with a situation where certain kinds of systems code (i.e., that which cares about real time guarantees) will not be able to use error handling at all.
JohnMC has some ideas on how to change code generation for ‘throws’ to avoid this problem, but I don’t understand his ideas enough to know if they are practical and likely to happen or not.
で、 Chris Lattner のこのメールに John McCall が返信してるから、それ見たらわかるかも。This combination of requirements means that all operations must be implicitly "unwindable" starting from almost any call site it makes. For the stability of the system, this unwinding process must restore any invariants that might have been temporarily violated; but the compiler cannot assist the programmer in this. The programmer must consciously recognize that an error is possible while an invariant is broken, and they must do this proactively --- that, or track it down when they inevitably forget. This requires thinking quite rigorously about one's code, both to foresee all the error sites and to recognize that an important invariant is in flux. How much of a problem this poses depends quite a lot on the code being written. There are some styles of programming that make it pretty innocuous. For example, a highly functional program which conscientiously kept mutation and side-effects to its outermost loops would naturally have very few points where any invariants were in flux; propagating an error out of an arbitrary place within an operation would simply abandon all the work done up to that point. However, this happy state falls apart quite quickly the more that mutation and other side-effects come into play. Complex mutations cannot be trivially reversed. Packets cannot be unsent. And it would be quite amazing for us to assert that code shouldn't be written that way, understanding nothing else about it. As long as programmers do face these issues, the language has some responsibility to help them.
More generally, by modeling both `throws` and `async` as effects on function types, we can eventually provide common abstraction tools to abstract over both effects in protocols and generic code
https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619#rethrows-could-be-generalized-to-support-potentially-async-operationseffects
ってのが専門用語を匂わせてる気がするawait try
と書いてたけど、プロポーザル通り try await
じゃないといけない気がしてきた。try
も await
も flatMap
と等価でモナドを剥がすわけだけど、 async throws
は Promise
が外側だからまず Promise
を剥がさないといけず、その後 Result
を剥がすと考えると try await
じゃないとおかしい。beginAsync
の rethrows
と body
の throws
ってやっぱダメじゃない?I agree. I think `rethorws` for `beginAsync` is problematic. For example, what happens when the `foo` in the following code throws an `Error` asynchronously? func foo() async throws { ... } beginAsync(foo) `foo` is acceptable as `beginAsync`'s `body` by its type. However its error might be thrown asynchronously and it is impossible to rethrow it. So the error must be ignored or treated as an universal error by untyped propagation. It breaks type safety about error handling. So I think the signature of `beginAsync` should be the following one. func beginAsync(_ body: () async -> Void) -> Void
beginAsync
rethrows the error.await!
についても違う方向で話されてたから blocking のことに言及してみた。 https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170814/039096.htmlasync(nonthrowing)
おもしろいけど、それをやっちゃうと throws
, async
に続く第三のエフェクトを導入したときに整合性をとるのに困ると思う。async/await
の Proposal と同じような内容が話されてる。 https://gist.github.com/koher/5cd16adac7a62b6d3eb0b910ccc13534import Accelerate extension Array where Element: Comparable { mutating func clip(low: Element, high: Element) { print("normal version") for i in 0..<self.count { self[i] = Swift.max(Swift.min(self[i], high), low) } } @_specialize(Float) func clipped(low: Element, high: Element) -> [Element] { var ret = self ret.clip(low: low, high: high) return ret } } extension Array where Element == Float { mutating func clip(low: Float, high: Float) { print("accelerate version") var low = low var high = high self.withUnsafeMutableBufferPointer { bp in let p = bp.baseAddress! vDSP_vclip(p, 1, &low, &high, p, 1, vDSP_Length(bp.count)) } } } var int = (0..<10).map { Int($0) } int.clipped(low: 3, high: 5) // normal version int.clip(low: 3, high: 5) // normal version var x = (0..<10).map { Float($0) } x.clipped(low: 3, high: 5) // normal version x.clip(low: 3, high: 5) // acceerate version
Float
用のclipped
を多重実装せずにaccelerate versionを呼べないかと思ってるんですが@_specialize
でもだめっぽい…… (edited)@_specialize
ってそのメソッドや関数の型パラメータに利くんじゃないっけ?private func
化して@_specialize
付けるとか?protocol ArrayClippable
を用意してprotocol ArrayClippable { static func clipArray(_ array: inout Array<Self>, min: Self, max: Self) } extension ArrayClippable { static func clippedArray(_ array: Array<Self>, min: Self, max: Self) -> Array<Self> { var ret = array Self.clipArray(&ret, min: min, max: max) return ret } } extension Array where Element : ArrayClippable { mutating func clip(min: Element, max: Element) { Element.clipArray(&self, min: min, max: max) } func clipped(min: Element, max: Element) -> Array<Element> { return Element.clippedArray(self, min: min, max: max) } }
extension Comparable : ArrayClippable { static func clipArray(_ array: inout Array<Self>, min: Self, max: Self) { // ... } }
@_specialize
で呼び分けるのは無理なんじゃないかな?Playground execution failed: error: MyPlayground.playground:10:1: error: extension of protocol 'Comparable' cannot have an inheritance clause extension Comparable: ArrayClippable { ^ ~~~~~~~~~~~~~~
つけられない?@_specialize について、 _ で始まるものは本当に使わないほうがいいです。意図的にやっているのかっていうくらい頻繁に仕様が変わります。
by @rintarostruct S<T> { var x: T @_specialize(where T == Int, U == Float) mutating func exchangeSecond<U>(_ u: U, _ t: T) -> (U, T) { x = t return (u, x) } }
_
に限らず破壊的変更がどのみち入るから( )ついでに直せばいいだけな気も。 (edited)struct S<T> { var x: T mutating func exchangeSecond<U>(_ u: U, _ t: T) -> (U, T) { x = t return (u, x) } } extension S where T == Float { mutating func exchangeSecond(_ u: Int, _ t: Float) -> (Int, Float){ x = t return (u, x) } }
@_specialize
をなくせば動きはするしね。@_specialize
では無理だと思う。MyArray
とMyArray2
なんですが、同じテストコードでモジュール外にあるMyArray
の方が数百倍遅くて原因が分かりません。 モジュールまたぐ時ってwhole module optimization以外何かありましたっけ?makeIterator
はCollection
に生えているのが使えるのでMyArray
とおなじところにMyFloatArray
を作ってためしましたArray
に詰めたいけれど型が不定だと初期値をどうするのやらFloat
に対して付けるんじゃないの?import Foundation var data = Data(bytes: [0x50, 0x4B, 0x01, 0x02, 0x41, 0x61]) data.removeFirst() print(data.startIndex) // swift-3.1: "0\n", swift-4.0: "1\n" data.customMirror // crash on swift-4.0 https://bugs.swift.org/browse/SR-5811
(edited)self[0..<nBytes]
ってやってるからw https://github.com/apple/swift/blob/master/stdlib/public/SDK/Foundation/Data.swift#L1773 (edited)Data
の件、 Array
と挙動が違って混乱を招くかも? Welcome to Apple Swift version 4.0 (swiftlang-900.0.59 clang-900.0.34.2). Type :help for assistance. 1> var array = [2, 3, 5] array: [Int] = 3 values { [0] = 2 [1] = 3 [2] = 5 } 2> print(array.startIndex) 0 3> array.removeFirst() $R0: Int = 2 4> print(array.startIndex) 0
subscript
の index
は startIndex
が最初の要素を指すよ。 1> import Foundation 2> var data = Data(bytes: [2, 3, 5]) 3> print(data.startIndex) 0 4> data.removeFirst() $R0: UInt8 = 2 5> print(data.startIndex) 1 6> print(data[1]) 3
(edited)Array
は必ず startIndex
が 0
で endIndex
が count - 1
として、わざわざ ArraySlice
という型を設けて分離しているのに対して Data
は一つの型でやってしまっていることとremoveFirst
が startIndex
に影響を及ぼすのか及ぼさないのかという仕様の違いをどっちも混ぜてしまったのが問題では。 1> var ns: ArraySlice<Int> = [2, 3, 5] ns: ArraySlice<Int> = 3 values { [0] = 2 [1] = 3 [2] = 5 } 2> print(ns.startIndex) 0 3> ns.removeFirst() $R0: Int = 2 4> print(ns.startIndex) 1
ArraySlice
とは一貫してるのかData
は最初からスライスだと考えれば良いのか。Data
はそういうのとは違うからInt
で適切だと思う。String
より遅いけど、計算量のオーダーは同じ。String
インスタンスを作るのと大して変わらない。String
から構築してるからString
がヒープに確保してるでっかい領域をCharacter
が参照してるだけになるのでは?String
から characters
を取り出したときに内部的にどうなってるか詳しいことはしらない。String
より EasyText の Text
の方が速そう。String
がややこしいこと考えずに書記素クラスタで扱えるのはとても良いと思う。$ swift Welcome to Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36). Type :help for assistance. 1> : (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D. >>> [x * x for x in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>>
[omochi@omochi-iMac SwiftSyntax (master *=)]$ swift Welcome to Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36). Type :help for assistance. 1> : (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D. >>> 3 + 3 6 >>> exit (lldb) ^D 1> ^D
>>> ^D (lldb) ^D 1> ^D $
var exit: Never
みたいなの生やせばexitで抜けれるようになるかしら$ swift Welcome to Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36). Type :help for assistance. 1> import Darwin 2> exit(0) REPL requires a running target process. error: REPL process is no longer alive, exiting REPL
なら抜けられる。 (edited)struct Hoge { var foo: Int = 0 var bar: Int = 0 } var hoge: Hoge = Hoge() { didSet { print("didSet") } }
みたいな、監視されている struct 値を更新するときに hoge.foo = 12 hoge.bar = 13
(edited) _ = { $0.foo = 12 $0.bar = 13 }(&hoge)
っていう方法よりシンプルでわかりやすいのってありますか?(hoge.foo, hoge.bar) = (12, 13)
これもだめ hoge.mutate
メソッドを生やしたら少し分かりやすいかも?protocol Mutable { } extension Mutable { mutating func mutate(_ f: (inout Self)->Void) { f(&self) } } struct Hoge: Mutable { var foo: Int = 0 var bar: Int = 0 }
(edited) hoge.mutate { $0.foo = 12 $0.bar = 13 }
いいかも、使わせていただきまっす!Mutable
プロトコルは名前が不自然なのでそこだけ変えて下さいwRxSwift.Variable
に使いたかっただけなのでこんな感じにしました。 extension RxSwift.Variable { func mutateValue(fn: (inout Element) -> Void) { fn(&value) } }
variable.value.foo = xxx
をマルチスレッドでやらかすと死ねるdidSet
の場合もですけど冪等に作っておいて多重実行は気にしないのが良いんですかね……extension RxSwift.Variable { func mutateValue(fn: (inout Element) -> Void) { _lock.lock() fn(&value) _lock.unlock() } }
できるかな?こんな感じが良さそう。[T1: BinaryFloatingPoint]
を[T2: BinaryInteger]
に変換するメソッドが書けるようになって喜んでたら [T1: BinaryInteger]
を[T2: BinaryFloatingPoint]
に変換するほうができなかったSignedInteger
とUnsignedInteger
に分けてそれぞれInt64
, UInt64
を経由して変換するというろくでもない方法に落ち着いてしまったfatal error: Array<Foo> does not conform to Encodable because Pedestrian does not conform to Encodable.:
(edited)Countable...Range
を一掃できるのもうれしい。Codable
のエラーメッセージがどうなってるかは知らないけど、原理的にはパスをたどれそうな気がする。エラーが throw
されてきたら素通しにせず、一度 catch
してからエラーメッセージにパスを追記して throw
しなおせばいいんじゃない?そうなってなかったら swift リポジトリへの PR チャンスかも?Encoder
, Decoder
のエラーに含まれるパスは「とりあえずあるレベル」だと記憶。 (edited)Decoder
につけなきゃいけないのか。=>
の inout
版がほしい気がしてきた。func =><T>(lhs: T, rhs: (inout T) throws -> ()) rethrows -> T { var value = lhs try rhs(&value) return value }
(edited)func =><T, U>(lhs: T, rhs: (T) throws -> U) rethrows -> U { return try rhs(lhs) }
var teamToCount: [String: Int] = [:] for user in users { let team = user.team if let count = teamToCount[team] { teamToCount[team] = count + 1 } else { teamToCount[team] = 1 } }
を、もし inout
版の =>
があれば reduce(into:_:)
を使わなくても let teamToCount: [String: Int] = [:] => { teamToCount in for user in users { teamToCount[user.team, default: 0] += 1 } }
って書けそうだなぁと。Codable
など劇的にコーディングが楽になる新機能だと思いますが、ちょっとした便利な小技もあります。 そんな、 Swift 4 の小技の魅力の一面を 3 行...infix operator => : SwifletPrecedence precedencegroup SwifletPrecedence { higherThan: AssignmentPrecedence associativity: left } func =><T>(lhs: T, rhs: (inout T) throws -> ()) rethrows -> T { var value = lhs try rhs(&value) return value } print([Int]() => { (a: [Int]) -> () in a.append(42) })
$ swift inout-let.swift inout-let.swift:14:40: error: value of type 'Any' has no member 'append' print([Int]() => { (a: [Int]) -> () in a.append(42) }) ^ ~~~~~~ inout-let.swift:14:40: note: cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members print([Int]() => { (a: [Int]) -> () in a.append(42) }) ^ ( as AnyObject)
(edited){ (a: inout [Int]) -> () in a.append(42) }
クロージャパラメータに inout が必要inout
がいるんですね・・・。考えてみれば当たり前だ。infix operator => : SwifletPrecedence precedencegroup SwifletPrecedence { higherThan: AssignmentPrecedence associativity: left } func =><T>(lhs: T, rhs: (inout T) throws -> ()) rethrows -> T { var value = lhs try rhs(&value) return value } print([Int]() => { (a: inout [Int]) -> () in a.append(42) }) print([Int]() => { a in a.append(42) }) print([Int]() => { $0.append(42) })
$ swift inout-let.swift inout-let.swift:15:25: error: value of type 'Any' has no member 'append' print([Int]() => { a in a.append(42) }) ^ ~~~~~~ inout-let.swift:15:25: note: cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members print([Int]() => { a in a.append(42) }) ^ ( as AnyObject) inout-let.swift:16:20: error: value of type 'Any' has no member 'append' print([Int]() => { $0.append(42) }) ^~ ~~~~~~ inout-let.swift:16:20: note: cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members print([Int]() => { $0.append(42) }) ^ ( as AnyObject)
$ swift --version Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36) Target: x86_64-apple-macosx10.9
reduce(into:_:)
だとうまく推論できたのも謎。// 型の準備 struct User { var team: String } // 値の準備 let users = [ User(team: "A"), User(team: "B"), User(team: "B"), User(team: "A"), User(team: "C"), User(team: "A"), User(team: "C"), User(team: "B"), User(team: "D"), User(team: "A"), ] // 集計 let teamToCount: [String: Int] = users.reduce(into: [:]) { teamToCount, user in teamToCount[user.team, default: 0] += 1 } // 出力 for (team, count) in (teamToCount.sorted { $0.key < $1.key }) { print("\(team): \(count)") }
teamToCount
が inout
なんですが、問題なく通ります。reduce(into:_:)
と違うのは inout
の後の第二引数があるかないかくらいな気がするけど・・・ func parseImportDecl(tokens: ArraySlice<TokenSyntax>) throws -> (decl: ImportDecl, rest: ArraySlice<TokenSyntax>)? { var index = tokens.startIndex ... return (decl: ImportDecl(keywordIndex: keywordIndex - tokens.startIndex, nameIndex: nameIndex - tokens.startIndex, tokens: Array(tokens[..<index])), rest: tokens[index...]) }
(edited)echo 'Dictionary(uniqueKeysWithValues: ["a","b","c"].enumerated())'|TOOLCHAINS=swift swiftc -
でswiftc
がクラッシュする。export TOOLCHAINS=org.swift.3020170918a
Dictionary(uniqueKeysWithValues: ["a","b","c"].enumerated().filter { $1 != "a" })
もクラッシュ。Dictionary(uniqueKeysWithValues: ["a","b","c"].enumerated().filter { $1 != "a" }.map { ($0, $1) })
でクラッシュ回避。.map { $0 }
でもいけますね 3> Dictionary(uniqueKeysWithValues: [(offset: 0, element: "a")]) $R2: [Int : String] = 1 key/value pair { [0] = { key = 0 value = "a" } }
Dictionary(uniqueKeysWithValues: ["a","b","c"].enumerated().lazy)
は別の形でクラッシュecho 'Dictionary(uniqueKeysWithValues: ["a","b","c"].enumerated())'|TOOLCHAINS=swift swiftc -でswiftcがクラッシュする。
swift-DEVELOPMENT-SNAPSHOT-2017-09-28-a
で直った。[swift-evolution] Idea: Public Access Modifier Respected in Type Definition The core team would only consider a refinement or change to access control if there were something actively broken that mattered for ABI stability. -Chris
$ swift Welcome to Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36). Type :help for assistance. 1> class Animal {} 2> class Cat: Animal {} 3> let a: () -> Cat = { Cat () } a: () -> Cat = 0x00000001000c5590 $__lldb_expr7`closure #1 () -> __lldb_expr_3.Cat in __lldb_expr_6 at repl.swift:3 4> let b: () -> Animal = a b: () -> Animal = 0x00000001000c5590 $__lldb_expr7`closure #1 () -> __lldb_expr_3.Cat in __lldb_expr_6 at repl.swift:3 5> let c: () -> [Cat] = { [Cat()] } c: () -> [Cat] = 0x00000001000c5680 $__lldb_expr11`closure #1 () -> Swift.Array<__lldb_expr_3.Cat> in __lldb_expr_10 at repl.swift:5 6> let d: () -> [Animal] = c error: repl.swift:6:25: error: cannot convert value of type '() -> [Cat]' to specified type '() -> [Animal]' let d: () -> [Animal] = c ^ 6> let e: [Cat] = [Cat()] e: [Cat] = 1 value { [0] = { __lldb_expr_1.Animal = {} } } 7> e is [Animal] $R0: Bool = true
is
は 真になるのに、関数型に組み込まれたときはその互換性が発揮されないclass Animal {} class Cat: Animal {} let a: () -> Cat = { Cat () } let b: () -> Animal = a // OK let c: () -> [Cat] = { [Cat()] } let d: () -> [Animal] = c // NG: error: cannot convert value of type '() -> [Cat]' to specified type '() -> [Animal]' let e: [Cat] = [Cat()] e is [Animal] // true
let f: () -> Cat? = { Cat() } let g: () -> Animal? = f // OK
こっちはできたwis
の振る舞いってdo { let a: (Animal) -> () = { _ in } let b: (Cat) -> () = a // OK } do { let a: ([Animal]) -> () = { _ in } let b: ([Cat]) -> () = a // error: cannot convert value of type '([Animal]) -> ()' to specified type } do { let a: (Animal?) -> () = { _ in } let b: (Cat?) -> () = a // OK }
do { let a: () -> () -> Cat = { { Cat() } } let b: () -> () -> Animal = a // OK } do { let a: () -> () -> [Cat] = { { [Cat()] } } let b: () -> () -> [Animal] = a // error: cannot convert value of type '() -> () -> [Cat]' to specified type '() -> () -> [Animal]' } do { let a: () -> () -> Cat? = { { Cat() } } let b: () -> () -> Animal? = a // OK }
Array
だけ壊れてるみたいdo { let a: [String: Cat] = ["": Cat()] let b: [String: Animal] = a // OK } do { let a: [String: [Cat]] = ["": [Cat()]] let b: [String: [Animal]] = a // OK } do { let a: [String: Cat?] = ["": Cat()] let b: [String: Animal?] = a // OK }
as
, is
をoperatorにしてユーザーが定義できる様にして欲しくなる話だね。do { let a: () -> [String: Cat] = { ["": Cat()] } let b: () -> [String: Animal] = a // error: cannot convert value of type '() -> [String : Cat]' to specified type '() -> [String : Animal]' }
代入文の型検査時は静的だから書き換えたisが評価できない
func as(lhs: A) -> B
の有無で決まるとか? (edited)func as<T, U>(_ v: My<T>) -> My<U> where U: T
こういうの。無くても出来るかな?class Box<out T>
とかやるのが楽だしわかりやすいとは思うけどstruct Box<T> { let value: T }
T
についての変性をどう扱うのかが気になってます。rintaro - 04/18/2017 この辺の話(Ty<Some> から Ty<Covariant> への変換) って https://devforums.apple.com/thread/261699 で語られているんですが、 https://devforums.apple.com/message/1102432#1102432 のArrayとかは出来るけど任意の value type には当てはまらないって文脈で > this doesn't apply to every value type (because value types can contain references and not enforce copy-on-write) らしいのですが、どういうケースなんでしょう?
protocool Foo: class
はできるけど、逆は出来ない。[Int]
とかってboxingしないメモリレイアウトじゃないんか??unsafeBitCast([Int](), [String].self)
これ動きそう 1> var a: [Int] = [0,1,2,3] a: [Int] = 4 values { [0] = 0 [1] = 1 [2] = 2 [3] = 3 } 2> var b: [String] = a as! [String] Could not cast value of type 'Swift.Int' (0x1013e0430) to 'Swift.String' (0x1013e36f8). 2017-10-06 11:35:18.511301+0900 repl_swift[28561:24355268] Could not cast value of type 'Swift.Int' (0x1013e0430) to 'Swift.String' (0x1013e36f8). b: [String] = <extracting data from value failed> Execution interrupted. Enter code to recover and continue. Enter LLDB commands to investigate (type :help for assistance.) Process 28561 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT frame #0: 0x00007fffc4cf5d42 libsystem_kernel.dylib`__pthread_kill + 10 libsystem_kernel.dylib`__pthread_kill: -> 0x7fffc4cf5d42 <+10>: jae 0x7fffc4cf5d4c ; <+20> 0x7fffc4cf5d44 <+12>: movq %rax, %rdi 0x7fffc4cf5d47 <+15>: jmp 0x7fffc4ceecaf ; cerror_nocancel 0x7fffc4cf5d4c <+20>: retq Target 0: (repl_swift) stopped.
class Animal {} class Cat: Animal {} let a: [[Cat]] = [[Cat()]] a is [[Animal]] // true
true
になるんだっけis
の検査と as
の成功が一致してないといけないのか。is
はあくまでインスタンスをチェックしてるだけで式の型を調べているわけではないと。 (edited) 1> let a: [Int] = [] a: [Int] = 0 values 2> type(of: a) $R0: [Int].Type = [Int]
1> let a: [Int] = [] a: [Int] = 0 values 2> type(of: a) $R0: [Int].Type = [Int] 3> func foo<T>(_ value: T) { 4. print(type(of: value)) 5. } 6> foo(a) Array<Int>
let cats: [Cat] = [Cat()] let animals: [Animal] = cats
一瞬、↑みたいなケースで cats
は animals
にコピーされないからインスタンスの型という概念が成り立たないかと思ったけど、コピーされないのはあくまでバッファであって、インスタンス自体の領域は別だからやっぱりインスタンスの型で判断できる気がする。animals
から cats
に as
で戻せなくなるのか。 (edited)Optional
の is
や as
もややこしくなるな・・・。final class _EmptyArrayStorage : _ContiguousArrayStorageBase { } final class _ContiguousArrayStorage<Element> : _ContiguousArrayStorageBase { }
// rawValue is passed inout to _isUnique. Although its value // is unchanged, it must appear mutable to the optimizer. @_versioned internal var rawValue: Builtin.BridgeObject
@_inlineable // FIXME(sil-serialize-all) public // @testable var nativeInstance: Native { @inline(__always) get { _sanityCheck(isNative) return Builtin.castReferenceFromBridgeObject(rawValue) } }
var objCInstance: ObjC { @inline(__always) get { _sanityCheck(isObjC) return Builtin.castReferenceFromBridgeObject(rawValue) } }
internal typealias _ArrayBridgeStorage = _BridgeStorage<_ContiguousArrayStorageBase, _NSArrayCore>
で、左側が さっきの pure swift のときの Storage の _ContiguousArrayStorageBase だから、as
とかでObjC側にブリッジしたりするときにめんどくさいことが起こるんだろう・・・[Animal] as? [Cat]
これを実現するためには、1要素毎にキャストして成功したらOKみたいな処理にする必要があってvar a: [Int] = [] var b: [String] = [] print(a == b)
これで結論が出ると思ったらコンパイルエラーだったwfunc foo(x: Any) { switch x { case let strings as [String]: print("strings \(strings)") case let integers as [Int]: print("integers \(integers)") default: print("unknwon") } } let ints: [Int] = [] foo(x: ints)
これで strings
判定されちゃうことになるので、やっぱキャスト出来ちゃうと駄目だと思います。 (edited)let x = [Int?.none, Int?.none, Int?.none] let y = x as? [String?] // [nil, nil, nil]
これもなかなか。func foo<T>(value: T, type: T.Type) { switch type { case is Array<String>.Type: print("strings \(value as! Array<String>)") case is Array<Int>.Type: print("integers \(value as! Array<Int>)") default: print("unknwon") } } let ints: [Int] = [] foo(value: ints, type: type(of: ints)) // integers []
(edited)func cast<T, U>(from: U) -> T? { return from as? T } let z: Int?? = cast(from: String?.none) switch z { case .none: print("a") case .some(.none): print("b") // ココ case .some(.some): print("c") }
で、やはりOptional.noneも同様にキャストが成功している。func foo<T>(value: T) { switch T.self { case is Array<String>.Type: print("strings \(value as! Array<String>)") case is Array<Int>.Type: print("integers \(value as! Array<Int>)") default: print("unknwon") } } let ints: [Int] = [] foo(value: ints)
var a: [Int] = [] var b: [String]? = a as? [String] // Cast from '[Int]' to unrelated type '[String]' always fails
この警告だけ (edited)func foo(_ x: Any) { switch type(of: x) { case is [String].Type: print("strings \(x as! [String])") case is [Int].Type: print("integers \(x as! [Int])") default: print("unknwon") } } var a: [Int] = [] foo(a) // integers []
(edited)as
で出来るのはおかしい。こっちは世界の崩壊を招くlet x = [Int?.none, Int?.none, Int?.none] x is [String?] // true type(of: x) is [String?].Type // false
let x = [Int??.none, Int??.none, Int??.none] let y = x as? [Int?] // [nil, nil, nil]
let x = [Int?.none, Int?.none, Int?.none] type(of: x) == [Int?].self // true
==
でも書けるね、継承を区別したいときはこれが必要になる (edited)as?
の考え方が違うってだけだと思う/// If the dynamic type of `obj` doesn't implement a `getIntegerValue()` /// method, the system returns a runtime error when you initialize /// `certainValue`. /// /// Alternatively, if you need to test whether `obj.getIntegerValue()` exists, /// use optional binding before calling the method. /// /// if let f = obj.getIntegerValue { /// print("The value of 'obj' is \(f())") /// } else { /// print("'obj' does not have a 'getIntegerValue()' method") /// } /// // Prints "The value of 'obj' is 100" public typealias AnyObject
(edited)public typealias AnyObject = Builtin.AnyObject public typealias AnyClass = AnyObject.Type
typealias Any = protocol<>
が実際にあったけど、 &
記法になったタイミングで Any
がキーワードになり、Parser で特別扱いになりました。typealias Any = Builtin.Any
になるかもしれないですね。let x = Int??.none as? Int? let y = Int?.none as? Int?? switch x { case .none: print("print") case .some: print("-") } switch y { case .none: print("-") case .some: print("print") }
(edited)let x = [Int?.none] as? [Int??] switch x?.first! { case .none: print("x") // !! case .some(.none): print("y") case .some(.some): print("z") } let y = [Int?.none] as [Int??] switch y.first! { case .none: print("x") case .some(.none): print("y") // !! case .some(.some): print("z") }
(edited)as?
の使用に対して警告だしながら > Conditional cast from '[Optional<Int>]' to '[Int??]' always succeedsEncoder
, Decoder
はサポートする型により、Codable
自身のコーディング表現をオーバーライドする様になってます。URL
をURL
自身にエンコードさせると、["relative":"http://apple.com"]
みたいな辞書表現になります。On implementing Codable conformance, if it uses container.encode(_:…) instead of container.encodeIfPresent(_:…), container.encode(url, forKey: .url) produces dictionary. But, `JSONDecoder` tries to decode Optional<URL> from string.
JSONEncoder
は"http://apple.com"
として扱いたいので、Encodable
がURL
だったらこうする、と特別扱いします。 } else if T.self == URL.self { // Encode URLs as single strings. return self.box((value as! URL).absoluteString)
Optional
, Set
, Array
にこれらの型が入っていた場合にも特別扱いしなければいけないのですが、Conditional Conformanceがないため、Decodable
のコードが素直にかけませんでした。extension Optional : Decodable
extension Optional : Decodable /* where Wrapped : Decodable */ { @_inlineable // FIXME(sil-serialize-all) public init(from decoder: Decoder) throws { // Initialize self here so we can get type(of: self). self = .none assertTypeIsDecodable(Wrapped.self, in: type(of: self)) let container = try decoder.singleValueContainer() if !container.decodeNil() { let metaType = (Wrapped.self as! Decodable.Type) let element = try metaType.init(from: container) self = .some(element as! Wrapped) } } }
(Swift 4.0のコードに変更) (edited)Decoder
の処理を優先して、こう書きたい。 let container = try decoder.singleValueContainer() if !container.decodeNil() { - let metaType = (Wrapped.self as! Decodable.Type) - let element = try metaType.init(__from: container) - self = .some(element as! Wrapped) + self = .some(try container.decode(Wrapped.self)) } } }
(edited)JSONEncoder
とかのCoder
で特別扱いされるURL
の側で「JSONEncoder
だったら辞書表現をやめる」みたいな力技で対応されていました。 https://github.com/apple/swift/pull/10766 (edited)Coder
ではバグっていたのです。Coder
実装がどれくらいあるのか知りませんが…YAML
のCoder
実装は未リリース。これがSwift 4.0.1で直りそう、というお話。
4.0.1に入るかどうか微妙な時期らしい…let results = scheduler.createObserver(Void.self) XCTAssertEqual(results.events, [next(0, ()), next(10, ())])
と書くとコンパイルエラーになってしまいます。 どのように書くと良いのでしょうか…? (そもそもObservable<Void>はTestableObserverでテストを書くべきでは無いのでしょうか…?)() == () // true
func f<X: Equatable>(x: X) {} f(x: ())
public func == (lhs: Recorded<Void>, rhs: Recorded<Void>) -> Bool { return lhs.time == rhs.time }
これその辺に書いたら何とかなりませんかねimport Foundation class A: Encodable { let f1: String init(f1: String) { self.f1 = f1 } } class B: A { let f2: String init(f1: String, f2: String) { self.f2 = f2 super.init(f1: f1) } } let b = B(f1: "f1", f2: "f2") let x = try JSONEncoder().encode(b) String(data: x, encoding: .utf8) // -> { "f1": "f1" }
(edited)protocol C: Encodable { var f3: String { get } } struct D: C { var f3: String } let d: C = D(f3: "f3") let y = try JSONEncoder().encode(d) // ここでコンパイルエラー
(edited)D
として渡すと通りますC
にencode
メソッドを足してD
や各々でエンコードってのが良さそうですかね/// A type that can encode itself to an external representation. public protocol Encodable { /// Encodes this value into the given encoder. /// /// If the value fails to encode anything, `encoder` will encode an empty /// keyed container in its place. /// /// This function throws an error if any values are invalid for the given /// encoder's format. /// /// - Parameter encoder: The encoder to write data to. func encode(to encoder: Encoder) throws }
protocol C: Encodable { var f3: String { get } func encode() throws -> Data } struct D: C { var f3: String func encode() throws -> Data { return try JSONEncoder().encode(self) } } let d: C = D(f3: "f3") let y = try d.encode()
open func encode<T : Encodable>(_ value: T) throws -> Data {
protocol P {} class Cat : P {} func f<X: P>(_ p: X) {} var a: P = Cat() var b: Cat = Cat() f(a) // NG f(b) // OK
C
のextensionにencode
書けばいいのか。protocol C: Encodable { var f3: String { get } func encode() throws -> Data } extension C { func encode() throws -> Data { return try JSONEncoder().encode(self) } } struct D: C { var f3: String } let d: C = D(f3: "f3") let y = try d.encode()
これがいける?toJSON() -> Data
という名前の方が良さそうOptional.none
は常にフィールド自体省略になってnull
にすることができない気が…… fileprivate func box<T : Encodable>(_ value: T) throws -> NSObject { return try self.box_(value) ?? NSDictionary() }
Container上では return try JSONSerialization.data(withJSONObject: topLevel, options: writingOptions)
encode(to:)
だとそうなりますね。Optional
だとencodeIfPresent(_:forKey:)
が使われるコードが生成される。これをencode(_:forKey:)
を使うコードを自分で書けばnull
が使われる。 https://github.com/apple/swift/blob/master/lib/Sema/DerivedConformanceCodable.cpp#L623-L624 (edited)Codable
使う旨味が減っちゃいますねぇ。 Optional.none
のあつかいもstrategyで書けたらいいんじゃないかと思うんですが データ全体じゃなく部分ごとにここは省略したいとかある可能性もあるんで微妙ですかねstruct Foo: Encodable { var optionalInt: Int? private struct FooEncode: Encodable { var optionalInt: KeyedOptional<Int> /* Foo <-> FooEncode 変換書く */ } func encode() throws -> Data { return FooEncode(self).encode() } }
こげな{ "objects": [ { "key": 1, "value": "foo" }, { "key": "2", "value": "bar" } ] }
こういう意地悪すると、 enum Key: Codable { case int(Int) case string(String) }
こんなのが生える{ "objects": [ { "key": 1, "value": "foo" }, { "key": null, "value": "bar" } ] }
で、 struct Object: Codable { let key: Int? let value: String }
(edited)superEncoder
/superDecoder
の利用は必須ではないですね。superclassのデータをキーsuper
に入れるかどうかはユーザー次第になってるかと。public class Comprehensive: Encodable { private let field1: String private let field2: String? private let field3: String? fileprivate init(field1: String, field2: String?, field3: String?) { self.field1 = field1 self.field2 = field2 self.field3 = field3 } } public class Child1: Comprehensive { public init(field1: String, field2: String) { super.init(field1: field1, field2: field2, field3: nil) } } public class Child2: Comprehensive { public init(field1: String, field3: String) { super.init(field1: field1, field2: nil, field3: field3) } }
めちゃくちゃ汚いですがこれでやりたかったことはできそうですpublic class AnyKeyPath {} public class PartialKeyPath<Root> : AnyKeyPath {} public class KeyPath<Root, Value> : PartialKeyPath<Root> {} public class WritableKeyPath<Root, Value> : KeyPath<Root, Value> {} public class ReferenceWritableKeyPath<Root, Value> : WritableKeyPath<Root, Value> {}
(edited)responds(to:)
とか使えるという話import Foundation class Cat { @objc func eat() {} } (Cat() as AnyObject).responds(to: #selector(Cat.eat)) // true
#selector(Cat.eat)
ってやってるけどclass Dog { @objc func eat() {} }
@objc
がついてないと当然Selectorは生えないのでfalseになる- (oneway void)release;
Codable
に準拠した型のlet
プロパティは、エンコードはされるけどデコードされることはないのね。let
プロパティをエンコードされない様にするには、let
プロパティを含まないCodingKeys
を用意すると。let
プロパティだ。NSDictionaryからDataを経由せずに直接デコードしたい…
作った。 https://github.com/norio-nomura/ObjectEncoder[String: Any]
, [Any]
or Any
as payload.import Foundation import ObjectEncoder // single value let string = "Hello, ObjectEncoder" let encodedString = try ObjectEncoder().encode(string) (encodedString as AnyObject).isEqual(to: string) // true let decodedString = try ObjectDecoder().decode(String.self, from: encodedString) // dictionary struct S: Codable { let p1: String } let s = S(p1: "string") guard let encodedS = try ObjectEncoder().encode(s) as? [String: Any] else { fatalError() } encodedS["p1"] // "string" let decodedS = try ObjectDecoder().decode(S.self, from: encodedS) decodedS.p1 // "string" // array let array: [S] = [s, s] guard let encoded = try ObjectEncoder().encode(array) as? [[String: Any]] else { fatalError() } encoded[0]["p1"] // "string" let decoded = try ObjectDecoder().decode([S].self, from: encoded) decoded[0].p1 // "string"
JSONEncoder
やPropertyListEncoder
みたいに、各型についての特殊な処理が一切入っていないCoders
実装になってます。[String: Any
や[Any]
へそのまま突っ込むイメージ。let source = CGImageSourceCreateWithURL(url as CFURL, nil), let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as NSDictionary? let decoded: ImageProperties = try ObjectDecoder().decode(from: properties)
みたいなAPIからNSDictionary
で返ってきたデータのモデリング。 (edited)JSONEncoder
かPropertyListEncoder
へData
以外も受け付けるAPIを作ってPR出すことも考えたけど、変換部分が邪魔だった。{ “id”: 1, “url”: “” }
というAPIのレスポンスがあったとして、 struct Hoge: Codable { let id: Int let url: URL? }
これでデコードしようとすると、Invalid URL StringということでdataCorrupted扱いになります。このとき、urlをnilで取り扱うためのワークアラウンド、どなたかお持ちですか? (edited)struct Hoge: Codable { let id: Int let jsonUrl: String? var url: URL { get { ... } set { ... } } }
struct Landmark: Codable { var name: String var foundingYear: Int var location: Coordinate var vantagePoints: [Coordinate] enum CodingKeys: String, CodingKey { case name = "title" case foundingYear = "founding_date" case location case vantagePoints } }
struct Hoge: Codable { let id: Int private let jsonUrl: String? var url: URL? { return URL(string: jsonUrl) } enum CodingKeys: String, CodingKey { case id case jsonUrl = "url" } }
(edited)struct Hoge: Codable { let id: Int private let jsonUrl: String? var url: URL? { return jsonUrl.flatMap(URL.init) } enum CodingKeys: String, CodingKey { case id case jsonUrl = "url" } }
(edited)struct Hoge: Codable { let id: Int let url: String? } extension Hoge { var urlurl: URL? {return url.flatMap {URL(string: $0)}} }
Codableがjsonをそのままうつしていると考えればこう (edited)url: String
は image: UIImageView
と同じレベルでやめたいurl
のほうを使わずに urlurl
を使うのが注意点かurl: String
は合理的だと思いますよ。 URL
オブジェクトはデータ構造というよりはユーティリティ。struct _Hoge: Codable { 略 }
と struct Hoge { 略 }
でこいつら相互変換可能に。hoge.カッコイイ名前.url
になるのではHoge
と Hoge.JSON
にするの良さそうCGSize(width: 100, height: 50)
に対応するのは { "width": 100, "height": 50 }
ではなくて [100, 50]
なんですよid: Int
から id String
に変わったとかあるので、外部APIを最初に受けるところはあまり厳格にしてもというのはあります。import Foundation struct Model<Base: Modelable> { var base: Base } protocol Modelable { var model: Model<Self> { get } } extension Modelable { var model: Model<Self> { return Model(base: self) } } struct Hoge: Codable, Modelable { fileprivate var int: Int fileprivate var url: String? } extension Model where Base == Hoge { var int: Int { return base.int } var url: URL? { return base.url.flatMap(URL.init) } } func x(hoge: Hoge) { hoge.model.url // URL? }
よさそうstruct Cat { subscript(ex ex: Void) -> Ex<Cat> { get { return Ex(self) } set { self = newValue.t } } var age: Int = 3 } struct Ex<T> { init(_ t: T) { self.t = t } var t: T } var cat = Cat() cat[ex: ()].t.age = 6 print(cat) // Cat(age: 6)
func M<A: Modelable>(_ arg: A) -> Never { fatalError() }
こいつ作っておけば cat[M].t.age = 6
ってできるんじゃん?cat[Rx].tap.subscribe ...
import Foundation struct Model<Base: Modelable> { var base: Base } protocol Modelable { var model: Model<Self> { get } } extension Modelable { var model: Model<Self> { return Model(base: self) } } struct Hoge: Codable, Modelable { fileprivate var int: Int fileprivate var url: String? var model: Model<Hoge> { get { return Model(base: self) } set { self = newValue.base } } } extension Model where Base == Hoge { var int: Int { get { return base.int } set { base.int = newValue } } var url: URL? { get { return base.url.flatMap(URL.init) } set { base.url = newValue?.absoluteString } } } func x(hoge: Hoge) { hoge.model.int var hoge = hoge hoge.model.url = URL(string: "https://www.google.com") }
いけた。struct Model<Base: Modelable> { var base: Base } protocol Modelable { var model: Model<Self> { get } } extension Modelable { var model: Model<Self> { get { return Model(base: self) } set { self = newValue.base } } } struct Hoge: Codable, Modelable { fileprivate var int: Int fileprivate var url: String? } extension Model where Base == Hoge { var int: Int { get { return base.int } set { base.int = newValue } } var url: URL? { get { return base.url.flatMap(URL.init) } set { base.url = newValue?.absoluteString } } } func x(hoge: Hoge) { hoge.model.int var hoge = hoge hoge.model.url = URL(string: "https://www.google.com") }
Model
に対してJSON<Model>
を作りたい (edited)URLComponents
として保持するのもありなのかも?と思ったけど、これはどういう状態なんだろうか。 import Foundation var comps = URLComponents(string: "")! comps.url! // (no URL)
(edited).url
が作れないって感じ?String
で持つべきだと思います?UUID
にするのはもちろんやるべきですし、私もそう書きます。UUID
で定義するなら、 UUID を String
で持つべきなのはどういうケースですか? (edited)UUID
、サーバから受ける場合は String
?url.flatMap {URL(string: $0)}
を都度書きたくない/// デコードしたStringと、TypedなURLは纏めて独自型に押し込めるほうがいい struct RobustURL: Codable { let rawValue: String var typed: URL? { return URL(string: rawValue) } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(rawValue) } init(from decoder: Decoder) throws { rawValue = try decoder.singleValueContainer().decode(String.self) } } /// CoreGraphicに依存しない独自型 struct Size: Codable { let width: Double let height: Double } import CoreGraphics extension Size { var asCGSize: CGSize { return CGSize(width: width, height: height) } } struct Fuga: Codable { let url1: RobustURL // 不正なURLが来うることを型として示せる let url2: RobustURL // 不正なURLが来うることを型として示せる let url3: URL // 不正なURLが来ないことを型として示せる let size: Size } let data = """ { "url1": "https://failable.url", "url2": "不正なURL", "url3": "https://ensured.url", "size": { "width": 100, "height": 50 } } """.data(using: .utf8)! let fuga = try! JSONDecoder().decode(Fuga.self, from: data) fuga.url1.typed // https://failable.url fuga.url2.typed // nil fuga.url3 // https://ensured.url fuga.size.asCGSize // {w 100 h 50} let json = try! JSONEncoder().encode(fuga) print(String(data: json, encoding: .utf8)!) // {"size":{"width":100,"height":50},"url3":"https:\/\/ensured.url","url1":"https:\/\/failable.url","url2":"不正なURL"}
ウソもデコードできるURL型を自分で定義
これすねasURL
は欲しくないですねstruct Bar: Codable, RawRepresentable { let rawValue: String init?(rawValue: String) { self.rawValue = rawValue } }
これはエンコードできないな。struct RobustURL: Codable, RawRepresentable { let rawValue: String var typed: URL? { return URL(string: rawValue) } init?(rawValue: String) { self.rawValue = rawValue } }
すっきり{"rawValue": ...}
の階層つくらないんですね、まじか‥init?(rawValue: String)
も要らないんじゃないかな。public
にする時には要るか。internal
なら要らないですね。: class
をつけることが多いんだけどprotocol
と mutating
関連の Proposal なかったっけ?: class
にしてしまっているprotocol Foo: class {} class Bar<X: class> {} Bar<Foo>() // 🙅
:class
ってそこでもつかえる? :AnyObject
にしてた。@objc protocol Foo: class {} class Bar<X: AnyObject> {} Bar<Foo>() // 🙆
@objc
は深入りしたくない・・・互換性のためにいろいろ諦めてそうだしちゃんと考えて無さそう (edited)@optional
なメソッドとかSwift上にそんな概念ないのにちゃんと考慮されたりするし:class
つけなくてもその前提条件だとコンパイルはできるんだけど、 考慮せずぜんぶ func にしてるんだって自覚を表明するためにつけといたほうがマシかなって思ってるAnyObject
って参照型なら OK じゃなくてクラスじゃないとダメなのか。 1> let a: () -> Int = { 42 } 2. let b: AnyObject = a error: repl.swift:2:20: error: value of type '() -> Int' does not conform to specified type 'AnyObject' let b: AnyObject = a ^ as AnyObject
typealias AnyObject
1> let a: () -> Int = { 42 } a: () -> Int = 0x00000001000c5030 $__lldb_expr8`closure #1 () -> Swift.Int in __lldb_expr_7 at repl.swift:1 2> let b = a b: () -> Int = 0x00000001000c5030 $__lldb_expr8`closure #1 () -> Swift.Int in __lldb_expr_7 at repl.swift:1 3> a === b error: repl.swift:3:3: error: cannot check reference equality of functions; operands here have types '() -> Int' and '() -> Int' a === b ~ ^ ~
var f: () -> String = { "hello" } var array: [() -> String] = [] array.append(f) print(array[0] === f)
/Users/omochi/temp/swer/Sources/swer/main.swift:5:16: Cannot check reference equality of functions; operands here have types '() -> String' and '() -> String'===
のオペランドの型が AnyObject
なのかも。let c = { (i: Int) in print("\(i)") } func f(i: Int) { print("\(i)") } (c as AnyObject) === (c as AnyObject) // false (f as AnyObject) === (f as AnyObject) // false
まあそもそもBoxのポインタ比較だと思うんでvar a = 0 let b: () -> Int = { a = a + 1; return a } let c = b print(b()) // 1 print(c()) // 2
@convention()
によって挙動がいろいろですね。c()
は 1
にならないとおかしい。
@convention
は7種類あるらしい - ``@convention(thin)`` indicates a "thin" function reference, which uses the Swift calling convention with no special "self" or "context" parameters. - ``@convention(thick)`` indicates a "thick" function reference, which uses the Swift calling convention and carries a reference-counted context object used to represent captures or other state required by the function. - ``@convention(block)`` indicates an Objective-C compatible block reference. The function value is represented as a reference to the block object, which is an ``id``-compatible Objective-C object that embeds its invocation function within the object. The invocation function uses the C calling convention. - ``@convention(c)`` indicates a C function reference. The function value carries no context and uses the C calling convention. - ``@convention(objc_method)`` indicates an Objective-C method implementation. The function uses the C calling convention, with the SIL-level ``self`` parameter (by SIL convention mapped to the final formal parameter) mapped to the ``self`` and ``_cmd`` arguments of the implementation. - ``@convention(method)`` indicates a Swift instance method implementation. The function uses the Swift calling convention, using the special ``self`` parameter. - ``@convention(witness_method)`` indicates a Swift protocol method implementation. The function's polymorphic convention is emitted in such a way as to guarantee that it is polymorphic across all possible implementors of the protocol.
@convention(thin) indicates a "thin" function reference, which uses the Swift calling convention with no special "self" or "context" parameters.
あー、ここに出てくる、 self と context がミソっぽいなあ 今の話let b: () -> Int = { [x=a] in a = x + 1; return a }
AnyObject
には入らなかった>7種類。class
やめるのダメ?var a = 3 let b: () -> Int = { [x=a] in a = x + 1 return a } print(b()) // 4 print(b()) // 4 print(b()) // 4
id
にはブロックが入った気がするから、AnyObject
!= id
var a = 3 let b: () -> Int = { [x=a] in a = x + 1 return a } let c = b print(b()) print(c()) print(b()) print(c())
4 4 4 4class
使うとか? class Ref<Value> { var value: Value }
&
と *
がほしくなってきた。 struct
と &
と *
の世界、実は良かったのでは? malloc
や free
が面倒だっただけで、そこをリファレンスカウントで勝手にやってくれるなら。&
じゃなくて、実体コピーした上で参照型化かな。struct Foo
を作ったけど、それを共有したいときは Foo *
として取り扱えるとうれしい。 (edited)*
の意味は C と同じになるけど、 &
はちょっと違うから別の記号が良さそう。Box<Foo>
と同じなんだけどbox 1
Optional
や Array
みたいに Box
( Reference
でもいい)のシュガーがあればいいのかも。Foo*
が Reference<Foo>
weak var foo: Foo*?
こうなる? (edited)class Reference<Value> { var value: Value }
prefix func *
もほしいかFoo*
が Reference<Foo>
だとうれしくない?weak var a: Reference<Int>?
で良いんじゃない? (edited)weak var foo: Foo*?
かな?let a: () -> Int = { 42 } let block = a as @convention(block) () -> Int as AnyObject
let a: () -> Int = { 42 } let block = a as AnyObject print(block)
as AnyObject
はそもそもそのままできそうですよlet a: () -> Int = { 42 } print(a as AnyObject === a as AnyObject) // false let b = a as @convention(block) () -> Int print(b as AnyObject === b as AnyObject) // true
() -> Int false @convention(block) () -> Int true
a
は as AnyObject
でボクシングされるけどb
はすでに AnyObject
だからlet a: () -> Int = { 42 } let b = a as AnyObject b === b // true
let a: () -> Int = { 42 } print(type(of: a)) print(a as AnyObject === a as AnyObject) let b: @convention(block) () -> Int = a as @convention(block) () -> Int print(type(of: b)) print(b as AnyObject === b as AnyObject) var x: AnyObject = b // NG: value of type '@convention(block) () -> Int' does not conform to specified type 'AnyObject'
dump(P.method)
もダメだった。var x: AnyObject = b // NG: value of type '@convention(block) () -> Int' does not conform to specified type 'AnyObject'
(edited)po
をやるみたいなの。>dump
(edited)print(b as AnyObject === b as AnyObject) // true var x: AnyObject = b // NG: value of type '@convention(block) () -> Int' does not conform to specified type 'AnyObject'
import Foundation let a = "str"; let b: NSString = a
class Cat {} let a: AnyObject = Cat() print(type(of: a)) // Cat
func f() -> Int { return 42 } let a: AnyObject = f as AnyObject print(type(of: a)) // _SwiftValue
_SwiftValue
なる型になってた。swift-string.swift:3:19: error: cannot convert value of type 'String' to specified type 'NSString' let b: NSString = a ^ as NSString
;
が付いてるas NSString
なら通る。明示的なキャストを言語的に強制してるということです。:tarunon:
絵文字ほしくなってきたなfalse
だから。 import Foundation let a = "str" a as NSString === a as NSString // false
===
が true
になるところimport Foundation let str = "str" let b = str as NSString (b as AnyObject === b as AnyObject) // true
と同じじゃないかと。 (edited)let b: @convention(block) () -> Int = { 42 } b as AnyObject === b as AnyObject // true
import Foundation let str = "str" let b = str as NSString let c: AnyObject = b // OK
let b: @convention(block) () -> Int = { 42 } b as AnyObject === b as AnyObject // true let c: AnyObject = b // ERROR
let b: @convention(block) () -> Int = { 42 } b as AnyObject === b as AnyObject // true b is AnyObject // true let c: AnyObject = b // ERROR
is AnyObject
が true
なのに代入できない・・・import Foundation let a = "aaa" a is NSString let b: NSString = a
is trueだけど代入できないのはよくあるからOptional
も含め@convention(block)
の時点で ObjC の世界に入っているわけだから、 AnyObject にそのまま突っ込めてもいい気はしますね。protocol Proto : AnyObject {} class Box1<X> {} Box1<Proto>() // OK class Box2<X: AnyObject> {} Box2<Proto>() // NG: 'Proto' is not convertible to 'AnyObject'
'Proto' is not convertible to 'AnyObject'
ってエラーメッセージ出てるけど Proto : AnyObject
なんだけどなあprotocol Proto {} struct S: Proto {} func foo<P: Proto>(x: P) {} let p: Proto = S() foo(x: p) // error: cannot invoke 'foo' with an argument list of type '(x: Proto)'
これと同じですね。AnySequence
とか使いたくなるケースの 8 割くらいは実はいらないんじゃないかなぁ。as
とか is
の挙動がReference
と同じように。後は使いやすいシュガーがあれば。protocol
と参照型の相性が良くないような。{ ∃X, X: Animal }
という存在型を略して Animal
型と書くのをやめてぱっと見で存在型だってわかるようにすればもうちょい理解しやすくなりそうですねtypealias AnySequence<Element> = Any<Sequence where .Iterator.Element == Element> let strings: AnySequence<String> = ["a", "b", "c"]
P1 & P2
は Any<T where T: P1, T: P2>
とか?Any<P1 & P2>
が Exitentialになるという仕様になってほしい P1 & P2
はあくまで protocol (edited)protocol<P1, P2>
が P1 & P2
になりました。import UIKit class MyScrollViewDelegate: NSObject, UIScrollViewDelegate { } let myScrollViewDelegate = MyScrollViewDelegate() let tableView = UITableView() let scrollView = tableView as UIScrollView scrollView.delegate = myScrollViewDelegate tableView.delegate is UITableViewDelegate // true myScrollViewDelegate === tableView.delegate // true myScrollViewDelegate is UITableViewDelegate // false
Objc絡むととたんにやばくなるやつunrecognized selector sent to instance 0xXXXXX
いつものだwAnimal
がbark()
メソッドをもってるとして、Any<Animal>
型のanimal
に対して animal.bark()
を呼びだせるのはAny<Animal>: Animal
なわけではなく、 Opening existentials のシンタックスシュガーだって考えればよさそう? let animal: Any<Animal> = Dog() // OK animal.bark() // ↑は以下のシンタックスシュガー // aの型はA。AはAnimalに準拠。 let a = animal openas A a.bark()
struct S { var a: Int = 3, b: Int = 4 }
CGFloat
って Swift 3.1 まで Float
や Double
の typealias
じゃなかったっけ? https://developer.apple.com/documentation/coregraphics/cgfloatstruct
になった?struct
でしたっけ?struct
の方が良いと思います。(環境によって Float
や Double
に切り替わるとやっかいなので)CGFloat
は昔 Double
か Float
の typealias
だったような気が…Obj-Cとの記憶が混乱したのか TimeInterval
と ObjC の両方と混同してたような気がします (edited)CGFloat
のコンパイルが通らなくなる夢を見てた気がしてたんですが、この頃の記憶かも。CoreGraphics
だし import Foundation
しないと使えないし@_fixed_layout public struct CGFloat { #if arch(i386) || arch(arm) /// The native type used to store the CGFloat, which is Float on /// 32-bit architectures and Double on 64-bit architectures. public typealias NativeType = Float #elseif arch(x86_64) || arch(arm64) /// The native type used to store the CGFloat, which is Float on /// 32-bit architectures and Double on 64-bit architectures. public typealias NativeType = Double #endif ... /// The native value. public var native: NativeType }
(edited)NativeType
の typealias
になったね +public struct CGFloat { +#if arch(i386) || arch(arm) + public typealias UnderlyingType = Float +#elseif arch(x86_64) || arch(arm64) + public typealias UnderlyingType = Double +#endif
(edited)昨日話していてそんな気がしたんですが やっぱりOptionalの暗黙変換とOptionalの共変性は矛盾した結果を生むので両方入ってるのは厳しいですね
// Kotlin open class Animal class Cat: Animal() // Rule A // Cat is subtype of Animal run { val cat: Cat = Cat() val animal: Animal = cat } // Rule B // Int is subtype of Int? run { val int: Int = 0 val intOptional: Int? = int // 0 } // Rule C // Optional has covariance run { val catOptional: Cat? = null val animalOptional: Animal? = catOptional // null } // Question // What happen replace `Cat : Animal` to `Int : Int?` run { val intOptional: Int? = null val intOptionalOptional: Int?? = intOptional // null (Int?? == Int?) }
String?.none as? Int? => Int??.some(.none)
は確かにおかしい結果を生むんですが、それ以前に共変性周りが崩壊していることに気がついたInt??
でネストを表現できないですがInt
は Int?
のサブタイプじゃないですからねぇ。Optional<Cat>
が Optional<Animal>
のサブタイプでも、 Int
が Int?
のサブタイプでなければ Optional<Int>
は Optional<Int?>
のサブタイプにならないですし。String?.none as? Int? => Int??.some(.none)
こいつをウソのサブタイプを倒した後でどう説明するのが良いのか、どう言う振る舞いであるべきなのかを考えないといけない気がするCat?.none as Animal?
これが暗黙のmapであるとするのであれば上の結果は正しいということになりますねString?.none as? Int? => Int??.some(.none)
String
→ Int
はパースですか?
String?.none as? Int?
ってやると結果が Int??.some(.none)
になるんですよ。つまりキャストが成功するas?
の ?
を忘れてた。 (edited)[String]() as? [Int]
これは 空配列になる。 1> Int?.none as? Int? $R0: Int?? = nil
(edited) 2> print(Int?.none as? Int?) Optional(nil)
1> dump(Int?.none as? Int?) ▿ Optional(nil) - some: nil $R0: Int?? = nil
String?.none as? Int? // .some(.none)
.none
であるべきだと思います。[String]() as? [Int] // .some([])
(edited).none
であるべきかとAnimal?.none as? Cat?
ではこれは?([String]() as [Any]) as? [Int]
(edited)Animal?.none as? Cat?
ダメな気がしてきたlet cats = [Cat]() let animals: [Animal] = cats // OK let cats2: [Cat] = animals as! [Cat] // これは?
(edited)let animals: [Animal] = cats // OK
↑ここで CoW を効かせたいからなぁ。map
すると先行コピーされちゃう。 (edited)map
でもいい気も?List
は参照型だけど、値型+ジェネリクス+ out
のときどうなるのかなって。interface
しか out
付けれなかった。 using System; namespace HelloWorld { interface IBox<out T> { T Get(); } struct SBox<T> : IBox<T> { private T _value; public SBox(T t) { _value = t; } public T Get() { return _value; } } class CBox<T> : IBox<T> { private T _value; public CBox(T t) { _value = t; } public T Get() { return _value; } } class Animal {} class Cat : Animal {} class Hello { static void Main() { IBox<Cat> sCat = new SBox<Cat>(new Cat()); IBox<Animal> sAnimal = sCat; // OK sCat = (IBox<Cat>)sAnimal; // OK Console.WriteLine(sCat); IBox<Cat> cCat = new CBox<Cat>(new Cat()); IBox<Animal> cAnimal = cCat; // OK cCat = (IBox<Cat>)cAnimal; // OK Console.WriteLine(cCat); } } }
beginAsync
の throws
をなくそうという話への反応が少なくて悲しい。 Chris Lattner と Joe Groff の見解が知りたい。(Foo) async -> Void
の戻り値を受けなくても別に問題ないですよね?await foo() await bar() await baz()
foo
の非同期処理が完了後 bar
が実行され、 bar
が完了後 baz
が実行されるだけで。 (edited)async -> Void
ができなきゃいけないって意味ですね。beginAsync
の throws
について反応がないのはなんでなんだろう?静的チェックの安全性を壊すか壊さないかの重要な話だと思うんですけどねぇ・・・。class Cat { func nya(_ a: Int) -> String { return String(a) } } let nya: (Cat) -> (Int) -> String = Cat.nya let cat = Cat() nya(cat)(3) // "3" Cat.nya(cat)(4) // "4"
- Indices.swift.gyb + Indices.swift
CountableRange
系の削除に期待。CountableRange
消す PR 書くの気持ち良さそう。Codable
とかAPI自体を見直さないとダメそう。>Conditional Conformanceimport Foundation struct S<Wrapped> { var value: Wrapped } extension S: Codable where Wrapped: Codable { enum CodingKeys: String, CodingKey { case value } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) value = try container.decode(Wrapped.self, forKey: .value) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(value, forKey: .value) } } let s = S<Int>(value: 1) let encoder = JSONEncoder() let data = try encoder.encode(s) print(String(data: data, encoding: .utf8)!) // {"value":1}
がswift-DEVELOPMENT-SNAPSHOT-2017-11-13-a
で動いた。extension S: Sequence where Wrapped: Sequence { func makeIterator() -> Wrapped.Iterator { return value.makeIterator() } }
はコンパイラクラッシュSequence
は Element
, Iterator
, SubSequence
とあって、それらが関係しているので複雑そうです。Wrapped.Iterator
を自分の Iterator
として解決する辺りとかヤバそう。[Any]
はCodable
では無くなる?extension Array: Codable where Element: Codable {}
かと。Equatable
とかと同様に。[AnyHashable: Any]
も。[String: Any]
の代わりに[String: Codable]
を使っても、Encoder,Decoder側に扱うAPIが無いかも。 Fatal error: Dictionary<String, Decodable & Encodable> does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.
extension Dictionary: Codable where Value: Codable {}
が必要な気がします。正確には Encodable
と Decodable
別々に。extension Dictionary: Codable where Key == String, Value: Codable {}
こうかな?AnyCodable
みたいなものが必要なんじゃないかな?と。Codable
は associatedtype
も Self
も持たないだたの protocol
だから、 Codable
型として existential になりますよね? struct Foo: Codable {} let foo: Codable = Foo() // OK
protocol P0 { } struct A : P0 {} func f<X: P0>(_ x: X) {} var a: P0 = A() f(a) // error
<T: Encodable>
を受けるAPIはありますがEncodable
を直接受けるAPIは無いです。<T: Encodable>
ではなくEncodable
を直接受け取れば良いのでは?」とコメントしたのですが、受け入れられませんでした。AnySequence
がなくせる話と絡んで、 Any<Codable>
とかできるようになるのかな? 1> let a: Any = 42 a: Int = 42 2> func foo<T: Any>(_ x: T) { print(x) } 3> foo(a) 42
Encodable
ではなく<T: Encodable>
なのは、decode()
側が<T: Decodable>
を使う必要があり、そちらと一貫性を持たせる為だと言われた。よさそうですね! んで、 `Animal` が`bark()` メソッドをもってるとして、`Any<Animal>` 型の`animal` に対して `animal.bark()` を呼びだせるのは`Any<Animal>: Animal` なわけではなく、 Opening existentials のシンタックスシュガーだって考えればよさそう? let animal: Any<Animal> = Dog() // OK animal.bark() // ↑は以下のシンタックスシュガー // aの型はA。AはAnimalに準拠。 let a = animal openas A a.bark()
@ukitaka 前結局理解できなかったのだけどAny<Animal>がAnimalのextentialでなくなる理由とか嬉しい点って何ですか?func foo<T: SomeProtocol>(...)
これに対して、Tの型はかなりカッチリしていてlet a = animal openas A // 将来opening existentialが入るとする
a
の型ってなんですか?test2
の型パラメータの <A> と同じって意味?protocol Animal {} struct Cat: Animal {} func bar<T: Animal>(_ x: T) { print(x) } let b: Animal = Cat() bar(b) // NG
(edited)func foo<T: Sequence>(_ x: T) { print(x) } let a: AnySequence<Int> = AnySequence([2, 3, 5]) foo(a) // OK
(edited)typealias AnySequence<Element> = Any<Sequence where .Iterator.Element == Element>
https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials (edited)foo
に Any<Sequence where .Iterator.Element == Element>
という Existential Type を渡せるということになります。Animal
が bar
に渡せてもいいように思うのですがいかがでしょう?remove()
実装、1個づつmoveInitialize()
してるけど、まとめて出来るはず。 https://qiita.com/omochimetaru/items/f32d81eaa4e9750293cdremoveSubrange
も O(n) だからいいじゃないかなと思ったりする https://developer.apple.com/documentation/swift/array/2884498-removesubrangemoveInitialize
にしても O(n)
なのは変わらないですね でも、ループを自分でやるより関数に畳み込むほうが最適化も期待できるし、コードはシンプルにできるので、ちゃんと動くなら書き換えたほうが良さそう。remove
系のメソッド全部確認して見たらやはりremoveLast
だけ O(1) だった /// - source: A pointer to the values to copy. The memory region /// `source..<(source + count)` must be initialized. The memory regions /// referenced by `source` and this pointer may overlap.
source and this may overlap
だからExpressibleByRegularExpressionLiteral
を話してる中で Chris Lattner が↓言ってるんですがおもしろくないですか? 2) I’d like to explore the idea of making // syntax be *patterns* instead of simply literals. As a pattern, it should be possible to bind submatches directly into variable declarations, eliminating the need to count parens in matches or other gross things. Here is strawman syntax with a dumb example: if case /([a-zA-Z]+: let firstName) ([a-zA-Z]+: let lastName)/ = getSomeString() { print(firstName, lastName) }
let sortedScores: [Int] = ... if case /([\(0)-\(59)]+: let 不可)([\(60)-\(69)]+: let 可)([\(70)-\(79)]+: 良)([\(80)-\(100)]: let 優)/ = sortedScores { print("優: \(優.count)人") print("良: \(良.count)人") print("可: \(可.count)人") print("不可: \(不可.count)人") }
a = someValue.someMember someValue.someMember = a mutateParameter(&someValue.someMember)
a = someValue[dynamicMember: "someMember"] someValue[dynamicMember: "someMember"] = a mutateParameter(&someValue[dynamicMember: "someMember"])
(edited)a = [2, 3, 5] a.empty?
とか。subscript(dynamicMember:)
内で適宜書き換えてあげないとだめっぽいですね。 (edited)add_trick
のままなだけで、 PyVal
の subscript
で読み替える実装にすれば addTrick
にもできるわけですね。 // import DogModule // import DogModule.Dog as Dog // an alternate let Dog = Python.import("DogModule.Dog") // dog = Dog("Brianna") let dog = Dog("Brianna") // dog.add_trick("Roll over") dog.add_trick("Roll over")
(edited)SOFT HYPHEN(U+00AD)
かな? https://github.com/swift-script/swift-script/pull/18 Apple のドキュメントをコピペすると入ってる時がある。subscript
には @_specialize
が許可されてない? struct Foo { @_specialize(where T == Int) // OK func foo<T>(_ x: T) -> T { return x } @_specialize(where T == Int) // Error subscript<T>(x: T) -> T { return x } }
(edited)subscript
は元々ジェネリックじゃなかったけど、ジェネリックになったときに対応忘れたとか? (edited)_
つきだから、 stdlib で使用する要求が無いと実装されないかもしれないですね。@_specialize
と言えば、昨日過去最多の @_specialize
をつけました・・・。 https://github.com/koher/EasyImagy/blob/dev-0.4.0/Sources/EasyImagy/ImageSpecialized.swift#L2-L677@_specialize
の付いてるコードは見つけられないかもしれないwRGBA
で倍増して、それを組み合わせたのでこんなことに・・・。 (edited)@inlineable
って @_inlineable
として既に実装されているような?_
を外して正規仕様にするために提案を整理してるけど内容は現状あるやつ@inlineable
ではできないけど、現状 @_versioned
でできてるから、これは別の提案で出す、とか。@_versioned
はわかりにくいから別の名前が良いって意見があった@_inlineable
も最初 @_fragile
だった。いずれにしてもパッとは分からないですねー。 1> let a = [2, 3, 5] a: [Int] = 3 values { [0] = 2 [1] = 3 [2] = 5 } 2> let f: (Int) -> Int = a.subscript Segmentation fault: 11
error: value of type '[Int]' has no member 'subscript' let f: (Int) -> Int = a.subscript ^ ~~~~~~~~~
Rintaro Ishizaki added a comment - 15 Sep 2016 4:51 AM Minimum repro: class C { subscript(x: Int) -> Void { return } } _ = C().subscript
subscript
のリファレンスを取る方法は封じられたんでしたっけ? (edited)class C { subscript(x: Int) -> Void { print(x) } } let idx = 12 let kp = \C.[idx] C()[keyPath: kp]
keypath はインデックス固定のリファレンスと見れなくもないか? やりたいこと逆ですがw(Int) -> Element
として高階関数に渡したいのでpublic class Disposable: DisposableConvertible
にしてユーザー側ではサブクラス作れないようにする (edited)Disposables.create
になっているけど、Disposable.init()
にしたかったんだよね // this is kind of ugly I know :( // Swift compiler reports "Not supported yet" when trying to override protocol extensions, so ¯\_(ツ)_/¯ /// Optimizations for map operator internal func composeMap<R>(_ transform: @escaping (Element) throws -> R) -> Observable<R> { return _map(source: self, transform: transform) }
_map
ってどこに定義されてるんだ?public class Disposable { public init() { // ここはクラスクラスタ } } private class CompositeDisposable: Disposable {} // ... その他諸々 protocol DisposableConvertible { var asDisposable: Disposable { get } } public extension Disposable: DisposableConvertible { var asDisposable: Disposable { return self } }
_NSArrayCore
ふくめ、このファイルの型は Foundation なしの stdlib で Foundation 系の型を扱うためのもの。みたいな感じだと思ってます。 (edited) func bind0<X: EventSourceProtocol>(modelStream: X) where X.Event == Model func bind1(modelStream: EventSource<Model>)
(edited)bind1(modelStream: .of(3))
とか書ける事もあって便利bind1(modelStream: Property(3).asEventSource())
変換が必要EventSource is EventSourceProtocol
だから、曖昧なんだけどEventSourceProtocol.of
と EventSource.of
が曖昧になりそうだ .of()
だけ書いてある場合 (edited)protocol MyProtocol { static var foo: Self { get } static var bar: Self { get } } final class MyClass: MyProtocol { static var foo: MyClass { return MyClass() } static var bar: MyClass { return MyClass() } } func func1<X: MyProtocol>(_ arg: X) {} func func1(_ arg: MyClass) {} func1(.foo)
おもちprotocolは
と解釈したprotocol AnimalProtocol { static func makeNya() -> Cat }
↑は Cat.makeNya() や Dog.makeNya() が呼べる事を示す定義であって、 AnimalProtocol.makeNya() という式で呼べる関数を定義するわけじゃないからいいけどprotocol AnimalProtocol {} class Cat : AnimalProtocol {} extension AnimalProtocol { static func makeNya() -> Cat { return Cat() } }
こういう話ではなくて?Float
使いますか? Double
使いますか? Java だと double
が普通だと思うんですが。Double
ですかね?そもそも指定がなかったらデフォルトで Double
使われますし…Double
のみ(もしくは Float
のみ)の API ってありますっけ? 1> let a: Float = -3.14 a: Float = -3.1400001 2> abs(a) $R0: Float = 3.1400001
var
と func
と init(
で数検索して見たけど func
だけFloatが1個少なくて軽く見て見たらFloatのページにだけ abs
がなかったけどドキュメント作った人のせいかCGFloat
なんですよねぇ。CGFloat
は標準ライブラリーで使えなかったっけ?let a = 0.12 print(type(of: a)) // Double
HashMap
の loadFactor
みたいなのないかと思ったけど、 https://docs.oracle.com/javase/jp/9/docs/api/java/util/HashMap.html#HashMap-int-float-Dictionary
にはなかった・・・。 https://developer.apple.com/documentation/swift/dictionaryDouble
を優先して使うのがいいのかな。
char
が一遍に8バイト処理できる計算になるけどそんな話聞いたことないですね(聞いたことないだけかもしれないが
Float
優先で使ってたんだろうと考えてみたけど、Float
になっていることがUITouch
の分解能ってどれくらいなんだろう? (edited)UITouch
の分解能が低いなら Float
でも合理的か。CGFloat
だから、 64bit 環境では 64bit 浮動小数点数になってると思う。Float
とか普通かと。GLK_INLINE GLKMatrix4 GLKMatrix4Add(GLKMatrix4 matrixLeft, GLKMatrix4 matrixRight) { #if defined(__ARM_NEON__) float32x4x4_t iMatrixLeft = *(float32x4x4_t *)&matrixLeft; float32x4x4_t iMatrixRight = *(float32x4x4_t *)&matrixRight; float32x4x4_t m; m.val[0] = vaddq_f32(iMatrixLeft.val[0], iMatrixRight.val[0]); m.val[1] = vaddq_f32(iMatrixLeft.val[1], iMatrixRight.val[1]); m.val[2] = vaddq_f32(iMatrixLeft.val[2], iMatrixRight.val[2]); m.val[3] = vaddq_f32(iMatrixLeft.val[3], iMatrixRight.val[3]); return *(GLKMatrix4 *)&m;
Float
と Double
のどちらが使われてるのかということですね。let a = 0.12
の場合 a
が Double
なので Double
の方が一般的かと思いますね Float
使ってたのかを考えると、昔の iOS アプリ開発から惰性でそうしてるだけな気がしてきました。 (edited)public struct CGFloat { /// The native type used to store the CGFloat, which is Float on /// 32-bit architectures and Double on 64-bit architectures. public typealias NativeType = Double
これ好きFloat
ではなく字面の意味通りのFloatだから(震え声public typealias Float64 = Double
なのでセーフFloat
と Double
って名前に統一感ないですよね。せめて Single
と Double
にするか、 Float32
と Float64
にするかしてほしい。 Swift が悪いわけじゃないけど。Float32
と Float64
作って、 Int
みたいな位置の Float
ほしい。Unsafe*Pointer<Int32>
受ける関数とかありますけどね。 そもそもInt
のサイズがネイティブに依存するメリットがいまいち分かっていないです。Int32
だろうが Int64
だろうが成り立つことがほとんどで、Int
って書いてるときはオーバーフローは Logic failure だし、バイト数で壊れるようなコードはそもそも Int
で書いちゃいけないかと。 (edited)Int32
って書いたコードは未来に Int128
が当たり前になった世界でも Int32
のままになっちゃうよ。Int64
とか Int32
使って、普通の環境は Int
でよしなりにプラットフォームに合わせて使わせてくれる方が嬉しいかな派ですねInt32
でも Int64
でも変わらないでしょう派()Int
が色々なビット数で動いてくれるとうれしいとは思う。Int32
で足りないシチュエーションって相当レアですよね…int
が 32 bit 固定だけど、今の 64 bit 当たり前の時代でも 32 bit に縛られちゃってるのでは?int
を long
にするとか無理では。List
を出したのは例であって、一般的に整数の意味合いで使われてる int
がずっと 32 bit のままが辛いということです。int
が 32 ビットのまま変わらず 20 年使われてますよ。UITableView
のセルの数&インデックスは Int32
だけど Array
の要素数&インデックスは Int64
で境界で全部明示的変換が必要ですとか辛くないですか?ArrayList
的なものを想定してると思うけど、 LinkedList
とかを考えると話が変わってくるのでは? List
は一般的なインタフェースだし。List
が動的に要素を計算するとかあり得るのでは?( List
のアップデート系のメソッドはオプションのはず)Int
から Int32
とかに変換するわけで、その境界で気付くんじゃないかな?普段は Int
として扱っていて、ビット数を気にする処理をする際に IntXX
に変換で十分だと思うけどなぁ。Int32
とか Int64
とか色分けするのは変だと思うし、それが 32 ビット(に限らない XX ビット)に取り残される原因を生むと思う。size
は int
なんです。int
を全部 long
にしましょうとか無茶でしょ?int
を 64 ビットにします(もしくは int
で書かれてた API の大部分を long
にします)とかやったら世界崩壊しそうな気が。 (edited)Int
はプリミティブすぎて影響範囲が他よりめちゃくちゃ大きいんじゃないかなぁ。long
を使ってたものが、 64 bit の世界では普通にとりあえ使えるようになってるのに、古い仕様にひきずられて永久に面倒な取り扱いを強要されるの辛そう。 (edited)fseek
はそんな事になってますねlong
なんだけど long が32bitの環境があるので64bit用のオーバーロードがOSによってはオリジナルで提供されているlong
は Java の64 bit 整数の意図ね。wchar_t
は 2バイト以上」っていうのも同じ問題をおこしましたよlong
でポインタをハードキャストしてるところをまた直さないといけないのか・・・IntXX
と Int
でキレイに分かれてたらそんなにひどいことにはならない気が。Int
、固定したいケースは IntXX
でうまくいくと思うんだけどなぁ。UITableView
のセル数は概念として一般的な整数だと思うんですよね。ユースケースを考えたら 99% のケースではそれが Int32
に収まるとしても。 (edited)Int32
になって、概念を写し取っているべきだとすると Int
になるんじゃないでしょうか。Int
っていうのはほとんどのケースには便利だけど、、、という感じですね。Int32
等にすることに(僕が)違和感を感じるのかもしれません。FooProtocol
か Foo
の foo
の実装をコメントアウトすれば動きます。少なくともエラーメッセージはおかしいと思います。 protocol FooProtocol { func foo(_ f: (inout Int) -> ()) } extension FooProtocol { func foo(_ f: (inout Int) -> ()) { var x = 0 f(&x) print(x) } } struct Foo : FooProtocol { func foo(_ f: (inout Int) -> ()) { var x = 42 f(&x) print(x) } } let a = Foo() a.foo { $0 += 1 }
inout-hof.swift:22:9: error: passing value of type 'Int' to an inout parameter requires explicit '&' a.foo { $0 += 1 } ^ &
(edited)a.foo { $0 += 1; () }
でも動きます。public typealias ArraySlice<T> = Slice<[T]>
にするPR (edited)Slice
を導入するのは大きな変更では?typealias Substring = Slice<String>
(edited)ImageSlice
の名前どうしよ。できるだけ ArraySlice
を踏襲するようにしてたのに。ImageProtocol
を導入して Image
と ImageSlice
で共通の実装を持たせるとこが今朝大分できたのに・・・。Slice
にできないと思う。ArraySlice
の名前が残るならそれでもいいけど、Subimage
にするか ImageSlice
にするかで迷って、 ArraySlice
的なアップデートができるからそっちに寄せて ImageSlice
にした。ArraySlice
おもしろくて、↓みたいな挙動する。 let a = [2, 3, 5, 7, 11] let b: ArraySlice<Int> = a[1...3] // [3, 5, 7] let c: ArraySlice<Int> = [3, 5, 7] b[1] // 3 c[0] // 3 b == c // true
9> b[b.startIndex..<b.endIndex] $R2: ArraySlice<Int> = 3 values { [1] = 3 [2] = 5 [3] = 7 } 10> c[c.startIndex..<c.endIndex] $R3: ArraySlice<Int> = 3 values { [0] = 3 [1] = 5 [2] = 7 }
let a = Image<Int>(width: 4, height: 3, pixels: [ 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, ]) let b: ImageSlice<Int> = image[1...2, 1...1] // [[8, 9]] let c = ImageSlice<Int>(width: 2, height: 1, pixels: [8, 9]) b[1, 1] // 8 c[0, 0] // 8 b == c // true
(edited)Slice<T> is T
みたいなサブタイピングが追加されそうな予感The downside of having two types is the inconvenience of sometimes having a Substring when you need a String, and vice-versa. It is likely this would be a significantly bigger problem than with Array and ArraySlice, as slicing of String is such a common operation. It is especially relevant to existing code that assumes String is the currency type -- that is, the default string type used for everyday exchange between APIs. To ease the pain of type mismatches, Substring should be a subtype of String in the same way that Int is a subtype of Optional<Int>. This would give users an implicit conversion from Substring to String, as well as the usual implicit conversions such as [Substring] to [String] that other subtype relationships receive.
Optional
だけでなくさらなる黒魔術を導入しようとしてるのか?Substring
is a String
無理だと思うんだけどな。let s = "abc" let t: Substring = s[s.index(s.startIndex, offsetBy: 1)...] // "bc" let i = t.startIndex print(t[i]) // "b" let u = String(t) print(u[i]) // "c"
Optional
はもう一歩踏み込んでいて、↓ができる。 class A { func foo() -> Int? { return nil } } class B : A { override func foo() -> Int { return 42 } }
String
と Substring
は今はできない。Int
も Int?
のサブタイプにはなってなくて、↓はエラー。 9> let a: Int? = 42 a: Int? = 42 10> a.map { $0 * 2 } $R0: Int? = 84 11> let b: Int = 42 b: Int = 42 12> b.map { $0 * 2 } error: repl.swift:12:1: error: value of type 'Int' has no member 'map' b.map { $0 * 2 } ^ ~~~
String
は UnsafePointer<UInt8>
に渡せるし。Substring should be a subtype of String in the same way that Int is a subtype of Optional<Int>.
let str: String = "hello" strlen(str) // これはだめにして strlen(^str) // こうするみたいな。
(edited)UnsafePointer
周りは &
がその役割してるのでは?in the same way that Int is a subtype of Optional<Int>.
はもっと強い意味だけど、 the default string type used for everyday exchange between APIs.
を解決するのにサブタイプは必要ないんじゃないか?っていう意見です。&
も構文失敗してる気がする。↓ややこしい。 func f(_ p: UnsafePointer<Int>) { print(p[0]) } func g(_ p: UnsafePointer<[Int]>) { print(p[0].count) } var a = 42 var b = [2, 3, 5] f(&a) f(&b) g(&b)
f(&b)
が良くないinternal
な後置演算子作って変換すればいいし^
で変換、今もできる。 prefix operator ^ prefix func ^(value: Substring) -> String { return String(value) } let s = "abc" let t: Substring = s[...] let u: String = ^t print(u)
strlen(str) // 元のコード // 変換先のコード str.withUnsafeBufferPointer { strlen($0) }
strlen(^substr) // 元のコード // 変換先のコード (^substr).withUnsafeBufferPointer { strlen($0) }
になるのではなく? (edited)var ary = [2, 3, 5] func f(_ p: UnsafePointer<Int>) { } f(ary) // は ary.withUnsafeBufferPointer { f($0.baseAddress!) } // の sugar
(edited)Array
の内部のポインタをそのまま取り回せないようにするのがその意図なんじゃない?( Array
をぶっ壊したり、ライフサイクルを超えて取り回したりできてしまうから)
This contrasts with Array, which can store its elements in either a contiguous region of memory or an NSArray instance if its Element type is a class or @objc protocol.
import Foundation prefix operator ^ prefix func ^(value: Substring) -> String { return String(value) } let s = "abc" let t: Substring = s[...] let u: String = ^t print(u) print(strlen(u)) // OK: 3 print(strlen(^t)) // OK: 3
ArraySlice
→ Array
にせよ、変換してから渡すから問題ないのでは?mutating
じゃないといけないときはダメだけど。import UIKit protocol ProtocolA {} class CustomCell: UITableViewCell, ProtocolA {} func hoge<T>(cellType: T.Type) where T: UITableViewCell & ProtocolA {} let typeA: (UITableViewCell & ProtocolA).Type = CustomCell.self let typeB: CustomCell.Type = CustomCell.self hoge(cellType: typeA) // error: generic parameter 'T' could not be inferred hoge(cellType: typeB) // ok
こういうもんなのかfunc fuga(cellType: (UITableViewCell & ProtocolA).Type) {} fuga(cellType: typeA) // ok fuga(cellType: typeB) // ok
ジェネリクスじゃなくしたら行けた。hoge(cellType: CustomCell.self)
[UITableViewCell & ProtocolA).Type]
に詰めてforEachで全部処理しようとしたら詰まったという感じですAny<Error>
が許されるようになれば T: Error
に Any<Error>
を入れられるようになるのかな??typealias AnySequence<Element> = Any<Sequence where .Iterator.Element == Element>
が成り立たないか。AnySequence
等の個別の実装をなくせない)だし、 typealias AnySequence<Element> = Any<Sequence where .Iterator.Element == Element>
でもそのために早期 ABI 安定化ができないのだとすると、どちらかを諦めるしかなさそうですね。Any<FooProtocol>
の形に統一するのがいいんじゃないかと思ってます。Self
や associatefdtype
を持つプロトコルは型として使えないのに、一部のプロトコルが型として使えることに気持ち悪さを感じてます。protocol
は interface
ではないので。protocol Functor { associatedtype A func fmap<FB where FB ~= Self>(f: A -> FB.A) -> FB }
protocol<P1, P2>
が Any<P1, P2>
ではなく P1 & P2
に置き換えられちゃったみたいだけど、 generalized existential と整合がとれなくなっちゃってる気がするんだけどどうするんだろう・・・。 https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.mdAny<P1, P2>
という表記で提案されていたのが最終的に P1 & P2
に変更されて採用されてるんです。The principle problem identified by community about the proposal is that "Any<T1, T2>” implies very strongly an “any of T1 OR T2” relationship
https://lists.swift.org/pipermail/swift-evolution-announce/2016-June/000182.htmlAny<P1 & P2 where ...>
になるんじゃないかと思います。P1 & P2
は廃止される?Any<P1 & P2>
の sugar として残る(残さざるおえない)んじゃないかなー。P1 & P2
is P1
になるってことですよね?まあゆるくなるだけだから互換性はありますが・・・。Any<P>
よりも P
が使われそうなのが微妙・・・P
を型として書くのは existential であることを意識しないで使ってしまうので廃止してほしい。P
を Any<P>
と書かないといけなければ、 func foo<T : P>(_ x: T)
が望ましいものが func foo(_ x: P)
と書かれるケースが減りそう。 func foo(_ x: Any<P>)
だと気持ち悪さがあるので。 (edited)class UITableView { var delegate: Any<UITableViewDelegate>? }
これは受け入れられるのだろうかid<UITableViewDelegate>
って書いてたんだよねprotocol
と interface
が概念的に違うことについて、公式ドキュメントでちゃんとした説明が必要だ・・・ (edited)Sequence<where Element == Int>
みたいな 構文になる可能性もありますね。P1<where ...> & P2<where ...>
Sequence<Int>
にしないのかみたいな話を誘発しそうです・・・
protocol
の static func
は必要では? Argo の Decodable
とかそれなしに成立しないし。 init
は static func
と同じだし。static func
を持てるのは制約を表すために普通に必要だと思うけどなぁ。たとえば↓とか。 protocol Group { // 群 static func •(lhs: Self, rhs: Self) -> Self // 結合法則を満たす演算 static var identity: Self { get } // 単位元 var inverse: Self { get } // 逆元 }
(edited)class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x == y)
interface
は protocol
とは別の概念だから異なってていいのでは? (edited)Sequence<Element>
じゃないの?ってとこだろうし。Sequence<Element>
はでも結局不可能でSequence<where Element == Int>
にしたとして、なんで Sequence<Int>
じゃないの?ってなってSequence<Int, _, _>
みたいにできるが…?protocol Sequence<Element> { ... }
とはできないからやっぱり混乱するだけじゃないかなぁ。<>
が出てくるから似た機能のGenericsが想起されて混乱する (edited)[ ]
はArrayに使っちゃったしなあSequence[where .Element == Int]
let array: [Sequence[where Element == Int].Type] = [Sequence[where Element == Int].self]
んー、なんとかいけるか?let a: Int[] = [1]
は未だに Fix-it 出してくれるという親切さ。Int[]
の時代とかありましたね。 Java から来るとわかりやすかったけど、今となっては何がよかったのか・・・AnyIterator
は struct
なんだけど、値型捨て過ぎなのでもう class
になった方がいいのでは? let a = [2, 3, 5] let i = AnyIterator(a.makeIterator()) let j = i print(i.next()) // Optional(2) print(j.next()) // Optional(3)
struct
なのに let
で next
できてることからも。next
が mutating func
じゃないという潔さ。class
にしなかった意味がよくわからない。beginAsync
has the following signature. func beginAsync(_ body: () async throws -> Void) rethrows -> Void However, I think it is better to forbid body
to throw errors, that is to say, to change its signature to the followi...async -> Never
? Theoretically, async -> Void
means you're awaiting a result with only one possible value, but if you're not waiting at all, then there is truly no result, yes? ··· On Sun, Nov 12, 2017 at 9:27 AM, Yuta Koshizawa via swift-evolution < swift-evolution@swift.org> wrote: Sorry, I had got s...Norio_Nomura
になってしまってモヤモヤする…Username Yuta_Koshizawa People can mention you as @Yuta_Koshizawa
omochimetaru
なんですけどomochi.metaru
になっていて@
の左の omochi.metaru
になった。username
変更しちゃうと今までの @
と紐付かなくて困るって話です? (edited)Problems happen when someone who have posted mails to the ML in the past sign up using GitHub authentication. They hope to change their username, but cannot do it after once their accounts were created. I wish there were some additional explaination in the sign up screen, and to get a chance to change the username even if signup is done using github authentication.
norio_nomura
にしておこうかな…joined
にリネームされました。 [[2, 3], [5, 7, 11]].joined()
joined()
の文字見た気がするのに、無視してました...Array
を用いるケース自体が稀な気がします
let array: [Any] = [1, 2, 3, [4, 5, 6], [7, [8, 9]]]
JSONSerialization
でパースすれば↑のような Array
が得られるでしょうが、どのようにネストされているか不定なネストした Array
を処理するようなことは実用上ほぼないんじゃないでしょうか。Array
をすべてつぶして flatten
したいようなケースはあると思います。[[[1], [2, 3]], [[4, 5, 6]]]
→ [1, 2, 3, 4, 5, 6]
とか。これならキレイに書く方法がありそうです。Data
の write(to:options:)
に倣って名詞を後ろにおいてもいいだろうということで write(to:formatting:)
→ write(to:format:)
としました。ありがとうございました。 (edited)extrapolatedBy:
を付与することで、 x
, y
が画像の範囲外にはみ出てしまった場合でも外挿してくれる API を提供しています。 let red = RGBA<UInt8>(red: 255, green: 0, blue: 0, alpha: 255) let pixel = image[x, y, extrapolatedBy: .filling(red)] // 例えば x = -1, y = -1 なら red が返される
(edited)extrapolatedBy
に与えているのは ExtrapolationMethod
型の値なのですが、次のように宣言されています。 enum ExtrapolationMethod<Pixel> { case filling(Pixel) // 画像の外側を与えられた値で埋める case edging // 画像の外側を最も近いエッジのピクセルで埋める case repeating // 画像の外側を画像の繰り返しで埋める case reflecting // 画像の外側を画像を鏡写しに折り返して埋める }
(edited)edge
, repeat
, reflection
に変えた方がいい気がしてるんですがいかがでしょうか。 (edited)edging
を縁取り的な意図で使ってるんですが、無限の大きさを持った縁なので 縁取りのイメージと合わない気が。一方で、今のままだと -ing
の統一感はあります。clampingToEdge
とかになると思うんですがちょっと長すぎかなと。extrapolatedBy
につながるので、 edge
でも意味は通るかなぁとは思うのですが、悩ましいですね・・・。 repeating
と reflecting
は repeat
, reflection
にします。 filling
は -ing
でも変じゃないですよね? (edited)repeat
が予約語だったから repeating
にしたんだった・・・。loop
とかかなぁ。.repeat
では済むのか。repeat
の方が一般的かな? CSS や GL も repeat だし。.repeat
にします。GL_TEXTURE_WRAP_S Sets the wrap parameter for texture coordinate ss to either GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, GL_MIRRORED_REPEAT, GL_REPEAT, or GL_MIRROR_CLAMP_TO_EDGE.
constant
かぁ。確かに filling(Pixel)
→ constant(Pixel)
にしてもいいかも?extrapolatedBy: .constant(red)
とか。 (edited)ExtrapolationMethod
がconstant
だとちょっと違和感ある気がしますね。 Padding
がconstant
だとしっくりくるんですがextrapolatedBy
を paddingBy
にした方がいいかな?interpolatedBy
とそろえたかったんだけど。paddedBy
かな?interpolatedBy
は .nearestNeighbor
とか .bilinear
とかが続く。padding
というタームは浸透してるから paddingBy
がいいけど、そうすると interpolatingBy
も検討しないといけない・・・。resizedTo(width: ..., height: ..., interpolatedBy: ...)
とかもあるからなぁ。enum ExtrapolationMethod<Pixel> { case filling(Pixel) // 画像の外側を与えられた値で埋める case edging // 画像の外側を最も近いエッジのピクセルで埋める case repeating // 画像の外側を画像の繰り返しで埋める case reflecting // 画像の外側を画像を鏡写しに折り返して埋める }
InterpolationMethod
です。subscript(x: Double, y: Double, interpolatedBy interpolationMethod: InterpolationMethod, extrapolatedBy extrapolationMethod: ExtrapolationMethod<Pixel>) -> Pixel
subscript
のラベルは名詞を並べるだけで良さそうだから https://developer.apple.com/documentation/swift/dictionary/2894528-subscriptsubscript(x: Double, y: Double, interpolation: InterpolationMethod, extrapolation: ExtrapolationMethod<Pixel>) -> Pixel
とか、 extrapolation
→ padding
とかでいいかもだけどExtrapolationMethod
を改造かなと思ってる。ExtrapolationMethod
の case
にも .repeat
とかを用意しておいて .all(.repeat)
の意味になるとか。で、 .all(.all(...))
とかはダメだから、 .repeat
だけを分離した型も必要になる。.bilinear
で横は nearestNeighbor
で、とかあり得るよね。resizedTo
の話ね。 (edited)InterpolationMethod
と ExtrapolationMethod
はそのままで、必要に応じて DirectedInterpolationMethod
とかを追加するのが良さそう。 .both(.bilinerar)
, .each(x: .nearestNeighbor, y: .bicubic)
とか。Dictionary
の subscript[_:default:]
に倣うなら subscript[_:_:interpolation:extrapolation:]
とかかなぁ。resizedTo(width:height:)
のラベルが難しい・・・DirectedInterpolationMethod
でカバーできる想定で。enum
じゃなくて sealed class
だったら階層的にサブタイピングでうまいことわけられるんだけどね。InterpolationMethod SimpleInterpolationMethod NearestNeighbor Bilinear Bicubic DirectedInterpolationMethod Both(SimpleInterpolationMethod) Each(SimpleInterpolationMethod, SimpleInterpolationMethod)
enum
よりはキレイに整理できそう。open
でない public
なクラスを使えば Swift でも同じようなことはできる。resizedTo(width:height:interpolation:)
は変な気がするけどなぁ。 interpolatedBy
とかじゃなくても、 write(to:format:)
と同じで別に気にしなくていい? (edited)To
の後に width
, height
, interpolation
が並列に並ぶのも気持ち悪い。resized(to: Size, interpolatedBy: InterpolationMethod)
とすることもできるけど、そうすると Size
の扱いが面倒。(Int, Int)
の点に対して補間するだけでは?.filling
はダメとか。さらに階層が増える・・・。.repeat
とかと概念的に違うんだよなぁ。Bool
とか)は .nearestNeighbor
の境界上と同じようにどちらかに寄せるとかかな。.custom
も考えてる。image[-100...100, -100...100, extrapolatedBy: .custom(...)]
InterpolationMethod
とかに case custom
を追加する話じゃないの? (edited).custom((Double, Double) -> Pixel)
と .custom((Int, Int) -> Pixel)
を追加するだけなので大変ではない。.custom((Double, Double, (Int, Int) -> Pixel) -> Pixel)
にしないとダメそう。(Int, Int) -> Pixel
が要らないんだよな。.custom
は保留しよう・・・(Int, Int) -> (Int, Int)
にできるしね。.filling
以外はこれで表現できる。.filling
のために ExtrapolationMethod
がジェネリックになってるのもややこしいといえばややこしい。interpolation
との組み合わせ爆発もするし。enum
じゃ階層にしてすべて ExtrapolationMethod
のサブタイプとかもできないしね。Int
だったら 0
とかそういうこと?protocol
を public
にしないといけない・・・。enum Foo { case foo case bar case piyo(String) case fuga(Int) static var specificFuga1 = Foo.fuga(1) } Foo.specificFuga1
こんな感じかなPixel
プロトコルを作ってしまうとUInt8
とか Float
にプロトコルを後付しないといけないしRGBA<Channel>
とかは付けれないし RGBA<UInt8>
とかだけに付けるには conditional conformance がいる。_Summable
っていうプロトコルを持っていて_Summable
が zero
みたいなのを持っててroteated(by:)
では斜めになったときの背景を zero
で埋めてる。( gyb で各型ごとに extrapolatedBy: .zero)
を付与して呼ぶメソッドを大量生成してる。)rotated(by:)
は使えなくてrotated(by:extrapolatedBy:)
じゃないと使えない。rotated
の interpolatedBy
省略で .bilinear
が使われるけど、それ以外では .nearestNeighbor
が使われるとかなってる。.nearestNeighbor
ですらなくて、それ相当の計算だけど。 InterpolationMethod
が使えるのは _Summable
だけだから。( Bool
に .bilinear
しようがない) (edited)Numeric
でもいけなくて、 UInt8
とか普通に足して平均とったらオーバーフローするからInt
や Double
に変換する処理とかがあって、そのあたりが全部 _Summable
で抽象化されてる。複雑・・・。AnyImage
も元は要らなかったんだけど、 image[-100...100, -100...100, extrapolatedBy: ...]
の結果を ImageSlice
で返そうとすると、 ImageSlice
が内部に持つのが Image
だけじゃダメになって、 ImageSlice
が AnyImage
を持つ必要に迫られた。internal
なメソッドでも @abiPublic
でインライン化できるのか。let double: Double = 3.123456 // 3.123456 var dec1 = Decimal(double) // 3.123456000000000512 var dec2: Decimal = Decimal() NSDecimalRound(&dec2, &dec1, 4, .bankers) dec2 // 3.1235
隠蔽してみた
extension Decimal { func rounded(exponent: Int, roundingMode: RoundingMode = .bankers) -> Decimal { var me = self var result = Decimal() NSDecimalRound(&result, &me, exponent, .bankers) return result } } dec1 // 3.123456000000000512 dec1.rounded(exponent: 4) // 3.1235
NSDecimalNumber, an immutable subclass of NSNumber, provides an object-oriented wrapper for doing base-10 arithmetic. An instance can represent any number that can be expressed as mantissa x 10^exponent where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.
Decimal
は NSDecimal
で c 構造体 みたいです。let dec1 = Decimal(double) // 3.123456000000000512 let n = (dec1 as AnyObject) type(of: n) // __ObjC.NSDecimalNumber.Type
_ObjectiveCBridgeable
っていう ReferenceConvertible
の一個下のレイヤーに準拠してますね。let double1: Double = 123456789000000000 let double2: Double = 1 print(String(format: "%.20f", double1)) print(String(format: "%.20f", double2)) print(String(format: "%.20f", double1 + double2)) let decimal1 = Decimal(string: "123456789000000000")! let decimal2 = Decimal(string: "1")! print(decimal1) print(decimal2) print(decimal1 + decimal2)
123456789000000000.00000000000000000000 1.00000000000000000000 123456789000000000.00000000000000000000 123456789000000000 1 123456789000000001
let decimal1 = Decimal(string: "123456789012345678901234567890")! let decimal2 = Decimal(string: "0.0000000000000000000000000001")! print(decimal1) // 123456789012345678901234567890 print(decimal2) // 0.0000000000000000000000000001 print(decimal1 + decimal2) // 123456789012345678901234567890
let decimal1 = Decimal(string: "123456789012345678901234567890")! let decimal2 = Decimal(string: "0.0000000000000000000000000001")! print(decimal1) // 123456789012345678901234567890 print(decimal2) // 0.0000000000000000000000000001 let decimal3 = decimal1 + decimal2 print(decimal3) // 123456789012345678901234567890 print(decimal1 == decimal3) // true!
let decimal1 = Decimal(string: "1234567890123456789012345678901234567890")! let decimal2 = Decimal(string: "1")! print(decimal1) // 1234567890123456789012345678901234567890 print(decimal2) // 1 let decimal3 = decimal1 + decimal2 print(decimal3) // 1234567890123456789012345678901234567890 print(decimal1 == decimal3) // true!
fileprivate var __exponent: Int8 fileprivate var __lengthAndFlags: UInt8 fileprivate var __reserved: UInt16 public var _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)
160bitimport Foundation print(MemoryLayout<Decimal>.size) // 20
let decimal = Decimal(string: "1234567890")! decimal._exponent // 1 decimal._length // 2 decimal._isNegative // 0 decimal._isCompact // 1 decimal._reserved // 0 decimal._mantissa // (.0 52501, .1 1883, .2 0, .3 0, .4 0, .5 0, .6 0, .7 0)
Decimal.greatestFiniteMagnitude // 3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Decimal.greatestFiniteMagnitude._mantissa // (.0 65535, .1 65535, .2 65535, .3 65535, .4 65535, .5 65535, .6 65535, .7 65535) Decimal.leastNonzeroMagnitude // 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 Decimal.leastNonzeroMagnitude._mantissa // (.0 1, .1 0, .2 0, .3 0, .4 0, .5 0, .6 0, .7 0)
(edited)func truelyLeastDecimal() -> Decimal { var x = Decimal(string: "0.1")! var r = Decimal() NSDecimalPower(&r, &x, 128, .plain) return r } func p(_ d: Decimal) { print("=====") print(d) print(d._mantissa) print(d._exponent) } p(Decimal.leastNormalMagnitude) p(truelyLeastDecimal()) /* ===== 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 (1, 0, 0, 0, 0, 0, 0, 0) -127 ===== 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 (1, 0, 0, 0, 0, 0, 0, 0) -128 */
Decimalには定義みあたらないけど、NSDecimalNumberのexponentは Decimal.leastNormalMagnitude > truelyLeastDecimal()
こうなってしまうのでCustomStringConvertible
じゃないものでも文字列に埋め込めるの良くなくないですか?何でもとりあえず文字列にしたいニーズはあるとして、それはデバッグ用にそういう関数を用意すれば良いだけに思います。 https://qiita.com/koher/items/6c855ddbda8797af4605CustomDebugStringConvertible
を分けてる意味がなくなっちゃってる気が。 print
が Any
をとれるのも微妙。Optional
は CustomDebugStringConvertible
だったと思うからString.init(describing: Any)
だったはずなんでString.init(describing: x)
って書けばいいんじゃねえかと"\debug() "
みたいなのでもいいけど 1> let a: Int? = 42 a: Int? = 42 2> a.debugDescription $R0: String = "Optional(42)" 3> a is CustomStringConvertible $R1: Bool = true
4> a is CustomDebugStringConvertible $R2: Bool = true
5> a.description error: repl.swift:5:1: error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'? a.description ^ ?
5> func debugPrint<T: CustomDebugStringConvertible>(_ value: T) { print(value) } 6> debugPrint(a) Optional(42)
7> func safePrint<T: CustomStringConvertible>(_ value: T) { print(value) } 8> safePrint(a) error: repl.swift:8:11: error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'? safePrint(a) ^
(edited)Optional
は CustomStringConvertible
ではないけど CustomDebugStringConvertible
ではある(↑)。 (edited)CustomDebugStringConvertible
だけ書いてある。 https://developer.apple.com/documentation/swift/optional (edited)extension Optional : CustomDebugStringConvertible where Wrapped : CustomDebugStringConvertible { ... }
String.init(describing)
で変換すればいいのか。 (edited)public init<T: TextOutputStreamable> (stringInterpolationSegment expr: T) public init<T: CustomStringConvertible> (stringInterpolationSegment expr: T) public init<T: TextOutputStreamable & CustomStringConvertible> (stringInterpolationSegment expr: T)
(edited) public init<T>(stringInterpolationSegment expr: T) { self = String(describing: expr) }
CustomDebugStringConvertibleのケースはこれに行きそう (edited)is
で true
はバグな気がする。 8> let b: CustomStringConvertible = a error: repl.swift:8:34: error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'? let b: CustomStringConvertible = a ^ ! 8> let c: CustomDebugStringConvertible = a c: CustomDebugStringConvertible = { payload_data_0 = 0x000000000000002a payload_data_1 = 0x0000000000000000 payload_data_2 = 0x0000000000000000 instance_type = 0x0000000101416700 libswiftCore.dylib`InitialAllocationPool + 80 protocol_witness_0 = 0x00000001013d1b78 libswiftCore.dylib`protocol witness table for <A> Swift.Optional<A> : Swift.CustomDebugStringConvertible in Swift }
String(describing:)
か debugDescription
すればいいんだから、直接埋め込める必要はない気がします。あえて言えば初心者が混乱するくらい??String(desciribing:)
使ったら?」って出せばいいからそこはダイジョブそう
enum
に偏りすぎな気が。 https://github.com/apple/swift-evolution/blob/master/proposals/0194-derived-collection-of-enum-cases.mdBool
は ValueEnumerable
でいいし、 ValueEnumerable
な associated value を持つ case
があってもその enum
は ValueEnumerable
でいい気がする。enum Foo { case bar case baz(Bool) }
は ValueEnumerable
になりたかったりしないかな・・・。The compiler will synthesize an implementation of ValueEnumerable for an enum type if and only if: - the enum contains only cases without associated values; - the enum is not @objc; - the enum has an explicit ValueEnumerable conformance (and does not fulfil the protocol's requirements).
ValueEnumerable
のときは生成で良い気が。 Codable
が Codable
なプロパティを持っている場合と同じように。Foo
の ValueCollection
が得られれば良いのでは?for x in Foo.allValues { }
みたいに書けるようになってもif x == .bar { ... } if case .baz(false) = x { ... }
こういう感じか。struct Card : ValueEnumerable { let suit: Suit // Suit : ValueEnumerable let number: Number // Number : ValueEnumerable } Card.allValues
(edited)Card.ValueCollection
To limit our scope, we are primarily interested in simple enums—those without any associated values—although we would also like to allow more complicated enums and structs to manually participate in this mechanism.
Int
に精度を明示すべきかどうかみたいな話とも関係してるんじゃないかなぁ。 Int
は ValueEnumerable
であってほしくないけど(概念としての整数は無限の要素を持つので) UInt8
は ValueEnumerable
であってほしい。同様に考えると Int32
や UInt32
も ValueEnumerable
であってほしいけど、 Int
を associated value に持つ enum
に ValueEnumerable
であってほしいわけじゃなくて、 Int
と Int32
の使い分けはやっぱり必要な気がする。Never
の bottom 化早く・・・
allValues
ができる Int64
とできない Int
を区別しておきたいですねぇ。Int64
だと count
が Int
で表せないという話は別として)Int32
か Int64
かの選択が揺らぐようなのをなくしたいです。Int32
等に変換して使いたいイメージですね。Int
にはビット演算とかオーバーフローとかもしたくないです。IntXX
でやって、アプリ用に Int
を使った型に変換するのか、それともエンティティレベルで Int
にして、 DB に保存するときにエラーにしてしまうのか。Int
に Java の BigInteger
とか Ruby の整数のような無限精度になってほしいわけではないですが、コードの書き手が単なる整数を意図しているのかそうではないのかを型として区別しておきたいという感じですね。UInt8
だとオーバーフローを利用した計算とかもよくやると思うんですけど、 Int32
ではそれは OK だけど Int
ではやりたくないです。そこは明示的に Int32
に変換してから処理したいです。Int
が実行環境によって精度が変わるかどうかは別として、 Int32
や Int64
より strict な型がほしいという感じですね。Int
が 32 bit に固定されていたとしても、 Int
に対してオーバーフローを利用した演算はしたくないです。Int
が無限長で有限長を使いたいならInt32
然りInt64
を使う、というのが個人的には一番しっくり来るInt
が現実的に 32bit や 64bit なのには不満はないです。無限精度にはなってほしくない。ValueEnumerable
の例で言えば func combination<T: ValueEnumerable, U: ValueEnumerable>(_ a: T.Type, _ b: U.Type) -> [(T, U)]
みたいな関数があったときにUInt8
や Int32
や Int64
は ValueEnumerable
だけど Int
は違ったらcombination(Int, Bool)
みたいなのを防げます。combination(UInt8, Bool)
はやりたいですし、 combination(Int32, Bool)
も同様に認められるべきだと思います。Int
に BigInt
(無限精度)になってほしいわけではなくて、今のままであってほしいと思います。Int
は単に整数であってほしいだけだと思うので、基本的には制限された Int
を使ってInt32
特有の操作をするときだけ明示的に Int32
に変換して使えたらいいんじゃないかと思います。Int
は最もよく利用する型なのでパフォーマンスも求められると思います。 BinaryInteger
として existential で扱うのは許容できないかと。たとえば、 count
は Int
を返しますが、これは整数を表していると思います。しかし、これが BinaryInteger
になるのは許容できないのではないかと。Int
や UInt
に ~
が使えるのとかは違和感を感じますね。 1> let n: UInt = 0 n: UInt = 0 2> ~n $R0: UInt = 18446744073709551615
3> ~(UInt64(n)) $R1: UInt64 = 18446744073709551615
とすべきかと。Int
のビット数を固定しなかったのが良くなかったという意見なのに対して、僕はそれでよかったと思ってるところでしょうか。そして、その裏には、 Int
は整数だからという気持ちがあります。Int
のビット数が固定されないことで、↑のようなビット演算が良くない行為となり、明示的な変換が求められます。ただ、もう少し踏み込んで Int
には ~
や &+
等はなくてよかったと思います。Int
はほぼ最大値を気にしなくてよい整数なんですよね。 BigInteger
が無限精度だと言っても実際には無限ではなくて、↓のような計算をすると精度が足りません。 $ irb irb(main):001:0> 2 ** (2 ** (2 ** (2 ** (2 ** 2)))) (irb):1: warning: in a**b, b may be too big => Infinity
(edited)BigInteger
で何であれ気にしないといけない、かもしれません。 (edited)Int
だろうが BigInteger
だろうがあまり関係なくどちらも整数と思えるという感じです。2 ** (2 ** (2 ** (2 ** 2)))
と入力すると楽しいことになります。irb(main):012:0> a.class => Bignum irb(main):013:0> a = 2 ** a (irb):13: warning: in a**b, b may be too big => Infinity irb(main):014:0> a.class => Float
2 ** (2 ** (2 ** (2 ** (2 ** 2))))
(edited)BigInteger
の限界を突破するということです。BigInteger
が必要そうですねぇ。AnyIterator
が Sequence
なのってなんでですか? https://developer.apple.com/documentation/swift/anyiterator
for x in (AnyIterator { hoge() }) {}
これができたら楽Sequence
としての責務を果たせてない気が。 let i = AnyIterator([2, 3, 5].makeIterator()) let i2 = i.makeIterator() print(i.next()) // 2 print(i2.next()) // 3 let i3 = i.makeIterator() print(i3.next()) // 5
AnySequence { AnyIterator { hoge() } }
になるのかなIteratorProtocol : Sequence
もほしくならない?Sequence
と IteratorProtocol
の責務が同じにならない?struct Countdown: Sequence, IteratorProtocol { var count: Int mutating func next() -> Int? { if count == 0 { return nil } else { defer { count -= 1 } return count } } }
IteratorProtocol
との役割分担がよくわからない・・・。extension Array { /// Sequence of all sub sequences of specified `length`. public func subSequences(of length: Int) -> SubSequenceSequence<Element> { return SubSequenceSequence(array: self, length: length) }
みたいなのを書いてたんですがSubSequenceIteratorをSequenceにしてそれを返すのも良いのか。Iterator
を返すと複数回呼んだ時に使用中のが返ってきたりする可能性も読み取れるような。 でもSequecne
自体も複数回走査が保証されてないなら……Sequence
が makeIterator
で消費されることがありなら、 extension IteratorProtocol { func makeIterator() -> Self { return self } }
的な実装が可能で、すべての IteratorProtocol
の実装は Sequence
として振る舞えることになる。protocol MySequence { associatedtype Iterator: MyIterator func makeIterator() -> Iterator } protocol MyIterator: MySequence { associatedtype Element func next() -> Element? } extension MyIterator { func makeIterator() -> Self { return self } }
MyPlayground.playground:3:20: Type may not reference itself as a requirement
実装都合な気もしてきたIteratorProtocol
は Sequence
でないけど AnyIterator
が Sequence
である理由はなんでしょうか?protocol MySequence { associatedtype Element associatedtype Iterator: MySequence where Iterator.Element == Element func makeIterator() -> Iterator func next() -> Element? }
こういう風にできると思うAnyIterator
が Sequence
なのに IteratorProtocol
が Sequence
じゃない理由がない?AnySequence
にinit追加したら良いんじゃと思うんですがどうでしょう? extension AnySequence { init(iterator: @escaping ()->Element?) { self.init { return AnyIterator(iterator) } } }
Sequence
がイテレータ経由で consume されるのも微妙な気が。
for
で回したいんだったら for in
に T: IteratorProtocol
を許容するようにした方がシンプルな気もするけどなぁ。map
とかもできるのかと思ったら単に AnyIterator
が Sequence
だからできるだけだった。AnyItearator
については使い捨てで操作したいこともあるし、かといって AnyIterator
AnySequence
で包むのはオーバーヘッドがあるからしかたないのかなぁ。 (edited)AnySequence
で包むのは〜の誤りですか?Sequence
として不適当な場合があるのかってことですねlet a = [0, 1, 2] var i1: IndexingIterator<[Int]> = a.makeIterator() print(i1.next()) // 0 var i2: IndexingIterator<[Int]> = i1.makeIterator() print(i2.next()) // 1
再列挙してないみたいです (edited)let a = [0, 1, 2] var i1: IndexingIterator<[Int]> = a.makeIterator() print(i1.next()) // 0 var i2: IndexingIterator<[Int]> = i1.makeIterator() print(i2.next()) // 1 print(i1.next()) // 1
分岐はしているみたいIndexingIterator
のmakeIterator
で途中状態を考慮したIterator
が帰ってくるというのは別に悪くないようにも思えますねぇIndexingIterator
は意図がよくわかんないけど、Zip2Iterator
は ZipSequence
経由で生成するから、逆に Iterator
から Sequence
を作りたいユースケースがないんじゃない?AnyIterator
の場合は Sequence
を作りたくなるユースケースが想定されて、しかも AnySequence
でラップするには無視できないオーバーヘッドがあって仕方なく Sequence
になってるのかと。IndexingIterator
はわかんない。AnySequence
で包んでから使う方がいいと思うけど。 (edited)IteratorProtocol
と Sequence
を混ぜるのは余計混乱するのでは?AnyIterator
の話じゃないのか。Sequence
は基本的には makeIterator
を複数回実行できることを想定しながらも、 consume するものにも使えるというのはまあいい気がしてきた。init
の裏技(by @lovee )、自身の型を返してサブクラス作るとセグフォ出ますね。class My { static func `init`(arg: Void) -> My { return MyMy() } } class MyMy: My { } My(arg: ())
(edited)observable.my.flatMap
みたいなmy
無くてこっちはあるとかそんなんは地獄っぽいのでアンスコがコスパ良いかなーって感じするlet a = [0: true, 1: false] // Error: `filter` is ambiguous // let x = a.filter { $0.value }.sorted(by: { $0.key < $1.key }) // Equivalent let x1 = a.filter { $0.value } let x2 = x1.sorted(by: { $0.key < $1.key})
4からDictionary
のfilter
が二種類になったみたいで、 分割して書いた場合は優先順位が付くけどsorted
が続いた場合は型推論で両方通るのでambiguousになる?みたいですlet x = (a.filter { $0.value } as Array).sorted(by: { $0.key < $1.key })
ソートを先にするとかで対応できますがいまいち……sorted
がついた場合も優先順位働かせることはできそうですけど やっぱ中間の型が隠れてるのは良くないとかいう判断なんでしょうかね。 (edited)sorted
だったのでそのまま書きましたがcount
の例のほうがより問題に近かったですね。values
に対する filter
だから違うか。count
だと通るというよく分からない状態 に let a = [0: true, 1: false] let xx = a.filter { $0.value }.count
enum E { case A(Int, String) case B(String, Float) } func foo(e: E) { switch e { case .A(_, let str): // something. fallthrough case .B(let str, _): print(str) } }
これが通るようになるらしいです。 https://github.com/apple/swift/pull/14041/files#diff-3f455e97fae02f7b158f90077919c17fR140String
の <
はこれで実装されているわけではない? This method implements the mathematical notion of lexicographical ordering, which has no connection to Unicode. If you are sorting strings to present to the end user, use String APIs that perform localized comparison.
String
の <
の順序変わるの??Character
の順序やんね?Character
の順序が (a|A) < (b|B) になってたらString
の lexicographicallyPrecedes
もその順になるのでは?$ swift Welcome to Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2). Type :help for assistance. 1> let a: Character = "a" a: Character = { _representation = smallUTF16 { smallUTF16 = 97 } } 2> let b: Character = "B" b: Character = { _representation = smallUTF16 { smallUTF16 = 66 } } 3> a < b $R0: Bool = false
(edited)var ss = ["apple", "Apple", "banana"] ss.sort() print(ss) // "["Apple", "apple", "banana"]\n" ss.sort { (a, b) in a.lexicographicallyPrecedes(b) } print(ss) // "["Apple", "apple", "banana"]\n"
@_inlineable @_versioned internal static func compare( _ left: _StringGuts, to right: _StringGuts ) -> Int { defer { _fixLifetime(left) } defer { _fixLifetime(right) } #if _runtime(_ObjC) // We only want to perform this optimization on objc runtimes. Elsewhere, // we will make it follow the unicode collation algorithm even for ASCII. // This is consistent with Foundation, but incorrect as defined by Unicode. // // FIXME: String ordering should be consistent across all platforms. if left.isASCII && right.isASCII { let leftASCII = left._unmanagedASCIIView let rightASCII = right._unmanagedASCIIView let result = leftASCII.compareASCII(to: rightASCII) return result } #endif return _compareDeterministicUnicodeCollation( _leftUnsafeStringGutsBitPattern: left.rawBits, _rightUnsafeStringGutsBitPattern: right.rawBits) }
// FIXME: String ordering should be consistent across all platforms.
String
の順序とかぐちゃぐちゃになってそう・・・ /// Compares two slices of strings with the Unicode Collation Algorithm. @inline(never) // Hide the CF/ICU dependency @effects(readonly) public // @testable static func _compareDeterministicUnicodeCollation(
3> ["apple", "Apple", "banana", "Banana"].sorted() $R2: [String] = 4 values { [0] = "Apple" [1] = "Banana" [2] = "apple" [3] = "banana" }
var ss = ["apple", "Apple", "banana", "Banana"] ss.sort() print(ss) // ["Apple", "Banana", "apple", "banana"]\n" ss.sort { (a, b) in a.lexicographicallyPrecedes(b) } print(ss) // ["Apple", "Banana", "apple", "banana"]\n"
lexicographicallyPrecedes
的な?/// Compares the strings via the Unicode Collation Algorithm on the root locale. /// Results are the usual string comparison results: /// <0 the left string is less than the right string. /// ==0 the strings are equal according to their collation. /// >0 the left string is greater than the right string. int32_t swift::_swift_stdlib_unicode_compare_utf16_utf16(const uint16_t *LeftString, int32_t LeftLength, const uint16_t *RightString, int32_t RightLength) { // ICU UChar type is platform dependent. In Cygwin, it is defined // as wchar_t which size is 2. It seems that the underlying binary // representation is same with swift utf16 representation. // On Clang 4.0 under a recent Linux, ICU uses the built-in char16_t type. return ucol_strcoll(GetRootCollator(), reinterpret_cast<const UChar *>(LeftString), LeftLength, reinterpret_cast<const UChar *>(RightString), RightLength); }
(edited) 4> ["👨👩👧👦", "👨", "👩"].sorted() $R3: [String] = 3 values { [0] = "👨" [1] = "👨👩👧👦" [2] = "👩" }
U+1F468 U+200D U+1F469 U+200D U+1F467 U+200D U+1F466
(edited)Language Swedish: z < ö German: ö < z Usage German Dictionary: of < öf German Phonebook: öf < of Customizations Upper-First A < a Lower-First a < A
Language Swedish: z < ö German: ö < z Usage German Dictionary: of < öf German Phonebook: öf < of Customizations Upper-First A < a Lower-First a < A
Table 2. Comparison Levels Level Description Examples L1 Base characters role < roles < rule L2 Accents role < rôle < roles L3 Case/Variants role < Role < rôle L4 Punctuation role < “role” < Role Ln Identical role < ro□le < “role”
Table 4. Context Sensitivity Contractions H < Z, but CH > CZ Expansions OE < Œ < OF Both カー < カア, but キー > キア
カー < カア, but キー > キア
これ確かに日本語ネイティブだから意識してないけどヤバイな。var ss = ["カア", "カー", "キイ", "キー"] ss.sort() print(ss) // ["カア", "カー", "キイ", "キー"] ss.sort { (a, b) in a.lexicographicallyPrecedes(b) } print(ss) // ["カア", "カー", "キイ", "キー"]
puts "カー" < "カア" # false puts "キー" < "キア" # false
public func _compareStringsPreLoop( selfUTF16: UnsafeBufferPointer<UInt16>, otherUTF16: UnsafeBufferPointer<UInt16> ) -> _Ordering {
private func _compareStringsPathological( selfUTF16: UnsafeBufferPointer<UInt16>, otherUTF16: UnsafeBufferPointer<UInt16> ) -> _Ordering { var selfIterator = NormalizedIterator(selfUTF16) return selfIterator.compare(with: NormalizedIterator(otherUTF16) ) }
(edited)internal func _tryNormalize( _ input: UnsafeBufferPointer<UInt16>, into outputBuffer: UnsafeMutableBufferPointer<UInt16> ) -> Int? { var err = __swift_stdlib_U_ZERO_ERROR let count = __swift_stdlib_unorm2_normalize( _Normalization._nfcNormalizer, input.baseAddress._unsafelyUnwrappedUnchecked, numericCast(input.count), outputBuffer.baseAddress._unsafelyUnwrappedUnchecked, numericCast(outputBuffer.count), &err ) guard err.isSuccess else { // The output buffer needs to grow return nil } return numericCast(count) }
> @dabrahams Sorry, I didn't read the string manifesto. > Hm, it seems that the string manifesto says "Swift string sorting will not be compatible with > Foundation sort order". > Is that correct with that understanding? Yes, non-localized String sorting will not work the same way in Swift as it does in Foundation. We intend to try very hard to keep localized sorting in sync, though.
なんか詳細忘れちゃったな…norio_nomura [12:54 PM] ASCIIに関しては一部の記号の扱いがFoundationとUnicodeで違うぽい。 [12:54 PM] で、大文字小文字のどちらが前に?ってのは、Unicodeでも「カスタマイズできるようにするべき」って書かれてる。 http://unicode.org/reports/tr10/#Case_Comparisons (edited)
@noescape
懐かしいstruct Cat : CustomStringConvertible { var name: String var description: String { return name } } var a: Cat = .init(name: "たま") var b: Cat? = .init(name: "くろ") // [1] print("\(a)") // [2] print("\(b?.description ?? "")") // [1] にたいして [2] の記述量が多く理不尽に感じる。 // 「noneなら空文字列」という事だけが言いたいだけなので、 // 例えば以下のように書きたい。 print("\(b ?? "")") // でもこれは ?? の両辺の型としておかしいので・・・ // 例えば \() を2引数関数のようにみなして print("\(b, "")") // などと書けるようにするとか?
Optional(2018)
から思っている事↑\(b, defaultValue: "")
の方がそれっぽいかなあ。 (edited)print("\(b, "")")
は現状でも書けちゃうという。 "\((b, ""))"
と解釈される。.description
になるか、 .debugDescription
になるか、 (3) になるか (4) になるか\(a)
ってかいてるときは気にする必要が無いのに (edited)Optional where Wrapped: CustomStringConvertible
なXにX ?? String
のオペレーターを追加するとスッと書けそうfoo?.description ?? ""
は必要最小限の表記な気が。foo.description(default: "")
くらい? (edited)"\(foo)"
だけで書けるのに、というモチベーションは変わらないですねえsubscript(key: Dictionary.Key, default defaultValue: @autoclosure () -> Dictionary.Value) -> Dictionary.Value { get set }
https://developer.apple.com/documentation/swift/dictionary/2894528-subscriptOptional
なのに空文字列に変換したいという特殊なニーズだから仕方ないんじゃないかなぁ?
extension Optional where Wrapped : CustomStringConvertible { func description(default defaultValue: String) -> String { return self?.description ?? defaultValue } }
(edited)String(describing: )
でいいんじゃないかと思ったんですがこの引数の型なんだろ……print("\(String(describing: b))") // "Optional(くろ)\n" var c: Cat? = nil print("\(String(describing: c))") // "nil\n"
(edited)OptionalはCustomDebugStringConvertibleですね CustomStringConvertibleではないです (ややこしや)
でもこれってよく考えるとそうなっていてほしくて、文字列に変換できちゃうと困るけど、デバッグ時には文字列にしたいんですよね。extension Optional: CustomStringConvertible where Wrapped: CustomStringConvertible
を自分で定義する、のはアリかもしれません。Optional
が CustomStringConvertible
だと "Optional(2018年)"
の温床になるのでダメだと思います。.none
のときに ""
にしたいって話だからまた違う。extension Optional : CustomStringConvertible where Wrapped : CustomStringConvertible
はまずそう。extension String { init<X>(describing: Optional<X>, default: String) }
↑こういう選択肢も・・・・infix operator ??? func ???<T>(lhs: Optional<T>, rhs: String) -> String { return lhs.map(String.init(describing:)) ?? rhs } let a: Int? = 0 print("\(a ??? "empty")") let b: Int? = nil print("\(b ??? "empty")")
(edited)\( )
とは別に用意する?"\(foo)"
である以上、それで CustomStringConvertible
でないものを弾けないと "Optional(2018年)"
を防ぎきれないと思うんですよねぇ。foo?.description ?? ""
このように書くのは全く正しいけど、長いからこう書きたくないってことなので。 (edited)"\(a)"
って書いちゃうケースを考えているんですね。 (edited)return "User(name=\(name), petCat=\(cat))"
CustomStringConvertible
でないものを文字列補間に突っ込んだら警告がいいと思うんですがどうでしょう? struct Foo {} let foo = Foo() print("\(foo)") // Foo()
(edited)class Foo { init!() { return nil } }
init!
定義できるのに今更気づいたのですが、完全に使いどころがわからない。 (edited)convenience init
から self.init()
で呼べるっていうのだけど、それにしても (edited)init?()
を self.init()!
で呼べるし。func foo() -> Int!
も一緒ですね。廃止して欲しい。CHANGELOG.md
に記述がありますね。 ### 2014-10-09 (Xcode 6.1) * Objective-C `init` and factory methods are now imported as failable initializers when they can return `nil`. In the absence of information about a potentially-`nil` result, an Objective-C `init` or factory method will be imported as `init!`.
今でもあるのかな?func f(_ value: Int?) {} @available(*, deprecated) func f(_ value: String?) {} f(nil) // error: ambiguous use of 'f'
deprecated
が付いてたら優先順位を変えてambiguous回避出来て欲しい。unavailable
はどうでしょう?nil
以外だとambiguousにならないので、残せるなら残したいのです。unavailable
指定はambiguous回避出来ますが、書き換え強制になっちゃうんですよね。async
な関数って inout
で渡された引数にアクセスできるのは最初の await
までだけになるのかな?inout
で検索してもヒットしない・・・。 https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619func aaa(_ x: inout Int) -> () -> Void { func bbb() { x = 3 } return bbb } var t: Int = 3 aaa(&t)
Swift:: Error: nested function cannot capture inout parameter and escape return bbb ^ Swift:: Error: expression resolves to an unused function aaa(&t) ^~~~~~~
await
までは使えてほしいなぁ。@escaping
でもできるっぽい。当然 @escaping
しようが同期的な範囲でしか使えないけど。 func update<T>(_ value: inout T, _ operation: @escaping (inout T) -> ()) { operation(&value) }
SwiftSourceKitClient
とか出来るのか。* Implementation: [apple/swift#13361](https://github.com/apple/swift/pull/13361)
https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md
After reflecting on the evolution process during both the Swift 3 and Swift 4 releases, the Core Team felt that we could strike a balance with not diluting attention from ABI stability while still enabling a broader range of proposals compared to Swift 4 by **requiring that all proposals have an implementation** before they are officially reviewed by the Core Team.
https://forums.swift.org/t/swift-5-start-your-engines/6456break: () -> Never
作れそう。async/await
相当のことできないと辛くなって、 Promise
改造して↓を作ったけど、 https://github.com/koher/AsyncKasync/await
in SwiftasyncFor
を作って、 break
を () -> Never
にできないか考えたけどダメだった。 func asyncFor<S: Sequence>(_ sequence: S, _ operation: @escaping (S.Element, _ break: @escaping () -> ()) -> Async<Void>) -> Async<Void>
https://github.com/koher/AsyncK/blob/master/Sources/AsyncK/Async.swift#L80async/await
in SwiftAProtocol: class
にしたらエラーなくなる。 protocol AProtocol { var value: Int { get set } } struct B<T: AProtocol> { private let t: T init(_ t: T) { self.t = t } func get() -> T { return t } } class A: AProtocol { var value: Int init(value: Int) { self.value = value } } func foo<T: AProtocol>(a: T) { let b = B<T>(a) b.get().value += 1 // ここでコンパイルエラー print(b.get().value) } foo(a: A(value: 2))
var b
が必要、 : class
が付くなら let b
で OK 、になるかなclass
制約は AnyObject
と同意になり、 class
は deprecated です。 https://github.com/apple/swift-evolution/blob/master/proposals/0156-subclass-existentials.md#class-and-anyobject: AnyObject
でまったく同じ意味になるので、機能が同じものを残しておく必要は無いということですね。To reduce source-breakage to a minimum, class could be redefined as typealias class = AnyObject and give a deprecation warning on class for the first version of Swift this proposal is implemented in.
: class
してた気がする。AnyObject
と言えば、 Swift の関数(クロージャ)は参照型だけど ===
が AnyObject
に対してしか使えなくて関数の同一性の比較ができないのどうにかならないのかな?protocol
と interface
作って、 protocol
は値型、 interface
は参照型と分けてしまってもいいんじゃないかという気がする。protocol MyProtocol: AnyObject { } class Foo: MyProtocol { } func foo<X>(_ v: X) where X: MyProtocol {} func bar<X>(_ v: X) where X: AnyObject {} let a: MyProtocol = Foo() let b: AnyObject = Foo() // foo(a) // 🙅 // bar(a) // 🙅 bar(b) // 🙆
Any
も同じ?b: AnyAnyObject
って感じだよね@objc
が入ると更に大変なことになる// foo(a) // 🙅 // bar(a) // 🙅
もうこのまま突き進んでこれが通る方に倒したいprotocol
の existential ですでにそうなのが・・・import Foundation @objc protocol MyProtocol: AnyObject { } class Foo: NSObject, MyProtocol { } func foo<X>(_ v: X) where X: MyProtocol {} func bar<X>(_ v: X) where X: AnyObject {} let a: MyProtocol = Foo() let b: AnyObject = Foo() foo(a) // 🙆 bar(a) // 🙆 bar(b) // 🙆
@objc protocol
は最早何でもあり (edited)@objc protocol
に限りなく近い (edited)@objc
が付いた protocol は型として使うとExistentialじゃなくなる?w%10 = open_existential_ref %9 : $Animal to $@opened("00DAD0B2-1796-11E8-B1A7-8C85902C8E53") Animal // users: %12, %12, %11 %11 = witness_method [volatile] $@opened("00DAD0B2-1796-11E8-B1A7-8C85902C8E53") Animal, #Animal.bark!1.foreign : <Self where Self : Animal> (Self) -> () -> String, %10 : $@opened("00DAD0B2-1796-11E8-B1A7-8C85902C8E53") Animal : $@convention(objc_method) <τ_0_0 where τ_0_0 : Animal> (τ_0_0) -> @autoreleased NSString // type-defs: %10; user: %12 %12 = apply %11<@opened("00DAD0B2-1796-11E8-B1A7-8C85902C8E53") Animal>(%10) : $@convention(objc_method) <τ_0_0 where τ_0_0 : Animal> (τ_0_0) -> @autoreleased NSString // type-defs: %10; user: %14
@objc protocol
class ReadWrite { } class ReadOnly: ReadWrite { } protocol IO { associatedtype Manifest: ReadWrite = ReadOnly } func foo<P>(_ arg: P) where P: IO, P.Manifest: ReadWrite { // ReadWrite }
class ReadOnly { } class ReadWrite: ReadOnly { } protocol IO { associatedtype Manifest: ReadOnly = ReadOnly } func foo<P>(_ arg: P) where P: IO, P.Manifest: ReadWrite { // ReadWrite } class UserDefaults: IO { } foo(UserDefaults()) // 🙅
(edited)Optional
では無いって条件みたいだけど適用できる?var foo = ImmutableFoo() foo = foo.update(...)
var foo = ImmutableFoo() foo.update(...)
でできて代入が不要になります。 immutable なのに mutating なのは名前がややこしいですが、 immutability は失われてないです。mutating func
を使えても、 self
自体の置き換えはできてもインスタンスの状態は変更できないのでインスタンスの immutability は保たれます。 (edited)class
自身が自分のオブジェクトを書き換えるのはどういうシチュエーションなのかが気になるんですね self
置き換えがあまりないのかな?Promise
とかそれで class
にしてます。self
書き換えというのがいまひとつどれを書き換えているのか。self
置き換え可能と、プロパティを書き換え可能は独立した機能な気がしてる。class
にとっては暗黙的に保証されてるけど、それとは別に var
に格納されているときに self
を書き換えられるという機能があり得るような。var
と let
の両方から参照されていることもあるから。mutating func
というか inout self
をしたい。func
( nonmutating func
の省略表記 ) と mutating func
がありますが、 cla...
protocol CharacterProtocol { init(name: String, hp: Int) var name: String { get } var hp: Int { get } mutating func update(hp: Int) } extension CharacterProtocol { mutating func update(hp: Int) { self = Self.init(name: name, hp: hp) } } class Character: CharacterProtocol { let name: String let hp: Int required init(name: String, hp: Int) { self.name = name self.hp = hp } } var character = Character(name: "ゆうしゃ", hp: 153) character.update(hp: character.hp - 10)
//: Playground - noun: a place where people can play import UIKit protocol SelfRewritable { var id: Int { get set } init(id: Int) mutating func increaseID() } extension SelfRewritable { mutating func increaseID() { self = Self(id: self.id + 1) } } class User: SelfRewritable { var id: Int required init(id: Int) { self.id = id } } var user = User(id: 0) user.increaseID() user.id
var foo = Foo() var bar = foo bar.update(...)
ってやったら、 foo
と bar
は別のものを指すようになるってことですよね。update
がmutating funcかどうかがパッと見た目にわからないから、ぼくはそれは嫌かなー。protocol
に : valueType
が欲しいかな、mutating func
入れたい場合はextension Array { mutating func update(_ operation: (inout Element) -> ()) }
Optional
にも同じものを追加することはできますが、class
で実装された Promise
は同じような API を持っているのに update
を追加できません。mutating func
に inout self
が混ざってしまっているのは、異なるものを一緒くたにしてしまっている気がします。struct
ラップはあり得るけど、 Array
の場合はミュータブルだからラップが必要だけど、 Promise
の場合イミュータブル的に振る舞うから、本来不要なラッパーができるのが微妙かなぁ。self
置き換えはまた別じゃない?mutating func
で self
置き換えができないようにして、それとは別に self
置き換えがあって区別できるのがいいかなと。そしたら、前者は var
な値型のみ、後者は var
ならクラスでも使えるようになる。mutating func
が構文のことではなく(今の Swift の class
のインスタンスメソッドと同じように) Stored Property を変更可能なメソッドのことならそうです。update func
という構文を踏襲するなら。 mutating func
は値型にのみ使用可能で Stored Property を更新可能、 self
置き換えは不可能、変数のような可変な lvalue に対してのみ呼び出し可能update func
は値型にも参照型にも使用可能で self
置き換えが可能、変数のような可変な lvalue に対してのみ呼び出し可能protocol
の mutating func
が class
でどうなるかは考え中。Range
とか)ですと update func と mutating func の区別は生じる気がしないでもない Range
の lowerBound
と upperBound
は let 宣言なのでmutating func
を呼び出しても let
プロパティは変更されないことが保証できるようになるね。struct Character { let name: String var hp: Int mutating func foo() { self = Character(name: "Hoge", hp: hp) } } var character = Character(name: "ゆうしゃ", hp: 153) character.foo() print(character.name) // "Hoge"
(edited)Character
はそもそも値型ではなく参照型で作るべきなのでは?と思わなくもないですねCharacter
が代入されるたびにコピーされてしまうのでwstruct
でやるのが僕の try! Swift のワークショップなのですupdate
は単語が微妙だったので replacing
にしてみました。 struct Character { let name: String var hp: Int mutating func a() { self = Character(name: "Hoge", hp: hp) // NG } replacing func b() { self = Character(name: "Hoge", hp: hp) // OK } mutating func c() { self = Character(name: name, hp: 42) // NG hp = 42 // OK } replacing func d() { self = Character(name: name, hp: 42) // OK } }
(edited)mutating replacing func
もありです。var character: Character
の方の宣言ですね…character
が a()
と c()
が呼べて b()
と d()
が呼べない、もしくはその逆の保証をするのかですね…var character
だと mutating
も replacing
も呼べる想定ですね。 (edited)mutating
と replacing
の区別をする意味がなくなるのでは?というところですmutating func
をコールした場合に let
プロパティが書き換えられてしまうことがないことを保証できます。replacing func
は class
でも使えます。replacing
は参照型限定にした方が実装の意味がある気がします replacing
導入しても、結局 var
だとその実装が呼べる呼べない区別ができなくなってしまいますのでlet
プロパティが書き換えられてるのって直感に反しませんか? struct Character { let name: String var hp: Int mutating func foo() { self = Character(name: "Hoge", hp: hp) } } var character = Character(name: "ゆうしゃ", hp: 153) character.foo() print(character.name) // "Hoge"
replacing func
を呼ぶ場合は &character.foo()
とか特殊な構文にしてもいいかも?var
宣言使うと replacing
も mutating
も使えるし、逆に let
宣言使うと replacing
も mutating
も使えなくなって、区別ができなくなってしまうのが気になるポイントですreplacing func
の呼び出しは inout
のように変化を付けたいですね。let
と var
以外の第3の宣言が必要になってくると言語仕様がどんどん複雑に… inout character: Character
的な宣言です?inout let
か inout var
にしないとですね &character.foo()
的な。var
じゃないと &character
使えないですね (edited)replacing func
は使えないようにしたいけど mutating func
は使いたいというニーズはほぼないと思うんですよね。extension Int { mutating func double() { self = self * 2 } } var i = 3 &i.double()
こう? (edited)inout
は mutating
と等価だから、 &
で被るのは微妙かも。let
宣言使う以外 &i
を防ぐ手段がない、というのば私が言いたいことですねreplacing
できないけど mutating
できる第三の変数みたいなのもあり得るけど、それの意味するところは再代入できるけどプロパティの更新はできる変数と定数の中間のものを作りたいということになるから (edited)replacing func
とは直交する話な気がします。mutating
可と replacing
不可を実現するのであって、概念としては直交してる気がします。)i.foo()
と書くか、&i.foo()
とかくかの区別だけしかできない、というのが私の懸念点ですねreplacing
を参照型だけのものにするか、もしくは値型にも replacing
を導入するのあら、何らかの宣言レベルでの区別が欲しいというのが私の考えですlet
プロパティを更新する挙動をするので利用者を驚かせてしまうと思うんですが、 var character = Character(name: "ゆうしゃ", hp: 153) character.foo() print(character.name) // "Hoge"
↓これだと驚かないと思うんですよね。 var character = Character(name: "ゆうしゃ", hp: 153) character = Character(name: "Hoge", hp: character.hp) print(character.name) // "Hoge"
var
自体ができることをどうこうしたいというわけではなくてlet
プロパティが書き換えられてしまうことを防ぎたいということです。&character.foo()
のようにコール時に明示的な区別がされれば OK だと思います。inout
ですでに行われていて、もし↓のコードが &
なしでコールできたらユーザーを驚かせてしまうと思うんですね。 var a = 2, b = 3 swap(&a, &b) print(a) // 3 print(b) // 2
var
で宣言するか let
で宣言するかで区別しているのでvar
のプロパティが変更されることは当然あり得ると思うんですが、 let
のプロパティが変更されるのは直感に反しませんか?struct
のプロパティで) var
か let
かを区別する機能を殺してると思うんですね。mutating func
と replacing func
を分ければ「たとえletであっても、ユーザー側でextensionを使ってsetterを生やせる。」も同じものは提供できなくなります。 replacing func
のコールで &
必須だと。i.foo()
より &i.foo()
の方がわかりやすい、というのはわかりますが、そこまでやるならやはり inout var i
宣言が欲しいかな、と思いますi += 1 // i = i + 1
だから、 i.=foo() // i = i.foo()
とか(いや、やりたいことと違う)。->
だったら (edited)i=>foo() // i = i -> foo()
とかワンチャンあったかも(ない)protocol P1 { init() } protocol P2 { init() } struct S { func a<T: P1>(_ type: T.Type) -> T { return a(type) ?? type.init() } private func a<T>(_ type: T.Type) -> T? { guard let p2Type = type as? P2.Type else { return nil } return p2Type.init() as? T } } extension Int: P1 {} S().a(Int.self) // Swift 4.1より前は無限ループ
Optional<T>
期待になるから private 側が呼び出されてるってことっぽい。 (inject_into_optional implicit type='T?' location=a.swift:12:16 range=[a.swift:12:16 - line:12:22] (call_expr type='T' location=a.swift:12:16 range=[a.swift:12:16 - line:12:22] nothrow arg_labels=_:
a(type)
のところの call_exprがTで、その一個上に inject_into_optional があるからそうっぽいT?
になっているのかな。[omochi@omochi-MB2 infer]$ export TOOLCHAINS=org.swift.3020180227a [omochi@omochi-MB2 infer]$ swift --version Apple Swift version 4.1-dev (LLVM c4ec2ab808, Clang af436f313e, Swift 5f2f440067)
swift-4.1-DEVELOPMENT-SNAPSHOT-2018-02-28-a
のREPLも落ちないです。 (edited)swift-DEVELOPMENT-SNAPSHOT-2018-02-27-a
ですね。master
のスナップショット。swift-4.1-branch
のスナップショットならorg.swift.3020180227a
ではなくorg.swift.4120180227a
なはず。org.swift.4120180228a
だと実行できた。(call_expr type='T?' location=a.swift:12:16 range=[a.swift:12:16 - line:12:22] nothrow arg_labels=_:
T?
で推論されて、暗黙キャストが無くなった (edited)Data
の↓の引数が UnsafeRawBufferPointer
じゃないのってなんでなんですっけ? init(bytes: UnsafeRawPointer, count: Int)
https://developer.apple.com/documentation/foundation/data/1780158-init (edited)NSData
とは別に Data
を作ったわけで、 RawBuffer
でもよかった気がするんですがいかがでしょう?Data
の導入とポインタ系の整理のタイミング問題でそうなってしまったとか?UnsafeRawBufferPointer
版が無いのは UnsafeRawBufferPointer
が出てきたときに Data
側対応が漏れているだけだと思います。Buffer
版を追加しようという提案はあり得そうですね。 https://developer.apple.com/documentation/foundation/data/1780455-init[array]
が再現のポイントです。それがなければ通ります。 func foo<R, T>( array: [T], operation: (Int) throws -> R ) rethrows -> R { return try array.withUnsafeBytes { [array] _ in return try operation(array.count) } }
error: repl.swift:5:16: error: call can throw, but the error is not handled; a function declared 'rethrows' may only throw if its parameter does return try array.withUnsafeBytes { [array] _ in ^ repl.swift:5:38: note: call is to 'rethrows' function, but argument function can throw return try array.withUnsafeBytes { [array] _ in ^
(edited)error: overlapping accesses to 'array', but modification requires exclusive access; consider copying to a local variable
です。
(edited)call can throw, ...
)はバグっぽい。 (edited)[array]
なくせば通りました。
defer { _precondition( inoutBufferPointer.baseAddress == pointer && inoutBufferPointer.count == count, "${Self} withUnsafeMutableBufferPointer: replacing the buffer is not allowed") (work, self) = (self, work) }
inout
の out
を除いたもの」として、 @escaping
なクロージャにキャプチャされるのを防ぐために使われてるのかな?withUnsafeBufferPointer
がそうなってないのが謎だけど。withUnsafe*Pointer
系は全部そうなってても良い気がする。withUnsafeMutableBufferPointer
だけそうなのがよくわからない。inout
の out
なし版( @escaping
に渡すのを防ぐだけ)がほしい。import UIKit protocol SomeChild where Self : UIViewController { func doSomething() } extension SomeChild { func doSomething() { print(self.title) } } class ChildViewController: UIViewController, SomeChild {} class ParentViewController: UIViewController { var children: [SomeChild] = [ChildViewController()] var child: SomeChild = ChildViewController() } let parent = ParentViewController() parent.child.doSomething() parent.children.forEach { $0.doSomething() }
childにアクセスすると問答無用でEXC_BAD_ACCESSになるprotocol SomeChild { func doSomething() } extension SomeChild where Self : UIViewController { func doSomething() { print(self.title) } }
class Object {} protocol SomeChild where Self : Object { func doSomething() } extension SomeChild { func doSomething() { print(self) } } class ChildObject: Object, SomeChild {} class ParentObject: Object { var children: [SomeChild] = [ChildObject()] var child: SomeChild = ChildObject() } let parent = ParentObject() parent.child.doSomething() parent.children.forEach { $0.doSomething() }
import UIKit protocol SomeChild where Self : UIViewController { func doSomething() init() } extension SomeChild { func doSomething() { print(self.title as Any) } } class ChildViewController: UIViewController, SomeChild {} class ParentViewController<C: SomeChild>: UIViewController { var children: [C] = [C()] var child: C = C() } let parent = ParentViewController<ChildViewController>() parent.child.doSomething() parent.children.forEach { $0.doSomething() }
class Object {} protocol SomeChild where Self : Object { func doSomething() } class ChildObject: Object, SomeChild { func doSomething() { print("hello") } } class ParentObject: Object { var children: [SomeChild] = [ChildObject()] var child: SomeChild = ChildObject() } let parent = ParentObject() parent.child.doSomething() parent.children.forEach { $0.doSomething() }
class Object {} protocol SomeChild where Self : Object { func doSomething() } class ChildObject: Object, SomeChild { func doSomething() { print("hello") } } let c: SomeChild = ChildObject() c.doSomething() // 😇
class Object { var x: Int = 1 } protocol SomeChild where Self : Object { func doSomething() } class ChildObject: Object, SomeChild { func doSomething() { print("hello") } } let c: SomeChild = ChildObject() c.x // Compile Error c.doSomething()
SomeChildはObjectを満たしているはずだが、xを呼び出せないlet c: SomeChild & Object = ChildObject()
って書くと動く。protocol SomeChild where Self : AnyObject { func doSomething() } class ChildObject: SomeChild { func doSomething() { print("hello") } } let c: SomeChild = ChildObject() c.doSomething()
最小これか。まあこれは protocol SomeChild: AnyObject {
って書けば良いじゃんて話ですが。Self:AnyObject
と:AnyObject
で動き変わるんすねTYPE MISMATCH IN ARGUMENT 0 OF APPLY AT expression at [<REPL>:12:1 - line:12:15] RangeText="c.doSomething()" argument value: %13 = open_existential_addr immutable_access %6 : $*SomeChild to $*@opened("25C7E87E-2911-11E8-A83E-0242AC110002") SomeChild // user: %14 parameter type: $@opened("25C7E87E-2911-11E8-A83E-0242AC110002") SomeChild
protocol … where Self: …
っていう書き方、最近まで知らなかった。Optional: Hashable where Wrapped: Hashable
になってないみたいですがなぜでしょう?Equatable
はもう実装されてるようなので、もし Hashable
も同じようにするならもう実装済みだろうと思いこんでました。func makeFunc<A, R>(argType: A.Type, returnType: R.Type, body: @escaping (A) -> R) -> (A) -> R { return body } makeFunc(argType: Int.self, returnType: String.self) { String($0) }(3) makeFunc(argType: (Int, Int).self, returnType: (String, String).self) { (String($0.0), String($0.1)) }((1, 2)) makeFunc(argType: Void.self, returnType: Void.self) { _ -> Void in print(33) }(()) // error makeFunc(argType: ().self, returnType: Void.self) { _ -> Void in print(33) }(()) // error makeFunc(argType: Void.self, returnType: ().self) { _ -> Void in print(33) }(()) print( ().self is Void.Type ) // false print( Void.self is ().Type ) // true
()
と Void
は違うものだった?print(().self is ()) // true
print(().self == ()) // true
何だこれは// error だけど 引数のところは許された・・・ makeFunc(argType: (), returnType: ()) { _ in print(33) }(())
()
を型としては扱えないようです。let a = [()]() // error
()
と 型の ()
があるのか()
が型である必要って今何かあるんですっけ? 前は()->Void
で必要でしたけど (edited)(Int) -> ()
型を禁止するわけにはいかないからでしょうか。 // Fold 'P & Q' into a composition type if (auto *binaryExpr = dyn_cast<BinaryExpr>(E)) {
Void
でいいと言うかもともと()->()
みたいな書き方しなかったですね……@objc
な関数や変数をoverrideすると、overrideしたそれも@objc
の扱いになっているっぽくて@objc
ではネストしたOptionalは許可されないので、コンパイルエラーになる()
型 の typealias であって、()
値 と ()
型 は違います。let hoge: ()
って書けましたっけ?(1, 2)
で、型の表記が(Int, Int)
なのだが、これが空になるとどちらも()
になるので混乱するlet void: Void = ()
って書いておいて、型ならVoid、値ならvoidを使うようにすれば楽になりそうだと一日考えて思った。let void = () func xxx() -> Void { return void }
こういうことです? グローバルにあるのも違和感なので extension Void { static var void: Void = () } func xxx() -> Void { return .void }
こんなかんじですかね(extensionかけないけど)let void: Void = () func nop<T>(_ arg: T) { } func undefined<T>() -> T { preconditionFailure() }
この辺大好き (edited)protocol Foo { } struct FooStruct: Foo { } protocol FooProtocol: Foo { } protocol Bar { func accept(_ foo: FooStruct) func accept<X>(_ foo: X) where X: FooProtocol } class BarImpl: Bar { func accept<X>(_ foo: X) where X: Foo { } }
protocol Root {} protocol Foo : Root {} protocol Bar : Root {} protocol A { func accept<F : Foo>(_ foo: F) func accept<B : Bar>(_ bar: B) } extension A { func accept<F : Foo>(_ foo: F) { print("Foo: \(foo)") } func accept<B : Bar>(_ bar: B) { print("Bar: \(bar)") } } class B : A { func accept<R : Root>(_ root: R) { print("Root: \(root)") } } struct FooImpl : Foo {} struct BarImpl : Bar {} B().accept(FooImpl()) B().accept(BarImpl())
(edited)protocol Root {} protocol Foo : Root {} protocol Bar : Root {} class A { func accept<F : Foo>(_ foo: F) { print("Foo: \(foo)") } func accept<B : Bar>(_ bar: B) { print("Bar: \(bar)") } } class B : A { override func accept<R : Root>(_ root: R) { print("Root: \(root)") } } struct FooImpl : Foo {} struct BarImpl : Bar {} B().accept(FooImpl()) B().accept(BarImpl())
protocol Root {} protocol Foo : Root {} protocol Bar : Root {} protocol A { func accept(_ foo: Foo) func accept(_ bar: Bar) } extension A { func accept(_ foo: Foo) { print("Foo: \(foo)") } func accept(_ bar: Bar) { print("Bar: \(bar)") } } class B : A { func accept(_ root: Root) { print("Root: \(root)") } } struct FooImpl : Foo {} struct BarImpl : Bar {} B().accept(FooImpl()) // Root: FooImpl() B().accept(BarImpl()) // Root: BarImpl()
(edited)let a: A = B() a.accept(FooImpl()) // Foo: FooImpl() a.accept(BarImpl()) // Bar: BarImpl()
accept
がジェネリックだったらちゃんとオーバーライドできてた。protocol Root {} protocol Foo : Root {} // 1 class A { func accept<R : Root>(_ root: R) { } } class B : A { override func accept<R : Foo>(_ root: R) { } } // 2 class A2 { func accept<R : Foo>(_ root: R) { } } class B2: A2 { override func accept<R : Root>(_ root: R) { } }
protocol Root {} protocol Foo : Root {} // 1 class A { func accept<R : Root>(_ root: R) { } } class B : A { override func accept<R : Foo>(_ root: R) { } } // 2 class A2 { func accept<R : Foo>(_ root: R) { } } class B2: A2 { override func accept<R : Root>(_ root: R) { } } struct RootImpl : Root {} struct FooImpl : Foo {} let b: B = B() // b.accept(RootImpl()) // コンパイルエラー let a: A = b a.accept(RootImpl()) // OK
(edited)protocol Root {} protocol Foo : Root { func foo() } // 1 class A { func accept<R : Root>(_ root: R) { } } class B : A { override func accept<R : Foo>(_ root: R) { root.foo() } } // 2 class A2 { func accept<R : Foo>(_ root: R) { } } class B2: A2 { override func accept<R : Root>(_ root: R) { } } struct RootImpl : Root {} struct FooImpl : Foo { func foo() { print("foo") } } let b: B = B() // b.accept(RootImpl()) // コンパイルエラー let a: A = b a.accept(RootImpl()) // 実行時エラー
master
からビルドした最新の swift でもダメだった。。。 (edited)protocol AnimalProtocol { func speak() -> String } protocol CatProtocol : AnimalProtocol { func nya() -> String } class AnimalEater { func accept<X : AnimalProtocol>(_ x: X) {} } class CatEater : AnimalEater { override func accept<X : CatProtocol>(_ x: X) { print(x.nya()) } } class Dog : AnimalProtocol { func speak() -> String { return "bow wow" } } let catEater = CatEater() let animalEater = catEater as AnimalEater let dog = Dog() animalEater.accept(dog) // file:///Users/omochi/work/playground/iOSGround.playground: error: Playground execution aborted: error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x0). // The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
(edited)StringProtocol.hasPrefix(_:)
が似た状況かも。 https://github.com/apple/swift/commit/f9b3e14137ac2f50e93aa2c1db28511e05f88e75public protocol StringProtocol … { … func hasPrefix(_ prefix: String) -> Bool
で、実装が extension StringProtocol { … public func hasPrefix<Prefix: StringProtocol>(_ prefix: Prefix) -> Bool {
になってる。 (edited)String
はジェネリックになってない。 extension String { … public func hasPrefix(_ prefix: String) -> Bool {
protocol ValuePrinter { func print(_ x: Int) func print(_ x: String) } protocol IntOrString { func asInt() -> Int? func asString() -> String? } extension Int : IntOrString { func asInt() -> Int? { return self } func asString() -> String? { return nil } } extension String : IntOrString { func asInt() -> Int? { return nil } func asString() -> String? { return self } } class OmniValuePrinter : ValuePrinter { // oneshot double override! func print<X: IntOrString>(_ x: X) { if let i = x.asInt() { Swift.print(i) } if let s = x.asString() { Swift.print(s) } } } let printer = OmniValuePrinter() as ValuePrinter printer.print(3) printer.print("a")
decl/**
に構文単位でいっぱいあるのか。$ nm /Users/norio/Library/Developer/Xcode/DerivedData/ObjectEncoder-cvflgtjlegddrlbosxxjagoaqewr/Build/Products/Debug/ObjectEncoder.framework/ObjectEncoder|xcrun swift-demangle … 00000000000305e0 t _protocol witness for Swift.SingleValueDecodingContainer.decode(Swift.String.Type) throws -> Swift.String in conformance ObjectEncoder.ObjectDecoder.Decoder : Swift.SingleValueDecodingContainer in ObjectEncoder 000000000002f000 t _protocol witness for Swift.SingleValueDecodingContainer.decode(Swift.Bool.Type) throws -> Swift.Bool in conformance ObjectEncoder.ObjectDecoder.Decoder : Swift.SingleValueDecodingContainer in ObjectEncoder 0000000000030430 t _protocol witness for Swift.SingleValueDecodingContainer.decode(Swift.Double.Type) throws -> Swift.Double in conformance ObjectEncoder.ObjectDecoder.Decoder : Swift.SingleValueDecodingContainer in ObjectEncoder 0000000000030280 t _protocol witness for Swift.SingleValueDecodingContainer.decode(Swift.Float.Type) throws -> Swift.Float in conformance ObjectEncoder.ObjectDecoder.Decoder : Swift.SingleValueDecodingContainer in ObjectEncoder …
class A: AProtocol { init() {} } fileprivate protocol AProtocol { var value: String { get } } extension AProtocol { var value: String { get { return "" } } }
struct B { fileprivate static var a = A() static var v: String { get { return a.value } } }
error: 'value' is inaccessible due to 'fileprivate' protection level
が正解かと。$ swiftc A.swift B.swift -emit-library -module-name MyMod
通る $ swiftc A.swift B.swift -emit-library -module-name MyMod -wmo
(edited)public func neverHappen(file: StaticString = #file, line: UInt = #line) -> Never { fatalError("never happen", file: file, line: line) } public func neverHappen<T0>(_ arg0: T0, file: StaticString = #file, line: UInt = #line) -> Never { fatalError("never happen: arg0=\(arg0)") }
例えばこうやって使う Observable.just(()).asDriver(onErrorRecover: { neverHappen($0) })
(edited)asDriver(onErrorRecover: neverHappen)
(edited)asDriver(onErrorRecover: neverHappen())
こういう事?asDriver(onErrorRecover: { _ in neverHappen() })
ってかいてたけど_ in
で捨てているのはもったいないから()
ついてるの何ってなるでしょfunc a(x: Int, y: Int = 3)
is (Int) -> Void
#file
とか)があるので、結局コンパイラがクロージャ的なコードを生成する必要がある。 (edited)xOrNone.map(cat.speak)
とかやるときも、 cat を束縛するクロージャができてますよね? // In the defining module. func foo_impl(a: Int, b: Int, c: Float) {...} func foo_b() -> Int { return 42 } func foo_c() -> Float { return 3.14 } // On the caller side. let bval = foo_b() let cval = foo_c() foo_impl(a: 192, b: bval, c: cval)
↑そうそうコレ // In the defining module. func foo_impl(a: Int, b bval: Int?, c val: Float?) { let b = bval ?? 42 let c = cval ?? 3.14 ... } // On the caller side. foo_impl(a: 192, b: nil, c: nil)
Hashable
の conformance とかまだ入ってないですね。 (edited)Double
の Vector3
とか Vector4
を作ってたけど、仕事で Float
にも対応したものがほしくなって、 ARKit の vector_float3
互換にもしたかったので、 gyb で色々生成してみた。(ついでにライブラリ名を SwiftyVector にリネーム) https://github.com/koher/SwiftyVector/blob/0.3.0/Sources/SwiftyVector/Concrete.swift.gyb (edited)protocol Foo { associatedtype Bar } protocol FooA: Foo where Bar == String { } struct FooAImpl: FooA { // typealias Bar == String // Not require } protocol FooB: Foo where Bar == [Piyo] { associatedtype Piyo } struct FooBImpl<P>: FooB { typealias Piyo = P typealias Bar = [Piyo] // Require }
これ結構気になってるprotocol Foo { associatedtype Bar } protocol FooA: Foo where Bar == String { } struct FooAImpl: FooA { // typealias Bar == String // Not require } protocol FooB: Foo where Bar == [Piyo] { associatedtype Piyo } struct FooBImpl<P>: FooB { typealias Piyo = P // typealias Bar = [Piyo] // Require }
<unknown>:0: error: unknown argument: '--version=latest'
Swift version 4.2-dev (LLVM d14a2b25f2, Clang c38020c511, Swift 22530b922f)
/usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'Foo' struct FooBImpl<P>: FooB { ^ /usercode/main.swift:2:20: note: protocol requires nested type 'Bar'; do you want to add it? associatedtype Bar ^ /usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'FooB' struct FooBImpl<P>: FooB { ^
protocol Foo { associatedtype Bar } protocol FooA: Foo where Bar == String { } struct FooAImpl: FooA { // typealias Bar == String // Not require } protocol FooB: Foo where Bar == [Piyo] { associatedtype Piyo } struct FooBImpl<P>: FooB { typealias Piyo = P // typealias Bar = [Piyo] // Require }
Swift version 4.1 (swift-4.1-RELEASE)
/usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'Foo' struct FooBImpl<P>: FooB { ^ /usercode/main.swift:2:20: note: protocol requires nested type 'Bar'; do you want to add it? associatedtype Bar ^ /usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'FooB' struct FooBImpl<P>: FooB { ^
protocol Foo { associatedtype Bar } protocol FooA: Foo where Bar == String { } struct FooAImpl: FooA { // typealias Bar == String // Not require } protocol FooB: Foo where Bar == [Piyo] { associatedtype Piyo } struct FooBImpl<P>: FooB { typealias Piyo = P // typealias Bar = [Piyo] // Require }
Swift version 4.2-dev (LLVM d14a2b25f2, Clang c38020c511, Swift 22530b922f)
/usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'Foo' struct FooBImpl<P>: FooB { ^ /usercode/main.swift:2:20: note: protocol requires nested type 'Bar'; do you want to add it? associatedtype Bar ^ /usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'FooB' struct FooBImpl<P>: FooB { ^
Swift version 4.1 (swift-4.1-RELEASE)
/usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'Foo' struct FooBImpl<P>: FooB { ^ /usercode/main.swift:2:20: note: protocol requires nested type 'Bar'; do you want to add it? associatedtype Bar ^ /usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'FooB' struct FooBImpl<P>: FooB { ^
typealias Bar = [Piyo]
が省略できないのは、 typealias Piyo = P
が無いと推論できない事だから?typealias Bar == String
が省略できるのは、 : FooA
だけで推論できることだから? a = someValue[dynamicMember: "someMember"] someValue[dynamicMember: "someMember"] = a mutateParameter(&someValue[dynamicMember: "someMember"])
みたいなのは現状でもやろうと思えばできて、 a = someValue.someMember someValue.someMember = a mutateParameter(&someValue.someMember)
のように書けるようにするだけだと思う。$ ls Python.framework/Versions/ 2.3 2.5 2.6 2.7 Current
(edited)/System/Library/Frameworks/Python.framework
は触らずに別 Framework にした方が良さそうに思います。map
が Python 2 系では List を返していたのが 3 では iterable なオブジェクトになってるとか、吸収しきれなさそう。/Library/Frameworks
にインストールされました。/System/Library/Frameworks
以下はそのままのようですね。 $ ls /Library/Frameworks/Python.framework/Versions 3.6 $ ls /System/Library/Frameworks/Python.framework/Versions 2.3 2.5 2.6 2.7 Current
pyenv
の場合 $ PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.6.4 python-build: use openssl from homebrew python-build: use readline from homebrew Downloading Python-3.6.4.tar.xz... -> https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tar.xz Installing Python-3.6.4... python-build: use readline from homebrew Installed Python-3.6.4 to /Users/norio/.pyenv/versions/3.6.4 208.44s user 58.92s system 162% cpu 2:44.55 total $ swift `python-config --ldflags` -I `python-config --prefix` Welcome to Apple Swift version 4.1 (swiftlang-902.0.48 clang-902.0.39.1). Type :help for assistance. 1> import Python 2> func execPy(_ code: String) { 3. Py_Initialize() 4. PyRun_SimpleStringFlags(code, nil) 5. Py_Finalize() 6. } 7> execPy("import sys\nprint(sys.version)") 3.6.4 (default, Apr 24 2018, 13:35:20) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] 8>
(edited)ArraySlice
で任意の範囲のインデックスを持つインスタンスを生成する方法ってありませんか?たとえば↓みたいな感じで。 // こんなことがしたい let slice: ArraySlice<String> = ["a", "b", "c"].offset(100) print(slice[100]) // "a" print(slice[101]) // "b" print(slice[102]) // "c"
(edited)ArraySlice
をそういう目的で使うのが間違いな気がしてきました。負のインデックスもできないみたいだし。startIndex
の前に insert
することもできない。struct A { var strings: AnySequence<String> { return AnySequence { () -> AnyIterator<String> in var buffer = staticStrings ?? [] return AnyIterator { if let first = buffer.popFirst() { return first } return nil } } } } let staticStrings: [String]? = ["1", "2"]
(edited)main.swift:6:32: error: cannot use mutating member on immutable value: 'buffer' is immutable if let first = buffer.popFirst() { ^~~~~~
(edited)buffer
はimmutableなの?main.swift:6:39: error: '[String]' requires the types '[String]' and 'ArraySlice<String>' be equivalent to use 'popFirst' if let first = buffer.popFirst() { ^
struct A { var strings: AnySequence<String> { return AnySequence { () -> AnyIterator<String> in var buf: [String] = ["1", "2"] return AnyIterator { if let first = buf.popFirst() { return first } return nil } } } }
main.swift:6:36: error: '[String]' requires the types '[String]' and 'ArraySlice<String>' be equivalent to use 'popFirst' if let first = buf.popFirst() { ^
var buf: [String] = ["1", "2"] buf.popFirst()
main.swift:2:5: error: '[String]' requires the types '[String]' and 'ArraySlice<String>' be equivalent to use 'popFirst' buf.popFirst() ^
Playground execution failed: error: iOSGround.playground:13:1: error: cannot use mutating member on immutable value: 'buf' is immutable buf.popFirst() ^~~
extension Collection where SubSequence == Self { /// Removes and returns the first element of the collection. /// /// - Returns: The first element of the collection if the collection is /// not empty; otherwise, `nil`. /// /// - Complexity: O(1) @_inlineable public mutating func popFirst() -> Element? { // TODO: swift-3-indexing-model - review the following guard !isEmpty else { return nil } let element = first! self = self[index(after: startIndex)..<endIndex] return element } }
'[String]' requires the types '[String]' and 'ArraySlice<String>' be equivalent
struct A { var strings: AnySequence<String> { return AnySequence { () -> AnyIterator<String> in var buffer = ArraySlice(staticStrings ?? []) return AnyIterator { if let first = buffer.popFirst() { return first } return nil } } } } let staticStrings: [String]? = ["1", "2"]
(edited)var buf = ArraySlice([1,2,3]) print(buf) buf.popFirst() print(buf)
[1, 2, 3] [2, 3]
stderr:main.swift:3:5: warning: result of call to 'popFirst()' is unused buf.popFirst() ^ ~~
var a = [2, 3, 5] print(a.popFirst()!)
(edited)main.swift:2:7: error: cannot use mutating member on immutable value: 'a' is immutable print(a.popFirst()!) ^
(edited)ArraySlice
ならちゃんと popFirst
があるから使える。 @swift-4.1.3
var a: ArraySlice<Int> = [2, 3, 5] print(a.popFirst()!)
(edited)error: cannot use mutating member on immutable value: 'a' is immutable
なるメッセージが出ることになるのかは興味深いけど・・・。ArraySlice
でやるべきと思ってたんだけど、 (edited)ArraySlice
はインデックスを保存するからextension Collection { func indexed() -> [(Index, Element)] { return indices.map { ($0, self[$0]) } } } let a = [ 2, 3, 5, 7, 11, 13] var b = a[2...4] print(Array(b.indexed())) _ = b.popLast() b.append(11) print(Array(b.indexed())) _ = b.popFirst() b.insert(5, at: 3) print(Array(b.indexed()))
Swift version 4.1 (swift-4.1-RELEASE)
[(2, 5), (3, 7), (4, 11)] [(2, 5), (3, 7), (4, 11)] [(3, 5), (4, 7), (5, 11)]
[(2, 5), (3, 7), (4, 11)]
を取り戻す方法がない気がしてます。extension Collection { func indexed() -> [(Index, Element)] { return indices.map { ($0, self[$0]) } } } let a = ["a", "b", "c", "d", "e", "f", "g"] var b = a[2...4] print(Array(b.indexed())) _ = b.popLast() b.append("e") print(Array(b.indexed())) _ = b.popFirst() b.insert("c", at: 3) print(Array(b.indexed()))
(edited)Swift version 4.1 (swift-4.1-RELEASE)
(edited)[(2, "c"), (3, "d"), (4, "e")] [(2, "c"), (3, "d"), (4, "e")] [(3, "c"), (4, "d"), (5, "e")]
startIndex
を変える方法がない気がしてる。endIndex
は伸びるんだけど。startIndex
の前に挿入できないと全部ずらすことになっていて O(N) になってるかも。appendFirst
とか pushFirst
とか insertFirst
がない。func indexed<T>(_ slice: ArraySlice<T>) -> [(Int, T)] { return (0..<slice.count).map { (offset: Int) in (offset, slice[slice.startIndex + offset]) } } let a = ["a", "b", "c", "d", "e", "f", "g"] var b = a[2...4] print(indexed(b)) _ = b.popLast() b.append("h") print(indexed(b)) _ = b.popFirst() b.insert("i", at: 3) print(indexed(b))
Swift version 4.1 (swift-4.1-RELEASE)
[(0, "c"), (1, "d"), (2, "e")] [(0, "c"), (1, "d"), (2, "h")] [(0, "i"), (1, "d"), (2, "h")]
ArraySlice
の subscript
の仕様を使うとおもしろいことができて、var cachedFrames = ArraySlice<Image<RGBA<UInt8>>>
としてキャッシュしておけば、 (edited)cachedFrames[frameIndex]
で簡単にアクセスできる。if cachedFrames.count > maxNumberOfCachedFrames { cachedFrames.removeFirst() }
みたいにして簡単に古いキャッシュを消せるしif cachedFrames.indices.contains(frameIndex) { ... }
みたいにキャッシュが存在するときの処理も簡単に書ける。(cachedFrames.startIndex ..< cachedFrames.endIndex).contains(frameIndex)
にすれば O(1) ) (edited)Array
とは別物になるしArraySlice
として作ってる。class A { deinit { print("deinit") } } var x: ArraySlice<A> = [A(), A(), A()] print("A") x.removeFirst() print("B") x.removeFirst() print("C") x.removeFirst() print("D")
(edited)Swift version 4.1 (swift-4.1-RELEASE)
(edited)A B C D
class A { deinit { print("deinit") } } var x: ArraySlice<A> = [A(), A(), A()] print("A") x.removeFirst() x[2] = A() print("B") x.removeFirst() x[2] = A() print("C") x.removeFirst() print("D")
Swift version 4.1 (swift-4.1-RELEASE)
A deinit B deinit C D
removeFirst
されたやつらも解放されると思うんだけど、どうやって試すのがいいかな・・・。class A { deinit { print("deinit") } } var x = [A(), A(), A()] print("A") x.removeLast() print("B") x.removeLast() print("C") x.removeLast() print("D")
Swift version 4.1 (swift-4.1-RELEASE)
A deinit B deinit C deinit D
removeFirst
じゃ ArraySlice
はバッファの中身をクリアしないっぽいな。removeFirst
された要素もコピーされてそう。startIndex
から endIndex
の分だけコピーされてんじゃないかと思ってるんですが。class A { deinit { print("deinit") } } var x: ArraySlice<A> = [A(), A(), A()] do { let y = x print("A") x.removeFirst() x[2] = A() print("B") x.removeFirst() x[2] = A() print("C") x.removeFirst() print("D") } print("E")
Swift version 4.1 (swift-4.1-RELEASE)
A B deinit C D deinit deinit E
/usercode/main.swift:7:9: warning: initialization of immutable value 'y' was never used; consider replacing with assignment to '_' or removing it let y = x ~~~~^ _
class A : CustomStringConvertible{ let val: Int init(_ val: Int) { self.val = val } deinit { print("deinit: \(self)") } var description: String { return "A(\(val))" } } var x: ArraySlice<A> = [A(1), A(2), A(3)] do { let y = x print("A") x.removeFirst() x[2] = A(4) print("B") x.removeFirst() x[2] = A(5) print("C") x.removeFirst() print("D") } print("E")
Swift version 4.1 (swift-4.1-RELEASE)
A B deinit: A(4) C D deinit: A(1) deinit: A(3) E
/usercode/main.swift:12:7: warning: initialization of immutable value 'y' was never used; consider replacing with assignment to '_' or removing it let y = x ~~~~^ _
A(1)
が deinit されるのか?deinit
されてるの変だ。なんで 1 も解放されてるんだろう・・・。 (edited)removeFirst
された要素はCoWでコピーされてない様ですね。 x[2] = A(4)
でCoW発生か。A(2)
も解放されないといけない気が。ちょっと紙に書いてみないと頭の中で CoW 含む参照関係が追えなくなってきたので間違ってるかもですが・・・。A(2)
もされるべきっぽいし、妙に中途半端。class A : CustomStringConvertible { let value: Int init(_ value: Int) { self.value = value } var description: String { return "A(\(value))" } deinit { print("deinit \(self)") } } do { var x: ArraySlice<A> = [] for i in 0..<20 { x.append(A(i)) } while let a = x.popFirst() { print("pop \(a)") } print("-----") }
Swift version 4.1 (swift-4.1-RELEASE)
pop A(0) pop A(1) pop A(2) pop A(3) pop A(4) pop A(5) pop A(6) pop A(7) pop A(8) pop A(9) pop A(10) pop A(11) pop A(12) pop A(13) pop A(14) pop A(15) pop A(16) pop A(17) pop A(18) pop A(19) ----- deinit A(0) deinit A(1) deinit A(2) deinit A(3) deinit A(4) deinit A(5) deinit A(6) deinit A(7) deinit A(8) deinit A(9) deinit A(10) deinit A(11) deinit A(12) deinit A(13) deinit A(14) deinit A(15) deinit A(16) deinit A(17) deinit A(18) deinit A(19)
class A : CustomStringConvertible { let value: Int init(_ value: Int) { self.value = value } var description: String { return "A(\(value))" } deinit { print("deinit \(self)") } } var i = 0 do { var x: ArraySlice<A> = [] do { for _ in 0..<20 { x.append(A(i)) i += 1 } while let a = x.popFirst() { print("pop \(a)") if !x.isEmpty { x[x.endIndex - 1] = A(i) i += 1 } } } print("-----") }
Swift version 4.1 (swift-4.1-RELEASE)
pop A(0) deinit A(19) pop A(1) deinit A(20) pop A(2) deinit A(21) pop A(3) deinit A(22) pop A(4) deinit A(23) pop A(5) deinit A(24) pop A(6) deinit A(25) pop A(7) deinit A(26) pop A(8) deinit A(27) pop A(9) deinit A(28) pop A(10) deinit A(29) pop A(11) deinit A(30) pop A(12) deinit A(31) pop A(13) deinit A(32) pop A(14) deinit A(33) pop A(15) deinit A(34) pop A(16) deinit A(35) pop A(17) deinit A(36) pop A(18) deinit A(37) pop A(38) ----- deinit A(0) deinit A(1) deinit A(2) deinit A(3) deinit A(4) deinit A(5) deinit A(6) deinit A(7) deinit A(8) deinit A(9) deinit A(10) deinit A(11) deinit A(12) deinit A(13) deinit A(14) deinit A(15) deinit A(16) deinit A(17) deinit A(18) deinit A(38)
while let
ちゃんと使ったの初めてかも。A(2)
もコピーされてるから、解放されなくて正解な気が。 @swift-4.1.3
class A : CustomStringConvertible{ let val: Int init(_ val: Int) { self.val = val } deinit { print("deinit: \(self)") } var description: String { return "A(\(val))" } } var x: ArraySlice<A> = [A(1), A(2), A(3)] do { let y = x // yは[A(1), A(2), A(3)]を保持 print("A") x.removeFirst() // xは[A(1), A(2), A(3)]を保持して[A(2), A(3)]を露出 x[2] = A(4) // CoW発生、xは[A(2), A(4)]を保持 print("B") x.removeFirst() // xは[A(2), A(4)]を保持して[A(4)]を露出 x[2] = A(5) // xは[A(2), A(5)]を保持して[A(5)]を露出、A(4)解放 print("C") x.removeFirst() // xは[A(2), A(5)]を保持して[]を露出 print("D") } // y解放、xに保持されていないA(1),A(3)解放 print("E")
(edited)A B deinit: A(4) C D deinit: A(1) deinit: A(3) E
stderr:main.swift:12:7: warning: initialization of immutable value 'y' was never used; consider replacing with assignment to '_' or removing it let y = x ~~~~^ _
A B deinit: A(4) C D deinit: A(1) deinit: A(3) E
stderr:main.swift:12:9: warning: initialization of immutable value 'y' was never used; consider replacing with assignment to '_' or removing it let y = x // yは[A(1), A(2), A(3)]を保持
x[2] = A(5)
でコピーが発生してなかったんですねDictionary
にある Key
のエントリーが含まれるかどうかを調べる一番いい方法って dictionary.keys.contains(key)
でいいんでしたっけ? O(1)
になることのドキュメントが見つけられず・・・。dictionary[key] != nil
は不細工なので。index(forKey:)
が目当てのメソッドじゃないしょうか?Keys
実装目的の最初に挙げられているくらいなので、O(1)
だと思うんですが、実装追えてないです。index(forKey:)
についての話です。dictionary[key] != nil
は一番わかりやすいと思いますね。Dictionary.Keys.contains(_:)
は最終これが呼ばれるので、O(1)
ですね。(Nativeの場合) (edited)index(forKey:)
と同じってこと?_variantBuffer.index(forKey:)
は結局 Dictionary.index(forKey:)
と同じですね。keys
は struct 1つ作るだけだからそんなにオーバーヘッドなさそう https://github.com/apple/swift/blob/82226642c2459c0f5d2054fe1f6545b57efafffb/stdlib/public/core/Dictionary.swift#L1169-L1173index(for:)
にしても nil
と比較することになると思うんで、ダイレクトにキーが存在するかを調べているということをコード上で表し、かつオーバーヘッドの少ない(最低でも O(1) の)方法はなんだろうということでした。dictionary.keys.contains(key)
使うことにします。_customContainsEquatableElement
が mayBeGet(key) != nil
を直接使わず index(forKey: key) != nil
なのは何故なんだろう。 (edited)NSDictionary
をラップしてたら O(1) じゃなくなってる?? (edited)NSDictionary
をラップしてないときは、 _variantBuffer
が NativeDictionaryBuffer
で、それだと、 index(forKey:)
も maybeGet
も _find
を呼ぶことになるので同じですよね。(値を取得しない分、 index(forKey:)
の方が得)NSDictionary
をラップしているときは、 _CocoaDictionaryBuffer
なので、 index(forKey:)
が中で mayBeGet
を呼ぶようになっているので、結局同じっぽいような。index(forKey:)
のほうが特なのですね。納得。 Cocoa のほうだと、maybeGet
で見つかったときに、比較的重い処理 https://github.com/apple/swift/blob/82226642c2459c0f5d2054fe1f6545b57efafffb/stdlib/public/core/Dictionary.swift#L3026-L3036 が入るので、何故なんだろうと思ったのです。index(for:)
を呼ばずに https://github.com/apple/swift/blob/82226642c2459c0f5d2054fe1f6545b57efafffb/stdlib/public/core/Dictionary.swift#L3397-L3420 みたいな分岐をして .cocoa
のときに maybeGet
なり何なりを使って検査するようにすれば NSDictionary
をラップしてても dictionary.keys.contains(key)
を O(1) にできるような気がするんですが、そんなことはないですか?_VariantDictionaryBuffer
にそれ用のメソッドを作って、_customContainsEquatableElement
から呼ぶべきだと思いますが、はいキーの有無だけであれば O(1)
にできると思います。 (edited)_VariantDictionaryBuffer
側に containsKey
みたいなメソッドを作ってそれを呼ぶのがいいんでしょうか。 (edited)NSDictionary
をラップしてても O(1) になってる? @swiftbot
import Foundation let native1M = [String: String](uniqueKeysWithValues: (1...1_000_000).map { ("\($0)", "\($0)") }) let cocoa1M = (native1M as NSDictionary) as! [String: String] let native2M = [String: String](uniqueKeysWithValues: (1...2_000_000).map { ("\($0)", "\($0)") }) let cocoa2M = (native2M as NSDictionary) as! [String: String] let n = 10 func measure(_ operation: () -> ()) { var sum: TimeInterval = 0 for i in 1...n { let start = Date.timeIntervalSinceReferenceDate operation() let end = Date.timeIntervalSinceReferenceDate let interval = end - start print("\(i): \(interval)") sum += interval } print("avg: \(sum / TimeInterval(n))") } let dictionaries: [(String, [String: String])] = [ ("Native 1M", native1M), ("Cocoa 1M", cocoa1M), ("Native 2M", native2M), ("Cocoa 2M", cocoa2M), ] for (name, dictionary) in dictionaries { print(name) measure { for i in 1...100 { _ = dictionary.keys.contains("\(i)") } } }
Swift version 4.1 (swift-4.1-RELEASE)
/usercode/main.swift:4:16: error: cannot convert value of type '[String : String]' to type 'NSDictionary' in coercion let cocoa1M = (native1M as NSDictionary) as! [String: String] ^~~~~~~~ /usercode/main.swift:6:16: error: cannot convert value of type '[String : String]' to type 'NSDictionary' in coercion let cocoa2M = (native2M as NSDictionary) as! [String: String] ^~~~~~~~
as! [String: String]
の時点でどうもNativeに変換されているっぽいですね。import Foundation let native1M = [NSString: NSString](uniqueKeysWithValues: (1...1_000_000).map { ("\($0)" as NSString, "\($0)" as NSString) }) let cocoa1M = NSDictionary(dictionary: native1M) as! [NSString: NSString] let native2M = [NSString: NSString](uniqueKeysWithValues: (1...2_000_000).map { ("\($0)" as NSString, "\($0)" as NSString) }) let cocoa2M = NSDictionary(dictionary: native2M) as! [NSString: NSString] let n = 10 func measure(_ operation: () -> ()) { var sum: TimeInterval = 0 for i in 1...n { let start = Date.timeIntervalSinceReferenceDate operation() let end = Date.timeIntervalSinceReferenceDate let interval = end - start print("\(i): \(interval)") sum += interval } print("avg: \(sum / TimeInterval(n))") } let dictionaries: [(String, [NSString: NSString])] = [ ("Native 1M", native1M), ("Cocoa 1M", cocoa1M), ("Native 2M", native2M), ("Cocoa 2M", cocoa2M), ] for (name, dictionary) in dictionaries { print(name) measure { for i in 1...100 { _ = dictionary.keys.contains("\(i)" as NSString) } } }
これで確認できました。 (edited)Native 1M 1: 0.00014901161193847656 2: 9.000301361083984e-05 3: 8.797645568847656e-05 4: 8.702278137207031e-05 5: 8.809566497802734e-05 6: 8.702278137207031e-05 7: 8.797645568847656e-05 8: 8.702278137207031e-05 9: 8.702278137207031e-05 10: 8.809566497802734e-05 avg: 9.392499923706054e-05 Cocoa 1M 1: 1.5260519981384277 2: 1.4988809823989868 3: 1.4609719514846802 4: 1.4877859354019165 5: 1.4853370189666748 6: 1.4784049987792969 7: 1.4788520336151123 8: 1.4836270809173584 9: 1.5162140130996704 10: 1.471932053565979 avg: 1.4888058066368104 Native 2M 1: 9.799003601074219e-05 2: 7.700920104980469e-05 3: 7.605552673339844e-05 4: 7.605552673339844e-05 5: 7.700920104980469e-05 6: 7.700920104980469e-05 7: 7.593631744384766e-05 8: 7.700920104980469e-05 9: 7.700920104980469e-05 10: 9.09566879272461e-05 avg: 8.020401000976562e-05 Cocoa 2M 1: 2.558851957321167 2: 2.5838489532470703 3: 2.5781790018081665 4: 2.586503028869629 5: 2.5640339851379395 6: 2.611467957496643 7: 2.587472081184387 8: 2.5809329748153687 9: 2.5621449947357178 10: 2.583261013031006 avg: 2.5796695947647095
Native 1M 1: 0.0001779794692993164 2: 9.298324584960938e-05 3: 9.000301361083984e-05 4: 8.90493392944336e-05 5: 8.893013000488281e-05 6: 8.90493392944336e-05 7: 0.00011992454528808594 8: 0.0001380443572998047 9: 9.799003601074219e-05 10: 9.59634780883789e-05 avg: 0.00010799169540405273 Cocoa 1M 1: 0.00013005733489990234 2: 9.799003601074219e-05 3: 9.799003601074219e-05 4: 9.799003601074219e-05 5: 9.799003601074219e-05 6: 9.799003601074219e-05 7: 9.799003601074219e-05 8: 9.906291961669922e-05 9: 9.799003601074219e-05 10: 9.799003601074219e-05 avg: 0.0001013040542602539 Native 2M 1: 0.00010597705841064453 2: 9.000301361083984e-05 3: 9.000301361083984e-05 4: 8.893013000488281e-05 5: 8.90493392944336e-05 6: 8.90493392944336e-05 7: 8.893013000488281e-05 8: 9.000301361083984e-05 9: 9.000301361083984e-05 10: 8.90493392944336e-05 avg: 9.109973907470704e-05 Cocoa 2M 1: 0.00010001659393310547 2: 9.799003601074219e-05 3: 9.799003601074219e-05 4: 9.906291961669922e-05 5: 9.799003601074219e-05 6: 9.799003601074219e-05 7: 9.799003601074219e-05 8: 9.799003601074219e-05 9: 9.894371032714844e-05 10: 9.799003601074219e-05 avg: 9.839534759521484e-05
dictionary.keys.contains(key)
ですが、↑の PR がマージされたので NSDictionary
をラップしてる場合でも O(1) になりましたNSDictionary
をラップしているときに)それを使わないようにして O(n) を回避しているので、 index(for:)
はそのままのはずですね。 (edited)index(for:)
からそれが呼ばれてるのだと思ってました。_variantBuffer
の index(for:)
を呼ぶ代わりに↓を呼ぶようになっています。 internal func containsKey(_ key: Key) -> Bool { if _fastPath(guaranteedNative) { return asNative.index(forKey: key) != nil } switch self { case .native: return asNative.index(forKey: key) != nil #if _runtime(_ObjC) case .cocoa(let cocoaBuffer): return SelfType.maybeGetFromCocoaBuffer(cocoaBuffer, forKey: key) != nil #endif } }
index(for:)
が呼ばれますが、 NSDictionary
をラップしているときは maybeGetFromCocoaBuffer
になります。 (edited)@compilerEvaluable
いいね。今はllvmが特定のc関数についてやってるだけなのかな? https://forums.swift.org/t/pitch-compile-time-constant-expressions-for-swift/12879Authors: Marc Rasi, Chris Lattner
から。Types This model supports: builtin integer type tuples of compiler-representable types user-defined structs whose fields are compiler-representable types, which are defined in the current module or which otherwise have a known/fixed layout. metatypes addresses of stack objects
Random
が struct
っぽくない挙動をする・・・。 var a = Random.default var b = a print(a.next()) print(b.next())
2190522142258413190 6214093798825593358
seed
を持つようにしただけだと、 Random.default.next()
と呼び出さない限り毎回初期化されちゃうから、Random.default
の度に色々やんないといけなくなっちゃう。struct
なのがミスリーディングな気も。 class
の方がよい?Generators as reference vs value types I think we should require Generators to be classes. It simplifies calling the functions and I don’t see any clear advantage of using struct Generators.
func foo<S : Sequence>(_ sequence: S) { ... } let a: Any = ... // a が Sequence か調べて Sequence のときは foo に渡すにはどうすればいい?
Any
に限らず Foo
の中から BarProtocol
を満たすものだけを渡したいってのができない。as
して渡すだけだから、型として表に見えなくていい。if let seq = a as Sequence { // でもこれができちゃうよ print(type(of: seq)) foo(seq) }
(edited)<S : Sequence>
に渡せるようにするだけの限定的なやつAny<Sequence where .Iterator.Element == String>
この表記、 型パラメータを一つ取るジェネリック型Anyがあるように見えてしまう気がしてきたAny<FooProtocol>
にしようという話があって、その時代に書かれたものだから。protocol<A, B>
を Any<A, B>
にしようってだけだったか。 https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#renaming-protocol-to-any-Any<A, B>
が A & B
になった以上、 generilized existential の構文も変更しないといけない気がする。それ以前に、あまり導入する気もなさそうな・・・。let a: Sequence where Element == Int & Foo
みたいになりそうだ。struct Foo { // method func a() -> Int { return 2 } // computed property var b: Int { return 3 } // stored property let c: Int = 5 } let foo = Foo() print(Foo.a(foo)()) // OK print(Foo.b(foo)) // NG print(Foo.c(foo)) // NG
print(foo[keyPath: \Foo.b])
func get<T, U>(_ keyPath: KeyPath<T, U>) -> (T) -> (U) { return { value in return value[keyPath: keyPath] } } struct A { let a: Int } let x = get(\A.a) print(x(A(a: 3)))
(edited)KeyPath
、ちょっと合ってるか自信ないですが、↓になってほしくないですか? class Animal { var a: Int = 42 } class Cat : Animal {} let k1: WritableKeyPath<Animal, Int> = \.a let k2: KeyPath<Animal, Int> = k1 // OK let k3: PartialKeyPath<Animal> = k2 // OK let k4: AnyKeyPath = k3 // OK let k1b: WritableKeyPath<Animal, Any> = k1 // NG: これはできなくてよい let k2b: KeyPath<Animal, Any> = k2 // NG: これはできてほしい let k1c: WritableKeyPath<Cat, Int> = k1 // NG: これはできてほしい let k2c: KeyPath<Cat, Int> = k2 // NG: これはできてほしい let k3c: PartialKeyPath<Cat> = k3 // NG: これはできてほしい
class WritableKeyPath<in Root, Value> : ... { ... } class KeyPath<in Root, out Value> : ... { ... } class PartialKeyPath<in Root> : ... { ... } class AnyKeyPath { ... }
(edited)KeyPath
に WritableKeyPath
を結合しても KeyPath
であってほしい気が。 https://developer.apple.com/documentation/swift/keypath/2945102-appendingstruct Foo { var a: Int } struct Bar { var foo: Foo } let k1: KeyPath<Bar, Foo> = \.foo let k2: WritableKeyPath<Foo, Int> = \.a let k: WritableKeyPath<Bar, Int> = k1.appending(path: k2)
struct Foo { var a: Int } struct Bar { var foo: Foo } let k1: KeyPath<Bar, Foo> = \.foo let k2: WritableKeyPath<Foo, Int> = \.a let k: WritableKeyPath<Bar, Int> = k1.appending(path: k2)
swift-4.1.1-RELEASE
/usercode/main.swift:11:39: error: cannot convert value of type 'KeyPath<Bar, Int>' to specified type 'WritableKeyPath<Bar, Int>' let k: WritableKeyPath<Bar, Int> = k1.appending(path: k2) ~~~^~~~~~~~~~~~~~~~~~~ as! WritableKeyPath<Bar, Int>
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } let k1: KeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a let k: PartialKeyPath<Bar> = k1.appending(path: k2)
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } let k1: KeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a let k: PartialKeyPath<Bar> = k1.appending(path: k2)
swift-4.1.1-RELEASE
/usercode/main.swift:11:49: error: cannot convert value of type 'PartialKeyPath<Foo>' to expected argument type 'ReferenceWritableKeyPath<_, _>' let k: PartialKeyPath<Bar> = k1.appending(path: k2) ^~
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } let k1: KeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a // let k: PartialKeyPath<Bar> = k1.appending(path: k2) let bar = Bar() // let a: Int = bar[keyPath: k1][keyPath: k2] let a: Any = bar[keyPath: k1][keyPath: k2] print(a) // OK: 42
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } let k1: KeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a // let k: PartialKeyPath<Bar> = k1.appending(path: k2) let bar = Bar() // let a: Int = bar[keyPath: k1][keyPath: k2] let a: Any = bar[keyPath: k1][keyPath: k2] print(a) // OK: 42
swift-4.1.1-RELEASE
42
KeyPath
に WritableKeyPath
を append して WritableKeyPath
生成するメソッドが(ドキュメント上?)できてしまってるの↓のせいか。 where
以下がコメントアウト。 extension _AppendKeyPath /* where Self == WritableKeyPath<T,U> */ {
https://github.com/apple/swift/blob/master/stdlib/public/core/KeyPath.swift#L1812(Writable)KeyPath
に PartialKeyPath
を append して PartialKeyPath
を得るの、とりあえず無理やりで extension
書けた。 @swiftbot
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } extension KeyPath { func appending(path: PartialKeyPath<Value>) -> PartialKeyPath<Root> { return (self as AnyKeyPath).appending(path: path as AnyKeyPath)! as! PartialKeyPath<Root> } } let k1: WritableKeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a let k: PartialKeyPath<Bar> = k1.appending(path: k2) let bar = Bar() let a: Any = bar[keyPath: k] print(a)
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } extension KeyPath { func appending(path: PartialKeyPath<Value>) -> PartialKeyPath<Root> { return (self as AnyKeyPath).appending(path: path as AnyKeyPath)! as! PartialKeyPath<Root> } } let k1: WritableKeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a let k: PartialKeyPath<Bar> = k1.appending(path: k2) let bar = Bar() let a: Any = bar[keyPath: k] print(a)
swift-4.1.1-RELEASE
42
didSet
の中で didSet
は呼ばれないけど・・・ struct Foo { var count: Int = 0 var a: Int = 42 { didSet { print("Foo.a didSet: \(count)") count += 1 } } } struct Bar { var count: Int = 0 var foo: Foo = Foo() { didSet { print("Bar.foo didSet: \(count), \(foo.count)") count += 1 foo.count += 100 } } } var bar = Bar() bar.foo.a = -1
(edited)struct Foo { var count: Int = 0 var a: Int = 42 { didSet { print("Foo.a didSet: \(count)") count += 1 } } } struct Bar { var count: Int = 0 var foo: Foo = Foo() { didSet { print("Bar.foo didSet: \(count), \(foo.count)") count += 1 foo.count += 100 } } } var bar = Bar() bar.foo.a = -1
swift-4.1.1-RELEASE
Foo.a didSet: 0 Bar.foo didSet: 0, 1
struct Foo { var count: Int = 0 var a: Int = 42 { didSet { print("Foo.a didSet: \(count)") count += 1 } } } struct Bar { var count: Int = 0 var foo: Foo = Foo() { didSet { print("Bar.foo didSet: \(count), \(foo.count)") count += 1 // foo.count += 100 self[keyPath: \Bar.foo].count += 100 } } } var bar = Bar() bar.foo.a = -1
(edited)struct Foo { var count: Int = 0 var a: Int = 42 { didSet { print("Foo.a didSet: \(count)") count += 1 } } } struct Bar { var count: Int = 0 var foo: Foo = Foo() { didSet { print("Bar.foo didSet: \(count), \(foo.count)") count += 1 // foo.count += 100 self[keyPath: \Bar.foo].count += 100 } } } var bar = Bar() bar.foo.a = -1
swift-4.1.1-RELEASE
Foo.a didSet: 0 Bar.foo didSet: 0, 1 Bar.foo didSet: 1, 101 Bar.foo didSet: 2, 201 Bar.foo didSet: 3, 301 Bar.foo didSet: 4, 401 Bar.foo didSet: 5, 501 Bar.foo didSet: 6, 601 Bar.foo didSet: 7, 701 Bar.foo didSet: 8, 801 Bar.foo didSet: 9, 901 Bar.foo didSet: 10, 1001 Bar.foo didSet: 11, 1101 Bar.foo didSet: 12, 1201 Bar.foo didSet: 13, 1301 Bar.foo didSet: 14, 1401 Bar.foo didSet: 15, 1501 Bar.foo didSet: 16, 1601 Bar.foo didSet: 17, 1701 Bar.foo didSet: 18, 1801 Bar.foo didSet: 19, 1901 Bar.foo didSet: 20, 2001 Bar.foo didSet: 21, 2101 Bar.foo didSet: 22, 2201 Bar.foo didSet: 23, 2301 Bar.foo didSet: 24, 2401 Bar.foo didSet: 25, 2501 Bar.foo didSet: 26, 2601 Bar.foo didSet: 27, 2701 Bar.foo didSet: 28, 2801 Bar.foo didSet: 29, 2901 Bar.foo didSet: 30, 3001 Bar.foo didSet: 31, 3101 Bar.foo didSet: 32, 3201 Bar.foo didSet: 33, 3301 Bar.foo didSet: 34, 3401 Bar.foo didSet: 35, 3501 Bar.foo didSet: 36, 3601 Bar.foo didSet: 37, 3701 Bar.foo didSet: 38, 3801 Bar.foo didSet: 39, 3901 ...
/usr/bin/swift[0x3f24d64] /usr/bin/swift[0x3f250a6] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7fb6ec227390] /usr/lib/swift/linux/libswiftCore.so(swift_conformsToProtocol+0x3e)[0x7fb6e840373e] /usr/lib/swift/linux/libswiftCore.so(+0x39c05f)[0x7fb6e83f505f] /usr/lib/swift/linux/libswiftCore.so(+0x39b82d)[0x7fb6e83f482d] /usr/lib/swift/linux/libswiftCore.so(+0x2e518a)[0x7fb6e833e18a] Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret /usercode/main.swift -disable-objc-interop -I /Libraries/All/.build/release -module-name main -lAll Segmentation fault (core dumped)
struct Stone { var name: String = "" { didSet { let newValue = name if recCount == 5 { return } recCount += 1 print("name.didSet begin: \(oldValue) -> \(newValue)") update() print("name.didSet end: \(oldValue) -> \(newValue)") } } var recCount = 0 mutating func update() { name = "jiro" } } var b = Stone() b.name = "taro"
struct Stone { var name: String = "" { didSet { let newValue = name if recCount == 5 { return } recCount += 1 print("name.didSet begin: \(oldValue) -> \(newValue)") update() print("name.didSet end: \(oldValue) -> \(newValue)") } } var recCount = 0 mutating func update() { name = "jiro" } } var b = Stone() b.name = "taro"
swift-4.1.1-RELEASE
name.didSet begin: -> taro name.didSet begin: taro -> jiro name.didSet begin: jiro -> jiro name.didSet begin: jiro -> jiro name.didSet begin: jiro -> jiro name.didSet end: jiro -> jiro name.didSet end: jiro -> jiro name.didSet end: jiro -> jiro name.didSet end: taro -> jiro name.didSet end: -> taro
name
の mutating func
を Stone
の didSet
から呼び出した場合の話だった。それだと直接編集するのと同じだから再帰しなくて当然か。struct Stone { var name: String = "" { didSet { let newValue = name if recCount == 5 { return } recCount += 1 print("name.didSet begin: \(oldValue) -> \(newValue)") name.append("x") print("name.didSet end: \(oldValue) -> \(newValue)") } } var recCount = 0 } var b = Stone() b.name = "taro"
struct Stone { var name: String = "" { didSet { let newValue = name if recCount == 5 { return } recCount += 1 print("name.didSet begin: \(oldValue) -> \(newValue)") name.append("x") print("name.didSet end: \(oldValue) -> \(newValue)") } } var recCount = 0 } var b = Stone() b.name = "taro"
swift-4.1.1-RELEASE
name.didSet begin: -> taro name.didSet end: -> taro
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar[keyPath: \Bar.foo?.a] = -1 // ⛔ print(bar.foo!.a)
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar[keyPath: \Bar.foo?.a] = -1 // ⛔ print(bar.foo!.a)
swift-4.1.1-RELEASE
/usercode/main.swift:10:27: error: cannot assign to immutable expression of type 'Int?' bar[keyPath: \Bar.foo?.a] = -1 // ⛔
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar.foo?.a = -1 // ✅ print(bar.foo!.a)
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar.foo?.a = -1 // ✅ print(bar.foo!.a)
swift-4.1.1-RELEASE
-1
Joe Groff added a comment - 7 Sep 2017 2:49 AM This is by design. You can't write back through optional chains, since there's nowhere to write to if the element is nil.
extension Optional { // Produce the `default` value if `self` is nil. subscript(default value: Wrapped) -> Wrapped { get { return self ?? value } set { self = newValue } } // Act like optional chaining on read, while allowing "writes" that drop // the value on the floor if `self` is nil. subscript<T>(droppingWritesOnNil path: WritableKeyPath<Wrapped, T>) -> T? { get { return self?[keyPath: path] } set { if let newValue = newValue { self?[keyPath: path] = newValue } } } }
KeyPath
で記述できました。 @swiftbot
extension Optional { // Produce the `default` value if `self` is nil. subscript(default value: Wrapped) -> Wrapped { get { return self ?? value } set { self = newValue } } // Act like optional chaining on read, while allowing "writes" that drop // the value on the floor if `self` is nil. subscript<T>(droppingWritesOnNil path: WritableKeyPath<Wrapped, T>) -> T? { get { return self?[keyPath: path] } set { if let newValue = newValue { self?[keyPath: path] = newValue } } } } struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar[keyPath: \Bar.foo[droppingWritesOnNil: \.a]] = -1 print(bar.foo!.a) bar.foo = nil bar[keyPath: \Bar.foo[droppingWritesOnNil: \.a]] = 999 print(String(describing: bar.foo))
(edited)extension Optional { // Produce the `default` value if `self` is nil. subscript(default value: Wrapped) -> Wrapped { get { return self ?? value } set { self = newValue } } // Act like optional chaining on read, while allowing "writes" that drop // the value on the floor if `self` is nil. subscript<T>(droppingWritesOnNil path: WritableKeyPath<Wrapped, T>) -> T? { get { return self?[keyPath: path] } set { if let newValue = newValue { self?[keyPath: path] = newValue } } } } struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar[keyPath: \Bar.foo[droppingWritesOnNil: \.a]] = -1 print(bar.foo!.a) bar.foo = nil bar[keyPath: \Bar.foo[droppingWritesOnNil: \.a]] = 999 print(String(describing: bar.foo))
swift-4.1.1-RELEASE
-1 nil
weak
でイミュータビリティが壊れた。 @swiftbot
struct Weak<Value : AnyObject> { weak var value: Value? { didSet { print("Weak.value didSet") } } init(_ value: Value) { self.value = value } } extension Weak : Equatable where Value : Equatable { static func ==(lhs: Weak<Value>, rhs: Weak<Value>) -> Bool { return lhs.value == rhs.value } } extension Weak : Hashable where Value : Hashable { var hashValue: Int { return value?.hashValue ?? 0 } } class Foo : Hashable { static func ==(lhs: Foo, rhs: Foo) -> Bool { return lhs === rhs } var hashValue: Int { return ObjectIdentifier(self).hashValue } } var dict: [Weak<Foo>: Int] = [:] var weak1: Weak<Foo>! var weak2: Weak<Foo>! do { let foo1 = Foo() weak1 = Weak(foo1) dict[weak1] = 111 print("weak1: \(weak1)") let foo2 = Foo() weak2 = Weak(foo2) dict[weak2] = 222 print("weak2: \(weak2)") for (key, value) in dict { print("\(key) -> \(value)") } print("dict[weak1]: \(String(describing: dict[weak1]))") print("dict[weak2]: \(String(describing: dict[weak2]))") } print("---") print("weak1: \(weak1)") print("weak2: \(weak2)") for (key, value) in dict { print("\(key) -> \(value)") } print("dict[weak1]: \(String(describing: dict[weak1]))") print("dict[weak2]: \(String(describing: dict[weak2]))")
(edited)struct Weak<Value : AnyObject> { weak var value: Value? { didSet { print("Weak.value didSet") } } init(_ value: Value) { self.value = value } } extension Weak : Equatable where Value : Equatable { static func ==(lhs: Weak<Value>, rhs: Weak<Value>) -> Bool { return lhs.value == rhs.value } } extension Weak : Hashable where Value : Hashable { var hashValue: Int { return value?.hashValue ?? 0 } } class Foo : Hashable { static func ==(lhs: Foo, rhs: Foo) -> Bool { return lhs === rhs } var hashValue: Int { return ObjectIdentifier(self).hashValue } } var dict: [Weak<Foo>: Int] = [:] var weak1: Weak<Foo>! var weak2: Weak<Foo>! do { let foo1 = Foo() weak1 = Weak(foo1) dict[weak1] = 111 print("weak1: \(weak1)") let foo2 = Foo() weak2 = Weak(foo2) dict[weak2] = 222 print("weak2: \(weak2)") for (key, value) in dict { print("\(key) -> \(value)") } print("dict[weak1]: \(String(describing: dict[weak1]))") print("dict[weak2]: \(String(describing: dict[weak2]))") } print("---") print("weak1: \(weak1)") print("weak2: \(weak2)") for (key, value) in dict { print("\(key) -> \(value)") } print("dict[weak1]: \(String(describing: dict[weak1]))") print("dict[weak2]: \(String(describing: dict[weak2]))")
swift-4.1.1-RELEASE
weak1: Optional(main.Weak<main.Foo>(value: Optional(main.Foo))) weak2: Optional(main.Weak<main.Foo>(value: Optional(main.Foo))) Weak<Foo>(value: Optional(main.Foo)) -> 222 Weak<Foo>(value: Optional(main.Foo)) -> 111 dict[weak1]: Optional(111) dict[weak2]: Optional(222) --- weak1: Optional(main.Weak<main.Foo>(value: nil)) weak2: Optional(main.Weak<main.Foo>(value: nil)) Weak<Foo>(value: nil) -> 222 Weak<Foo>(value: nil) -> 111 dict[weak1]: nil dict[weak2]: nil
struct
は weak
プロパティを持てないのがいいんじゃないかと思います。weak
は GC であっても必要になるんじゃないでしょうか?KeyPath
のパターンマッチしたくないですか?今だと KeyPath
から構成要素を取り出す方法がないので。 struct Foo { var values: [Int] = [2, 3, 5] } let foo = Foo() let path = \Foo.values[0] print(foo[keyPath: path]) switch path { case \Foo.values[let i]: print(i) default: print("default") }
import Foundation func darwinStyleFoo(_ size: Int = 32) { print("darwin style foo") } func unknownStyleFoo(_ size: Int = 32) { print("unknown style foo") } #if _runtime(_ObjC) let foo = darwinStyleFoo #else let foo = unknownStyleFoo #endif foo(32) //OK // foo() // NG
@_silgen(…)
みたいな裏技は無いかなあ。import Foundation func darwinStyleFoo(_ size: Int) { print("darwin style foo") } func unknownStyleFoo(_ size: Int) { print("unknown style foo") } #if _runtime(_ObjC) let _foo = darwinStyleFoo #else let _foo = unknownStyleFoo #endif func foo(_ size: Int = 32) { _foo(size) }
こういうスタイルはどうですか?darwinStyleFoo
と unknownStyleFoo
は明示的にどちらも呼べるようにしたいfoo
を呼ぶとプラットフォームによって適切な方が呼ばれるようにしたい(その場合でもデフォルト引数を設定したい)foo
を呼んだ場合でもスタックトレースは darwinStyleFoo
等と同じにしたいdarwinStyleFoo
等のコードを foo
の中にも展開するとかでしょうか・・・。[StackTraceElement]
がとれて間の一つを取り除けたとしても、 foo
を呼び出しているのにその次が darwinStyleFoo
になって整合性が崩れそう。class X { static let defaultArgument = 1 func foo(_ bar: Int = defaultArgument) { ... } }
こういうのいけるはずdefault2
でGeneric parameter 'T' could not be inferred
となる理由が分からない。 @swift-4.2.4
import Foundation infix operator ∘ : CompositionPrecedence precedencegroup CompositionPrecedence { associativity: left higherThan: TernaryPrecedence } public struct Converter<T, U> { public typealias Handler = (T) -> U let handler: Handler public init(_ handler: @escaping Handler) { self.handler = handler } public func compose<A>(_ other: Converter<A, T>) -> Converter<A, U> { return .init { self.handler(other.handler($0)) } } public static func ∘ <A, B, C>(_ lhs: Converter<B, C>, _ rhs: Converter<A, B>) -> Converter<A, C> { return .init { lhs.handler(rhs.handler($0)) } } } public typealias Symbol = (String, String) public typealias Demangler = Converter<Symbol, Symbol> public typealias SymbolFormatter = Converter<Symbol, String> // Demangler extension Converter where T == Symbol, U == Symbol { #if os(macOS) public static let `default` = Converter { ($0.0.uppercased(), $0.1) } #elseif os(Linux) public static let `default` = Converter { ($0.0.lowercased(), $0.1) } #endif } // SymbolFormatter extension Converter where T == Symbol, U == String { #if os(macOS) public static let defaultStyle = Converter { "macOS: \($0)" } #elseif os(Linux) public static let defaultStyle = Converter { "linux: \($0)" } #endif public static let default1 = SymbolFormatter.defaultStyle.compose(Demangler.default) public static let default2 = SymbolFormatter.defaultStyle ∘ Demangler.default }
(edited)<stdin>:49:63: error: generic parameter 'T' could not be inferred public static let default2 = SymbolFormatter.defaultStyle ∘ Demangler.default <stdin>:22:24: note: in call to operator '∘' public static func ∘ <A, B, C>(_ lhs: Converter<B, C>, _ rhs: Converter<A, B>) -> Converter<A, C> {
(edited) public static func ∘ <A>(_ lhs: Converter<T, U>, _ rhs: Converter<A, T>) -> Converter<A, U> { return .init { lhs.handler(rhs.handler($0)) } }
let a: Range<Int> = 1..<10 print(a.map { $0 * 2 })
swift-4.1.1-RELEASE
/usercode/main.swift:2:7: error: value of type 'Range<Int>' has no member 'map' print(a.map { $0 * 2 }) ^ ~~~
Sequence
や Collection
を conform してる。Range
が Sequence
じゃないのか。let a: CountableRange<Int> = 1..<10 print(a.map { $0 * 2 })
let a: CountableRange<Int> = 1..<10 print(a.map { $0 * 2 })
swift-4.1.1-RELEASE
[2, 4, 6, 8, 10, 12, 14, 16, 18]
2018-05-29-a 2018-05-08-a 2018-05-02-a 2018-04-25-a 2018-04-23-a 4.1.1 4.1 4.0.3 4.0.2 4.0 3.1.1 3.1 3.0.2 3.0.1
let a: Range<Int> = 1..<10 print(a.map { $0 * 2 })
let a: Range<Int> = 1..<10 print(a.map { $0 * 2 })
swift-4.2-DEVELOPMENT-SNAPSHOT-2018-05-29-a
[2, 4, 6, 8, 10, 12, 14, 16, 18]
extension Never: Equatable { public static func == (lhs: Never, rhs: Never) -> Bool { switch (lhs, rhs) { } } } let a: [Never] = [] print(a == a) // OK
https://github.com/apple/swift-evolution/blob/master/proposals/0215-conform-never-to-hashable-and-equatable.md (edited)extension Never: Equatable { public static func == (lhs: Never, rhs: Never) -> Bool { switch (lhs, rhs) { } } } let a: [Never] = [] print(a == a) // OK
swift-4.1.1-RELEASE
true
func a(a: Int, b: String, c: Never) -> Float { }
swift-4.1.1-RELEASE
/usercode/main.swift:2:1: error: missing return in a function expected to return 'Float' } ^
Error
も付けてほしいですよね。 Equatable
と Hashable
だけでなくて。extension Never : Error {} enum Result<T, E: Error> { case success(T) case failue(E) } let a: Result<Int, Never> = .success(42) print(a)
extension Never : Error {} enum Result<T, E: Error> { case success(T) case failue(E) } let a: Result<Int, Never> = .success(42) print(a)
swift-4.1.1-RELEASE
success(42)
NoError
じゃなくても Never : Error
自分で後付でもいいのか。struct Cat { var name: String? func nya() { if let name = name { print(name) } } }
struct Cat { var name: String? func nya() { if let name = name { print(name) } } }
swift-4.1.1-RELEASE
2018-05-29-a 2018-05-08-a 2018-05-02-a 2018-04-25-a 2018-04-23-a 4.1.1 4.1 4.0.3 4.0.2 4.0 3.1.1 3.1 3.0.2 3.0.1
struct Cat { var name: String? func nya() { if let name = name { print(name) } } }
struct Cat { var name: String? func nya() { if let name = name { print(name) } } }
swift-3.0.2-RELEASE
swift-3.1.1-RELEASE
swift-4.0.3-RELEASE
if let name = self.name { }
じゃないといけなかった気がするんだけど勘違い・・・?self
は不要だったと思う。struct Cat { func name() -> String { return "tama" } func nya() { let name = self.name() } }
struct Cat { func name() -> String? { return "tama" } func nya() { if let name = name() {} } }
let var = exp
と if let var = exp
では 右辺と左辺の名前衝突ルールが違うんですね。var image: Image<RGBA<UInt8>> = Image(nsImage: imageView.image!) image = Image(image[1000..<1640, 1000..<1640]) imageView.image! = image.nsImage
import Foundation class UDPSocket { let queue = DispatchQueue(label: "socket") let socket = Socket() func noreturn() { queue.async { [weak self] in let socket = self!.socket socket.noreturn() } } deinit { socket.canceled = true print("UDPSocket deinit.") } } class Socket { var canceled = false func noreturn() { while !canceled { } } deinit { print("Socket deinit.") } } var udp: UDPSocket? = UDPSocket() udp!.noreturn() sleep(1) udp = nil sleep(1)
(edited)import Foundation class UDPSocket { let queue = DispatchQueue(label: "socket") let socket = Socket() func noreturn() { queue.async { [weak self] in let socket = self!.socket socket.noreturn() } } deinit { socket.canceled = true print("UDPSocket deinit.") } } class Socket { var canceled = false func noreturn() { while !canceled { } } deinit { print("Socket deinit.") } } var udp: UDPSocket? = UDPSocket() udp!.noreturn() sleep(1) udp = nil sleep(1)
swift-4.1.1-RELEASE
UDPSocket deinit. Socket deinit.
import Foundation class UDPSocket { let queue = DispatchQueue(label: "socket") let socket = Socket() func noreturn() { queue.async { [weak self] in // let socket = self!.socket self?.socket.noreturn() } } deinit { socket.canceled = true print("UDPSocket deinit.") } } class Socket { var canceled = false func noreturn() { while !canceled { } } deinit { print("Socket deinit.") } } var udp: UDPSocket? = UDPSocket() udp!.noreturn() sleep(1) udp = nil sleep(1)
import Foundation class UDPSocket { let queue = DispatchQueue(label: "socket") let socket = Socket() func noreturn() { queue.async { [weak self] in // let socket = self!.socket self?.socket.noreturn() } } deinit { socket.canceled = true print("UDPSocket deinit.") } } class Socket { var canceled = false func noreturn() { while !canceled { } } deinit { print("Socket deinit.") } } var udp: UDPSocket? = UDPSocket() udp!.noreturn() sleep(1) udp = nil sleep(1)
swift-4.1.1-RELEASE
self?.hoge.fuga()
この式はif let sself = self { sself.hoge.fuga() }
こう展開されてる。if let sself = self { let hoge = sself.hoge hoge.fuga() }
self?.
を使うとif let sself = self { sself.hoge.fuga() }
という展開だとしたら、 sself が強参照もってしまうので、だめじゃないですか? queue.async { [weak socket] in socket?.noreturn() }
queue.async { [weak self] in let socket = self?.socket socket.noreturn() }
strong_retain
とstrong_release
はペアになってないときもあるんですか?strong_retain
は1回しか呼ばれてないのにstrong_release
は2回呼ばれている func foo() -> Int { var i = 1 let closure: ((Int) -> Void) = { _ in } closure(10) return i }
// foo() sil hidden @$S7sample23fooSiyF : $@convention(thin) () -> Int { bb0: %0 = alloc_stack $Int, var, name "i" // users: %3, %13 %1 = integer_literal $Builtin.Int64, 1 // user: %2 %2 = struct $Int (%1 : $Builtin.Int64) // users: %14, %3 store %2 to %0 : $*Int // id: %3 // function_ref closure #1 in foo() %4 = function_ref @$S7sample23fooSiyFySicfU_ : $@convention(thin) (Int) -> () // user: %5 %5 = thin_to_thick_function %4 : $@convention(thin) (Int) -> () to $@callee_guaranteed (Int) -> () // users: %12, %11, %10, %7, %6 debug_value %5 : $@callee_guaranteed (Int) -> (), let, name "closure" // id: %6 strong_retain %5 : $@callee_guaranteed (Int) -> () // id: %7 %8 = integer_literal $Builtin.Int64, 10 // user: %9 %9 = struct $Int (%8 : $Builtin.Int64) // user: %10 %10 = apply %5(%9) : $@callee_guaranteed (Int) -> () strong_release %5 : $@callee_guaranteed (Int) -> () // id: %11 strong_release %5 : $@callee_guaranteed (Int) -> () // id: %12 dealloc_stack %0 : $*Int // id: %13 return %2 : $Int // id: %14 } // end sil function '$S7sample23fooSiyF'
thin_to_thick_function
がretain
されたモノを返すのでは。_ = { (_: Int) -> Void in }
sil_stage canonical import Builtin import Swift import SwiftShims // main sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>): // function_ref closure #1 in %2 = function_ref @$S4mainySicfU_ : $@convention(thin) (Int) -> () // user: %3 %3 = thin_to_thick_function %2 : $@convention(thin) (Int) -> () to $@callee_guaranteed (Int) -> () // user: %4 strong_release %3 : $@callee_guaranteed (Int) -> () // id: %4 %5 = integer_literal $Builtin.Int32, 0 // user: %6 %6 = struct $Int32 (%5 : $Builtin.Int32) // user: %7 return %6 : $Int32 // id: %7 } // end sil function 'main' // closure #1 in sil private @$S4mainySicfU_ : $@convention(thin) (Int) -> () { // %0 // user: %1 bb0(%0 : $Int): debug_value %0 : $Int // id: %1 %2 = tuple () // user: %3 return %2 : $() // id: %3 } // end sil function '$S4mainySicfU_'
thin_to_thick_function
か。ドキュメントを探すとそれっぽい記述が見つかりました。@ callee_guaranteedが@convention(thick)を示しているの知らなかったので気づきませんでした。ありがとうございます! @convention(thick) indicates a "thick" function reference, which uses the Swift calling convention and carries a reference-counted context object used to represent captures or other state required by the function. This attribute is implied by @callee_owned or @callee_guaranteed.
(edited)extension Optional { @_specialize(exported: true, where Wrapped == Int, S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }
extension Optional { @_specialize(exported: true, where Wrapped == Int, S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }
swift-4.1.1-RELEASE
/usercode/main.swift:2:48: warning: redundant same-type constraint 'Wrapped' == 'Int' @_specialize(exported: true, where Wrapped == Int, S == [Int]) ^ /usercode/main.swift:2:58: note: same-type constraint 'S.Element' == 'Int' implied here @_specialize(exported: true, where Wrapped == Int, S == [Int]) ^
extension Optional { @_specialize(exported: true, where Wrapped == Int) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }
extension Optional { @_specialize(exported: true, where Wrapped == Int) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }
swift-4.1.1-RELEASE
/usercode/main.swift:2:6: error: too few type parameters are specified in '_specialize' attribute (got 1, but expected 2) @_specialize(exported: true, where Wrapped == Int) ^ /usercode/main.swift:2:6: error: Missing constraint for 'S' in '_specialize' attribute @_specialize(exported: true, where Wrapped == Int) ^
extension Optional { @_specialize(exported: true, where S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }
extension Optional { @_specialize(exported: true, where S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }
swift-4.1.1-RELEASE
/usercode/main.swift:2:6: error: too few type parameters are specified in '_specialize' attribute (got 1, but expected 2) @_specialize(exported: true, where S == [Int]) ^ /usercode/main.swift:2:6: error: Missing constraint for 'Wrapped' in '_specialize' attribute @_specialize(exported: true, where S == [Int]) ^
func
の方を消すってどういうこと?func
の where
を消すと↓ Wraped
と S.Element
が同じ型である保証がなくなって +
ができない。 extension Optional { @_specialize(exported: true, where Wrapped == Int, S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] { return Array(sequence) + (self.map { [$0] } ?? []) } }
extension Optional { @_specialize(exported: true, where Wrapped == Int, S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] { return Array(sequence) + (self.map { [$0] } ?? []) } }
swift-4.1.1-RELEASE
/usercode/main.swift:4:16: error: generic parameter 'S' could not be inferred return Array(sequence) + (self.map { [$0] } ?? []) ^
@_specialize(exported: true, where Wrapped == Int, S == [_])
みたいな記述を認めてほしいね。_
が他の構文の where
でも書けるなら Parameterized Extension がなくても↓みたいなのもできるし。 extension Optional where Wrapped == _? { func flatten() -> Wrapped.Wrapped? { return flatMap { $0 } } }
@_specialize(exported: true, where Wrapped == Int, S == [Int])
で警告が出なくなるというのもいいけど、記述が冗長であることは否めない。 Int
じゃなくて Foo<Bar<Baz>>
とかだと 2 回書くのは大変。_
が書けると Higher Kind Type が書けるようになる気がするprotocol
がジェネリックじゃないからギリギリ書けない?↓みたいなことが書きたいけど。 protocol Functor { associatedtype Value func map<F>(_ f: (Value) -> F.Value) -> F where F<_> == Self<_> }
var foo: Foo?
というプロパティを持つ型があって、そのメソッドで↓のようなことをやりたいときにうまい書き方ってありますか? if self.foo == nil { // self.foo の初期化 self.foo = ... } let foo: Foo = self.foo! // 以下、foo を使うコード
lazy var foo: Foo = ...
にしない理由は何でしょう?class ViewController { var foo: Foo? = nil func bar(_ x: Int) { if self.foo == nil { // self.foo の初期化 self.foo = Foo(x) } let foo: Foo = self.foo! // 以下、foo を使うコード ... } }
func bar(_ x: Int) { let foo = self.foo ?? (self.foo = Foo(x), self.foo!).1
(edited)!
が残ってるlet foo: Foo = { guard let foo = self.foo else { let foo = ... self.foo = foo return foo } return foo }()
よくこれ書いてます!
はないけど self.foo
が 2回出てくるので それが嫌だったら高階関数用意したほうが良いと思いますextension Optional { mutating func ensure(default val: @autoclosure () -> Wrapped) -> Wrapped { if let v = self { return v } let v = val() self = v return v } } class C { var foo: Foo? func test(x: Int) { let foo = self.foo.ensure(default: Foo(x)) }
(edited)func initUnwrap<T>(_ optional: inout T?, initialize: () -> T) -> T { if let value = optional { return value } let value = initialize() optional = value return value } let foo: Foo = initUnwrap(&self.foo) { Foo(x) }
inout
より @rintaro さんの mutating func
の方がキレイかな。mutating func
は self
が inout
引数を体現してる。ensure
普通に標準ライブラリにほしい。Bool
の toggle
が通ったことを考えるとこれもいけるのでは?Dictionary
の subscript(_:default:)
は同じじゃないですか?Bool.toggle
は、メソッド単発の小さな改良のための Proposal の意でした。extension Optional { @_specialize(exported: true, kind: partial, where S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }
kind: partial
だと警告消えますね。出力されるコードが同等なのかは調べてないです。extension Optional { @_specialize(exported: true, kind: partial, where S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }
swift-4.1.1-RELEASE
partial
になっちゃってたら微妙ですね・・・。$cat full.swift extension Optional { @_specialize(exported: true, kind: full, where Wrapped == Int, S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } } let a: Int? = 7 let b: [Int] = [2, 3, 5] print(a.foo(b)) $cat partial.swift extension Optional { @_specialize(exported: true, kind: partial, where S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } } let a: Int? = 7 let b: [Int] = [2, 3, 5] print(a.foo(b)) $swift -O full.swift full.swift:2:60: warning: redundant same-type constraint 'Wrapped' == 'Int' @_specialize(exported: true, kind: full, where Wrapped == Int, S == [Int]) ^ full.swift:2:70: note: same-type constraint 'S.Element' == 'Int' implied here @_specialize(exported: true, kind: full, where Wrapped == Int, S == [Int]) ^ [2, 3, 5, 7] $swift -O partial.swift [2, 3, 5, 7] $md5 full MD5 (full) = 728efb9ffc6814bb17e0eedb72c5cda2 $md5 partial MD5 (partial) = c07fb4573afb1d8ae2ee53f30f341383
(edited)-module-name
揃えないと同じバイナリにならないので$ swiftc -O partial.swift -module-name Test -o partial $ swiftc -O full.swift -module-name Test -o full
で試したら一致しました。full
の方で特殊化できてなかったらどの道これ以上やりようがないですし、仕方なさそうですね。
protocol Functor { associatedtype Value associatedtype Transformed<T> func map<T>(_ transform: (Value) -> T) -> Transformed<T> } extension Optional { typealias Value = Wrapped typealias Transformed<T> = Optional<T> func map<T>(_ transform: (Wrapped) -> T) -> Optional<T> { ... } }
(edited)Functor
の制約としては緩いですよね。Self
は associatedtype
含めて同一になっちゃうから強すぎて、Self
から associatedtype
を剥がしたものを記述できる構文が必要なはず。SelfProtocol
みたいなものが必要。 protocol Functor { associatedtype Value func map<R: SelfProtocol>(_ transform: (Value) -> R.Value) -> R }
Optional
は Protocol
じゃないもんな。protocol Functor { associatedtype Value func map<R: MyKind>(_ transform: (Value) -> R.Value) -> R }
try!
や as!
は optional!
と同様の問題は抱えてるはずで、 !!
じゃ不十分というのはその通りだ。 (edited)try! foo.bar()! !! "失敗した理由"
try? foo.writeToFile(path) ?? fatalError("Reason")
!!
が良いらしい。 Symbology mismatchが強調になってるんだけど、たしかに ?? でデフォルト値を与えると見せかけて fatalError で死ぬ っていうのも他の !
用法と比べて危険性が見えにくい気もする。// https://github.com/apple/swift/blob/9286b3627d90523752cb1a2ffc02f7500ead0ea8/stdlib/public/core/FloatingPoint.swift.gyb#L2405-L2466 func test<T: BinaryFloatingPoint>(range: Range<T>) { print("\(T.self): \(range)") let delta = range.upperBound - range.lowerBound let maxSignificand = T.RawSignificand(1) << (T.significandBitCount + 1) // rand = generator.next(upperBound: maxSignificand) \in 0...maxSignificand-1 let zero: T.RawSignificand = 0 let maxx = maxSignificand - 1 let minimum = T(zero) * .ulpOfOne / 2 print("minimum == 0: \(minimum == 0)") let lb = delta * minimum + range.lowerBound print("lb == range.lowerBound: \(lb == range.lowerBound)") let maximum = T(maxx) * .ulpOfOne / 2 print("maximum < 1: \(maximum < 1)") let ub = delta * maximum + range.lowerBound print("ub < range.upperBound: \(ub < range.upperBound)") print("") } let floatRanges: [Range<Float>] = [1..<2, 2..<4] for range in floatRanges { test(range: range) } let doubleRanges: [Range<Double>] = [1..<2, 2..<4] for range in doubleRanges { test(range: range) }
Float: 1.0..<2.0 minimum == 0: true lb == range.lowerBound: true maximum < 1: true ub < range.upperBound: false Float: 2.0..<4.0 minimum == 0: true lb == range.lowerBound: true maximum < 1: true ub < range.upperBound: false Double: 1.0..<2.0 minimum == 0: true lb == range.lowerBound: true maximum < 1: true ub < range.upperBound: false Double: 2.0..<4.0 minimum == 0: true lb == range.lowerBound: true maximum < 1: true ub < range.upperBound: false
Float.random(1..<2)
で2が生成されうることの確認なんですがbugs.swift.orgに投げればいいんですかねFloat.random(in: Float(2.0) - .ulpOfOne ..< Float(2.0)) == Float(2.0)
で true 出てしまう。let range = 0..<0 print(Int.random(in: range))
Fatal error: Can't get random value with an empty range Current stack trace: 0 libswiftCore.so 0x00007f60e2191750 _swift_stdlib_reportFatalError + 168 1 libswiftCore.so 0x00007f60e1eec56a <unavailable> + 1488234 2 libswiftCore.so 0x00007f60e212859e <unavailable> + 3831198 3 libswiftCore.so 0x00007f60e1eec56a <unavailable> + 1488234 4 libswiftCore.so 0x00007f60e20838e9 <unavailable> + 3156201 5 libswiftCore.so 0x00007f60e1fb13d0 <unavailable> + 2294736 6 libswiftCore.so 0x00007f60e1fb1690 static FixedWidthInteger<>.random(in:) + 27 8 swift 0x0000000001041ffe <unavailable> + 12853246 9 swift 0x0000000001046122 <unavailable> + 12869922 10 swift 0x00000000004f82e2 <unavailable> + 1016546 11 swift 0x00000000004de86b <unavailable> + 911467 12 swift 0x00000000004d9ba0 <unavailable> + 891808 13 swift 0x000000000048a308 <unavailable> + 566024 14 libc.so.6 0x00007f60e52d3740 __libc_start_main + 240 15 swift 0x0000000000487fc9 <unavailable> + 557001 #0 0x0000000004106e24 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x4106e24) #1 0x0000000004104cb2 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4104cb2) #2 0x0000000004106fd2 SignalHandler(int) (/usr/bin/swift+0x4106fd2) #3 0x00007f60e6ba9390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #4 0x00007f60e20838f1 $Ss18_fatalErrorMessage__4file4line5flagss5NeverOs12StaticStringV_A2HSus6UInt32VtFTf4nnddn_n (/usr/lib/swift/linux/libswiftCore.so+0x3028f1) #5 0x00007f60e1fb13d0 $Ss17FixedWidthIntegerPss08UnsignedC09MagnitudeRpzs06SignedC06StrideRpzrlE6random2in5usingxSnyxG_qd__zts21Random
0..<0
自体は OK (edited)ClosedRange
Range
の場合は Optional 返すでもいい気が。 (edited)let range1 = 0..<1 print(String(describing: range1.first)) print(String(describing: Int.random(in: range1))) let range2 = 0..<0 print(String(describing: range2.first)) // print(String(describing: Int.random(in: range2))) // nil になってほしい
Optional(0) 0 nil
range.random()
じゃないのかが書いてあるね。でもいまいち英文の意味がわからない・・・。Also, since we decided pretty early on that we're going to trap if for whatever reason we can't get a random number
この trap って何を意味してる? signal を trap する話?それとも単にエラーハンドリング的な意味?Collection.random()
が空のときに nil を返すことも決定したので、 range.random() を正規の表現にすると、前述の non-nil のポリシーに例外を持たせる事になってしまう (edited)print(String(describing: "abcdefg".randomElement()))
Optional("b")
String
の Index
は Int
じゃないので。subscript
は O(1) だけどcount
から最初にインデックスのインデックスを決めて、.count
も O(n) なので、なかなか重い処理だ。String
って生成時に count
も生成してるんじゃないのか・・・。RandomAccessCollection
って subscript
によるランダムアクセスが O(1) なんじゃなくて(当然これも満たされるけど)、インデックス間の任意個数分の移動が O(1) ってややこしいな・・・。Collection
の subscript
が O(1) は保証されてるんだっけ・・・。 String
は O(1) として。count
の一番の用途じゃないの?
Yeah, that is a concern, though it won't happen with non-pathological RNGs =) Maybe for the test use an RNG that instead generates [.min, .max, basic LCG sequence ...].
Never
to Equatable
and Hashable
, has been accepted! https://t.co/zBneV1oWiZ The core team also concluded: 1. Never
should also conform to Error
and Comparable
2. Never
should become a blessed bottom type Exactly the resolution I hope...Base
が値型だとオーバーヘッド大きそう。 struct Extension<Base> { let base: Base init (_ base: Base) { self.base = base } } protocol ExtensionCompatible { associatedtype Compatible static var ex: Extension<Compatible>.Type { get } var ex: Extension<Compatible> { get } } extension ExtensionCompatible { static var ex: Extension<Self>.Type { return Extension<Self>.self } var ex: Extension<Self> { return Extension(self) } }
https://qiita.com/motokiee/items/e8f07c11b88d692b2cc5UIColor.yamabuki
のように書いているExtensionを以下のようにオシャレに書くことができる、と...
var ex: Extension<Self> { return Extension(self) }
foo.ex.bar
ってやるだけで 80 バイトのコピーが走るってことですよね?set
しようとしたら大変ですが。BaseClass
があるパターンなので、値型に対してもバラバラに記述しないといけないケースをプロトコルでどうにかならないかなと。Foo: ExComaptible
するのと、 共通のProtocolを置くのって、どれも同じことじゃないですか?extension Reactive where Base : P0 { ... } extension Reactive where Base : P1 { ... }
Image
型とかで、ピクセル単位でループしてたりするとちょっと許容できなさそうですね。 (edited)Example<Base>
が class
になってますが、これは struct
でないとパフォーマンス的によりまずい気がします(ヒープに確保されるので)。 (edited)ex.
だとずいぶん遅くなって謎・・・。何か間違ってんのかなぁ。: ExtensionConvertible
するだけだと -c release
でも ex
が特殊化されてないみたいで、 ex_
に比べて数十倍遅くなってしまった・・・。extension
に ex
の実装を書き下すと(おそらく) Extension
が特殊化されて数倍の遅さ程度になった。Foo
が struct
で Bar
が class
measure
してるので swift test -c release
で実行。@inlinable
とか使うとtestFooDirect
とtestFooExample
は同等になった。 public struct Example<Base> { @usableFromInline let base: Base @inlinable public init(_ base: Base) { self.base = base } } public protocol ExampleCompatible { associatedtype CompatibleType var ex: CompatibleType { get } } extension ExampleCompatible { @inlinable public var ex: Example<Self> { return Example(self) } }
extension Foo { @inlinable public var ex_ab: Int { return a + b } } extension Example where Base == Foo { @inlinable public var ab: Int { return base.a + base.b } }
(edited)testBarExample
は@inlinable
を色々つけてもtestBarDirect
より10倍くらい遅い。public final class Bar
にしたらtestBarExample
とtestBarDirect
も同等になった。 (edited)@_specialize
でspecialize実装を生成しておくよりも@inlinable
を活用すべき、という理解で合ってるのかな?@inlinable
はかなり有効ですね。final
でないクラスで遅くなるのは、 ex
の呼び出しと ex
の持つ self
経由での呼び出しとで 2 回動的ディスパッチが走るからでしょうか?で、 final
にすると両方静的ディスパッチになるので速くなる。 (edited)Bar
が final
でなかったら、 bar.ex
が動的ですよね。ex
が self
を呼び出すところは特殊化できないなら動的になって、self.ab
するところが動的か静的か。 let bar = Bar() var sum = 0 for _ in 0..<N { sum += bar.ex.ab }
ab
は Extension
だからオーバーライドできないことが保証されて静的になるってことですね。final
付けるかどうかの差は、 ex
関係なくて単に bar.ex
が動的/静的ディスパッチになっているかの差?bar.ex_ab
のときも速くなりそう。 (edited)extension Example where Base == Bar { @inlinable public var ab: Int { return base.a + base.b } }
の@inlinable
有無で速度が違うし。@inlineable
がないとインライン化されないんでしょうか?@inlineable
はモジュールをまたいでインライン化可能できるようにする指定子ExPerformanceTests
で使ってるから。Example<T>
の self
への代入によるコピーコストまでなかったことにはならない気がするんですよねぇ。
extension Foo: ExampleCompatible { public var ex: Example<Foo> { return Example(self) } } extension Example where Base == Foo { public var ab: Int { return base.a + base.b } }
Example<T>
が値を保持してるのを参照にしなきゃいけないし。 @nonescaping
的な、即時解放されることが保証されてればコピー省略できるのかなぁ。@inlinable
やfinal
を活用してのExPerformanceTests
のmeasuredはこんな感じ。 …testBarDirect]' measured [Time, seconds] average: 0.009, … …testBarExample]' measured [Time, seconds] average: 0.010, … …testFooDirect]' measured [Time, seconds] average: 0.004, … …testFooExample]' measured [Time, seconds] average: 0.004, …
(edited)…testBarDirect]' measured [Time, seconds] average: 0.018, … …testBarExample]' measured [Time, seconds] average: 0.614, … …testFooDirect]' measured [Time, seconds] average: 0.024, … …testFooExample]' measured [Time, seconds] average: 0.767, …
-emit-ir
できるんだっけ?
(edited)let N = 10_000_000 public struct Example<Base> { let base: Base public init(_ base: Base) { self.base = base } } public protocol ExampleCompatible { associatedtype CompatibleType var ex: CompatibleType { get } } extension ExampleCompatible { public var ex: Example<Self> { return Example(self) } } public struct Foo { public var a: Int = 1 public var b: Int = 2 public var c: Int = 3 public var d: Int = 4 public var e: Int = 5 public var f: Int = 6 public var g: Int = 7 public var h: Int = 8 public var i: Int = 9 public var j: Int = 10 public init() {} } extension Foo { public var ex_ab: Int { return a + b } } extension Foo: ExampleCompatible { public var ex: Example<Foo> { return Example(self) } } extension Example where Base == Foo { public var ab: Int { return base.a + base.b } } let foo = Foo() var sum = 0 for _ in 0..<N { sum += foo.ex.ab } print(foo)
swift-4.1.1-RELEASE
Foo(a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10)
let N = 10_000_000 public struct Example<Base> { let base: Base public init(_ base: Base) { self.base = base } } public protocol ExampleCompatible { associatedtype CompatibleType var ex: CompatibleType { get } } extension ExampleCompatible { public var ex: Example<Self> { return Example(self) } } public struct Foo { public var a: Int = 1 public var b: Int = 2 public var c: Int = 3 public var d: Int = 4 public var e: Int = 5 public var f: Int = 6 public var g: Int = 7 public var h: Int = 8 public var i: Int = 9 public var j: Int = 10 public init() {} } extension Foo { public var ex_ab: Int { return a + b } } extension Foo: ExampleCompatible { public var ex: Example<Foo> { return Example(self) } } extension Example where Base == Foo { public var ab: Int { return base.a + base.b } } let foo = Foo() var sum = 0 for _ in 0..<N { sum += foo.ex.ab } print(foo)
<unknown>:0: error: option '-emit-ir' is not supported by 'swift'; did you mean to use 'swiftc'?
nal
の度にインスタンス作ってるだけでなく、何かを設定するときも大量にインスタンス作ってます、例えばサンプルコード出してみると self.nal.layout(self.viewA) { $0 .setTopCenter(by: { $0.topCenter }) .setWidth(by: { $0.width }) .fitHeight() }
上記画面コンポーネントは viewA
だけだけど、作ったインスタンスは nal
$0
.setTopCenter
$0.topCenter
setWidth
$0.width
.fitHeight
の7つあります
.ex
形式にしないで 直接extensionを書けば良いと思います。 まあ、今の会話の流れだと、それでもinline化によってコピーが回避されてる説が出てきましたが。let N = 10_000_000 public struct Example<Base> { let base: Base public init(_ base: Base) { self.base = base } } public protocol ExampleCompatible { associatedtype CompatibleType var ex: CompatibleType { get } } extension ExampleCompatible { public var ex: Example<Self> { return Example(self) } } public struct Foo { public var a: Int = 1 public var b: Int = 2 public var c: Int = 3 public var d: Int = 4 public var e: Int = 5 public var f: Int = 6 public var g: Int = 7 public var h: Int = 8 public var i: Int = 9 public var j: Int = 10 public init() {} } extension Foo { public var ex_ab: Int { return a + b } } extension Foo: ExampleCompatible { public var ex: Example<Foo> { return Example(self) } } extension Example where Base == Foo { public var ab: Int { return base.a + base.b } } let foo = Foo() var sum = 0 for _ in 0..<N { sum += foo.ex.ab } print(foo)
; ModuleID = '-' source_filename = "-" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" %TSi = type <{ i64 }> %T4main3FooV = type <{ %TSi, %TSi, %TSi, %TSi, %TSi, %TSi, %TSi, %TSi, %TSi, %TSi }> %swift.type = type { i64 } %swift.protocol = type { i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i32, i32, i16, i16, i32 } %swift.refcounted = type { %swift.type*, i64 } %swift.full_boxmetadata = type { void (%swift.refcounted*)*, i8**, %swift.type, i32, i8* } %swift.opaque = type opaque %swift.type_pattern = type opaque %swift.protocol_requirement = type { i32, i32 } %swift.protocol_conformance = type { i32, i32, i32, i32 } %swift.type_metadata_record = type { i32, i32 } %Ts27_ContiguousArrayStorageBaseC = type <{ %swift.refcounted, %Ts10_ArrayBodyV }> %Ts10_ArrayBodyV = type <{ %TSC22_SwiftArrayBodyStorageV }> %TSC22_SwiftArrayBodyStorageV = type <{ %TSi, %TSu }> %TSu = type <{ i64 }> %T4main7ExampleV = type <{}> %T4main7ExampleV.2 = type <{}> %T4main7ExampleVyAA3FooVG = type <{ %T4main3FooV }> @_T04main1NSivp = hidden local_unnamed_addr global %TSi zeroinitializer, align 8 @_T04main3fooAA3FooVvp = hidden local_unnamed_addr global %T4main3FooV zeroinitializer, align 16 @_T04main3sumSivp = hidden local_unnamed_addr global %TSi zeroinitializer, align 8 @_T0s23_ContiguousArrayStorageCyypGML = linkonce_odr hidden local_unnamed_addr global %swift.type* null, align 8 @_T0ypML = linkonce_odr hidden local_unnamed_addr global %swift.type* null, align 8 @_swift_getExistentialTypeMetadata = external local_unnamed_addr global %swift.type* (i1, %swift.type*, i64, %swift.protocol**)* @_swift_allocObject = external local_unnamed_addr global %swift.refcounted* (%swift.type*, i64, i64)* @_T0s27_ContiguousArrayStorageBaseC16countAndCapacitys01_B4BodyVvpWvd = external local_unnamed_addr global i64, align 8 @0 = private constant [11 x i8] c"4main3FooV\00", section "swift3_typeref" @"\01l__swift3_reflection_descriptor" = private constan
$ swift demangle S4main7ExampleVA2A3FooVRszlE2abSivg $S4main7ExampleVA2A3FooVRszlE2abSivg ---> (extension in main):main.Example<A where A == main.Foo>.ab.getter : Swift.Int
swiftc -O -emit-ir main.swift | grep S4main7ExampleVA2A3FooVRszlE2abSivg
するととんでもなく長い行が出てくるdefault
じゃなくてきちんと表示されるようになります。 struct StoredDefaultArgument { Expr *DefaultArg = nullptr; Initializer *InitContext = nullptr; StringRef StringRepresentation; };
class Example { func test() { let f = { [weak weakSelf = self] in guard let `self` = weakSelf else { return } // Set a breakpoint at here print(self) } f() } } Example().test()
今度は謎の結果に… (lldb) po self error: <EXPR>:3:1: error: use of unresolved identifier 'self' self ^~~~ (lldb) fr var (@lvalue example.Example?) weakSelf = 0x00000001009002b0 (example.Example) self = 0x00000001009002b0 {}
self
という名前での変数宣言をやめましょうということですね…guard let this = self else { return }
は案外賢かったのかもしれない… (edited)zelf
派ですが、あまり zelf
の人いないですね・・・。wself
と sself
と uself
(edited)pageControl.rx.controlEvent(.valueChanged) .subscribe(onNext: { [weak self] in guard let currentPage = self?.pageControl.currentPage else { return } guard let contentOffset = self?.scrollView.contentOffset else { return } guard let currentIndex = self?.currentIndex else { return } ...
self
派も weakSelf
capture 派も救われる…self
の再定義をしないのは、デバッグは関係なくて、self
に再定義したそのスコープでさらにクロージャーを作るときにself?.
方式だと、 self
によらない処理が書かれていると self
が解放されていてもそれが走ってしまいませんか?[weak self]
を書き忘れた場合などで、 self
が強なのか弱なのかゴチャゴチャしてくるからですreturn
したいような・・・。{ [weak self] in self?.titleLabel.alpha = 0 self?.titleLabel.transform = CGAffineTransform(translationX: 0, y: labelHeight) }
こういう場合はいちいちguard書くよりはself?.の方が私はいいんじゃないかと思います。self?.titleLabel.alpha = 0
のときは生きてるけど self?.titleLabel.transform = ...
では死んでることがあり得るかということですよね?guard let
していると、途中で解放されたときにそのクロージャからのみキャプチャされている状態になって、それで変なことになることがないかなというのが前から少し気になってました。self
に限らず weak
キャプチャしたオブジェクトが異なるスレッドで解放される可能性がある場合、リファレンスカウントの減少から deinit
の実行までは排他的でもアトミックでもないと思うんですが、そうすると weak
キャプチャしてる側で常にぶっ壊れる可能性がないですか?異なるスレッドからの解放はそもそもやっちゃいけない?self?.foo()
を呼び出したときに、 self
のリファレンスカウントをチェックしたときは 1 だったけど、 foo
が呼ばれる前に別スレッドから self
が解放されてしまったとか。guard let
self = self else { return }
してると一見安全そうだけど、この場合でもリファレンスカウントのチェックから代入してリファレンスカウント増やすまでの間に別スレッドから解放されてしまうおそれがありそう。 (edited)self?.foo()
は fooを呼び出す前に self を retain するはずですweak
の場合のみ?それとも ?.
全般??.
は その strong が生きてるので + 1 はいらんですねvar foo: Foo? = Foo()
の foo
が、 foo?.bar()
を呼んでいる間に別スレッドから nil
にされてしまう場合とかありそう。?.
に限らない? foo.bar()
でも foo
が書き換えられてインスタンスが解放されることはあり得る?std::atomic
ですimport Dispatch class Foo { func bar() {} } var foo: Foo? = Foo() func a() { foo?.bar() foo = nil } func b() { foo?.bar() foo = nil } DispatchQueue.global(qos: .default).async { a() } DispatchQueue.global(qos: .default).async { b() }
let old = refCount() let new do { if (checkZero) { return } new = old + 1 } while ( CAS (old, new) )
↑シンプルにこういう形ですよ (edited)oldbits
は参照なのか。 auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
oldbits
と newbits
が値だったら関数ローカルな存在でどことも共有されてないのでそもそも排他する必要がなくないということなんだけど、それ自体は値だけど中にポインタか参照を持ってるのかな?template <typename RefCountBits> class RefCounts { std::atomic<RefCountBits> refCounts; ... }
(edited)oldbits
と newbits
は値型の関数ローカルの変数で、そこに書き戻したのがどうやってオブジェクトが保持している RefCounts
に反映されてるの? (edited)while (!refCounts.compare_exchange_weak(oldbits, newbits, std::memory_order_relaxed));
↑ここのCASで書き戻してるrefCounts
と oldBits
を比較してるのか。compare_exchange_weak
が副作用のあるメソッドだとわかってなかった。pair
って入ってると辛そう。let foo = foo(at: path) // 名前が被ってコンパイルエラー
self.
で済むなら self.
を付ける、関数はできるだけ作らない、とかでしょうか?self.
をつけて解決してます (edited)func foo(at: Path)
を作る。let f = foo(at: path)
ですね。 そうじゃなかったらなんとか別の変数名を考える。Decodable
のイニシャライザをサブクラスでオーバーライドできる様になってた。 @swift-4.1.3 @swift-4.2.4
import Foundation private class Person: Codable { let name: String init(name: String) { self.name = name } } private class Employee: Person { let id: Int init(name: String, id: Int) { self.id = id super.init(name: name) } enum CodingKeys: String, CodingKey { case id } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(Int.self, forKey: .id) try super.init(from: decoder) } override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try super.encode(to: encoder) } }
(edited)<stdin>:29:19: error: method does not override any method from its superclass override func encode(to encoder: Encoder) throws { ~~~~~~~~ ^ <stdin>:28:1: error: 'required' initializer 'init(from:)' must be provided by subclass of 'Person' ^ <unknown>:0: note: 'required' initializer is declared in superclass here
(edited)Encodable
のメソッドも同じだった。@dynamicMemberLookup
と @dynamicCallable
に加えて、 @dynamicConvertible
ほしくないですか?今だと PythonObject
から Swift に戻すときに↓のようにしないといけなくて面倒。 import Python let np = Python.import("numpy") let a = np.array([[1, 2], [3, 4]]) let b = np.array([[5], [6]]) let c = np.matmul(a, b) let d: Int = Int(c[0, 0])! // ここで明示的変換が必要 print(d)
@dynamic
系は安全性を崩して書きやすさをとっているので、 dynamic に限って暗黙の型変換を許すのは良いバランスじゃないかということです。import Python let np = Python.import("numpy") let a = np.array([[1, 2], [3, 4]]) let b = np.array([[5], [6]]) let c = np.matmul(a, b) let d: Int = c[0, 0] // 暗黙の型変換 print(d)
Int
を右に書くか左に書くかの違いに見えますが、引数に渡すときに意味があります。@dynamicMemberLookup @dynamicCallable @dynamicConvertible struct PythonObject { ... func converted() -> Int { ... } func converted() -> Bool { ... } func converted() -> String { ... } func converted() -> (PythonObject, PythobObject) { ... } ... }
(edited)Int
や Bool
等については extension
の init
になってるけど、タプルは extension
作れないので PythonObject
に tuple2
とかのメソッドが生えてて統一感もない。converted
の例は今適当に書いたから深く考えてなかった。 (edited)@dynamicMemberLookup
が付与されているときのみ @dynamicConvertible
にできるとか?extension Int { init(_ : py PythonObject) }
↑これの実装+暗黙呼び出しがしたいって感じですかね init側じゃなくてconvert側でもいいけど@dynamicMemberLookup
な型を引数にとる init(_:)
があれば暗黙型変換とかいう機能でもいいのかもしれないけど (edited)@dynamic
系ってあえてプロトコルじゃなくしてあって@dynamicCallable
の subscript
の引数って Dictionary
じゃなくてもいいし、associatedtype
でできるかconverted
という名前で解決するパターンだと attribute と親和しそうなんよね。PythonObject
は暗黙的に変換されるから
(edited)import Python let np = Python.import("numpy") let elements: [[Int]] = [[1, 2], [3, 4]] let a = np.array(elements) // OK
@dynamicCallable
の subscript
の引数の型が (edited)PythonObject
じゃなくて PythonConvertible
になってるから。@dynamicCallable
はメソッドコールを subscript
に変換する。[[Int]]
が PythonConvertible として定義されてて、全部それで受け取るのか。 (edited)a = someValue(keyword1: 42, "foo", keyword2: 19)
が a = someValue.dynamicallyCall(withKeywordArguments: [ "keyword1": 42, "": "foo", "keyword2": 19 ])
のシュガー。extension Array: PythonConvertible where Element: PythonConvertible { ... }
だろうね。
x.toSwiftInt()
みたいな事をやったところでnil
が返ってくるInt(pythonObject)
も変換できなかったら nil
let d: Int = c[0, 0] // 暗黙の型変換
!
や precondition
に失敗したときと同じイメージです。std::atomic
とか使ってたのでstdlib内を探したら _stdlib_atomicCompareExchangeStrongPtr
https://github.com/apple/swift/blob/master/stdlib/public/core/Runtime.swift.gyb#L25 とか見つけたのですが、stdlib内で使ってるところが見当たらず。何か違うメカニズムを使ったほうがよいのかな。LLVMEnablePrettyStackTrace()
みたいなのを付け加えようとしてるのです。_stdlib_atomicCompareExchangeStrongPtr
使います。func returnStringCollection() -> opaque Collection where _.Element == String
みたいなのを出来るようにする提案 (edited)func makeOpaque<T>(_: T.Type) -> opaque Any { /* ... */ } var x = makeOpaque(Int.self) x = makeOpaque(Double.self) // error: "opaque" type from makeOpaque<Double> is distinct from makeOpaque<Int>
<T>
で判断するのかな??makeOpaque
の実装知らないと判断できない気がするんですけど、モジュール跨いだときどうするんだろう。(まだ読んでないprotocol P { } extension Int : P { } extension String : P { } func f1() -> opaque P { return "opaque" } func f2() -> opaque P { return 42 } var a = f1() a = f2()
opaque
なら実装詳細を隠したままconcrete typeを使ってパフォーマンス出せるよ、って事かな?↓これの最後の行はエラー??
concrete typeに出来なければエラー、みたいな制限がつくのかな? (edited)func f() -> P { if Bool.random() { return 17 } else { return "hello, existential" } }
こういうのはできないってことらしいfunc f() -> P { /* ... */ } func g() -> opaque P { /* ... */ } let fArray = [f(), f(), f()] // contains a mix of String and Int at run-time let gArray = [g(), g(), g()] // homogeneous array of g()'s opaque result type
let vf1 = f1() // type of vf1 is the opaque result type of f1()
Effect on API resilience Opaque result types are part of the result type of a function/type of a variable/element type of a subscript, which cannot be changed without affecting API resilience. We could allow an API originally specified using an opaque result type to later evolve to specify the specific result type. The result type itself would have to become visible to clients, and this might affect source compatibility, but (mangled name aside) such a change would be resilient.
However, the underlying concrete type can change from one version to the next without breaking ABI, because that type is not known to clients of the API.
真の型を変えても ABI が変わらないってことはー、コンパイル時に解決するというのと矛盾しているような。熟読しないとわからないでござる。ABI
、 APIのタイポじゃなかろうか・・・var a = f1() a = f2()
が同一モジュール内はコンパイルエラーだけど、モジュールまたいだらコンパイル通るということになりませんか?func foo<C: Collection>(_ : C) { } foo(c) // okay: unlike existentials, opaque types work with generics
extension BidirectionalCollection { public func reversed() -> opaque BidirectionalCollection where _.Element == Element where Self: RandomAccessCollection -> _: RandomAccessCollection where Self: MutableCollection -> _: MutableCollection { return ReversedCollection<Self>(...) } }
struct ReversedCollection<C: BidirectionalCollection>: BidirectionalCollection {...} extension ReversedCollection: RandomAccessCollection where C: RandomAccessCollection {}
extension BidirectionalCollection { public func reversed() -> opaque BidirectionalCollection where _.Element == Element { return ReversedCollection<Self>(...) } }
(edited)extension RandomAccessCollection { public func reversed() -> opaque RandomAccessCollection where _.Element == Element { return ReversedCollection<Self>(...) } }
func h() -> opaque Comparable { return /* ... */ } var hArray = [h(), h(), h()] hArray.sort() // okay! the Element type is Comparable, and all types are the same
It is not a plan for Swift 3, nor an official core team communication
@dynamicCallable
でもずいぶん昔に通ったイメージ。// $ swift -swift-version 5 protocol CopyInitializable { init(copy: Self) } extension CopyInitializable { init(copy: Self) { self = copy } } class Animal : CopyInitializable { convenience init(animal: Animal) { self.init(copy: animal) } }
a.swift:13:25: error: cannot convert value of type 'Animal' to expected argument type 'Self' self.init(copy: animal) ^~~~~~ as! Self
> おかしいのは、ここで self.init(_with: ) が呼び出せてしまうことじゃないですか? > AnimalProtocolが引数に受けてる Self は、 AnimalBase が final で無いのだから > 何かのサブクラスである可能性があって (たとえばCat ) > なのに、 obj: AnimalBase を渡せてしまっている
NoError
要らずでいい感じ。 enum Result<Value, Error: Swift.Error> { case success(Value) case failure(Error) func get() throws -> Value { switch self { case .success(let value): return value case .failure(let error): throw error } } } extension Result where Error == Never { func get() -> Value { switch self { case .success(let value): return value } } } let a: Result<Int, Never> = .success(42) print(a.get())
enum Result<Value, Error: Swift.Error> { case success(Value) case failure(Error) func get() throws -> Value { switch self { case .success(let value): return value case .failure(let error): throw error } } } extension Result where Error == Never { func get() -> Value { switch self { case .success(let value): return value } } } let a: Result<Int, Never> = .success(42) print(a.get())
<stdin>:15:30: error: same-type constraint type 'Never' does not conform to required protocol 'Error' extension Result where Error == Never { ^ <stdin>:24:8: error: type 'Never' does not conform to protocol 'Error' let a: Result<Int, Never> = .success(42) ^ <stdin>:17:9: error: switch must be exhaustive switch self { ^ <stdin>:17:9: note: add missing case: '.failure(_)' switch self { ^
let a: Int = fatalError()
<stdin>:1:14: error: cannot convert value of type 'Never' to specified type 'Int' let a: Int = fatalError() ^~~~~~~~~~~~
protocol Animal {} func foo<A: Animal>(_ a: @autoclosure () -> A) {} foo(fatalError())
<stdin>:3:1: error: global function 'foo' requires that 'Never' conform to 'Animal' foo(fatalError()) ^ <stdin>:2:6: note: where 'A' = 'Never' func foo<A: Animal>(_ a: @autoclosure () -> A) {} ^
func f<E: Error>(_ e: E) {} f(fatalError())
(edited)<stdin>:2:3: warning: will never be executed f(fatalError()) ^ <stdin>:2:3: note: a call to a never-returning function f(fatalError()) ^ Fatal error: : file <stdin>, line 2 Current stack trace: 0 libswiftCore.so 0x00007f9510c6da50 _swift_stdlib_reportFatalErrorInFile + 115 1 libswiftCore.so 0x00007f9510bad167 <unavailable> + 3617127 2 libswiftCore.so 0x00007f95109c0785 <unavailable> + 1599365 3 libswiftCore.so 0x00007f9510bacf92 <unavailable> + 3616658 4 libswiftCore.so 0x00007f95109bf9bd <unavailable> + 1595837 5 libswiftCore.so 0x00007f9510b6581f <unavailable> + 3323935 6 libswiftCore.so 0x00007f95109bf149 <unavailable> + 1593673 8 swift 0x00000000010b39de <unavailable> + 13318622 9 swift 0x00000000010b7a62 <unavailable> + 13335138 10 swift 0x0000000000519d85 <unavailable> + 1154437 11 swift 0x00000000004ee6e5 <unavailable> + 976613 12 swift 0x00000000004e9aa1 <unavailable> + 957089 13 swift 0x0000000000494fa0 <unavailable> + 610208 14 libc.so.6 0x00007f9513136740 __libc_start_main + 240 15 swift 0x0000000000492dd9 <unavailable> + 601561 Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret - -disable-objc-interop -swift-version 5 -module-name main #0 0x00000000044580a4 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x44580a4) #1 0x0000000004455f70 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4455f70) #2 0x0000000004458252 SignalHandler(int) (/usr/bin/swift+0x4458252) #3 0x00007f9514a0c390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #4 0x00007f9510b65823 $Ss17_assertionFailure__4file4line5fla
(edited)func f<X: Error>(_ error: X) {} f(fatalError())
<stdin>:2:3: warning: will never be executed f(fatalError()) ^ <stdin>:2:3: note: a call to a never-returning function f(fatalError()) ^ Fatal error: : file <stdin>, line 2 Current stack trace: 0 libswiftCore.so 0x00007f6cf4244a50 _swift_stdlib_reportFatalErrorInFile + 115 1 libswiftCore.so 0x00007f6cf4184167 <unavailable> + 3617127 2 libswiftCore.so 0x00007f6cf3f97785 <unavailable> + 1599365 3 libswiftCore.so 0x00007f6cf4183f92 <unavailable> + 3616658 4 libswiftCore.so 0x00007f6cf3f969bd <unavailable> + 1595837 5 libswiftCore.so 0x00007f6cf413c81f <unavailable> + 3323935 6 libswiftCore.so 0x00007f6cf3f96149 <unavailable> + 1593673 8 swift 0x00000000010b39de <unavailable> + 13318622 9 swift 0x00000000010b7a62 <unavailable> + 13335138 10 swift 0x0000000000519d85 <unavailable> + 1154437 11 swift 0x00000000004ee6e5 <unavailable> + 976613 12 swift 0x00000000004e9aa1 <unavailable> + 957089 13 swift 0x0000000000494fa0 <unavailable> + 610208 14 libc.so.6 0x00007f6cf670d740 __libc_start_main + 240 15 swift 0x0000000000492dd9 <unavailable> + 601561 Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret - -disable-objc-interop -swift-version 5 -module-name main #0 0x00000000044580a4 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x44580a4) #1 0x0000000004455f70 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4455f70) #2 0x0000000004458252 SignalHandler(int) (/usr/bin/swift+0x4458252) #3 0x00007f6cf7fe3390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #4 0x00007f6cf413c823 $Ss17_assertionFailure__4file4line5fla
func f<X: Error>(_ error: @autoclosure ()->X) {} f(fatalError())
Never
は Error
や Equatable
になっただけで、 bottom になったわけではないはず。ただ、たしか Chris Lattner か Joe Groff が forum の議論の最初の方で bottom type に言及してた気が。Equatable
入れるなら Error
も入れるのがいいんじゃない?みたいな会話があったと思う。Chris Lattner Jun 13 Moderate -1. Never is special because it has no instances - it is uninhabited by design. This means that it can conform to all protocols. I'd rather see a global solution rather than cherry picking these two specific protocols.
Joe_Groff Jun 13 If we're cherry-picking protocols to conform Never to, Error seems like another good one to whitelist, since it's useful to use Never to close off the error path in Result<T, Never>-like abstractions.
https://forums.swift.org/t/se-0215-conform-never-to-equatable-and-hashable/13586 (edited)let range: Range<Float> = 0..<1 let x = range.randomElement()
<stdin>:2:10: error: ambiguous reference to member 'randomElement(using:)' let x = range.randomElement() ^~~~~ Swift.Collection:11:17: note: found this candidate public func randomElement<T>(using generator: inout T) -> Self.Element? where T : RandomNumberGenerator ^ Swift.Collection:12:17: note: found this candidate public func randomElement() -> Self.Element? ^
Range<Float>
は Collection じゃないので、実際には randomElement()
生えていないということだと思います。エラーメッセージも悪いし、Xcode で補完されちゃうのも悪い。var a = [2, 3, 5] var b = a[...] a.removeFirst() print(a.startIndex) b.removeFirst() print(b.startIndex)
ArraySlice
の popFirst
は startIndex
を動かすけど、 removeFirst
は動かさずに前に詰める(というか、先頭ポインタを動かして startIndex
は動かさない)のかと思ってた。removeFirst
とpopFirst
で返り値型が違うんですねArray
と ArraySlice
の同じメソッドで startIndex
が動くか動かないかという挙動が違う件ですね。startIndex
が動かないようにも実装できるはず。ArraySlice
のremoveFirst
の実装がどれなのか確定できてないですが上位プロトコルで実装されててArraySlice
以外の事情も絡んでそうな気がしてます。 https://github.com/apple/swift/blob/ae6f5dd60460f340d98cf24c402c6489bb5a4f2b/stdlib/public/core/Collection.swift#L1765-L1781func foo<C: RangeReplaceableCollection>(_ collection: C) -> C.Index? { var collection = collection if collection.isEmpty { return nil} collection.removeFirst() return collection.startIndex } var a: Array<Int> = [2, 3, 5] var b: ArraySlice<Int> = a[...] print(foo(a)!) print(foo(b)!)
Collection
の Index
は opaque として扱われるべきで、実値は気にするべきではないと思います。Array
と ArraySlice
は直接関係があるわけでないしいいのかな。==
と =
が並んで読みづらい・・・。 var strings: opaque Collection where _.Element == String = ["hello", "world"]
let strings: Any<Sequence where .Iterator.Element == String> = ["a", "b", "c"]
だから、書き換えたら↓みたいな感じ? let strings: Sequence where _.Iterator.Element == String = ["a", "b", "c"]
opaque
がなければ Generalized existential になるのか。Foo
と Bar
があって、両方に Cat
という型があって、 Bar
モジュールに Bar
という型がある場合に、 Foo
の中から Bar.Cat
を表現するにはどうすればいいですか?↓のようにすると Bar.Bar.Cat
を意味してしまってエラーになってしまいました。 // Foo モジュールにて import Bar struct Cat { init(_ cat: Bar.Cat) { ... } }
(edited)import Bar as B
とかできればいいんですが・・・。Result
は パッケージ名変えて欲しい。Foo
のソースの中で必要とするのがBar.Cat
だけであれば、 import struct Bar.Cat
で解決できるかも。import struct Bar.Cat typealias BarCat = Bar.Cat
として BarCat
としてアクセスできました。難点は、 BarCat
を使う API が public
な場合、 typealias
を露出させないといけないこと。typealias AntiResult<T, E: Error> = Result<T, E>
とかでも良さそうな気も。typealias
露出しないでもいけるかな・・・ (edited)Result
だと無理そう・・・typealias
以前に、 norio さんが挙げてくれた方法も自モジュールで Result
型を宣言してたら無理そう。 (edited)Alamofire
で宣言されたものとの alias
だから良いだけで。Foo.Cat
と Bar.Cat
と Bar.Bar
の問題だから回避できたけど、Foo.Bar
と Bar.Bar
を Foo
の中で解決するのはきつそう。1 Cat
にならないのってなんででしょう? 1 Animal
の方のメソッドがジェネリックでないから? @swift-4.2.4
class Animal<T> {} class Cat<T, U>: Animal<T> {} struct Hoge { func foo(_: Animal<Hoge>) { print("1 Animal") } func foo<U>(_: Cat<Hoge, U>) { print("1 Cat") } } Hoge().foo(Cat<Hoge, Int>()) // 1 Animal func foo<T>(_: Animal<T>) { print("2 Animal") } func foo<T, U>(_: Cat<T, U>) { print("2 Cat") } foo(Cat<Hoge, Int>()) // 2 Cat
(edited)PatialKeyPath
と WritableKeyPath
でこのパターンでオーバーロードして死んだ。Comparing 2 viable solutions --- Solution #0 --- Fixed score: 0 0 0 0 0 0 0 0 0 0 0 ... Constraint restrictions: Cat<Hoge, Int> to Animal<Hoge> is [superclass] Cat<Hoge, Int> to Cat<Hoge, Int> is [deep equality] ... --- Solution #1 --- Fixed score: 0 0 0 0 0 0 0 0 0 0 0 ... Constraint restrictions: Cat<Hoge, Int> to Cat<Hoge, Int> is [deep equality] ...
comparing solutions 1 and 0 Comparing declarations func ffoo<U>(_: Cat<Hoge, U>) { } and func ffoo(_: Animal<Hoge>) { } comparison result: not better Comparing declarations func ffoo(_: Animal<Hoge>) { } and func ffoo<U>(_: Cat<Hoge, U>) { } comparison result: better
swift -Xfrontend -debug-constraints a.swift
Comparing declarations func foo<T>(_: Animal<T>) { } and func foo<T, U>(_: Cat<T, U>) { } comparison result: not better Comparing declarations func foo<T, U>(_: Cat<T, U>) { } and func foo<T>(_: Animal<T>) { } (found solution 0 0 0 0 0 0 0 0 0 0 0) comparison result: better
(edited)open class Animal<T> class Cat<T, U>: Animal<T>() class Hoge { fun foo(a: Animal<Hoge>) { println("1 Animal") } fun <U> foo(a: Cat<Hoge, U>) { println("1 Cat")} } fun <T> foo(a: Animal<T>) { println("2 Animal") } fun <T, U> foo(a: Cat<T, U>) { println("2 Cat") } fun main(args: Array<String>) { Hoge().foo(Cat<Hoge, Int>()) // 1 Cat foo(Cat<Hoge, Int>()) // 2 Cat }
(edited)1 Cat
が自然だと思うんだけどなぁ。PartialKeyPath<T>
と、そのサブクラスである KeyPath<T, U>
や WritableKeyPath<T, U>
でオーバーロードして、 T
に Self
とか入れたときに後者が勝つ方法がなさそう。U
を Any
にしてしまえば勝てるだろうけど・・・。 (edited)Any
にしても variance がうまく働かない?let a: KeyPath<[Int], Int> = \[Int].count let b: KeyPath<[Int], Any> = a
<stdin>:2:30: error: cannot convert value of type 'KeyPath<[Int], Int>' to specified type 'KeyPath<[Int], Any>' let b: KeyPath<[Int], Any> = a ^
// A concrete type is better than an archetype. // FIXME: Total hack. if (type1->is<ArchetypeType>() != type2->is<ArchetypeType>()) { if (type1->is<ArchetypeType>()) ++score2; else ++score1; continue; }
class Animal {} class Cat<T>: Animal {} func foo(_: Animal) { print("Animal") } func foo<T>(_: Cat<T>) { print("Cat") } foo(Cat<Int>())
class Animal { func fooImpl() { print("Animal") } } class Cat<T>: Animal { override func fooImpl() { print("Cat") } } func foo(_ animal: Animal) { animal.fooImpl() } func foo<T>(_ cat: Cat<T>) { cat.fooImpl() } foo(Cat<Int>())
func foo(_ animal: Animal) func foo<T>(_ cat: Cat<T>)
foo<T>
って完全に腐ってる?class Animal {} class Cat<T>: Animal {} func foo(_: Animal) { print("Animal") } func foo<T>(_: Cat<T>) { print("Cat") } let f: (Cat<Int>) -> Void = foo f(Cat<Int>())
Animal
優先は辛い・・・class Animal {} class Cat<T>: Animal {} func foo<X>(_: X, _: Animal) { print("Animal") } func foo<X, T>(_: X, _: Cat<T>) { print("Cat") } foo((), Cat<Int>())
class Animal {} class Cat<T>: Animal {} func foo<T: Animal>(_: T) { print("Animal") } func foo<T>(_: Cat<T>) { print("Cat") } foo(Cat<Int>())
Never
が bottom でも無理?Animal
と Cat<T>
だと Cat<T>
を優先してほしいのが自然な感覚な気がするんですが、継承で型パラを足すというのがレアケースなので考慮漏れの可能性もあるかなと思うんですが。確か、初期の Swift は継承で型パラ足すのできなかったですよね?Animal
が protocol
の場合も同結果になるのは結構きついと思った。 (edited)class A {} protocol BProtocol { associatedtype Info: A var info: Info? { get set } } class B<T1: A>: BProtocol { var info: T1? } protocol CProtocol { associatedtype Property: BProtocol var property: Property? { get set } } class C<T2>: CProtocol where T2 : BProtocol { var property: T2? }
<T2: B<A>>
で二重になってたのが悪かったんですかね、やっぱり。class A {} protocol BProtocol { associatedtype Info: A var info: Info? { get set } } class B<T1: A>: BProtocol { var info: T1? } protocol CProtocol { associatedtype Property: BProtocol var property: Property? { get set } } // vvvvvvvv ここが違う class C<T2 : BProtocol>: CProtocol { var property: T2? }
(edited)<T2: BProtocol>
にしても当初表現したかった形としては成立してるので、その形に修正しようかな。。ありがとうございます。最新 master ではコンパイル通ったので放っておいても直る
Swift 5っていつ頃リリース予定でしたっけ?Optional
を flatten することが引き起こすちょっと微妙な(おもしろい?)例。 @swift-4.2.4
func foo(_ x: [Int?]?) -> Any { return x?[0] as Any } func bar<T>(_ x: [T]?) -> Any { return x?[0] as Any } let a: [Int?]? = [2, 3, 5] print(String(describing: foo(a))) print(String(describing: bar(a)))
(edited)T
に Int?
を埋めたら同じコードに見えるけど結果が異なる。enum Sample<T> { case Foo(T) case Bar(T) } indirect enum Sample1<T> { case Foo(T) case Bar(T) } enum Sample2<T> { indirect case Foo(T) case Bar(T) } enum Sample3<T> { indirect case Foo(T) indirect case Bar(T) }
`enum ArithmeticExpression { case number(Int) indirect case addition(ArithmeticExpression, ArithmeticExpression) indirect case multiplication(ArithmeticExpression, ArithmeticExpression) } let five = ArithmeticExpression.number(5) let four = ArithmeticExpression.number(4) let sum = ArithmeticExpression.addition(five, four) let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2)) print(sum) print(product)
`addition(main.ArithmeticExpression.number(5), main.ArithmeticExpression.number(4)) multiplication(main.ArithmeticExpression.addition(main.ArithmeticExpression.number(5), main.ArithmeticExpression.number(4)), main.ArithmeticExpression.number(2))
enum ArithmeticExpression { case number(Int) indirect case addition(ArithmeticExpression, ArithmeticExpression) indirect case multiplication(ArithmeticExpression, ArithmeticExpression) } let five = ArithmeticExpression.number(5) let four = ArithmeticExpression.number(4) let sum = ArithmeticExpression.addition(five, four) let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2)) func evaluate(_ expression: ArithmeticExpression) -> Int { switch expression { case let .number(value): return value case let .addition(left, right): return evaluate(left) + evaluate(right) case let .multiplication(left, right): return evaluate(left) * evaluate(right) } } print(evaluate(product))
`class A<E> { convenience init<X>(_ array: [X]) where X : Equatable, X == E { self.init(array, { (a, b) in a == b }) } init(_ array: [E], _ eq: @escaping (E, E) -> Bool) {} }
struct KeySortedArray<Element, Key : Comparable> { init(_ array: [Element], _ getKey: @escaping (Element) -> Key) {} } extension KeySortedArray where Element : Comparable, Element == Key { init(_ array: [Element]) { } } class Cat { init() { KeySortedArray(self.blocks, { $0.time }) } }
#0 0x000000000410b054 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x410b054) #1 0x0000000004108ee2 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4108ee2) #2 0x000000000410b202 SignalHandler(int) (/usr/bin/swift+0x410b202) #3 0x00007f0b420d2390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #4 0x00000000016b1b09 swift::FunctionType::get(swift::Type, swift::Type, swift::AnyFunctionType::ExtInfo const&) (/usr/bin/swift+0x16b1b09) #5 0x00000000017fde74 swift::GenericFunctionType::substGenericArgs(swift::SubstitutionMap const&) (/usr/bin/swift+0x17fde74) #6 0x00000000017fdd02 swift::GenericFunctionType::substGenericArgs(llvm::ArrayRef<swift::Substitution>) (/usr/bin/swift+0x17fdd02) #7 0x000000000147abe0 swift::UncurriedCandidate::UncurriedCandidate(swift::ValueDecl*, unsigned int) (/usr/bin/swift+0x147abe0) #8 0x000000000147da0f swift::CalleeCandidateInfo::collectCalleeCandidates(swift::Expr*, bool) (/usr/bin/swift+0x147da0f) #9 0x00000000014667e1 (anonymous namespace)::FailureDiagnosis::visitApplyExpr(swift::ApplyExpr*) (/usr/bin/swift+0x14667e1) #10 0x000000000144d4d6 swift::ASTVisitor<(anonymous namespace)::FailureDiagnosis, bool, void, void, void, void, void>::visit(swift::Expr*) (/usr/bin/swift+0x144d4d6) #11 0x0000000001446642 swift::constraints::ConstraintSystem::diagnoseFailureForExpr(swift::Expr*) (/usr/bin/swift+0x1446642) #12 0x000000000144cb76 swift::constraints::ConstraintSystem::salvage(llvm::SmallVectorImpl<swift::constraints::Solution>&, swift::Expr*) (/usr/bin/swift+0x144cb76) #13 0x0000000001352138 swift::TypeChecker::solveForExpression(swift::Expr*&, swift::DeclContext*, swift::Type, swift::FreeTypeVariableBinding, swift::ExprTypeCheckListener*, swift::constraints::ConstraintSystem&, llvm::SmallVectorImpl<swift::constraints::Solution>&, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) (/usr/bin/swift+0x1352138) #14 0x0000000001356465 swift::TypeChecker::typeCheckExpression(swift::Ex
let display.Name: String = "hola" print(display.Name)
`<stdin>:2:12: error: consecutive statements on a line must be separated by ';' let display.Name: String = "hola" ^ ; <stdin>:2:17: error: consecutive statements on a line must be separated by ';' let display.Name: String = "hola" ^ ; <stdin>:2:17: error: expected expression let display.Name: String = "hola" ^ <stdin>:2:5: error: type annotation missing in pattern let display.Name: String = "hola" ^ <stdin>:2:13: error: reference to member 'Name' cannot be resolved without a contextual type let display.Name: String = "hola" ~^~~~
func writeData() throws { // ここにclose失敗のthrowsが必要, スコープ脱出時だからtry catchはできない let stream = try OutputStream() try stream.write("aaa") }
(edited)do
で囲んだらちょうどスコープ脱出時になるのでは?
do { let stream = try OutputStream() try stream.write("aaa") } catch ... { // stream が破棄されて deinit の throws も catch }
struct Foo { var a: Any func bar(_ out: OutputStream) { a = out } func baz() { do { let stream = try OutputStream() bar(stream) } catch let error { // ここで stream が破棄されるか調べるのが大変 ... } } }
deinit
をthrows
にするのは無茶な気がする。endScope<T>(T)
が入ると明示的に呼び出せる・・・deinit
の throws
がついてないと子の deinit
で throws
つけられないし厳しそうObject
を継承してるわけじゃないから。Any
はクラスじゃないし。でも existential が破綻するのか。deinit throws
が公開されてる型なのかチェックしないといけないとか、無理すぎる。func run(_ body: @escaping () -> Void) { body() } class Foo { var a: Int = 42 func bar() { run { a += 1 } } }
<stdin>:9:13: error: reference to property 'a' in closure requires explicit 'self.' to make capture semantics explicit a += 1 ^ self.
func run(_ body: @escaping () -> Void) { body() } class Foo { var a: Int = 42 func bar() { func incrementA() { a += 1 } run(incrementA) } }
self.
いらないんですっけ?仕様?チェック漏れ? { }
で書いたときと func で書いた時で self の暗黙キャプチャ挙動が変わるincrementA
)について?
(edited)write(to:)
)あるの初めて知った。 @swift-4.2.4
var out = "" "abc".write(to: &out) "def".write(to: &out) print(out)
+
演算子と何が違うん?と思いましたが、 to:
の先は TextOutputStream
なんですね。String
は TextOutputStream
というのがおもしろいです。String
は可変長配列のように末尾に効率的に文字列を追加できるので、文字列を書き出す対象になれるということですね。一方で、(値型なので)他の言語でよくあるイミュータブルな文字列インスタンスと同じような性質も持っていると。String
と StringBuilder
と Writer
を合わせたような感じの役割を果たしてる感じでしょうか。dump(to:)
も同じ感じですね。これが TextOutputStream な理由は、(1) 標準出力とかに吐き出す都合で直接吐けた方が楽 or (2) 単にでかいテキストの末尾追加が発生しやすいから、のどちらかだと思うんですが、多分 1 ? https://developer.apple.com/documentation/swift/1641218-dump# (edited)String
なことで stream 的な性質を併せ持つこともできたということじゃないでしょうか?できるならそうしたいけど参照型のイミュータブルな String
ではできなかったという意味合いで。func foo(_ x: UInt8) { print("UInt8: \(x)") } func foo(_ x: Int) { print("Int: \(x)") } foo(.max)
<stdin>:10:6: error: ambiguous use of 'max' foo(.max) ^ Swift.UnsignedInteger:4:23: note: found this candidate public static var max: Self { get } ^ Swift.SignedInteger:4:23: note: found this candidate public static var max: Self { get } ^
func foo(_ x: UInt8) { print("UInt8: \(x)") } func foo(_ x: Int) { print("Int: \(x)") } foo(.max - 1)
Int: 9223372036854775806
1
が Int
と推論されて、それによって .max
の型が Int
と推論され、 Int.max
になってる?.max
じゃダメだけど .max - 1
だと OK なのが、おもしろいというかわかりにくいというか・・・func foo(_ x: UInt8) { print("UInt8: \(x)") } func foo(_ x: UInt16) { print("Int: \(x)") } foo(.max+0)
func foo(_ x: UInt8) { print("UInt8: \(x)") } func foo(_ x: UInt16) { print("Int: \(x)") } foo(.max+0)
swift-4.1.1-RELEASE
/usercode/main.swift:9:9: error: ambiguous use of operator '+' foo(.max+0) ^ Swift.UInt8:9:24: note: found this candidate public static func + (lhs: UInt8, rhs: UInt8) -> UInt8 ^ Swift.UInt16:9:24: note: found this candidate public static func + (lhs: UInt16, rhs: UInt16) -> UInt16 ^
ExpressibleBy...Literal
があるから任意の型に任意のリテラルを設定できるけど、何も指定しなかった場合のデフォルトは決まってて、その関係で 1
の型が決定されて .max
の型も推論されてると思われる。let a = .max - 1 print(a) let b: UInt8 = .max - 1 print(b)
9223372036854775806 254
let a = .max
<stdin>:2:10: error: reference to member 'max' cannot be resolved without a contextual type let a = .max ~^~~
String
?標準出力?func foo(_ x: UInt8) {} func foo(_ x: Int) {} foo(.max - 1)
---Constraint solving for the expression at [<stdin>:6:1 - line:6:13]--- ---Initial constraints for the given expression--- (call_expr type='()' location=<stdin>:6:1 range=[<stdin>:6:1 - line:6:13] arg_labels=_: (overloaded_decl_ref_expr type='$T0' location=<stdin>:6:1 range=[<stdin>:6:1 - line:6:1] name=foo number_of_decls=2 function_ref=single decls=[ main.(file).foo@<stdin>:2:6, main.(file).foo@<stdin>:4:6]) (paren_expr type='($T6)' location=<stdin>:6:10 range=[<stdin>:6:4 - line:6:13] (binary_expr type='$T6' location=<stdin>:6:10 range=[<stdin>:6:5 - line:6:12] (overloaded_decl_ref_expr type='$T1' location=<stdin>:6:10 range=[<stdin>:6:10 - line:6:10] name=- number_of_decls=20 function_ref=unapplied decls=[ Swift.(file).Float.-, Swift.(file).Double.-, Swift.(file).Float80.-, Swift.(file).UInt8.-, Swift.(file).Int8.-, Swift.(file).UInt16.-, Swift.(file).Int16.-, Swift.(file).UInt32.-, Swift.(file).Int32.-, Swift.(file).UInt64.-, Swift.(file).Int64.-, Swift.(file).UInt.-, Swift.(file).Int.-, Swift.(file).FloatingPoint.-, Swift.(file).Numeric.-, Swift.(file).BinaryInteger.-, Swift.(file).Strideable.-, Swift.(file).Strideable.-, Swift.(file).Strideable.-, Swift.(file).Strideable.-]) (tuple_expr implicit type='($T4, $T5)' location=<stdin>:6:5 range=[<stdin>:6:5 - line:6:12] (unresolved_member_expr type='$T4' location=<stdin>:6:6 range=[<stdin>:6:5 - line:6:6] name='max' arg_labels=') (integer_literal_expr type='$T5' location=<stdin>:6:12 range=[<stdin>:6:12 - line:6:12] value=1))))) Score: 0 0 0 0 0 0 0 0 0 0 0 Type Variables: $T0 [lvalue allowed] subtype_of_existential involves_type_vars bindings={} @ locator@0x68fcd10 [OverloadedDeclRef@<stdin>:6:1] $T1 [lvalue allowed] subtype_of_existential involves_type_vars bindings={} @ locator@0x68fce60 [Ove
--- Solution #0 --- Fixed score: 0 0 0 0 0 1 0 0 0 0 0 Type variables: $T1 as (UInt8, UInt8) -> UInt8 @ locator@0x68fce60 [OverloadedDeclRef@<stdin>:6:10] --- Solution #1 --- Fixed score: 0 0 0 0 0 0 0 0 0 0 0 Type variables: $T1 as (Int, Int) -> Int @ locator@0x68fce60 [OverloadedDeclRef@<stdin>:6:10]
enum ScoreKind { // These values are used as indices into a Score value. /// A fix needs to be applied to the source. SK_Fix, /// A reference to an @unavailable declaration. SK_Unavailable, /// An implicit force of an implicitly unwrapped optional value. SK_ForceUnchecked, /// A user-defined conversion. SK_UserConversion, /// A non-trivial function conversion. SK_FunctionConversion, /// A literal expression bound to a non-default literal type. SK_NonDefaultLiteral, /// An implicit upcast conversion between collection types. SK_CollectionUpcastConversion, /// A value-to-optional conversion. SK_ValueToOptional, /// A conversion to an empty existential type ('Any' or '{}'). SK_EmptyExistentialConversion, /// A key path application subscript. SK_KeyPathSubscript, /// A conversion from a string, array, or inout to a pointer. SK_ValueToPointerConversion, SK_LastScoreKind = SK_ValueToPointerConversion, };
{ ∃X where X: Animal, X }
の事で 、その考え方ではプロトコルに準拠してるのはXであって Existential ではないから、という説明になるらしいですけど、僕はよくわかってないです。
func package<T: P>(_ t: T) -> P { return t } let p0: P = package(Cat()) let p1: P = package(p0)
こうすると入れ子になると思います。実装上それを避ける事はできそうですが、変なケースが生じないかとか、入れ子にならないことが正しいのか?とか気になります。 (edited)protocol P { var x: Any { get set } } struct S: P { var x: Any } let p: P = S(x: 42)
Any
も existential だからすでに埋め込めるのでは?protocol Q {} protocol P { var x: Q { get set } } struct T: Q {} struct S: P { var x: Q } let p: P = S(x: T())
(edited)P
には Q
が埋め込まれてない?<T> T: P
のときに T を P にアップキャストする挙動のことですfunc package<T: P>(_ t: T) -> P { // ここでPのExistentialが作られて、Tはその中に保持される return t } // これは普通 let p0: P = package(Cat()) // このとき、callee側ではPではなくT: Pとして来るので、 // 同じように T を保持する P が作られて、 結果的に P { P { T } } になりそう。 let p1: P = package(p0)
protocol P { var x: Q { get set } }
↑ここを見て言いましたt
を使おうとした瞬間に open されてもう一度 existential container に包まれるんじゃないのかなぁ。P
を T
として扱おうとした時点で、暗黙的に open
するんじゃない?let p1: P = package(p0 /* あ〜ここか */)
func foo<T: P>(_ t: T) { t.foo() }
T
が P
でも動かないといけないわけでopen
してから渡すかlet b = Bool.random() switch b { case true: print("T") case false: print("F") }
Bool
だけ特殊?@dynamicCallable
の PR が作り直されていて [WIP]
も外れてる。 https://github.com/apple/swift/pull/20305AnyHashable
の実装みてたんですが、 associatedtype
無しの Self
制約だけであれば protocol
で type erasure 書けるんですねー。protocol Equattable { func equaals(to other: Self) -> Bool } extension Int: Equattable { func equaals(to other: Int) -> Bool { return self == other } } extension String: Equattable { func equaals(to other: String) -> Bool { return self == other } } protocol _AnyEquattableBox { func _unbox<T: Equattable>() -> T? func _equaals(to box: _AnyEquattableBox) -> Bool? } struct _ConcreteEquattableBox<Base: Equattable> : _AnyEquattableBox { var _baseEquattable: Base init(_ base: Base) { self._baseEquattable = base } func _unbox<T: Equattable>() -> T? { return (self as _AnyEquattableBox as? _ConcreteEquattableBox<T>)?._baseEquattable } func _equaals(to box: _AnyEquattableBox) -> Bool? { guard let rhs: Base = box._unbox() else { return nil } return _baseEquattable.equaals(to: rhs) } } struct AnyEquattable: Equattable { var _box: _AnyEquattableBox init<E: Equattable>(_ base: E) { _box = _ConcreteEquattableBox(base) } func equaals(to rhs: AnyEquattable) -> Bool { return _box._equaals(to: rhs._box) ?? false } } let intVal1 = AnyEquattable(1) let intVal2 = AnyEquattable(1) let intVal3 = AnyEquattable(2) let strVal = AnyEquattable("test") assert(intVal1.equaals(to: intVal2)) assert(!intVal1.equaals(to: intVal3)) assert(!intVal1.equaals(to: strVal))
_canonicalBox
というのが何故必要なのか? let a = (42 as Int as AnyHashable) let b = (42 as NSNumber as AnyHashable) let c = (42 as Double as AnyHashable) a == b // true b == c // true a == c // was false(!), now true
print(AnyHashable(42 as Int) == AnyHashable(42.0 as Double))
print(AnyHashable(42 as Int) == AnyHashable(42.0 as Double))
protocol AnyEquatableBox { func _unbox<T: Equatable>() -> T? func _equals(to box: AnyEquatableBox) -> Bool? } struct ConcreteEquatableBox<Base: Equatable>: AnyEquatableBox { var base: Base init(_ base: Base) { self.base = base } func _unbox<T>() -> T? where T : Equatable { return ((self as AnyEquatableBox) as? ConcreteEquatableBox<T>)?.base } func _equals(to box: AnyEquatableBox) -> Bool? { guard let rhs: Base = box._unbox() else { return nil } return base == rhs } } struct AnyEquatable: Equatable { var box: AnyEquatableBox init<E>(_ base: E) where E: Equatable { box = ConcreteEquatableBox(base) } static func == (lhs: AnyEquatable, rhs: AnyEquatable) -> Bool { return lhs.box._equals(to: rhs.box) ?? false } } class Animal: Equatable { static func == (lhs: Animal, rhs: Animal) -> Bool { return lhs === rhs } } class Cat: Animal { } let cat1 = Cat() let cat2 = cat1 as Animal assert(AnyEquatable(cat1) == AnyEquatable(cat2))
Assertion failed: : file <stdin>, line 46 Current stack trace: 0 libswiftCore.so 0x00007fde1b750fa0 _swift_stdlib_reportFatalErrorInFile + 215 1 libswiftCore.so 0x00007fde1b4a95c1 <unavailable> + 1504705 2 libswiftCore.so 0x00007fde1b6e60c2 <unavailable> + 3850434 3 libswiftCore.so 0x00007fde1b4a8d0a <unavailable> + 1502474 4 libswiftCore.so 0x00007fde1b6e5f5c <unavailable> + 3850076 5 libswiftCore.so 0x00007fde1b4a8d0a <unavailable> + 1502474 6 libswiftCore.so 0x00007fde1b642a28 <unavailable> + 3181096 7 libswiftCore.so 0x00007fde1b4a85a9 <unavailable> + 1500585 9 swift 0x0000000001043efe <unavailable> + 12861182 10 swift 0x0000000001048022 <unavailable> + 12877858 11 swift 0x00000000004f8b42 <unavailable> + 1018690 12 swift 0x00000000004df0bb <unavailable> + 913595 13 swift 0x00000000004da3f0 <unavailable> + 893936 14 swift 0x000000000048a348 <unavailable> + 566088 15 libc.so.6 0x00007fde1e0f5740 __libc_start_main + 240 16 swift 0x0000000000488009 <unavailable> + 557065 #0 0x000000000410ac94 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x410ac94) #1 0x0000000004108b22 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4108b22) #2 0x000000000410ae42 SignalHandler(int) (/usr/bin/swift+0x410ae42) #3 0x00007fde1f9cb390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #4 0x00007fde1b642a30 $Ss17_assertionFailure__4file4line5flagss5NeverOs12StaticStringV_SSAHSus6UInt32VtFTf4nxnnn_n (/usr/lib/swift/linux/libswiftCore.so+0x308a30) #5 0x00007fde1b4a85a9 (/usr/lib/swift/linux/libswiftCore.so+0x16e5a9) #6 0x0000
print(AnyHashable(42 as Int) == AnyHashable(42.0 as Double))
の挙動は意図したものなのね。 https://github.com/apple/swift/pull/17396class Animal : Hashable { static func ==(a: Animal, b: Animal) -> Bool { return a === b } func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self)) } } class Cat : Animal {} class Dog : Animal {} let a = Cat() let b: Animal = a let c: Animal = Dog() print(AnyHashable(a) == AnyHashable(c), 3)
(edited)AnyHashable
を作るときに、どんな_AnyHashableBox
を使うかカスタマイズできる_HasCustomAnyHashableRepresentation
ってのがあって、元がDouble
でもInt64(exactly:)
で表現可能なら、_IntegerAnyHashableBox
を使う仕組みになってる。 https://github.com/apple/swift/blob/master/stdlib/public/core/FloatingPointTypes.swift.gyb#L1820-L1831AnyHashable(42 as Int) == AnyHashable(42.0 as Double)
がtrue
になる。_AnyHashableBox
がinternal
だから、stdlib外の型ではカスタマイズ出来ないな。protocol AnyEquatableBox { func _unbox<T: Equatable>() -> T? func _equals(to box: AnyEquatableBox) -> Bool? } struct ConcreteEquatableBox<Base: Equatable>: AnyEquatableBox { var base: Base init(_ base: Base) { self.base = base } func _unbox<T: Equatable>() -> T? { return self.base as? T } func _equals(to box: AnyEquatableBox) -> Bool? { guard let rhs: Base = box._unbox() else { return nil } return base == rhs } } struct AnyEquatable: Equatable { var box: AnyEquatableBox init<E>(_ base: E) where E: Equatable { box = ConcreteEquatableBox(base) } static func == (lhs: AnyEquatable, rhs: AnyEquatable) -> Bool { return lhs.box._equals(to: rhs.box) ?? false } } class Animal: Equatable { static func == (lhs: Animal, rhs: Animal) -> Bool { return lhs === rhs } } class Cat : Animal {} class Dog : Animal {} let a = Cat() let b: Animal = a let c: Animal = Dog() assert(AnyEquatable(a) == AnyEquatable(b)) assert(AnyEquatable(a) != AnyEquatable(c)) assert(AnyEquatable(b) == AnyEquatable(a)) assert(AnyEquatable(b) != AnyEquatable(c)) assert(AnyEquatable(c) != AnyEquatable(a)) assert(AnyEquatable(c) != AnyEquatable(b))
_unbox
を シンブルな conditional cast に変えただけ。_ObjectiveCBridgeable
の影響を受けそう。JSONDecoder.decode
とかは throws
じゃなくて -> Result<...>
であるべきということになりそうだけどどうだろう? throws
で扱うのに対して、 JSON のデコードの失敗はもっと確定的な処理で、 "an regular return value with a more specific failure type" に適しているように感じる。一種の Simple Domain Error になる?Result
が追加されると、既存 API を変更するか、もしくは言語の期待する使い分けと異なるデザインの API が放置されるかという状況になって、どっちになっても結構辛そうな気がする。String(data:using:)
とかも Result
を return
すべきってことになるよね。throws
じゃなくて Result
になったらずいぶん使いづらいと思う。 https://developer.apple.com/documentation/swift/keyeddecodingcontainer/2921334-decode (edited)String(data:using:)
の例も別のメソッド/イニシャライザでOKthrows
を使うべきと言っていて0.
を付けた感じだと思うんですよね。0.4.2
みたいな。1.0
にした段階で 1.x
の間は後方互換を保ち、そのスパンが 5 年くらいがいいのかなと。async/await
や actor
, typed throws など)が片付くまではそういう形を続けて、それらが片付いたらもう少しきっちり互換性を守るべきなのかなぁと思います。throws
は "systemic error" に用いるべきというのもちょっと懐疑的で、↓のようになるのが望ましいと思えない・・・。結局 Result
→ throws
のための get
だらけ。 extension Foo: Decodable { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.a = try container.decode(Int.self, forKey: .a).get() self.b = try container.decode(String.self, forKey: .b).get() } }
init
は Result
を return
できないから throws
にせざるを得ない。init?
のようなものを導入するなら、昔僕が提案してたみたいな |
を導入して、 T|E
を Result<T, E>
のシュガーにした上で、 init|
とか導入しないと・・・。init(from decoder: ) throws(JSONError)
とか。Result
を return
の両方を扱えないよ。init
が throw
する場合と Result
を return
する場合の両方を扱えないといけないはず。String.init(data:using:)
は Result
を return
したいけど、 Database.init(path:)
は throw
したいはず。Result
が必要なのって、 async/await
に対する Future
みたいなもので、局所的にしかたなく使うものであって、 API に現れる種類のものじゃないと思うんよね。init
じゃなくて Result
を return
するメソッドとすると↓になる。 extension Foo: Decodable { static func from(_ decoder: Decoder) -> Result<Foo, DecodeError> { return Result { let container = try decoder.container(keyedBy: CodingKeys.self) let a = try container.decode(Int.self, forKey: .a).get() let b = try container.decode(String.self, forKey: .b).get() return Foo(a: a, b: b) } } }
get
で throws
への変換を挟まないともっと悲惨になる。extension Foo: Decodable { static func from(_ decoder: Decoder) -> Result<Foo, DecodeError> { let container = try decoder.container(keyedBy: CodingKeys.self) let a: A switch container.decode(Int.self, forKey: .a) { case .success(let value): a = value case .failure(let error): return .failure(error) } let b: B switch container.decode(Int.self, forKey: .b) { case .success(let value): b = value case .failure(let error): return .failure(error) } return .success(Foo(a: a, b: b)) } }
(edited)decode
を Result
を return
するようにしても何がうれしいのかさっぱりだし、 decode
を throws
のままにするならどこで Result
使うの?って感じ。extension Foo: Decodable { static func from(_ decoder: Decoder) -> Result<Foo, DecodeError> { return decoder.container(keyedBy: CodingKeys.self).flatMap { container in return curry(Foo.init) <^> container.decode(Int.self, forKey: .a) <*> container.decode(String.self, forKey: .b) } } }
Result
の利用を推奨してる? I want to clarify that I favor adding Result only to address situations where manual propagation is necessary. Manual propagation will always occasionally be useful; adding async/await (or whatever we end up doing) will surely eliminate most of the most common situations, but they'll still exist, e.g. when using futures (which are the equivalent of manual propagation for async/await and accordingly trigger similar requirements). I will continue to recommend against using Result as an alternative mechanism for ordinary propagation of errors, although I have no doubt that some people will insist on doing so; and I continue to believe that using a different error type than Error is generally misguided for the same reason it's generally misguided with ordinary error-handling. I also worry that the ergonomics of this type are significantly hurt by the attempt to support typed errors and (especially) non-Error-conforming errors.
https://forums.swift.org/t/se-0235-add-result-to-the-standard-library/17752/129-public enum Result<Value, Error> { - case success(Value), failure(Error) +public enum Result<Value, Error: Swift.Error> { + case value(Value), error(Error) }
(edited)Error: Swift.Error
になったのもいいと思う。Result
乱用は絶対に起こると思うけど、 Result
追加するなら考えられる限りほぼベストな実装だと思う。JSONDecoder.decode(_:from:)
は Result
を return
するのがいいの?それともしないのがいいの?って聞いてみた。 https://forums.swift.org/t/revised-se-0235-add-result-to-the-standard-library/18371/26@_frozen public enum Result<Value, Error: Swift.Error> { case value(Value) case error(Error) public func map<NewValue>( _ transform: (Value) -> NewValue ) -> Result<NewValue, Error> public func mapError<NewError>( _ transform: (Error) -> NewError ) -> Result<Value, NewError> public func flatMap<NewValue>( _ transform: (Value) -> Result<NewValue, Error> ) -> Result<NewValue, Error> public func flatMapError<NewError>( _ transform: (Error) -> Result<Value, NewError> ) -> Result<Value, NewError> public func unwrapped() throws -> Value } extension Result where Error == Swift.Error { @_transparent public init(catching body: () throws -> Value) } extension Result : Equatable where Value : Equatable, Error : Equatable { } extension Result : Hashable where Value : Hashable, Error : Hashable { }
Error: Swift:Error
じゃできないことだった気がするからError
の場合でも URLResponse
を持ちたかったのか。URLSession.shared.dataTask(with: url) { (result: Result<(URLResponse, Data), (Error, URLResponse?)>) in // Type added for illustration purposes. switch result { case .success(let response): handleResponse(response.0, data: response.1) case .failure(let error): handleError(error) } }
Error
型のプロパティでやるのが順当だろうけど、元が Obj-C で NSError
だからなぁ。URLResponse
をすでに持ってる型なの?URLRespose
を持ってないのであれば解決しない気が。extension URLError { /// The URL which caused a load to fail. public var failingURL: URL? { get } /// The string for the URL which caused a load to fail. public var failureURLString: String? { get } /// The state of a failed SSL handshake. public var failureURLPeerTrust: SecTrust? { get } }
(result, response)
を受け取るんでいいのでは?URLResponse
を Optional
にしたくないのか。Optional
だけどData
が存在するな気もするな。URLResponse
がエラーケースで失われてるのか・・・value
, error
でもいいんじゃないかなって感じ。Result
をいつ使うべきかが示されないと throws
との使い分けで混乱を生みそう。JSONDecoder.decode
は throws
のままでいいって考えなのかなぁ?async/await
で解決できる非同期処理だと思うんよね。
Result
を使うのはいいとしてdataTask
は URLSessionDataTask
を return
してるから async/await
にできない。URLSessionDataTask
に func completion() async throws -> (Data, URLResponse)
というメソッドを追加するのがいいと思う。struct URLSessionDataTaskError : Error { var response: URLResponse? var error: Error } (Result<(Data, URLResponse), URLSessionDataTaskError>) -> Void
URLSessionDataTask に func completion() async throws URLSessionDataTaskError -> (Data, URLResponse)
(edited)URLSessionDataTask
が enum
で色んな case
を持ってて// vvvvvvv ここが気に食わない struct URLSessionDataTaskError : Error {
URLResponse
を持つ場合だけその case
が response
を保持するのがいいんじゃない?enum URLSessionDataTaskError: Error { case ... case ...(URLResponse) ... }
URLSessionDataTask
がエラーになりうる様々な case
URLSessionDataTaskError
導入してる時点で Foundation
との整合性を考える必要性はないのでは。completion
の話だけ書き込んでおくか。.dataCorrupted
にマッチしてないっぽい。 let jsonData: Data = "".data(using: .utf8)! let result = Result<[Int], Error> { try JSONDecoder().decode([Int].self, from: jsonData) } switch result { case .value(let value): XCTFail("\(value)") case .error(DecodingError.dataCorrupted(let context)): XCTAssertTrue(context.codingPath.isEmpty) case .error(let error): XCTFail("\(error)") }
https://travis-ci.org/koher/SwiftResult/jobs/461127891Result
、 covariance はなくていいんだろうか。特に Result<Int, FooError>
を Result<Int, Error>
に代入できないの辛そう。b = a.mapError { $0 as Error }
ですね。a.flatMap { Result { try foo() }.mapError { $0 as Error } }
となって辛そう。a.flatMap { Result { try foo() }.mapError { $0 as! FooError } }
as!
でダウンキャストResult<Foo, Error>
を使ってれば問題ないか。Error
の self-conform で特別扱いするなら、 Result<Value, Error: Swift.Error = Swift.Error>
も認めて Result<Foo>
って書けるようにしてほしい。Result
にだけデフォルト型パラを認めてくれたら便利そう。 (edited)Result
で第二型パラがなかったら Swift.Error
と解釈するとか。import Foundation @objc protocol Animal {} func take<X: Animal>(_ a: X) {} func outer(_ a: Animal) { take(a) }
@objc
だとself-conformしてる・・・DecodingError
ではなく NSError
になるっぽい・・・。 import Foundation do { _ = try JSONDecoder().decode([Int].self, from: "".data(using: .utf8)!) } catch let error { print(error) }
NSError
なのか。import Foundation do { _ = try JSONDecoder().decode([Int].self, from: "".data(using: .utf8)!) } catch let error { print("\(type(of: error)): \(error)") }
(edited)DecodingError: ⚠️ [DecodingError.dataCorrupted: The given data was not valid JSON.]
(edited)Result
のための構文の必要性についても話し合われていたので、将来的に追加されるかもしれませんね。たとえば↓のような構文とかいいかもしれません。 guard let number = result else let error { // error を使うオペレーション } // number を使うオペレーション
@_exported import WinSDK // Clang module // WinBase.h public let HANDLE_FLAG_INHERIT: DWORD = 0x00000001 // WinBase.h public let STARTF_USESTDHANDLES: DWORD = 0x00000100 // WinBase.h public let INFINITE: DWORD = DWORD(bitPattern: -1) // WinBase.h public let WAIT_OBJECT_0: DWORD = 0 // minwindef.h public let FALSE: BOOL = 0 // minwindef.h public let TRUE: BOOL = 1
protocol P1 { associatedtype Input } protocol P2 { associatedtype Input } struct Box<T> {} extension Box: P1 where T: P1 { typealias Input = T.Input } extension Box: P2 where T: P2 { } func inputType<T>(_ type: T.Type) -> T.Input.Type where T: P2 { fatalError() } struct A: P2, P1 { typealias Input = Int } let _ = inputType(Box<A>.self)
(edited)swift: /home/buildnode/jenkins/workspace/oss-swift-package-linux-ubuntu-16_04/llvm/include/llvm/Support/Casting.h:106: static bool llvm::isa_impl_cl<swift::SugarType, const swift::TypeBase *>::doit(const From *) [To = swift::SugarType, From = const swift::TypeBase *]: Assertion `Val && "isa<> used on a null pointer"' failed. Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret - -disable-objc-interop -I /Libraries/.build/x86_64-unknown-linux/debug -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIOZlib/include -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIOHTTPParser/include -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIOSHA1/include -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIOAtomics/include -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIODarwin/include -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIOLinux/include -module-cache-path /Libraries/.build/x86_64-unknown-linux/debug/ModuleCache -D DEBUG -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOZlib.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOHTTPParser.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOSHA1.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOAtomics.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIODarwin.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOLinux.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/checkouts/swift-nio-zlib-support-c4b8a10c/module.modulemap -module-name main -lLibraries #0 0x000000000460a1c4 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x460a1c4) #1 0x0000000004607f90 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4607f90) #2 0x000000000460a372 SignalHandler(int)
protocol P1 { associatedtype Input } extension P1 { typealias InputP1 = Input } protocol P2 { associatedtype Input } extension P2 { typealias InputP2 = Input } struct Box<T> {} extension Box: P1 where T: P1 { typealias InputP1 = T.InputP1 } extension Box: P2 where T: P2 { typealias Input = T.InputP2 } func inputType<T>(_ type: T.Type) -> T.Input.Type where T: P2 { fatalError() } struct A: P2, P1 { typealias Input = Int } let _ = inputType(Box<A>.self)
protocol P1 { associatedtype Input } extension P1 { typealias InputP1 = Input } protocol P2 { associatedtype Input } extension P2 { typealias InputP2 = Input } struct Box<T> {} extension Box: P1 where T: P1 { typealias InputP1 = T.InputP1 } extension Box: P2 where T: P2 { typealias Input = T.InputP2 } func inputType<T>(_ type: T.Type) -> T.Input.Type where T: P2 { fatalError() } struct A: P2, P1 { typealias Input = Int } let _ = inputType(Box<A>.self)
swift-DEVELOPMENT-SNAPSHOT-2018-12-15-a
Fatal error: file /usercode/main.swift, line 28 Current stack trace: 0 libswiftCore.so 0x00007f51ac83c200 _swift_stdlib_reportFatalErrorInFile + 115 1 libswiftCore.so 0x00007f51ac7773fc <unavailable> + 3396604 2 libswiftCore.so 0x00007f51ac7774ee <unavailable> + 3396846 3 libswiftCore.so 0x00007f51ac582f2a <unavailable> + 1347370 4 libswiftCore.so 0x00007f51ac742f42 <unavailable> + 3182402 5 libswiftCore.so 0x00007f51ac5822f9 <unavailable> + 1344249 8 swift 0x0000000000cec7de <unavailable> + 9357278 9 swift 0x0000000000cf09f2 <unavailable> + 9374194 10 swift 0x0000000000515eff <unavailable> + 1138431 11 swift 0x00000000004eae3e <unavailable> + 962110 12 swift 0x00000000004e6712 <unavailable> + 943890 ...
protocol P1 { associatedtype Input } extension P1 { typealias InputP1 = Input } protocol P2 { associatedtype Input } extension P2 { typealias InputP2 = Input } struct Box<T> {} extension Box: P1 where T: P1 { typealias InputP1 = T.InputP1 } extension Box: P2 where T: P2 { typealias Input = T.InputP2 } func inputTypeP2<T>(_ type: T.Type) -> T.Input.Type where T: P2 { return T.Input.self } func inputTypeP1<T>(_ type: T.Type) -> T.Input.Type where T: P1 { return T.Input.self } struct A: P2, P1 { typealias Input = Int } print(inputTypeP1(Box<A>.self)) // セグフォ print(inputTypeP2(Box<A>.self))
protocol P1 { associatedtype Input } extension P1 { typealias InputP1 = Input } protocol P2 { associatedtype Input } extension P2 { typealias InputP2 = Input } struct Box<T> {} extension Box: P1 where T: P1 { typealias InputP1 = T.InputP1 } extension Box: P2 where T: P2 { typealias Input = T.InputP2 } func inputTypeP2<T>(_ type: T.Type) -> T.Input.Type where T: P2 { return T.Input.self } func inputTypeP1<T>(_ type: T.Type) -> T.Input.Type where T: P1 { return T.Input.self } struct A: P2, P1 { typealias Input = Int } print(inputTypeP1(Box<A>.self)) // セグフォ print(inputTypeP2(Box<A>.self))
swift-DEVELOPMENT-SNAPSHOT-2018-12-15-a
swift: /home/buildnode/jenkins/workspace/oss-swift-package-linux-ubuntu-16_04/llvm/include/llvm/Support/Casting.h:106: static bool llvm::isa_impl_cl<swift::SugarType, const swift::TypeBase *>::doit(const From *) [To = swift::SugarType, From = const swift::TypeBase *]: Assertion `Val && "isa<> used on a null pointer"' failed. Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret /usercode/main.swift -disable-objc-interop -module-name main /usr/bin/swift[0x4608a04] /usr/bin/swift[0x46067d0] /usr/bin/swift[0x4608bb2] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7f533b723390] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x38)[0x7f5339e62428] /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a)[0x7f5339e6402a] /lib/x86_64-linux-gnu/libc.so.6(+0x2dbd7)[0x7f5339e5abd7] /lib/x86_64-linux-gnu/libc.so.6(+0x2dc82)[0x7f5339e5ac82] /usr/bin/swift[0x19464f3] /usr/bin/swift[0xef6b2c] /usr/bin/swift[0xf0af9e] /usr/bin/swift[0xf0eb07] /usr/bin/swift[0xef3041] /usr/bin/swift[0xe7e311] ...
P1
の方が死んじゃいましたswift_decompose_double()
, swift_decompose_float()
, swift_decompose_float80()
とswift_format_exponential()
をYamsで使いたかったのだけど、libswiftCore
からexportされていないので、SwiftDtoa.h
とSwiftDtoa.cpp
をソースコードごとコピーしてくるしか手が無い。 (edited)nm -gU /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/libswiftCore.dylib
で出てくるものならリンクできる。 -g Display only global (external) symbols. -U Don't display undefined symbols.
Data.withUnsafeBytes(_:)
は、Data
が小さい場合にスコープ外で無効となるポインタをクロージャへ渡す場合がある。 https://bugs.swift.org/browse/SR-9578withCString(_:)
は、null termination追加があるから以前から毎回一時メモリじゃないかな。 (edited) internal var isFastZeroTerminated: Bool
ほほう。 https://github.com/apple/swift/blob/master/stdlib/public/core/StringObject.swift#L946struct InlineData
https://github.com/apple/swift/blob/master/stdlib/public/Darwin/Foundation/Data.swift#L681-L696protocol Animal {} protocol Cat: Animal {} protocol Owner { associatedtype Pet var pet: Pet { get } } func callBark(_ animal: Animal){} func callPetBark<T: Owner>(_ t: T) where T.Pet == Animal { callBark(t.pet) } func callPetBark<T: Owner>(_ t: T) where T.Pet == Cat { callBark(t.pet) } struct Lion: Cat {} struct Breeder: Owner { typealias Pet = Animal var pet: Animal { return Lion() } } let owner = Breeder() callPetBark(owner)
assoctypeが「あるexistentialのサブタイプである」、っていう制約を書きたい (edited)Breeder
のpet
がAnimal
である時点で無理そう。 error: protocol type 'Animal' cannot conform to 'Animal' because only concrete types can conform to protocols にハマる。protocol Animal {} protocol Cat: Animal {} protocol Owner { associatedtype Pet var pet: Pet { get } } func callBark(_ animal: Animal){} func callPetBark<T: Owner>(_ t: T) where T.Pet : Animal { callBark(t.pet) } struct Lion: Cat {} struct GenericBreeder<T>: Owner { typealias Pet = T init(pet: T) { self.pet = pet } var pet: T } struct AnyAnimal : Animal { var value: Animal } struct AnyAnimalBreeder : Owner { init<X: Animal>(pet: X) { self.pet = AnyAnimal(value: pet) } var pet: AnyAnimal } struct ConcreteLionBreeder : Owner { typealias Pet = Lion var pet: Lion { return Lion() } } let o1 = GenericBreeder<Lion>(pet: Lion()) callPetBark(o1) let o2 = AnyAnimalBreeder(pet: Lion()) callPetBark(o2) let o3 = ConcreteLionBreeder() callPetBark(o3)
where T.Pet : Animal
と書いてしまうと、exisとは関係ないプロトコル制約の意味になっちゃって、 それが使えないねえimport Foundation enum Key: String, Decodable { case a, b, c } let json = """ { "a": "__a__", "b": "__b__", "c": "__c__" } """.data(using: .utf8)! do { print(try JSONDecoder().decode([String: String].self, from: json)) let ret = try JSONDecoder().decode([Key: String].self, from: json) print(ret) } catch { print(error) }
(edited)["a": "__a__", "c": "__c__", "b": "__b__"] ⚠️ [DecodingError.typeMismatch: Value of type 'Array<Any>' required for key ''.]
(edited)extension Dictionary : Decodable where Key : Decodable, Value : Decodable {
[Int: User]
だったものを [UserId: User]
のような感じにしたかったんですが、直接やるのは少し難しいですね (edited)let a: [Int: String] = [1: "a", 2: "b"] let b: [String: String] = Dictionary(uniqueKeysWithValues: a.map { ("\($0)", $1) })
// むりやり左から右の流れに・・・ let c: [String: String] = Optional(a.map { ("\($0)", $1) }).map(Dictionary.init)!
Dictionary
の代わりを作るとか…encodedOffset
をdeprecated
にする提案をswift-5.0-RELEASE
へ入れるには遅すぎた、という結論へ至ったらしい。まあ仕方ない。swift-5.0-RELEASE
へ入れるのを諦めるのは各offsetに対応したAPIを追加する事で、encodedOffset
をdeprecated
にして代替となるString.Index.utf16Offset(in:)
とString.Index.init(utf16Offset:in:)
だけはswift-5.0-RELEASE
へ入れようという方向へ切り替わったのか? https://github.com/apple/swift-evolution/blob/master/proposals/0241-string-index-explicit-encoding-offset.md (edited)string.utf16.index(string.startIndex, by: offset)
という書き方だと (edited)String.Index(encodedOffset: offset)
でパラメータがoffsetだけだったのに対して func processEdit(editedNSRange: NSRange, changeInLenght delta: Int) { let editedRange = Range<String.Index>(uncheckedBounds: ( String.Index(encodedOffset: editedNSRange.lowerBound), String.Index(encodedOffset: editedNSRange.upperBound) )) ...
public init<S: StringProtocol>(utf16Offset offset: Int, in s: S) {
こうなってるな。String.Index
はオフセットが同じでも、対象となるString
により中身が変わってきます。 コメントに構造が記されてます。 https://github.com/apple/swift/blob/master/stdlib/public/core/StringIndex.swift#L17-L33
/* String's Index has the following layout: ┌──────────┬───────────────────┬────────────────┬──────────┐ │ b63:b16 │ b15:b14 │ b13:b8 │ b7:b0 │ ├──────────┼───────────────────┼────────────────┼──────────┤ │ position │ transcoded offset │ grapheme cache │ reserved │ └──────────┴───────────────────┴────────────────┴──────────┘ - grapheme cache: A 6-bit value remembering the distance to the next grapheme boundary - position aka `encodedOffset`: An offset into the string's code units - transcoded offset: a sub-scalar offset, derived from transcoding The use and interpretation of both `reserved` and `grapheme cache` is not part of Index's ABI; it should be hidden behind non-inlinable calls. However, the position of the sequence of 14 bits allocated is part of Index's ABI, as well as the default value being `0`. */
(edited)Should the Swift language adopt an official style guide and formatting tool?
これに割かれるリソースを想像すると、賛成しづらいね。 (edited)OVERVIEW: Swift Format Tool USAGE: swift [options] <inputs> OPTIONS: -help Display available options -in-place Overwrite input file with formatted file. -indent-switch-case Indent cases in switch statements. -indent-width <n> Number of characters to indent. -line-range <n:n> <start line>:<end line>. Formats a range of lines (1-based). Can only be used with one input file. -o <file> Write output to <file> -tab-width <n> Width of tab character. -use-tabs Use tabs for indentation.
#available(macOS 10.13, *)
がtrue
になる。 @swift-5.0.3
if #available(macOS 10.13, *) { print("#available(macOS 10.13, *)") } else { print("not #available(macOS 10.13, *)") }
#available(macOS 10.13, *)
protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo1() -> F func foo2() -> F } struct S { func foo1() -> __opaque Foo { return 2 } func foo2() -> __opaque Foo { return 3 } } let s = S() var a = s.foo1() let b = s.foo2() a = b
/path/to/opaque-result.swift:17:5: error: cannot assign value of type '(__opaque main.(file).S.foo2()@/path/to/opaque-result.swift:11:10)' to type '(__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)' a = b ^ as! (__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)
(edited)-> (some Foo, some Foo)
が現時点で許されてなかった。 protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foos() -> (F, F) } struct S { func foos() -> (__opaque Foo, __opaque Foo) { return (2, 3) } } let s = S() var (a, b) = s.foos() a = b
/path/to/opaque-result-2.swift:9:21: error: 'opaque' types are only implemented for the declared type of properties and subscripts and the return type of functions func foos() -> (__opaque Foo, __opaque Foo) { return (2, 3) } ^
(edited)S
を S: P
にできてなかった・・・protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo1() -> F func foo2() -> F } struct S: P { func foo1() -> __opaque Foo { return 2 } func foo2() -> __opaque Foo { return 3 } } let s = S() var a = s.foo1() let b = s.foo2() a = b
/path/to/opaque-result.swift:9:8: error: type 'S' does not conform to protocol 'P' struct S: P { ^ /path/to/opaque-result.swift:5:20: note: ambiguous inference of associated type 'F': '(__opaque main.(file).S.foo2()@/path/to/opaque-result.swift:11:10)' vs. '(__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)' associatedtype F: Foo ^ /path/to/opaque-result.swift:11:10: note: matching requirement 'foo2()' to this declaration inferred associated type to '(__opaque main.(file).S.foo2()@/path/to/opaque-result.swift:11:10)' func foo2() -> __opaque Foo { return 3 } ^ /path/to/opaque-result.swift:10:10: note: matching requirement 'foo1()' to this declaration inferred associated type to '(__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)' func foo1() -> __opaque Foo { return 2 } ^ /path/to/opaque-result.swift:17:5: error: cannot assign value of type '(__opaque main.(file).S.foo2()@/path/to/opaque-result.swift:11:10)' to type '(__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)' a = b ^ as! (__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)
S
の foo1
と foo2
の __opaque Foo
が別の型とみなされてるっぽい。protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo() -> F } struct S: P { func foo() -> __opaque Foo { return 42 } } let s = S() var a = s.foo() let b = s.foo() a = b
protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func makeFoo(from: Int) -> F func foo() -> F } struct S: P { func makeFoo(from: Int) -> __opaque Foo { return from } func foo() -> F { return makeFoo(from: 42) } } let s = S() var a = s.foo() let b = s.makeFoo(from: -1) print(a) a = b print(a)
42 -1
foo1
と foo2
を作るとエラーになる・・・opaque-type-ast
で、最近 Joe Groff が触ってるっぽい opaque-type-runtime
が別にありますし。protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo() -> F func foos() -> (F, F) } extension P { func foos() -> (F, F) { return (foo(), foo()) } } struct S: P { func foo() -> Int { return Int.random(in: 0..<100) } } let s = S() var (a, b) = s.foos() print(a) a = b print(a)
9 52
typealias
で opaque result type はまだ許されてないようです。 protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo() -> F } struct S: P { typealias F = __opaque Foo func foo() -> F { return 42 } } let s = S() var a = s.foo() let b = s.foo() a = b
/path/to/opaque-result-0.swift:9:19: error: 'opaque' types are only implemented for the declared type of properties and subscripts and the return type of functions typealias F = __opaque Foo ^
associatedtype
を確定する typealias
とは別ですが、一般的な typealias
は今回の Proposal 外の Future Directions に入ってるので、 protocol
でも将来的なサポートになるかもです。 https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md#opaque-type-aliases (edited)associatedtype
の型を具象型側で some Foo
にした場合、 some Foo
を返す複数 API 間で同一の型として扱えそうです。(some Foo)?
作れた
protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo() -> F func fooOrNil() -> F? } struct S: P { func foo() -> __opaque Foo { return 42 } func fooOrNil() -> F? { return Bool.random() ? foo() : nil } } let s = S() var a = s.fooOrNil() print(String(describing: a)) let b: Int? = -1 a = b // Error
/path/to/opaque-result-3.swift:19:5: error: cannot assign value of type 'Int?' to type '(__opaque main.(file).S.foo()@/path/to/opaque-result-3.swift:10:10)?' a = b // Error ^ as! (__opaque main.(file).S.foo()@/path/to/opaque-result-3.swift:10:10)
(edited)makeFoo() -> <F: Foo> F
↑この記法は何が起きてるかわかりやすいと思った
(edited)func foo(x: some P) { /* ... */ }
func foo<T: P>(x: T) { /* ... */ }
(edited)makeFoo() -> <F: Foo> F
←これは見た目的に直感的にそれが伝わるlet a: <F: Foo> F = makeFoo() typealias Bar = <F: Foo> F
func makeColl<X>() -> some Collection<.Element == X>
(edited)some Foo
と some Foo
なら英語的に別の型を意味しそうという利点はある。struct LazyMapCollection
で書けば良い気がしますが (edited)foo1
の返す some Foo
には Bar
と名前をつけて、 foo2
の返す some Foo
には Baz
と名前をつければ↓のようなことは起こらない。 var a: some Foo = foo1() let b: some Foo = foo2() a = b // `some Foo` と `some Foo` は字面上同じ型っぽいけど別の型なのでエラー
-> <F: Foo> F
のような記法を導入するなら何かジェネリクス的な記法で解決できないかなと。特に具体的なアイデアがあるわけじゃないけど。 typealias Bar = foo1.(some Foo) typealias Baz = foo2.(some Foo)
foo1
の some Foo
間で別名を付けれてしまうので微妙。 var a: Bar = foo1()<Bar> let b: Baz = foo2()<Baz> a = b // エラー: `Bar` を `Baz` に代入できないのは自然に見える let c: Qux = foo1()<Qux> a = c // `foo1` の `some Foo` に別の名前を与えられてしまっている
some Foo
ではなく -> <F: Foo> F
を採用したとして、うまく馴染む記法はないかなぁと。
where
後置記法を導入しちゃったから where
でやろうとすると混ざっちゃうよね。 <>
の中に書けばいけるかもだけど、引数側の型パラと並ぶとややこしそう。 func makeColl<X>() -> some Collection<.Element == X>
(edited)func makeColl<X: Foo>() -> <C: Collection where C.Element == X> C where X.Bar == Int
(edited)where
だけ後置できないの微妙。where
の後置好きじゃない。: some Foo
をつけずに var a = foo1() let b = foo2() a = b // エラー
some
という新しいキーワードによって表現されていて (edited)func makeColl<X: Foo>() -> <C: Collection where C.Element == X> C where X.Bar == Int
↑これを推してる人もいるっぽいですね。明示的なCが手に入るのが良いところfunc makeColls() -> <C: Collection, D: Collection> (C, D) where D.Element ==C { return ([1,2,3] , [ [1], [2] ]) }
こういうのが書けますねopaque-type-ast
ブランチ)では変数の型として some Foo
を書くことはできないみたいです。仕様なのか現状の実装状況がそうなだけなのかわからないけど。public protocol P { mutating func flip() } private struct Witness: P { mutating func flip() { /* ... */ } } public var someP: some P = Witness()
public
がついてるのはグローバル変数だから、ローカル変数とは扱いが異なるかも。let strings: some Collection = ["hello", "world"]
some Foo
で受けられるわけだし、変数で禁止する必要もなさそうに思います。
(edited)var a: some Foo (S.foo1) = s.foo1() let b: some Foo (S.foo2) = s.foo2() a = b // 納得できる代入エラー
(edited)var a: some Foo (P.FooType) = s.foo1() let b: some Foo (P.FooType) = s.foo2() a = b
と書かないとだめ。 (edited)some Foo
はシンタックスシュガーになるみたいなので、戻り値 some Foo
も後からシュガーにできるのかな? func makeFoo() -> some Foo { /*...*/ } // ↑を↓のシュガーにできる? func makeFoo() -> <F: Foo> F { /*...*/ }
func useFoo(_ foo: some Foo) { /* ... */ } // ↑は↓のシュガーとして将来的に導入される模様 func useFoo<F: Foo>(_ foo: F) { /* ... */ }
func makeFooPair() -> <F: Foo> (F, F) { /*...*/ }
-> <F: Foo> (F, F)
みたいなのを ABI 上位表現できなくなったりしないかなと。let c: LazyMapCollection = xs.lazy.map { ... }
struct Wrapper<T> { var value: T } func testParsing() { let wrapped1: Int by Wrapper let wrapped2: Int by Wrapper = Wrapper(value: 5) let wrapped3 by Wrapper = Wrapper(value: 5) _ = wrapped1 _ = wrapped2 _ = wrapped3 }
func testSuppressUnwrap() { var wrapped1 by WrapperWithInitialValue = 5 let z = ^wrapped1 }
dyn
キーワードを付けるようにするという決定が下されて、 Swift での過去の議論では同様のキーワードとして any
が議論されていた。そして、そうなれば、 any Foo
は existential type に、 some Foo
は opaque type になると見越している?some
キーワードが 、ExistentialをAnyHogehogeとして慣習的に使ってる事の対としてキレイって意見自体は、someの打診をしたスレで言ってた気がするany Foo
と some Foo
すごくキレイだと思うんだけど、それなら top type と bottom type も Ceylon みたいに Anything
と Nothing
だったらキレイだったのになぁ。Nothing
の方が bottom type の本質を表している気がする。そして Nothing
の対比としては Anything
だろうなと。Any
と Nothing
だし、 Any
は top type じゃなくて Any?
が top とかグダグダ。Any
と None
とかでもいいのかもだけど、 Any
と None
が対として自然なのかわからない・・・。Optional
の some
はほぼ使うことがないからいいんじゃないかな?.some
はキーワードじゃなくてただの enum
の case
だし。言語の一部ではない。if case let .some(foo) = foo { ... }
が if case let foo? = foo { ... }
みたいなシュガーが用意されてるから、 Optional.some
を使うケースってほぼない気がする。opaque public typealias CertainMutableCollection<T> : MutableCollection & RangeReplaceableCollection = [T]
(edited)any Foo
と some Foo
だとめちゃくちゃキレイ。 https://forums.swift.org/t/se-0244-opaque-result-types/21252/140 PR のブランチをチェックアウトしてコンパイラをビルドすれば…
CIによってビルドされたmacOS用toolchainがPRのコメントに載っているので、それをダウンロードすれば自分でビルドしなくても試せます。 コメント: https://github.com/apple/swift/pull/21137#issuecomment-468118328 toolchain: https://ci.swift.org/job/swift-PR-toolchain-osx/216//artifact/branch-master/swift-PR-21137-216-osx.tar.gz (edited)func makeAnimals() -> <A: Animal> (A, A) { return (Cat(), Cat()) }
(edited)// opaque type alias opaque typealias A: Animal = Cat func makeAnimals() -> (A, A) { return (Cat(), Cat()) }
swift-5.0-RELEASE
とで、SourceKitのレスポンスが違う…DocInfoTests.testDocInfoRequest()
の結果がXcode 10.2と https://swift.org/download/ からダウンロードしたものの結果が違うのです。$ xcrun --toolchain org.swift.5020190325a swift test --filter testDocInfoRequest
で再現できます。 (edited)sourcekitten
を使ったテストでも良いですか?key.request: source.request.docinfo key.sourcetext: " /** Multiple line commented function. **/ func multiLineCommentedFunc() { } "
*
が普通の文字列として判定されるか、リストの bullet として判定されるかの差異ですね。 (edited)let bar: Bar = \Foo.bar(foo)
(edited)func bar(_: Foo)
メソッドを呼び出す keyPath になってしまう。(\Foo.bar)(foo)
どうでしょう。let bar = (\Foo.bar as (Foo) -> Bar)(foo)
(foo)
と let bar: Bar
から (Foo) -> Bar
を期待していると解釈してくれれば let bar: Bar = (\Foo.bar)(foo)
でも通る可能性あるかな?さすがにきついかな…。,
無い場合は普通のfunc ボディ内に書いたのと同じルールで分割されるので、ルール上の問題はないのですが、_ = [ "foo": foo .method() "bar": baz ]
こういうのはつらい。<コーディング規約で禁止するしかない。 (edited)func test() { foo .method() bar }
は func test() { foo.method(); bar; }
であり、そのルールで動くので、上に書いた例でも問題はなくパースできるのですが、リーダビリティ的には止めて欲しいっていう意味です。 (edited)let solutions = [ 2*(1-sqrt(a))*sqrt(b+sqrt(b)) -(1+sqrt(a))(sqrt(2*b)-sqrt(2)) ] // two solutions let solutions = [ 2*(1-sqrt(a))*sqrt(b+sqrt(b)) - (1+sqrt(a))(sqrt(2*b)-sqrt(2)) ] // one solution
$ docker run --privileged -it -v `pwd`:`pwd` -w `pwd` --rm norionomura/swift:pr-22714 swift Welcome to Swift version 5.0-dev (LLVM 771292f12a, Swift 474d293b05). Type :help for assistance. 1> print( 2. "hello" 3. "world" 4. ) hello world
public func matmul_base<T: Numeric>(lhs: [T], rhs: [T], m: Int, n: Int, p: Int) -> [T] { return matmul_internal(lhs: lhs, rhs: rhs, m: m, n: n, p: p) } @inlinable public func matmul_inlinable<T: Numeric>(lhs: [T], rhs: [T], m: Int, n: Int, p: Int) -> [T] { return matmul_internal(lhs: lhs, rhs: rhs, m: m, n: n, p: p) } public func matmul_specialize(lhs: [Double], rhs: [Double], m: Int, n: Int, p: Int) -> [Double] { return matmul_internal(lhs: lhs, rhs: rhs, m: m, n: n, p: p) }
以上のように内部実装は共通でインターフェースだけ違うのを計測してみると、 https://github.com/t-ae/inlinable-performance/blob/master/Tests/inlinable-performanceTests/inlinable_performanceTests.swift
Test Case '-[inlinable_performanceTests.inlinable_performanceTests testBase]' passed (11.864 seconds). Test Case '-[inlinable_performanceTests.inlinable_performanceTests testInlinable]' passed (11.498 seconds). Test Case '-[inlinable_performanceTests.inlinable_performanceTests testSpecialize]' passed (0.340 seconds).
と素のものとinlinableで同程度になっています。 インライン化と同時にじゃないとspecializeされないってのがありそうかなーと思っています。swift test -c release
で計測してる?-emit-module
でモジュール作って、 -I
でインポート とかな気がする--- !Passed Pass: sil-generic-specializer Name: sil.Specialized DebugLoc: File: /Users/araki/Desktop/t-ae/inlinable-performance/Tests/inlinable-performanceTests/inlinable_performanceTests.swift Line: 30 Column: 26 Function: 'closure #1 in inlinable_performanceTests.testInlinable()' Args: - String: 'Specialized function ' - Function: '"inlinable_performance.matmul_inlinable<A>(lhs:rhs:m:n:p:)"' - String: ' with type ' - FuncType: '(@guaranteed Array<Double>, @guaranteed Array<Double>, Int, Int, Int) -> @owned Array<Double>' ...
うーん?--- !Missed Pass: sil-generic-specializer Name: sil.NoDef DebugLoc: File: /Users/araki/Desktop/t-ae/inlinable-performance/Tests/inlinable-performanceTests/inlinable_performanceTests.swift Line: 31 Column: 13 Function: 'closure #1 in inlinable_performanceTests.testInlinable()' Args: - String: 'Unable to specialize generic function ' - Callee: '"XCTest.XCTAssertEqual<A>(_:_:_:file:line:)"' - String: ' since definition is not visible' ...
呼び出し側がミスしてる?// matmul_inlinable<A>(lhs:rhs:m:n:p:) sil [serialized] @$s21inlinable_performance07matmul_A03lhs3rhs1m1n1pSayxGAH_AHS3itSjRzlF : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> (@guaranteed Array<τ_0_0>, @guaranteed Array<τ_0_0>, Int, Int, Int) -> @owned Array<τ_0_0> // matmul_specialize(lhs:rhs:m:n:p:) sil @$s21inlinable_performance17matmul_specialize3lhs3rhs1m1n1pSaySdGAH_AHS3itF : $@convention(thin) (@guaranteed Array<Double>, @guaranteed Array<Double>, Int, Int, Int) -> @owned Array<Double>
SIL的にはinlinableはジェネリックなままみえてそうですね-O
つけわすれてました。 つけるとmatmul_inlinable
が消える代わりにmatmul_internal
がジェネリックなまま出てきますね。'@inlinable' attribute can only be applied to public declarations, but 'matmul_internal' is private
なるメッセージが出るのでpublic onlyと誤解した可能性が高そうです。class C { @inlinable func f() {} // error }
ここでerrorって言ってるのは少なくともコンパイルエラーのことではなさそう@inlinable public func x() { let c = C() c.f() }
Class 'C' is internal and cannot be referenced from an '@inlinable' function
結局CがusableFromInlineじゃないと他のところでコンパイルが通らないですね。[omochi@omochi-iMac-PC43 mdi]$ swiftc -emit-module m.swift [omochi@omochi-iMac-PC43 mdi]$ swiftc -silgen -I . a.swift
↑これでコンパイルできたけど、実行ができない。@inline(always)
とかか。$ swiftc -parse-as-library -emit-object m.swift $ swiftc -emit-module m.swift $ swiftc -I . a.swift m.o -Xlinker -rpath -Xlinker /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx $ ./main
$ swiftc -emit-library m.swift $ swiftc -L . -lm -I . a.swift -Xlinker -rpath -Xlinker /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx $ ./a
@inlinable
にした場合、内部で使っているジェネリックメソッドも@inlinable
にしておかないと、@_specialize
を使わない限り完全にはspecializeされないって事だよね。これは悩ましいな。 (edited)@inlinable internal
にするのがやりたいことではないです? (edited)@inlinable internal
なメソッドを変更した場合、ABIが変わるって事だよね。@usableFromInline internal
にすると、今度はそれを@inlinable
から使った場合に遅くなってしまう。@inlinable
を付けただけだと遅くなる場合がある。@_specialize(T = Int)
の使いどころか?@_specialize
を付けないとダメだった気がする。func gGeneric<T>(_ x: T) { f(x) } func gInt(x: Int){ f(x) } func f<T>(_ x: T) { ... }
@_specialize
がもっと書きやすければいいのだけど。 ジェネリックパラメータが多いと大変。 extension StringProtocol { @usableFromInline @_specialize(where Self == String, S == String, T == String) @_specialize(where Self == String, S == String, T == Substring) @_specialize(where Self == String, S == Substring, T == String) @_specialize(where Self == String, S == Substring, T == Substring) @_specialize(where Self == Substring, S == String, T == String) @_specialize(where Self == Substring, S == String, T == Substring) @_specialize(where Self == Substring, S == Substring, T == String) @_specialize(where Self == Substring, S == Substring, T == Substring) …
(edited)@_specialize(where Self == String x Substring, S == String x Substring , T == String x Substring)
(edited)@inlinable
周りは検証し直さないといけないな。 (edited){ "": { }, "/Users/omochi/temp/mdi/m.swift": { "object": "/Users/omochi/temp/mdi/b/m.swift.o" } }
#!/usr/bin/env ruby require "shellwords" cmd = [ "swiftc", "-emit-dependencies", "-emit-module", "-emit-module-path", "b/m.swiftmodule", "-output-file-map", "map.json", "-parse-as-library", "-emit-object", "/Users/omochi/temp/mdi/m.swift" ] system(cmd.shelljoin)
(edited)Result
とか滑り込みだったっけ?Result
よりは重要そうだけど、実装も大変そうだから余裕ももって 5.1 は回避されるかもしれないなぁ。func main() -> Int { let hoge: Int? = 1 let fuga = hoge .map { $0 * 2 } .map { $0 + 3 } .map { $0 * 4 } return fuga ?? 99 } main()
(edited)error: unable to invoke subcommand: /usr/bin/swift-swiftc (No such file or directory)
(edited)OVERVIEW: Swift compiler USAGE: swift OPTIONS: -assert-config <value> Specify the assert_configuration replacement. Possible values are Debug, Release, Unchecked, DisableReplacement. -continue-building-after-errors Continue building, even after errors are encountered -debug-info-format=<value> Specify the debug info format type to either 'dwarf' or 'codeview' -debug-info-store-invocation Emit the compiler invocation in the debug info. -debug-prefix-map <value> Remap source paths in debug info -D <value> Marks a conditional compilation flag as true -enable-library-evolution Build the module to allow binary-compatible library evolution -enforce-exclusivity=<enforcement> Enforce law of exclusivity -framework <value> Specifies a framework which should be linked against -Fsystem <value> Add directory to system framework search path -F <value> Add directory to framework search path -gdwarf-types Emit full DWARF type info. -gline-tables-only Emit minimal debug info for backtraces only -gnone Don't emit debug info -g Emit debug info. This is the preferred setting for debugging with LLDB. -help Display available options -index-store-path <path> Store indexing data to <path> -I <value> Add directory to the import search path -j <n> Number of commands to execute in parallel -L <value> Add directory to library link search path -l<value> Specifies a library which should be linked against -module-cache-path <value> Specifies the Clang module cache path -module-link-name <value> Library to link against when using this module -modul
(edited)func main() -> Int { let hoge: Int? = 1 let fuga = hoge .map { $0 * 2 } .map { $0 + 3 } .map { $0 * 4 } return fuga ?? 99 }
(edited)sil_stage canonical import Builtin import Swift import SwiftShims func main() -> Int
(edited)protocol X { func say(sentence: String) func say() } struct A: X { // error: Type 'A' does not conform to protocol 'X' func say(sentence: String = "I'm fine, you ?") { print("A : \(sentence)") } }
(edited)<stdin>:6:8: error: type 'A' does not conform to protocol 'X' struct A: X { ^ <stdin>:3:8: note: protocol requires function 'say()' with type '() -> ()'; do you want to add a stub? func say() ^
(edited)func foo() -> Int { return 42 } func foo(_ x: Int = -1) -> Int { return x } print(foo())
foo
に対して↓とか。 foo() foo(_)
nil
を渡してたりしたし、明示的に省略するのは良さそうな気が。public enum RGB: CaseIterable { case r, g, b } public let count = 100_000_000 @inlinable public func testB() -> Int { var result = 0 for _ in 0..<count { for c in RGB.allCases { switch c { case .r: result += 1 default: break } } } return result }
このようなtestBを別モジュールから呼んだ場合RGB.allCases
で毎回Array
生成しているらしくかなりコストが大きくなってしまう…… 自動実装のallCases
がinlinableじゃないってことなんでしょうけど盲点になりそう。 (edited)RGB.allCases
をforの外で呼んだ場合のtestA
との比較public enum RGB: Int, CaseIterable { case r, g, b } public let count = 100_000_000 public func testNormal() -> Int { var result = 0 for _ in 0..<count { for c in RGB.allCases { result += c.rawValue } } return result } @inlinable public func testInlinable() -> Int { var result = 0 for _ in 0..<count { for c in RGB.allCases { result += c.rawValue } } return result }
inlinableなはずのtestInlinable
のほうがArray生成されてtestNormal
より遅くなるという結果に。RGB.allCases.getter
でallocateUninitializedBuffer云々出てます。doIt { [guard weak self] in //... code }
callAsFunction
に決まったみたいallSatisfy
加えるなら someSatisfy
的なのもほしくないですか? and と or 的に。 https://github.com/apple/swift-evolution/blob/master/proposals/0207-containsOnly.mdcontainsOnly
のプロポーザルでしたand
と or
ベースで考えてたからSequence where Element == Bool
に対する API がほしいかも。// x.allFalse() x.allSatisfy(!) !x.contains(true)
たしかに現状使えるのだと前者のほうが短いのでこう書きたくなる(ならない)array.allSatisfy(\.self)
[true, true, true].allSatisfy(\.self)
<stdin>:1:31: error: cannot convert value of type 'WritableKeyPath<_, _>' to expected argument type '(Bool) throws -> Bool' [true, true, true].allSatisfy(\.self) ^~~~~~
x.allSatisfy(Bool.init(_:))
いまつかえるのだとこれですね。\.self
がほぼid
だよand
とか or
が使いやすそう。 [true, true, false].and() [true, true, false].or()
.all
と.any
(edited)allSatisfy
との対では all
がわかりやすいextension Sequence where Element == Bool
で実装したメソッドは全ての Sequence
のサジェストに出てきちゃうんですよね…w (edited)protocol LogicalOperatable
作って static func &&
と static func ||
宣言して、extension Bool: LogicalOperatable
適合して extension Sequence where Element: LogicalOperatable
で and()
と or()
もしくは all()
と any()
作れば問題ない["a"].
だとjoined
の候補でるけど、[1].
だと出てこないから、extension Array where Self.Element == String
はうまく動いてそうElement
conforms to Sequence
. joined()
とか。 https://developer.apple.com/documentation/swift/arrayjoined
はArray<String>に対して書かれているのでますます謎だHTMLBuilder
をXcode 11 betaで動かそうとしてるけど、いまいちうまくいかないな。 https://forums.swift.org/t/pitch-function-builders/25167buildBlock
をサポートしてるのかな?buildExpression
とかも無さげ。@_functionBuilder
はViewBuilder
に最低限必要な機能だけ実装されてるぽい。func colorStyle<S : ColorStyle>(_ style: S.Member) -> Self.Modified<ColorStyleModifier<S>>
みたいな感じでラップされて伝播してた (edited)struct RedStyle : ColorStyle
?RedStyle.Member.blue
とかも生えてる?extension StaticMember where Base : ColorStyle
これのせいで生えてそうMyView().colorStyle(.red)
↑これはなんで解決できるんだ? func colorStyle
のS
は何が推論されてるの?StaticMember.red
はおかしくない?StaticMember<Base>.red
の Base
を何かで埋めないと。.colorStyle(.red)
の推論ね。{ return .init(.init()) }
の方じゃない。struct StaticMember<Base> { init(_ base: Base) {} } protocol ColorStyle { typealias Member = StaticMember<Self> } struct RedStyle : ColorStyle {} struct BlueStyle : ColorStyle {} extension StaticMember where Base : ColorStyle { static var red: RedStyle.Member { return .init(.init()) } static var blue: BlueStyle.Member { return .init(.init()) } } protocol View {} extension View { func colorStyle<S : ColorStyle>(_ style: S.Member) { } } struct MyView : View {} MyView().colorStyle(.red) MyView().colorStyle(.blue)
<S> StaticMember<S>.red
って許されてるのかstruct StaticMember<Base> { init(_ base: Base) {} } protocol ColorStyle { typealias Member = StaticMember<Self> } struct RedStyle : ColorStyle {} struct BlueStyle : ColorStyle {} extension StaticMember where Base : ColorStyle { static var red: RedStyle.Member { return .init(.init()) } static var blue: BlueStyle.Member { return .init(.init()) } } protocol View {} extension View { func colorStyle<S : ColorStyle>(_ style: S.Member) { } } struct MyView : View {} MyView().colorStyle(RedStyle.Member.red) MyView().colorStyle(BlueStyle.Member.red)
(edited)struct Static<Base> {} extension Static { static var foo: Static<Int> { return .init() } } func useMember<T>(member: Static<T>) {} useMember(member: .foo)
(edited)MyView().colorStyle(RedStyle.Member.red) MyView().colorStyle(BlueStyle.Member.red)
↑これのどっちも通るわけですよRedStyle.Member
は StaticMember<RedStyle>
で BlueStyle.Member
は StaticMember<BlueStyle>
だからBase
は不確定だけど Base = <S> S where S : ColorStyle
な状態を解決としてるように思える (edited)MyView().colorStyle(RedStyle.Member.red) MyView().colorStyle(BlueStyle.Member.red) MyView().colorStyle(.red) // これは↑のどちらでもない
struct StaticMember<Base> { init(_ base: Base) {} } protocol ColorStyle { typealias Member = StaticMember<Self> } struct RedStyle : ColorStyle {} struct BlueStyle : ColorStyle {} extension StaticMember where Base : ColorStyle { static var red: RedStyle.Member { print(self) return .init(.init()) } } protocol View {} extension View { func colorStyle<S : ColorStyle>(_ style: S.Member) {} } struct MyView : View {} MyView().colorStyle(.red) MyView().colorStyle(RedStyle.Member.red) MyView().colorStyle(BlueStyle.Member.red)
StaticMember<RedStyle> StaticMember<RedStyle> StaticMember<BlueStyle>
.red
は StaticMember<RedStyle>.red
らしい。.staticProp
は その返り値の型と受ける型が同一って制約があるのか?.red
に対してStaticMember<S: ColorStyle>
の状態で static var red
をconditionalに見つけて、red
の返り値が StaticMember<RedStyle>
だから、.red
が StaticMember<RedStyle>.red
の扱いになるのかstruct StaticMember<Base> { init(_ base: Base) {} } protocol ColorStyle { typealias Member = StaticMember<Self> } struct RedStyle : ColorStyle {} struct BlueStyle : ColorStyle {} extension StaticMember where Base : ColorStyle { static var red: RedStyle.Member { print(self) return .init(.init()) } } protocol View {} extension View { func colorStyle<S : ColorStyle>(_ style: S.Member) {} func anyStyle<S>(_ style: StaticMember<S>) {} } struct MyView : View {} MyView().colorStyle(.red) MyView().anyStyle(.red)
StaticMember<RedStyle> StaticMember<RedStyle>
struct StaticMember<Base> { init(_ base: Base) {} } protocol ColorStyle { typealias Member = StaticMember<Self> } protocol FontStyle { typealias Member = StaticMember<Self> } struct RedStyle : ColorStyle {} struct BlueStyle : ColorStyle {} struct RedFontStyle : FontStyle {} extension StaticMember where Base : ColorStyle { static var red: RedStyle.Member { print(self) return .init(.init()) } } extension StaticMember where Base : FontStyle { static var red: RedFontStyle.Member { print(self) return .init(.init()) } } protocol View {} extension View { func colorStyle<S : ColorStyle>(_ style: S.Member) {} func fontStyle<S: FontStyle>(_ style: S.Member) {} func anyStyle<S>(_ style: StaticMember<S>) {} } struct MyView : View {} MyView().colorStyle(.red) MyView().fontStyle(.red) MyView().anyStyle(.red)
<stdin>:41:20: error: ambiguous use of 'red' MyView().anyStyle(.red) ^ <stdin>:16:16: note: found this candidate static var red: RedStyle.Member { ^ <stdin>:23:16: note: found this candidate static var red: RedFontStyle.Member { ^
StaticMember
ってStaticMember<S>
で引数を受けてる意味はなくてstyle.base
で <S>
を取り出すだけ?func background<Background>(Background, alignment: Alignment) -> Self.Modified<_BackgroundModifier<Background>> func background<S>(S.Member) -> Self.Modified<_BackgroundModifier<Rectangle.Filled<S>>> func background<S>(S.Member, cornerRadius: Length) -> Self.Modified<_BackgroundModifier<RoundedRectangle.Filled<S>>> func background<S>(S, cornerRadius: Length) -> Self.Modified<_BackgroundModifier<RoundedRectangle.Filled<S>>>
func background<S>(_ content: S.Member, cornerRadius: Length) -> Self.Modified<_BackgroundModifier<RoundedRectangle.Filled<S>>> where S : ShapeStyle func background<S>(_ content: S, cornerRadius: Length) -> Self.Modified<_BackgroundModifier<RoundedRectangle.Filled<S>>> where S : ShapeStyle
(edited)protocol CopyInitializable {} extension CopyInitializable { init(copy: Self) { self = copy } } class Animal : CopyInitializable { init() {} convenience init(a: Int) { let copy = Animal() // `Self`にforce castしたいがここに`Self`が書けないので // self.init(copy: copy as! Self) // type(of: self)でSelf型を作って、 // それをジェネリクスに渡すことで`as!`を実行できる self.init(copy: forceCast(copy, to: type(of: self))) } } func forceCast<X, T>(_ x: X, to type: T.Type) -> T { return x as! T }
import Foundation let gregorian = Calendar(identifier: .gregorian) let utc = TimeZone(identifier: "UTC")! let components = gregorian.dateComponents(in: utc, from: Date()) func date(from components: DateComponents, with nanosecond: Int) -> Date { var components = components components.nanosecond = nanosecond return components.date! } print(date(from: components, with: 999499976).description) print(date(from: components, with: 999499977).description)
2019-06-25 10:39:38 +0000 2019-06-25 10:39:39 +0000
import Foundation let iso8601WithFractionalSecondFormatter: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSS" formatter.timeZone = TimeZone(secondsFromGMT: 0) return formatter }() let gregorian = Calendar(identifier: .gregorian) let utc = TimeZone(identifier: "UTC")! let components = gregorian.dateComponents(in: utc, from: Date()) func date(from components: DateComponents, with nanosecond: Int) -> Date { var components = components components.nanosecond = nanosecond return components.date! } let date1 = date(from: components, with: 999499976) print(iso8601WithFractionalSecondFormatter.string(from: date1)) print(gregorian.dateComponents(in: utc, from: date1).nanosecond!) let date2 = date(from: components, with: 999499977) print(iso8601WithFractionalSecondFormatter.string(from: date2)) print(gregorian.dateComponents(in: utc, from: date2).nanosecond!)
2019-06-25T11:26:43.999000 999499917 2019-06-25T11:26:44.000000 999500036
YAMLEncoder
で、10000回に5回くらいしか起きないバグの原因になってる。 (edited)DateFormatter
、ドキュメントによるとdateFormat
文字列には Unicode Technical Standard #35 を使うと書かれてるけど、S
Fractional Secondの扱いが Unicode Technical Standard #35 と違ってるんだ。 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html#//apple_ref/doc/uid/TP40002369-SW13 (edited)Fractional Second - truncates (like other time fields) to the count of letters.
となってるけど、実際の挙動はtruncateではなくroundになってる。 http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns (edited)public struct Date : ReferenceConvertible, Comparable, Equatable { fileprivate var _time: TimeInterval }
import Foundation print(Date(timeIntervalSinceReferenceDate: 0)) print(Date().timeIntervalSinceReferenceDate)
(edited)import Foundation let iso8601WithFractionalSecondFormatter: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSS" formatter.timeZone = TimeZone(secondsFromGMT: 0) return formatter }() var d = Date(timeIntervalSinceReferenceDate: 0) for _ in 0..<10 { print(iso8601WithFractionalSecondFormatter.string(from: d)) d += 0.0001 }
2001-01-01T00:00:00.000000 2001-01-01T00:00:00.000000 2001-01-01T00:00:00.000000 2001-01-01T00:00:00.000000 2001-01-01T00:00:00.000000 2001-01-01T00:00:00.001000 2001-01-01T00:00:00.001000 2001-01-01T00:00:00.001000 2001-01-01T00:00:00.001000 2001-01-01T00:00:00.001000
import Foundation let iso8601WithFractionalSecondFormatter: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSS" formatter.timeZone = TimeZone(secondsFromGMT: 0) return formatter }() let gregorian = Calendar(identifier: .gregorian) let utc = TimeZone(identifier: "UTC")! let components = gregorian.dateComponents(in: utc, from: Date()) var d = Date(timeIntervalSinceReferenceDate: 0) // 2001年 for _ in 0..<10 { print(gregorian.dateComponents(in: utc, from: d).nanosecond!) d += 0.000001 } d = Date(timeIntervalSinceReferenceDate: 86400 * 365 * 20) // 2021年 for _ in 0..<10 { print(gregorian.dateComponents(in: utc, from: d).nanosecond!) d += 0.000001 }
0 1000 2000 3000 4000 5000 5999 6999 8000 9000 0 953 1907 2861 3814 4768 5722 6675 7629 8583
JSONEncoder
は標準機能だと、任意のDate
のインスタンスを元の値を維持したままDateFormatter
使ってエンコード/デコード出来ないから、Date()
のround tripテストだけ特別なことしてる。 https://github.com/apple/swift/blob/master/test/stdlib/TestJSONEncoder.swift#L255-L257 (edited)DateFormatter
使ったエンコード/デコードだ。Double
にするから大丈夫だったはず。Double
だ。JSONSerialization
がDouble
の精度を落としちゃうからダメって書かれてる…JSONEncoder
は任意のDate
インスタンスのエンコード/デコードを標準の手法でテストされてない?ISO8601DateFormatter
も精度が足りないし。DateFormatter
がsub-millisecondで四捨五入する挙動はドキュメントと一致していない、というissueを登録した。 https://bugs.swift.org/browse/SR-11049Package.swift
のdependencies
を .package(url: "https://github.com/apple/swift-syntax", .revision("swift-DEVELOPMENT-SNAPSHOT-2019-07-10-m")),
に書き換えてswift-5.1-DEVELOPMENT-SNAPSHOT-2019-07-09-a
を使うと動いた。 (edited) .package(url: "https://github.com/apple/swift-syntax", .revision("xcode11-beta1")),
Unable to format ../../SwiftLint/Package.swift: SwiftSyntax parser library isn't compatible
とか言われて動かない。libSyntax
が使う型情報のハッシュを計算しておいて、実行時にこれを照合して互換性があるライブラリかチェックしてるぽい。 https://github.com/apple/swift/blob/master/utils/gyb_syntax_support/__init__.py#L164-L170 (edited)SwiftSyntax
に依存すると、TOOLCHAINにバンドルしないとほぼ成り立たないツールになってしまう様に見える。 (edited)struct Foo { var value: Int func a() -> Int { return value } mutating func b() -> Int { value += 1; return value } } print(type(of: Foo.a))
(Foo) -> () -> Int
struct Foo { var value: Int func a() -> Int { return value } mutating func b() -> Int { value += 1; return value } } print(type(of: Foo.b))
<stdin>:7:20: error: partial application of 'mutating' method is not allowed print(type(of: Foo.b)) ^
(inout Foo) -> () -> Int
じゃダメですか?is encouraged
から is *required*
に変わってたのか。protocol Animal: AnyObject { func foo() -> Int } class Cat: Animal { func foo() -> Int { return 42 } } let cat = Cat() let animal: Animal = cat print(MemoryLayout.size(ofValue: cat)) print(MemoryLayout.size(ofValue: animal))
// A はinterface void foo(a A) { a.hoge(); a.fuga(); a.piyo(); }
_swift_dynamicCast
が担ってて、実装はこれ。 https://github.com/apple/swift/blob/master/stdlib/public/runtime/Casting.cpp#L2271
case MetadataKind::Existential: return _dynamicCastToExistential(dest, src, srcType, cast<ExistentialTypeMetadata>(targetType), flags);
searching the methods
とは書いてあるけど、そのへんは、JVM規格側なんで、__TEXT,__swift5_proto
セクションにprotocol conformance descriptor
へのポインタが列挙されてる。protocol conformance descriptor
には以下 が含まれる。 protocol descriptor
プロトコルnominal type descriptor
そのプロトコルに準拠する型。メタデータとは別witness table
その型がプロトコル準拠するに必要なwitness一覧nominal type descriptor
にはメタデータへのポインタが含まれる。swift_conformsToSwiftProtocol
は渡されたメタデータが準拠するプロトコル情報を探してきてくれる。実装はこちら。 https://github.com/apple/swift/blob/swift-5.1-branch/stdlib/public/runtime/ProtocolConformance.cpp#L544-L608
(edited) * frame #0: 0x00000001012f3d13 libswiftCore.dylib`swift_conformsToSwiftProtocolImpl(… frame #1: 0x00000001012f3a56 libswiftCore.dylib`swift_conformsToProtocol [inlined]… frame #2: 0x00000001012f3a11 libswiftCore.dylib`swift_conformsToProtocol [inlined]… frame #3: 0x00000001012f3a11 libswiftCore.dylib`swift_conformsToProtocol… frame #4: 0x00000001012c4efa libswiftCore.dylib`swift::_conformsToProtocol… frame #5: 0x00000001012c8e07 libswiftCore.dylib`_conformsToProtocols… frame #6: 0x00000001012c849e libswiftCore.dylib`_dynamicCastToExistential(…
(edited)otool -l /usr/lib/swift/libswiftCore.dylib
で見る__TEXT,__swift5_proto
のサイズは0x1288バイトで、含まれるprotocol conformance descriptorの数は 4744/8=593 Section sectname __swift5_proto segname __TEXT addr 0x0000000000379758 size 0x0000000000001288 offset 3643224 align 2^2 (4) reloff 0 nreloc 0 flags 0x00000000 reserved1 0 reserved2 0
(edited)YamsPackageTests
だとサイズ0x5a4
でprotocol conformance descriptorの数は180__swift5_types
ですね。 Section sectname __swift5_types segname __TEXT addr 0x000000000037a9e0 size 0x000000000000065c offset 3647968 align 2^2 (4) reloff 0 nreloc 0 flags 0x00000000 reserved1 0 reserved2 0
Any
、引数で使われた場合は Never
になるんでしょうか? protocol Animal { associatedtype Foo associatedtype Bar func foo() -> Foo func bar(_ x: Bar) -> Bar } struct Cat: Animal { func foo() -> Int { ... } func bar(_ x: Bool) -> Bool { ... } } struct Dog: Animal { func foo() -> Int { ... } func bar(_ x: String) -> String { ... } } let animal: any Animal<Foo == Int> = Bool.random() ? Cat() : Dog() print(type(of: animal.foo)) // () -> Int print(type(of: animal.bar)) // これは (Never) -> Any になる?
set
と get
で型がずれちゃいますよね。ただ、ある引数が Never
ってアクセスできないことを意味しているとも考えられて、 set
が Never
== readonly と解釈すればおかしくないかも。
any P
で set
がつぶれることと any P
が P
を満たすことは両立できるんじゃないかな?たとえば↓のコードで、 getSetValue
に foo
を渡すときに existential container を分解して特殊化されてない版を実行させることに問題はなくない? protocol Foo { associatedtype Value var value: Value { get set } } struct Bar: Foo { var value: Int } func getSetValue<F: Foo>(of foo: F) -> F { var foo: F = foo let value: F.Value = foo.value // get foo.value = value // set return foo } var foo: any Foo = Bar(value: 42) let value: Any = foo.value // foo.value = value // NG foo = getSetValue(of: foo)
var a = [1, 2, 3] func f(_ index: Int) { let b = Array(a[index...]) + Array(a[..<index]) print(b) } f(0) // [1, 2, 3] f(1) // [2, 3, 1] f(2) // [3, 1, 2]
f
の実装に関して、簡潔に書いたらこうなったんですがパフォーマンスが悪そう どういうのがいいかなArray(a[index...] + a[..<index])
にすると少しだけ良くなりそう.first(where:)
をメソッドチェーンの末尾に使う場合はメソッドチェーンの開始に.lazy
を入れると早くなる、とか (edited)ArraySlice
みたいな型を作ってラップして返すのが良さそう。Array
になってほしい時だけ選択的にコストを払って変換。Collection
が SubSequence
みたいに associatedtype
持っててもおもしろそう。循環コレクションから循環コレクションとっても循環コレクションのままでいいので二重にラップされないみたいな。Range
の SubSequence
が Range
自身なように。lazy
方式かなぁ。ShiftedArray<Element>
とかより Shifted<Base: Collection>
とかの方がいいかな?名前は別として。Index
が Int
の縛りがないと難しいかな。String
の Index
も Int
でいいんじゃない?Shifted
を生成する部分を O(1) にしたいのではなく?Shifted(str, str.index(str.startIndex, offsetBy: 4))
とか書かざるを得ないと思うのでShifted(str, 4)
として 内部で同じことをすることはできるけど、まあその場合もこれ以上早い方法は無いし。Shifted<String>
があったとして、その subscript
のインデックスはずらして循環させた String
と同じものである必要があるよね?そのインデックスを生成する手間を考えたら、そのシフトされた String
を生成した方が速くない?なので、 インデックスが Int
以外のときは Shifted
が不要な気がする。より正確に言えば、 Int64
とか O(1) で計算できるやつはできるけど、そこはサポートしなくてよいかと。Element
は同じものを期待してるけど、Shifted.Index
で良い String
で事前に O(N) 支払うならその API が返す型は String
でよくない?Collection
に associatedtype ShiftedCollection: Collection where ShiftedCollection.Element == Self.Element, ShiftedCollection.Index == Self.Index
を付けて func shifted(by: Int) -> ShiftedCollection
として、 Array
は Shifted<Array<Element>>
、 String
は String
を返すのがいいかなぁ。 shifted
が O(1) か O(N) かが型によって変化するのが微妙かな。protocol Iterator { associatedtype Element associatedtype Failure: Error func _next() throws -> Element? } extension Iterator { func next() throws -> Element? { try _next() } } // 例外飛ばない版 // 本体の定義が`_next`なのは、もし`next`だとここで自己再起しちゃうから。。 extension Iterator where Failure == Never { func next() -> Element? { try! _next() } } // こういう仕組みが必要なのかもしれない // iterator.nextの呼び出しから例外が飛ばないことがXの型からわかるとき、 // dropCからも例外を飛ばないということを宣言したい // rethrows句自体はすでにあるけど、クロージャを渡した場合に限られた機能なので、 // これを拡張する。 func dropC<X: Iterator>(iterator: X, n: Int) rethrows(from iterator.next) { for _ in 0..<n { _ = try iterator.next() } }
func next() throws(when Failure!=Never) -> Element?
func next() throws(Failure) -> Element?
こうかければいいんだ。func dropC<X: Iterator>(iterator: X, n: Int) throws(X.Failure)
(edited)throws
は throws(Swift.Error)
と同じ意味として互換性が得られるthrows
付き関数を try
なしで呼んで結果を Result
で受け取れてもいいように思いますが。let a = Result { try f() }
で包む必要があるねResult
が戻り値の関数を try
付きで呼ぶのも。try f().get()
になるやつ。func f() throws(BError) -> T let r = Result<T, AError> { try f() }
func f() throws(BError) -> T let r = Result { try f() } // 型パラメータ不要
try!
相当は欲しいですね。
() -> Void
is () throws -> Void
でしょ。parseInt
の NumberFormatException
が非検査例外なのが飛び抜けてヤバいのに印象が引っぱられてて、他はそうでもないと思うようになりました。NullPointerException
で台無しってのと、 try!
がないってのはその通りで、そのあたりの相乗効果で使いづらい。class MyE extends Exception{}; class A<E extends Exception> { void f() throws E {} } public class Main { public static void main(String[] args) throws Exception { try { new A<MyE>().f(); } catch (MyE e) {} } }
catch(A|B|C e) {
ができる。class A { typealias Error = NumberFormatException | IOException; void f() throws Error { } }
A.Error
で再throwsを書ければA.f(float x) throws Error
みたいな関数があったときに、 アプリモジュールで追加してる Angle
みたいな型があったとして、a.f(angle)
ってそのまま渡せるように A.f(Angle angle) throws Error
を追加するみたいなケース。 実装としては、angle.valueを元のfに転送するだけ。enum DownloadError // ←ここに名前がつくかどうかじゃなくて { case cacheFile(FileIOError) // ←このケースと case downloadFileIOError(FileIOError) // このケースが区別できるのがtag case network(NetworkError) }
(edited)DateComponents.date
がmacと比べてめちゃくちゃ遅いっぽい…… ubuntuはDocker上でしかためしてないんでオーバーヘッドはあると思いますが流石にこの差はなにかありますよね? mac: $ cat main.swift import Foundation var comps = DateComponents( calendar: Calendar(identifier: .gregorian), timeZone: TimeZone(secondsFromGMT: 0)! ) comps.year = 2019 comps.month = 11 comps.day = 10 comps.hour = 10 comps.minute = 10 comps.second = 10 comps.nanosecond = 10 let start = Date() var total: TimeInterval = 0 for _ in 0..<10_000 { total += comps.date?.timeIntervalSinceNow ?? 0 } print("time: \(Date().timeIntervalSince(start))sec, \(total)") $ swiftc -O main.swift $ ./main time: 0.011227965354919434sec, 14087090925.878277
ubuntu on dokcer on mac: root@15ebe51be255:/var/vapor-app# swiftc -O main.swift root@15ebe51be255:/var/vapor-app# ./main time: 1.0126609802246094sec, 14088576437.363556
$ swiftc -O main.swift $ ./main time: 0.4973900318145752sec, 14075794190.737843
$ pbpaste|docker-swift41 -O - time: 0.272868990898132sec, 14066769520.098 $ pbpaste|docker-swift42 -O - time: 0.32283997535705566sec, 14066729445.257643 $ pbpaste|docker-swift50 -O - time: 0.5901000499725342sec, 14066676853.962284 $ pbpaste|docker-swift51 -O - time: 0.5475540161132812sec, 14066619176.887285 $ pbpaste|docker-swiftnightly -O - time: 0.779636025428772sec, 14066542714.192633 $ pbpaste|swift42 -O - time: 0.013067960739135742sec, 14066457781.279984 $ pbpaste|swift50 -O - time: 0.01632702350616455sec, 14066407578.90026 $ pbpaste|swift51 -O - time: 0.014343976974487305sec, 14066365250.005945
(edited)# cat main2.swift import Foundation let df = DateFormatter() df.dateFormat = "yyyy/MM/dd HH:mm:ss.SSSSSSS" let start = Date() var total: TimeInterval = 0 for _ in 0..<10_000 { let year = 2019 let month = 11 let day = 10 let hour = 10 let minute = 10 let second = 10 let microsecond = 101010 let date = df.date(from: "\(year)/\(month)/\(day) \(hour):\(minute):\(second).\(microsecond)") total += date?.timeIntervalSinceNow ?? 0 } print("time: \(Date().timeIntervalSince(start))sec, \(total)")root@15ebe51be255:/var/vapor-app# swiftc -O main2.swift root@15ebe51be255:/var/vapor-app# ./main2 time: 0.32200801372528076sec, 14063930163.143023
$ pbpaste|docker-swift42 -O - time: 0.27475202083587646sec, 14051535205.538399 $ pbpaste|swift42 -O - time: 0.012828946113586426sec, 14051471374.175034
_CFCalendarComposeAbsoluteTimeV
がICUを呼び出しているところだと思うんですが、 DateFormatter
使った方はmacOSとそんなに変わらないのね。 targets: [ .target(name: "Complex", dependencies: ["Real"]), .target(name: "Numerics", dependencies: ["Complex", "Real"]), .target(name: "NumericsShims", dependencies: []), .target(name: "Real", dependencies: ["NumericsShims"]), ... ]
sqrt
の前に x * x
とかでオーバーフローしないって意味ですよね?sqrt(x*x + y*y)
として実装するとオーバーフローしちゃうかもしれないから (edited)float function hypot(float a, float b) { let [float x, float y] = [ // x ≥ y max(abs(a), abs(b)), min(abs(a), abs(b)) ]; if (x == 0) // x = y = 0 return 0; // avoid division by zero let float t = y / x; // 0 < t ≤ 1 return x * sqrt(1 + t * t); // > 0 }
Complex
に sqrt
がなさそう
import Numerics let a: Complex<Double> = -1 let i: Complex<Double> = .sqrt(a) print(i)
(edited)import Numerics extension Complex { static func *(lhs: Self, rhs: RealType) -> Self { return Self(lhs.real * rhs, lhs.imaginary * rhs) } static func *(lhs: RealType, rhs: Self) -> Self { return Self(lhs * rhs.real, lhs * rhs.imaginary) } } extension Complex where RealType: ElementaryFunctions { static func sqrt(_ z: Self) -> Self { let length = z.length let phase = z.phase return .sqrt(length) * Self(.cos(phase / 2), .sin(phase / 2)) } } let a: Complex<Double> = -1 let i: Complex<Double> = .sqrt(a) print(i)
(6.123233995736766e-17, 1.0)
Complex
はSignedNumeric
じゃないんですね。 https://developer.apple.com/documentation/swift/signednumeric
func f(_ x: X) -> X { print("top-level") return x } struct X { static func f(_ x: X) -> X { print("static") return x } } let x = X() let _: X = .f(x) // static
func f(_ x: X) -> X { print("top-level") return x } struct X { static func f(_ x: X) -> X { print("static") return x } } let x = X() let _: X = .f(x) // static
swift-5.1-RELEASE
static
func f(_ x: X) -> X { print("top-level") return x } struct X { // static func f(_ x: X) -> X { // print("static") // return x // } } let x = X() let _: X = .f(x)
func f(_ x: X) -> X { print("top-level") return x } struct X { // static func f(_ x: X) -> X { // print("static") // return x // } } let x = X() let _: X = .f(x)
swift-5.1-RELEASE
/usercode/main.swift:14:13: error: type 'X' has no member 'f' let _: X = .f(x) ~^~~~
struct X {} struct Y { static func f(_ x: X) -> X { print("Y") return x } } let x = X() let _: X = .f(x)
struct X {} struct Y { static func f(_ x: X) -> X { print("Y") return x } } let x = X() let _: X = .f(x)
swift-5.1-RELEASE
/usercode/main.swift:11:13: error: type 'X' has no member 'f' let _: X = .f(x) ~^~~~
_: X = .f(x)
だったらXの方で解決されますよgetCachedResultOrEmit
みたいなものを経由するようになっててllvm::Value *
なんだよねSILType
とか出会うたびにそれが呼ばれるからasync/await
だけじゃなく actor
までいくかな?actor
までいけば Logic Failure をハンドリングできるようになるかも。func makeAnimalPair() -> <A: Animal> (A, A) { ... }
Foo<some Animal>
と some Animal<.Foo == Bar>
は別の話だったany
も進めてほしいなぁ。 any
はシンタックスだけの話だから対応は一番簡単だと思う。any
必須にするだけで?any Sequence<.Element == Int>
型引数に any Sequence<.Element == Int>
を渡せないし。 (edited)some Sequence<.Element == Int>
なら渡せるのおもしろいな。extension any Equatable: Equatable { static func ==... }
any Sequence
の Sequence
準拠するかな?AnySequence
は SubSequence
も決まってるしなー。AnyCollection
とか Index
まで決まってるし。any Sequence<Element = Int>
と any Sequence<Element = Int, SubSequence = Array<Int>>
はSubSequence
は Collection
かSequence
は Iterator
と Element
しか持ってないんだった。typealias AnySequence<E> = any Sequence<Element = T, SubSequence = any Sequence... //
←ここが再帰すると書けないな? (edited)SubSequence
出てこない。Collection
の話としてlet c: any Collection<.Element == Int> = [2, 3, 5] let sc = c[1...]
sc
の型はどうなるの?any Collection<.Element == Int>
? (edited)AnyCollection.SubSequence
が AnyCollection
だからAny
になるか、コンパイルエラーのどっちか。some Collection
かも。some Collection
じゃないかしら?associatedtype SubSequence : Collection = Slice<Self> where Self.Element == Self.SubSequence.Element, Self.SubSequence == Self.SubSequence.SubSequence
Any
まではいかなくて、↑にマッチする最も抽象的な型?Never
を指定したことにして呼び出せないっていうのが良いんじゃないかlet c
に束縛されたsomeです。var c
だったら、再代入のたびに identityが変わる。protocol Foo { associatedtupe X var x: X { get set } }
var foo: any Foo = ... let x = foo.x foo.x = ...
x
の型は Any
で、 set
のときは Never
じゃないといけないよね?protocol Foo { associatedtupe X: Bar var x: X { get set } }
var foo: any Foo = ... let x = foo.x
これなら x
は any Bar
でいける。 x
の型は 空の some
じゃないですかね?var foo
に対しての some。get foo.x
と set foo.x
は、同じ型 同じだけど具体的に見えないから some。var x
に再代入があったら、そこから仕切り直し。some
にしようとすると x
の型をコンパイル時に決定しないといけないんじゃない?var foo: any Foo = Foo1() let x = foo.x foo = Foo2() foo.x = x
set
は Never
じゃないと。foo
が some
なら get
して set
できていいけど、 any
だからインスタンスから差し替えられる。foo = Foo2()
の行がある時点でfoo
は別のidentityであることが静的に解析できるから。foo.x
の型が変わっちゃだめじゃない?var foo: any Foo = Foo1() let x = foo.x foo = Foo2() foo.x = x
↑これは↓のような解釈 let foo_1: any Foo = Foo1() let x = foo_1.x let foo_2 = Foo2() foo_2.x = x // foo_2.Xとfoo_1.Xは違う型だからコンパイルエラー
set
を Never
扱いの方が自然だと思うけど。var
の型が変わるというのは変だと思う。var c: AnyCollection<Int> = foo() print(c[c.startIndex])
var c: AnyCollection<Int> = foo() let s = c.startIndex print(c[s]) // 絶対OK c = goo() print(c[s]) //クラッシュしうる
(edited)any Collection<.Element == Foo, .Index == Int>
とかでもいいわけだし。.Index == Int
の指定があるなら、再代入の前後でIndexに型互換性があることをチェックできる。.Index == Hoge
の指定を省略した時の挙動を考えてました。protocol Foo { associatedtype X: Bar where Baz: X var x: X { get set } }
Index
が contravariant position で Never
になっちゃうのでいいんじゃない?ってこと。Never
まで飛ばずに contravariant position を any Baz
にとどめられる。Baz: X: Bar
って関係ってことBaz < X < Bar
って書いた方がいいかな?Never < Baz < X < Bar < Any
==
)だけAnyCollection
はデフォルト型パラがなかったから、しかたなく型パラを制限してるだけでlet c: any Collection<E = Int> = foo() print(c[c.startIndex])
これすら使えなくなっちゃう (edited)associatedtype
を型パラに持ってた方が良かったと思うんよね。get
して set
が必要なときは指定すればいいんだから。let c: any Collection<E = Int, I = Int> = foo() print(c[c.startIndex])
↑こうしろって事ですか?typealias
だって書けるわけだし。class C1 {} class C2: C1 {} class C3: C2 {} protocol Foo { associatedtype X: C1 where C3: X var x: X { get set } }
swift: /home/buildnode/jenkins/workspace/oss-swift-5.1-package-linux-ubuntu-16_04/llvm/include/llvm/Support/Casting.h:255: typename cast_retty<X, Y *>::ret_type llvm::cast(Y *) [X = swift::DependentMemberType, Y = swift::TypeBase]: Assertion `isa<X>(Val) && "cast<Ty>() argument of incompatible type!"' failed. Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret - -disable-objc-interop -I /Libraries/.build/x86_64-unknown-linux/debug -I /Libraries/.build/checkouts/swift-nio/Sources/CNIOAtomics/include -I /Libraries/.build/checkouts/SwiftBacktrace/Sources/CSwiftBacktrace/include -I /Libraries/.build/checkouts/SwiftBacktrace/Sources/Clibunwind/include -I /Libraries/.build/checkouts/OpenCombine/Sources/COpenCombineHelpers/include -module-cache-path /Libraries/.build/x86_64-unknown-linux/debug/ModuleCache -D DEBUG -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOAtomics.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/checkouts/SwiftBacktrace/Sources/CSwiftBacktrace/include/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/checkouts/SwiftBacktrace/Sources/Clibunwind/include/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/COpenCombineHelpers.build/module.modulemap -module-name main -lLibraries 1. While type-checking 'Foo' (at <stdin>:5:1) 2. While validating 'Foo' (at <stdin>:5:1) /usr/bin/swift[0x493d544] /usr/bin/swift[0x493b160] /usr/bin/swift[0x493d968] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7fe4b6470390] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x38)[0x7fe4b4995428] /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a)[0x7fe4b499702a] /lib/x86_64-linux-gnu/libc.so.6(+0x2dbd7)[0x7fe4b498dbd7] /lib/x86_64-linux-gnu/libc.so.6(+0x2dc82)[0x7fe4b498dc82] /usr/bin/swift[0x1447f41] /usr/bin/swift[0x1447cef] /usr/bin/swift[0x1458fb0] /usr/bin/swift[0x144c03f] /usr/bin/swift[0x1470c04] /usr/bin/swift[0x14ce5b9] /usr/bin/swift[0x1455f72] /usr/
protocol P{} protocol Q { associatedtype P: X }
protocol P{} protocol Q { associatedtype P: X }
swift-5.1.3-RELEASE
/usercode/main.swift:3:21: error: use of undeclared type 'X' associatedtype P: X ^
protocol P1 {} protocol P2 {} protocol Q { associatedtype X where X: P1, X: P2 }
protocol P1 {} protocol P2 {} protocol Q { associatedtype X where X: P1, X: P2 }
swift-5.1.3-RELEASE
class C1 {} class C2: C1 {} class C3: C2 {} protocol Foo { associatedtype X where C3: X var x: X { get set } }
swift: /home/buildnode/jenkins/workspace/oss-swift-5.1-package-linux-ubuntu-16_04/llvm/include/llvm/Support/Casting.h:255: typename cast_retty<X, Y *>::ret_type llvm::cast(Y *) [X = swift::DependentMemberType, Y = swift::TypeBase]: Assertion `isa<X>(Val) && "cast<Ty>() argument of incompatible type!"' failed. Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret - -disable-objc-interop -I /Libraries/.build/x86_64-unknown-linux/debug -I /Libraries/.build/checkouts/swift-nio/Sources/CNIOAtomics/include -I /Libraries/.build/checkouts/SwiftBacktrace/Sources/CSwiftBacktrace/include -I /Libraries/.build/checkouts/SwiftBacktrace/Sources/Clibunwind/include -I /Libraries/.build/checkouts/OpenCombine/Sources/COpenCombineHelpers/include -module-cache-path /Libraries/.build/x86_64-unknown-linux/debug/ModuleCache -D DEBUG -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOAtomics.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/checkouts/SwiftBacktrace/Sources/CSwiftBacktrace/include/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/checkouts/SwiftBacktrace/Sources/Clibunwind/include/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/COpenCombineHelpers.build/module.modulemap -module-name main -lLibraries 1. While type-checking 'Foo' (at <stdin>:5:1) 2. While validating 'Foo' (at <stdin>:5:1) /usr/bin/swift[0x493d544] /usr/bin/swift[0x493b160] /usr/bin/swift[0x493d968] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7f433d746390] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x38)[0x7f433bc6b428] /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a)[0x7f433bc6d02a] /lib/x86_64-linux-gnu/libc.so.6(+0x2dbd7)[0x7f433bc63bd7] /lib/x86_64-linux-gnu/libc.so.6(+0x2dc82)[0x7f433bc63c82] /usr/bin/swift[0x1447f41] /usr/bin/swift[0x1447cef] /usr/bin/swift[0x1458fb0] /usr/bin/swift[0x144c03f] /usr/bin/swift[0x1470c04] /usr/bin/swift[0x14ce5b9] /usr/bin/swift[0x1455f72] /usr/
$ cat lower.swift class C1 {} class C2: C1 {} protocol Foo { associatedtype X where C2: X } $ swiftc lower.swift Stack dump: ... <unknown>:0: error: unable to execute command: Segmentation fault: 11 <unknown>:0: error: compile command failed due to signal 11 (use -v to see invocation)
(edited):>
使って簡単に lower bound を書けるのかぁ。 class A {} class B extends A {} trait T[-X >: B] { def bar(x: X): Unit } class C extends T[A] { def bar(x: A): Unit = { println("C.bar: " + x) } } val c: C = new C() val t: T[B] = c t.bar(new B())
C.bar: Playground$B@16418d1a
class C1 {} class C2: C1 {} protocol Foo { associatedtype X where C2: X }
error: type 'C2' constrained to non-protocol, non-class type 'Self.X'
class C1 {} class C2: C1 {} protocol Foo { associatedtype X where C2: X } print(1)
diagnose
は↑のエラーメッセージが出ることをX: AnyObject
でもダメなのかな? X
自体はクラスじゃないしダメ?C: P
の右側のPはプロトコルじゃないといけないから: AnyObject
なら絶対クラスだよな。文言からすると protocol じゃないのが駄目なのか? (edited)X: C
は書けるしな。associatedtype
は仮定の話だけど、クロージャ型は現実の課題。class C1 {} class C2: C1 {} func foo<X>(_ x: X) where C2: X {}
<stdin>:4:29: error: type 'C2' constrained to non-protocol, non-class type 'X' func foo<X>(_ x: X) where C2: X {} ^
class BaseViewController<Inherit: UIViewController>: Inherit {}
<stdin>:5:30: error: type 'C2' constrained to non-protocol, non-class type 'Self.X' associatedtype X where C2: X ^
<stdin>:5:30: error: type 'C2' constrained to non-protocol, non-class type 'Self.X' associatedtype X where C2: X ^
Inherit
のは upper bound じゃない?それぞれ、 Inferit
がBaseViewController
の、 UIViewController
が Inherit
の。Inherit
に対して BaseViewController というlower boundを与えているのでは さっきから実験している <X> C: X
の形なのでInherit
不定の状態がイメージできなかったけど、ジェネリック関数の中とかだといいのか。X
に対する upper/lower bounds が効いてるか。 class C1 {} class C2<X: C1>: X { func foo(_ x: X) -> X { x } func bar() { let c1: C1 = foo(self) } }
template <Self> class RetainCounted<Self> { Self * retain() { ... } } class Cat: public RetainCounted<Cat> {} auto c1 = new Cat(); auto c2 = c1->retain(); // c2の型がCat*になってほしい
(edited)c1
は Cat *
だけど c2
は Cat
? Cat *
?とかわけわからなくなったので Swift に直してみたけど、↓みたいなことって理解で良い? class X<K> { func foo() -> K { ... } } class C: X<C> {} let c1 = C() let c2 = c1.foo() // c2の型がCになってほしい
class X<K> { var value: K { fatalError() } func foo() -> K { value } } class C: X<C> { override var value: C { self } } let c1 = C() let c2 = c1.foo() print(c2)
(edited)class X<K> { var value: K { fatalError() } func foo() -> K { return value } } class C: X<C> { override var value: C { return self } } let c1 = C() let c2 = c1.foo() print(c2)
(edited)print("Hello world")
?:
の評価は4通りやるにせよ、計算効率がめっちゃよくなる?(適切にone-wayを入れられる場所なら)numbers[indicesOfEvens]
の結果のコレクションにおけるインデックスの扱いがどうなるんだろうと思ってたけど(他の言語みたいに array[range]
のインデックスが 0
から始まらないのでどう整合性をとるのかなと)、↓こうなるのかぁ。 subranges
, where m is the number of /// elements indicated by subranges
. public subscript(subranges: RangeSet<Index>) -> DiscontiguousSlice<Self> { get set } } /// A collection wrapper that provides access to the elements of a collection, /// indexed by a set of indices. public struct DiscontiguousSlice<Base: Collection>: Collection { /// The collection that the indexed collection wraps. public var base: Base { get set } /// The set of index ranges that are available through this indexing /// collection. public var subranges: RangeSet<Base.Index> { get set } /// A position in an DiscontiguousSlice
. struct Index: Comparable { // ... } public var startIndex: Index { get } public var endIndex: Index { set } public subscript(i: Index) -> Base.Element { get } public subscript(bounds: Range<Index>) -> Self { get } } ```ArraySlice
とかのインデックスの取り扱いって他言語と比較して特殊だと思うんだけど( 0 スタートになるのが多い)、その利点や不利益についてどこかで論じられてたりしたっけ?Index == Int
に関しては?[0]
がスライスの開始点を現せるようにしたところで、 [1]
ができちゃったらダメだし。 (edited)Collection
プロトコルを考える時点で無理かな?Array
や ArraySlice
については Collection
にひきづられて今の仕様になってるってこと?ゼロスタートしないこと自体に何かの利益があるわけではなくて。Array
は 0 スタートだけど、 ArraySlice
もコピーなく 0 スタートにできるけどしない理由についてだね。 (edited)func fifth<C: Collection>(_ c: C) -> C.Element? { return c[.first + 4] } let array = [1,2,3,4,5,6,7,8,9] print(fifth(array)!) // 5 print(fifth(array[2...])!) // 7
fifth
の中身では .first + 4
という形で 「4番目」と書いてるけど、array
でも array[2...]
でも、「4番目」が得られてる。ArraySlice
を 0スタートにすると Slice
にならなくなる。 ArraySliceがSliceでありながら0スタートになるようにすると、 StringのSliceが作れなくなる。ArraySlice
が使いにくくなってるとしたら悲しいから、 ArraySlice
が 0 始まりじゃないことによる利点はないのかな?Collection
としてまとめて扱えること?ArraySlice
が 0 はじまりじゃないこと自体が使いやすいケースもあると思うんだけどなぁ。if let i = absences.firstIndex(where: { $0 > 0 }) { // 1 let absencesAfterFirst = absences[(i + 1)...] // 2 if let j = absencesAfterFirst.firstIndex(where: { $0 > 0 }) { // 3 print("The first day with absences had \(absences[i]).") // 4 print("The second day with absences had \(absences[j]).") } }
firstIndex
的なメソッドでは startIndex
を引数に取る事が多いけどvar array = [5, 4, 3, 2, 1] array[1...3].sort() print(array) // 5, 2, 3, 4, 1
へえ。subscript set
で O(N) になる?get
もか。その subscript get/set
の O(N) を省略できたら理想的なんだけどね。オーナーシップで扱えるようになるのかもしれないけど、そこらへんを賢くやってくれるとうれしいなぁ。sort
の例ってどこから出てきたの? rintaro さんのリンク先になくない?struct GetModify { var x: String = "👋🏽 Hello" var property: String { get { print("Getting",x); return x } modify { print("Yielding",x) yield &x print("Post yield",x) } } }
https://forums.swift.org/t/modify-accessors/31872var array = [5, 4, 3, 2, 1] var slice = array[1...3] slice.sort() array[1...3] = ArraySlice(Array(slice)) print(array)
(edited)subscript set
しても同じ結果に。var array = [5, 4, 3, 2, 1] var slice = array[1...3] slice.sort() array[1...3] = slice print(array) // 5, 2, 3, 4, 1
[5, 2, 3, 4, 1]
array
の 1...3
にコレクションを代入してるだけでArraySlice
のインデックスが 0 はじまりでないからできることの例になってない気がする。var array = [5, 4, 3, 2, 1] var slice = array[1...3] slice.sort() array[0...1] = slice print(array)
[2, 3, 4, 3, 2, 1]
subscript set
されるときに ArraySlice
側のインデックスが利いてくるのかと思ったんよね。ImageSlice
は ArraySlice
を踏襲してたつもりだけど 2 次元では真似できないな・・・。 < 幅も変えられる (edited)set
がなかった・・・。サイズ違いは precondition
してるのかと思ってたけど。 public subscript(xRange: Range<Int>, yRange: Range<Int>) -> ImageSlice<Pixel> { return ImageSlice(image: self, xRange: xRange, yRange: yRange) }
extension
スコープからしかアクセスできないようなアクセス修飾子ほしくないですか? private
より狭いやつ。 @swift-5.1.5
struct Foo {} extension Foo { private var a: Int { 42 } func f() { print(a) } // OK } extension Foo { func g() { print(a + 1) } // これを禁止したい } Foo().g()
f
のサブの処理を a
に切り出すようなときに、 a
が他から可視である必要がないので。ローカル関数でもいいんですけど、スコープがネストするし個人的に読みづらい気がするんですよね。extension
で共通に使われるけど他からは使われない関数とかだとローカル関数にできないし。extension
をファイルを分けて private
にするとかがいいんでしょうか。private
は今の fileprivate
だったような気もしてきました・・・。class Foo { private var fooPrivate = "private property" } extension Foo { func bar() -> String { return fooPrivate } } print(Foo().bar())
class Foo { private var fooPrivate = "private property" } extension Foo { func bar() -> String { return fooPrivate } } print(Foo().bar())
swift-5.1.3-RELEASE
private property
class Foo { private var fooPrivate = "private property" } extension Foo { func bar() -> String { return fooPrivate } } print(Foo().bar())
class Foo { private var fooPrivate = "private property" } extension Foo { func bar() -> String { return fooPrivate } } print(Foo().bar())
swift-3.0.1-RELEASE
/usercode/main.swift:7:16: error: 'fooPrivate' is inaccessible due to 'private' protection level return fooPrivate ^ /usercode/main.swift:2:17: note: 'fooPrivate' declared here private var fooPrivate = "private property" ^
private
は今の fileprivate
で、その後レキシカルスコープまで狭められたけど、さらにその後同一の型の extension
からは参照できるように修正された?fileprivate
な便利メソッドを extenion
で既存の型に生やしたりします。 (edited)private
な API (メソッドやプロパティ)って同じファイル内でも別の型からは呼べなくないですか? (edited)CGRect.integral()
をImageResizerから呼んでいます。 import UIKit import AVFoundation public struct ImageResizer: ImageProcessing { ... } private extension CGRect { func integral(_ scale: CGFloat) -> CGRect { return CGRect(x: floor(origin.x * scale) / scale, y: floor(origin.y * scale) / scale, width: ceil(size.width * scale) / scale, height: ceil(size.height * scale) / scale) } }
private extension
は fileprivate
になるとさっき調べていたときに見た気がします。private
は fileprivate
だということだったと思います。struct Foo { var value: Int } extension Foo { private var squareValue: Int { value * value } func f() { print(squareValue) } } struct Bar { var foo: Foo func g() { print(foo.squareValue + 1) } } Bar(foo: Foo(value: 42)).g()
<stdin>:12:26: error: 'squareValue' is inaccessible due to 'private' protection level func g() { print(foo.squareValue + 1) } ^~~~~~~~~~~ <stdin>:6:17: note: 'squareValue' declared here private var squareValue: Int { value * value } ^
squareValue
の private
を fileprivate
にするか、 private extension Foo
にしないといけないんですが、後者の場合 f
のアクセスレベルにも影響があるので f
を分離しないといけません。が、 fileprivate
にしたいものだけ分離して private extension Foo
のように書けば良いので fileprivate
は要らないってことですか?private extension Foo
と書くよりも、個別に fileprivate
を書いた方がわかりやすいような気がします。private extension
方式で書いていると、本物の private
と混同してしまったりはしませんか?しないからその方式なのかもしれませんが・・・。private func
とかは結局 fileprivate
なんですよね。private extension CGRect { ... }
^ こう書いて、ファイルないの他のTypeから使えるの、自然だと思ってたけど私が単にそれに慣れただけって気がしてきましたね。class Foo { ... // 同じ型なのでprivate extension Fooのメソッド・プロパティが呼べる } private extension Foo { ... }
^ これと (edited)class Foo { ... // fileprivate extension CGRectのメソッド・プロパティが呼べる } fileprivate extension CGRect { ... }
これを使い分ける方が一貫性がありそう、っていう話ですね? (edited)private extension Foo { func f() { ... } }
と extension Foo { fileprivate func f() { ... } }
が同じになるのは、前者では字面上の private
に引きづられて fileprivate
ではなく private
と勘違いしそうだなという感じです。僕が慣れてないだけかもしれませんが。private
がほしいなと思ったと。 scopeprivate
みたいな感じか、今の private
を typeprivate
みたいにするか。まあでも今でも複雑だし、 tarunon さんの言うようにファイル分けるが現実的そうですね。func f(_ array: [[Int]]) -> Int { array .filter { $0.count > 2 } .reduce([Int.min, Int.min]) { switch ($0, $1) { case let (max, new) where max[0] > new[0]: return max case let (max, new) where max[0] == new[0]: return max case let (max, new) where max[0] < new[0]: return new } }[1] } print(f([[0, 0], [0, 1], [0, 3]]))
<stdin>:5:9: error: switch must be exhaustive switch ($0, $1) { ^ <stdin>:5:9: note: add missing case: '(_, _)' switch ($0, $1) { ^
SWIFT_CROSS_MODULE_OPTIMIZATION = YES
とすると、-cross-module-optimization
が有効になる。_forEachField()
ってのが入ってる。 https://github.com/apple/swift/pull/29042mutable func updateAllDynamicProperties()
自体は十分に作れる素養はあるので (edited)foo( label1: { }, label2: { } )
とあんまり辛さが変わってないと思うけど・・・ (trailing closureの嬉しさは末尾の })
の回避にあると思う).first { ... }
とか良い例で、初見だとナンジャコリャでしょArraySlice<Element>
にできて Slice<Array<Element>>
にできないことってありますか?一見、 where Base.Index == Int
とか色々付けて特殊化すればパフォーマンス含めて同じにできそうに思うんですが・・・。どうして typealias ArraySlice<Element> = Slice<Array<Element>>
でないのかという疑問です。初期の Swift ではできなかったから?C
を持たずにバッファだけを持てば Array
のがわの分だけ領域が節約できると。Array
は struct
だからメモリアクセスの手数は変わらなくできないかな?Slice
ってサボって楽するためのものでしかなくて、個別実装した方が実行効率は良くなるのか。SliceProtocol
があって、デフォルト実装いっぱいついてて、バッファの部分だけ個別実装とかもありだったのかな。Slice
がある分には良さそう (edited)ArraySlice
の方が効率良くて Slice
はサボり用ということで納得しました。ありがとう (edited)AnyCollection
だと Slice
より良い実装が無い、とかあるかしら?URLSession
ってまだ Alamofire が動かないくらいダメなの? https://forums.swift.org/t/alamofire-on-linux-possible-but-not-release-ready/34553callAsFunction
は subscript
との一貫性を考えたら subscript
のように特殊構文が良かった気がする・・・。 struct Foo { invoke(a: Int, b: String) -> Bar { ... } }
みたいな。 subscript
が getForSubscripting
や setForSubscripting
でないのと同じように。 (edited)let foo = value.callAsFunction
ができるのがうれしい理由の一つ?Foo.init
もあるし……という気がしますね。callAsFunction
って subscript
と同種の存在だと思うんだけどなぁ。[]
で呼び出すか ()
で呼び出すか。subscript
はジェネリックだし・・・throws
はまだできないんだっけ?struct Foo { subscript(i: Int) throws -> String { "XYZ" } }
(edited)<stdin>:2:23: error: expected '->' for subscript element type subscript(i: Int) throws -> String { "XYZ" } ^ <stdin>:2:23: error: expected subscripting element type subscript(i: Int) throws -> String { "XYZ" } ^ <stdin>:2:23: error: single argument function types require parentheses subscript(i: Int) throws -> String { "XYZ" } ^~~~~~ ( )
(edited)throws
はまだできないのか。昔議論されたまま放置か。// SE-0249 let getUserId1: (User) -> String = \.id // OK //print(\.id(user)) // NG print(user[keyPath: \.id]) // OK
// SE-0253 struct GetUserId { func callAsFunction(_ user: User) -> String { user.id } } let getUserId = GetUserId() //let getUserId2: (User) -> String = getUserId // NG let getUserId2: (User) -> String = getUserId.callAsFunction // OK print(getUserId(user)) // OK
KeyPath
は関数型に暗黙変換できるけど value(...)
の呼び出しはできない。 callAsFunction
は関数型に暗黙変換できないけど value(...)
の呼び出しはできる。callAsFunction
、↓すらできないのか・・・。関数のように見えるけど関数として渡せないって辛くないのかな。 let ids: [String] = users.map(getUserId)
//let getUserId2: (User) -> String = getUserId // NG
の時点で当然な気はしますね。KeyPath
と比べるとバランス悪いなと。.callAsFunction
つければいいとか書いてあるけど。callAsFunction
が関数型に暗黙変換できるなら、 KeyPath
も単に callAsFunction
付ければ良かっただけだと思うんよね。users.map(getUserId.callAsFunction)
より users.map({ getUserId($0) })
が短い()
呼び出しできないものが関数型に暗黙変換できて、 ()
呼び出しできるものが関数型に暗黙変換できないって、バランスの取り方としてどうなんだと。let getUserId1: (User) -> String = \User.id // OK let getUserId2 = \.id as (User) -> String // OK let getUserId3 = \User.id as (User) -> String // NG
これはバグっぽいですねcallAsFunction
、無名 func
にすれば良かったのでは。そうすれば新しいキーワードも要らないし、 func
が関数ということとも一貫性があるし。 struct Foo { func(_ x: Int) -> Bar { ... } } let foo = Foo() foo(42)
(edited)func setForSubscripting
は変って感じ? init
が static func initializeAsInstance
でないのは?プロパティ初期化が特殊だから?func
良かったと思うんだけどなぁ。キーワードかぶることもないし、これまでのコードを殺すこともないし。初見殺しより特定のメソッド名に役割を与える方が微妙な気が。KeyPath
との変換の整合性はどう思う?KeyPath
は関数型に暗黙型変換できるようになったけど、 ()
呼び出しはできない。 callAsFunction
が付いてる型は ()
呼び出しできるけど関数型に暗黙変換できない。[keyPath: keyPath]
形式は、さっきと同じで、左辺値の概念が入ってるんで、callAsFunction
が暗黙的変換されるようになったら、 SE-0249 で色々がんばらなくても KeyPath
に callAsFunction
付けたらそれで済むと思うんだけど、それだと key path 式に限らず KeyPath
全般が変換できるようになるのと、 KeyPath
に ()
でコールできるようになるのでまずいかな?foo[keyPath: \.bar]
と \.bar(foo)
は主従が逆だからいいんじゃないかな? callAsFunction
を持つのは KeyPath
側、 subscript(keyPath:)
を持つのは Foo
側。
throws!
という概念を追加して、 try
できるけど何も書かない場合は try!
扱い というのはどうか、という話題が発生して、 そこにクリスラトナーが、 足し算のオーバーフローや配列の範囲外アクセスを throws!
であることにすれば、 エラーハンドリングできるようになる って意見を書いている https://forums.swift.org/t/handling-c-exceptions/34823/38?u=omochimetaru!
が常に付いてる Optional であるのと同じノリで、 throws! な func は デフォルトで try!
が付いてる(ただしはがせる) みたいな感じっすなthrows!
でのインポートは、 Python interop にもあったら便利だった、というのもある。func fatalError(...) throws! -> Never
とか?preconsition
とか assert
は最適化でエラー投げたり投げなかったりになるけどいいのか?と思ったけど、別にシグネチャ的には問題なさそう。!
が氾濫するの、 Obj-C のときもそうだったけど、仕方ないとはいえ辛みがあるなぁ。 Obj-C は大分解消されてきたのに。foo.eraseToAnyPublisher()
って AnyPublisher(foo)
するのと何か違うんでしょうか?swift -frontend -repl
とかしてたまに使うintegrated REPLをswift
から削除するにあたり、使ってる人がいるかどうか調査するスレッドが出来てたので、コメントしておいた。 https://forums.swift.org/t/rfc-removing-the-integrated-repl/35441/4enum Foo { case a(Int, String) } switch Foo.a(42, "XYZ") { case .a(let x): print("\(x.0), \(x.1)") }
(edited)42, XYZ
stderr:<stdin>:6:9: warning: cannot match several associated values at once, implicitly tupling the associated values and trying to match that instead case .a(let x): ^
(edited)print([true, true, true].allSatisfy()) print([true, false, true].allSatisfy())
<stdin>:1:37: error: missing argument for parameter #1 in call print([true, true, true].allSatisfy()) ^ <#(Bool) throws -> Bool#> Swift.Sequence:3:28: note: 'allSatisfy' declared here @inlinable public func allSatisfy(_ predicate: (Self.Element) throws -> Bool) rethrows -> Bool ^ <stdin>:2:38: error: missing argument for parameter #1 in call print([true, false, true].allSatisfy()) ^ <#(Bool) throws -> Bool#> Swift.Sequence:3:28: note: 'allSatisfy' declared here @inlinable public func allSatisfy(_ predicate: (Self.Element) throws -> Bool) rethrows -> Bool ^
extension Sequence where Element == Bool { func allSatisfy() -> Bool { return allSatisfy { $0 } } } print([true, true, true].allSatisfy()) print([true, false, true].allSatisfy())
RangeSet
https://github.com/apple/swift-evolution/blob/master/proposals/0270-rangeset-and-collection-operations.md が revert された。@extensible enum
ができたらいいよねみたいなスレ。if let
直したら半分くらいのコードはビルドできなくなりそう$ /usr/bin/python --version Python 2.7.16
if let foo =
は if case let foo? =
があるけどダメ?case
省略したい気持ちはすごくわかる。 switch
の case
もだるい・・・。switch
のcase
めっちゃ書いてるな()
や {}
と色合わせてほしい気も? (edited)struct
のプロパティで let
使った方が良さそうな新しい(?)パターンを発見した。 (参考) https://qiita.com/omochimetaru/items/7265e440418b38088ccb (edited)Identifiable
みたいなやつで、 id
をキーにした Dictionary
を作った場合、 id
か書き換えられるとキーと整合性が取れなくなる。Dictionary
の使い方の問題の範疇かな?id
の場合はキーとして使われやすいのでそういうケースが起こりやすいという違いはあるけど。struct Foo: Identifiable { typealias ID = Int var id: ID var value: String } let foos: [Foo] = [ .init(id: 1, value: "ABC"), .init(id: 2, value: "XYZ"), ] var idToFoo: [Foo.ID: Foo] = .init(uniqueKeysWithValues: foos.map { ($0.id, $0) }) print(idToFoo) idToFoo[1]?.id = 999 print(idToFoo)
[1: main.Foo(id: 1, value: "ABC"), 2: main.Foo(id: 2, value: "XYZ")] [1: main.Foo(id: 999, value: "ABC"), 2: main.Foo(id: 2, value: "XYZ")]
1
で引ける Foo
の id
が 999
になってしまっているのが、 id
が let
なら防げる。他のプロパティでも同じことだけど、 id
はキーとなることが想定されているものだから(そのために Hashable
が強制されてるわけだし)、 let
の方が望ましそう。id
と value
は let
宣言しますね struct Foo: Identifiable { typealias ID = Int let id: ID let value: String }
id
を let
にしても↓はできてしまうみたいな話ですね。 struct Foo: Identifiable { let id: Int var value: String } struct Bar { var foo: Foo } var bar = Bar(foo: Foo(id: 1, value: "ABC")) //bar.foo.id = 999 // NG bar.foo = Foo(id: 999, value: bar.foo.value) // OK
var
が適切でない既知パターンとしては複数のプロパティが連動しているケースで片方だけ変更された場合に困るということがありましたが( didSet
等で辻褄を合わせるか、変更を禁止するか)、 id
はキーとしての利用が想定されているので Dictionary
に入れた後で変更されると困るというのは新しいパターンかなと。0 4157596543199416033 1 4157596543199416033 2 4157596543199416033 3 4157596543199416033 4 4157596543199416033 5 4157596543199416033 6 4157596543199416033 7 4157596543199416033 8 4157596543199416033 9 4157596543199416033
(edited)struct A: Hashable, CustomStringConvertible { let value: Int var description: String { value.description } func hash(into hasher: inout Hasher) { hasher.combine(0) // ハッシュは常にコンフリクトする } } let set1 = Set((0..<10).map(A.init)) let set2 = Set((0..<10).map(A.init)) set1.union(set2) .forEach { print($0, $0.hashValue) }
(edited)Hashable
は Equatable
ですね。CVarArg
めっちゃくわしいひといますか?extension Optional : CVarArg { }
がなぜ動くのか知りたい..._ObjectiveCBridgeable
が関係してそうなんだけど。import Foundation extension Optional: CVarArg { } struct S { } let a: Any? = S() let s = String(format: "%@", a) print(s)
<stdin>:2:1: error: type 'Optional<Wrapped>' does not conform to protocol 'CVarArg' extension Optional: CVarArg { ^ Swift.CVarArg:2:9: note: protocol requires property '_cVarArgEncoding' with type '[Int]' var _cVarArgEncoding: [Int] { get } ^ <stdin>:10:15: error: incorrect argument labels in call (have 'format:_:', expected 'repeating:count:') let s = String(format: "%@", a) ^~~~~~~ repeating count: <stdin>:10:30: error: cannot convert value of type 'Any?' to expected argument type 'Int' let s = String(format: "%@", a) ^ as! Int
Foundation.framework
あるなしの違い... (edited)Foundation.framework
に魔法があるのか// protocol witness for CVarArg._cVarArgEncoding.getter in conformance A? sil shared [transparent] [serialized] [thunk] @$sxSgs7CVarArg4mainsABP05_cVarB8EncodingSaySiGvgTW : $@convention(witness_method: CVarArg) <τ_0_0> (@in_guaranteed Optional<τ_0_0>) -> @owned Array<Int> { // %0 // user: %2 bb0(%0 : $*Optional<τ_0_0>): // function_ref CVarArg<>._cVarArgEncoding.getter %1 = function_ref @$ss7CVarArgP10Foundations21_ObjectiveCBridgeableRzrlE05_cVarB8EncodingSaySiGvg : $@convention(method) <τ_0_0 where τ_0_0 : CVarArg, τ_0_0 : _ObjectiveCBridgeable> (@in_guaranteed τ_0_0) -> @owned Array<Int> // user: %2 %2 = apply %1<τ_0_0?>(%0) : $@convention(method) <τ_0_0 where τ_0_0 : CVarArg, τ_0_0 : _ObjectiveCBridgeable> (@in_guaranteed τ_0_0) -> @owned Array<Int> // user: %3 return %2 : $Array<Int> // id: %3 } // end sil function '$sxSgs7CVarArg4mainsABP05_cVarB8EncodingSaySiGvgTW'
CVarArg
で struct
とか Optional<T>
が動かなくてひじょうにしんどいのになんかこれは動くしなんやねんos_log
とかで不便すぎる"\(foo)"
ってしてます。{private}
とか。Any?
にすればなんでもいけるようになる"{private}%s %s", "\(foo)", "\(bar)"
ちょっとうろ覚えですけどこんな感じっすね。左側は必要に応じてマスクできるようにしておいて、右辺(というのか?)はStringにしちゃう。 (edited)“\(foo)”
は型検査もクソもないから僕も嫌いだけど楽だから使っちゃってる (edited)Foundation.framework
ない状況ないから、Any
== id
の世界がいい...%@
はos_log
ではデフォでprivate”\(private: foo)”
なら自作できそうdo { let _ = publisher.sink { value in ... } ... }
ってやっちゃうと即時キャンセルされちゃうけど、 let cancellable =
にしても、その後使わないから警告になっちゃうから、ただスコープの間参照を保持しつづけてくれるみたいなの。 (edited)public extension UIView { public func neko() -> String { "meow"} }
が複数存在したときとかextension
で someMethod
が被ったとして、別ファイルで、 import Foo extension Int { func foo_someMethod() -> Int { someMethod() } }
を作るなどして呼び分ける?import as
とか)。まあ、最悪やらないといけなかったときになんとかする手段ということで。foo.(ModuleName)bar
とかできるといいですよね。 (edited)foo.[Module]bar()
(edited)protocol
に適合して外部モジュールに食わせるとかもできなくなりそうです・・・。[]
は subscript
と衝突するかなと。[]
はいいかもですね。 foo.[Module]bar
foo.Module::bar
よりも foo.[Module]bar
の方が読みやすそう。 (edited)dropLast(while:)
と suffix(while:)
がほしくなった。 Array
で持っていて、指定された範囲にマッチするものを抜き出したい。history .dropLast(while: { $0.date >= range.upperBound }) .suffix(while: { $0.date >= range.lowerBound })
のようなことがしたくなる。 RandomAccessCollection
なら dropLast(while:)
と suffix(while:)
があってもいいと思う。 SortedDictionary
があれば解決する話ではあるけど。suffix(while:)
の方は lastIndex(where:)
で意図どおりなんでしょうか?lastIndex(where:)
だとインデックスしかとれないんで、それを使って SubSequence
がほしいですね。firstIndex(where:)
なのかと思ったのですがprefix(_:Int)
と suffix(_:Int)
と prefix(while:)
と drop(while:)
と firstIndex(where:)
と lastIndex(where:)
はあるんですよね。index(where:)
が firstIndex(where:)
になったように、 drop(while:)
を dropFirst(while:)
に変えたくなってしまいそうなやつですね。dropLast
がないからそのままですが、 dropLast
が導入されたら禍根を残しそうですね。dropLast(while:)
がないのは多分複雑どがO(n)になるからじゃないですかね drop
も理屈上O(n)になるのか drop(while:)
はSequenceのメソッドだから、dropLast(while:)
作りたいならせめてCollectionじゃないと無理ですねRandomAccessCollection
を想定しています。BidirectionalCollection
でいいのかな? (edited)index(before:)
が BidirectionalCollection
にしかなくて、なので lastIndex(where:)
も Collection
にはないね。 https://developer.apple.com/documentation/swift/bidirectionalcollectionBidirectionalCollection
に更新したけど、問題なくテスト通った。class NoValue {} class OneValue { var x: Int } let noValue = NoValue() let oneValue = OneValue(x: 0)
<stdin>:2:7: error: class 'OneValue' has no initializers class OneValue { var x: Int } ^ <stdin>:2:22: note: stored property 'x' without initial value prevents synthesized initializers class OneValue { var x: Int } ^ = 0 <stdin>:5:16: error: 'OneValue' cannot be constructed because it has no accessible initializers let oneValue = OneValue(x: 0) ^~~~~~~~
public
なら、default initializerは internal
になるって書いてありました。そんなこと書いてあったのか。記憶になかった。 https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html#ID20 (edited)init()
が要求されてるから、継承したクラスで init()
をなくせないように required
をつけなきゃいけないということですね。import Foundation import XCTest public final class EncodableTests: XCTestCase { struct S: Encodable { func encode(to encoder: Encoder) throws { var c = encoder.singleValueContainer() try c.encode(A()) try c.encode(B()) // EXC_BREAKPOINT } } struct A: Encodable { var a: Int = 1 } struct B: Encodable { var b: Int = 2 } func testEncodeDouble() throws { let encoder = JSONEncoder() _ = try encoder.encode(S()) } }
func encode(to encoder: Encoder) throws { try A().encode(to: encoder) try B().encode(to: encoder) }
"\(foo)"
の実装がどうなるのか見た https://github.com/apple/swift/blob/87df9961a5c8ec5f6db11ae2587c13ae57ac1bd4/stdlib/public/core/StringInterpolation.swift<T>
5. Any.Type
の場合 の5つのオーバーロードがあるっぽい_print_unlocked
っていう謎の関数に転送されていてString.init<T>(describing: T)
の実装も、 _print_unlocked
への転送だった。_print_unlocked
の実装は結構面白くて_openExistential
を使ってる if _openExistential(type(of: value as Any), do: _isOptional) { let debugPrintable = value as! CustomDebugStringConvertible debugPrintable.debugDescription.write(to: &target) return }
<T>
を受けるので必ずopenできるがその先でoptionalかチェックするから、これでAnyの中に入ってるOptionalとかをおそらく必ず展開できて、 OptionalだからCustomDebugStringConvertibleが通るMirror(reflecting: value)
foo
はオプショナルの場合の fix-itが2つ出るが、片方は、 String(describing: foo)
への書き換えだから、 String.init(describing:)
の実装と 4の実装が同じ( _print_unlocked
)なのは暗黙的な規約っぽい。_print_unlocked
は第二引数でStreamを取れるけどstdlib internalなので、 これはユーザーに提供されていない事になる。 String.init(describing:)
を使ってエミュレートできるけど、ストリーム処理可能な場合にパフォーマンスロスが出ちゃう。 public func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) { print("error \(error, "")") }
↑Optionalの時に警告出るけど、第二引数でデフォルト与えるようにしたくてOptional( ... )
いらん)extension DefaultStringInterpolation { public mutating func appendInterpolation<T>(_ x: Optional<T>, _ defaultValue: String) where T: TextOutputStreamable, T: CustomStringConvertible { if let x = x { self.appendInterpolation(x) } else { self.appendInterpolation(defaultValue) } } public mutating func appendInterpolation<T>(_ x: Optional<T>, _ defaultValue: String) where T: TextOutputStreamable { if let x = x { self.appendInterpolation(x) } else { self.appendInterpolation(defaultValue) } } public mutating func appendInterpolation<T>(_ x: Optional<T>, _ defaultValue: String) where T: CustomStringConvertible { if let x = x { self.appendInterpolation(x) } else { self.appendInterpolation(defaultValue) } } public mutating func appendInterpolation<T>(_ x: Optional<T>, _ defaultValue: String) { if let x = x { self.appendInterpolation(x) } else { self.appendInterpolation(defaultValue) } } }
UTF16View
の count
って内部表現が UTF-16 のときは O(1) になりますか?[Character]
とか NSString
とかで書いた方が良さそうですね。struct Foo { lazy var value = 1 mutating func reset() { $__lazy_storage_$_value = nil } } var foo = Foo() print(foo.value) foo.value = 2 print(foo.value) foo.reset() print(foo.value)
(edited)$__lazy_storage_$_{name}
への直接アクセスは意図されておらず、塞がれるのでご注意を。$
がキーワードとして使えるようになった影響で穴が空いた?$__lazy_...
は アクセスしないために $
ついてたのかなって思った。 (edited)lazy
って @Lazy
にならないのかな?@Lazy var foo: Foo = ...
これの時に、initialValue/wrappedValueのinitをescaping autoclosureにしていても、宣言した位置で...
が評価されてるからダメみたいですね@Lazy
が例に挙げられてなかったっけ?Codable
の実装初期PRでは、 KeyedDecodingContainer
はKeyedDecodingContainerProtocol
に準拠するけど、 KeyedEncodingContainer
はKeyedEncodingContainerProtocol
に準拠してない、 と一貫性がなかったのを思い出した。 https://github.com/apple/swift/pull/9004#pullrequestreview-37206353 (edited)Dictionary
が Array
に encode
される。 @swift-5.2.5
import Foundation struct Foo: Hashable { var value: String } extension Foo: Encodable { func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(value) } } let dictionary: [Foo: Int] = [Foo(value: "a"): 42] let data = try! JSONEncoder().encode(dictionary) let string = String(data: data, encoding: .utf8)! print(string)
String
だと object になる。JSONEncoder
側は、 encode
前に型で判断してるのか。 (edited)encode
で対応しようがないよね?encode
を独自実装しても?mutating func encode<T>(_ value: T, forKey key: KeyedEncodingContainer<K>.Key) throws where T : Encodable
こいつが呼ばれるときに一度 JSONEncoder
に処理が戻って、そこから Encodable.encode
が呼ばれるか判断されるってことやんね?encode
実装で対処可能ってことは。 (edited)Dictionary
のラッパーを用意して、それの encode/decode
を実装することにした。さすがに保持してる型の encode/decode
は色んなプロパティが生えてて辛いので。struct Entry { var manyCountArray: [Foo] mutating func update() { ... } } var map: [String: Entry] = ... for item in items { guard var entry = map[item.name] else { continue } entry.update(item) map[item.name] = entry }
↑このパターンって素朴な解釈では map[item.name]
と var entry
で 2つ参照があるから entry.update
の中で manyCountArray
の要素を変更したら配列のコピーが発生しそうだけど、 どうだろう (edited)map[item.name]?.update(item)
こう書けばmodifyなので参照1つなのが表明できるんだけど。 (edited)update
が manyCountArray
を変更するならコピー発生するんじゃないかな?( entry
に一度代入するパターンのとき) (edited)map[item.name]?.update(item)
のパターンで書くとき、根っこから深くなればなるほど、そのパスをたどらないといけなくて辛いなと思うんだけど、変数に代入する代わりに inout
なクロージャに渡して別名付けるとかすればいいのかな。modify(map[item.name]) { entry in entry.update(item) }
的な。map[item.name]
の get から set までに map[item.name]
が触られていないことを検出して、entry
へのコピーを共有に書き換えてくれたらコピーされずに済みそうだけどそこまでやってくれるのかなあと思って。map[item.name]?.modify { entry in entry... entry... }
って書いてます実際は
modify
しようとしたら inout
で同時編集のエラーにならない?foo.bar.modify { (bar) in bar.a.modify { (a) in bar.b.modify { (b) in bar.a = a + b bar.b = a - b } } }
↑これはいけるはず (edited)foo.bar.a.modify { (a) in foo.bar.b.modify { (b) in
↑これはだめ (edited) bar.a.modify { (a) in bar.b.modify { (b) in
↑ bar
に対する同時編集にならない? (edited)a
と b
が stored property なら 分割されるsubscript
は computed だから↓ができないのか。 swap(&array[i], &array[j])
var foo: Foo { get }
になっててダメとかある?users[i].modify { user1 in users[j].modify { user2 in guard user1.points >= points else { return } user1.points += points user2.points = min(user2.points + points, 9999) } }
`
(edited)func modify<T>(_ t: inout T, _ f: (inout T) -> Void) { f(&t) } struct S { var a: Int = 3 var b: Int = 1 } var s = S() modify(&s) { (s) in modify(&s.a) { (a) in modify(&s.b) { (b) in a = a + b b = a - b } } } print(s)
S(a: 4, b: 3)
func modify<T>(_ t: inout T, _ f: (inout T) -> Void) { f(&t) } struct S { var a: Int = 3 var b: Int { get { _b } set { _b = newValue } } var _b: Int = 1 } var s = S() modify(&s) { (s) in modify(&s.a) { (a) in modify(&s.b) { (b) in a = a + b b = a - b } } } print(s)
<stdin>:14:12: error: overlapping accesses to 's.a', but modification requires exclusive access; consider copying to a local variable modify(&s.a) { (a) in ^~~~ <stdin>:15:16: note: conflicting access is here modify(&s.b) { (b) in ^~~~
Swift.swap(a[i], a[j])
は違反だけど a.swapAt(i, j)
はOKfunc modify<T>(_ t: inout T, _ f: (inout T) -> Void) { f(&t) } protocol P { var a: Int { get set } var b: Int { get set } } struct S: P { var a: Int = 3 var b: Int = 1 } var s = S() func foo<T: P>(_ s: inout T) { modify(&s) { (s) in modify(&s.a) { (a) in modify(&s.b) { (b) in a = a + b b = a - b } } } print(s) } foo(&s)
(edited)<stdin>:15:16: error: overlapping accesses to 's', but modification requires exclusive access; consider copying to a local variable modify(&s.a) { (a) in ^~~~ <stdin>:16:20: note: conflicting access is here modify(&s.b) { (b) in ^~~~
(edited)var s = s
が必要ですねvar
にコピーじゃなくて inout
にした。 overlapping になった。let a = foo.bar.baz.qux a.x += 1 a.y *= 2
みたいな一時的に変数に保存して操作することができないから modify
を使ってたけど、?.
が挟まると writable にできない…。async
なメソッドでもメソッドコール中は self
が retain されてて await
してる間に self
が解放されることはないのかな? (edited)async/await
は必ず終わる前提だから問題になることはないのか。前も似た話した気がしてきた。mutating func
の中で await
したらその後は変更不能になるよね?すごく使いづらそうな気が・・・。 (edited)mutating func
は async mutating func
の間違い?mutating func foo() async -> Bar
await
の後ろって非同期コールバックと同じだと思うんだけど、非同期コールバックから self
を変更できないのと同じことにならない?async
であることが強制されることで、書き込み先が消えないのか。async/await
だと今の値型と非同期処理の相性の悪さを解消できるのか。beginAsync
を使うわけで、 beginAsync
を挟むと結局 self
変更できなくなる。Button("Run") { self.foo = heavyOperation() }
↑の heavyOperation
が重いから非同期にしたところで、 Button("Run") { beginAsync { self.foo = await heavyOperation() } }
みたいになって self
を更新できない。ただし、 SwiftUI の @State
なら更新できるのがややこしいけど。@State
でしか更新できないのは正当なんですけど、↑のようなコードは self
を書き換えられなくなるけど、 foo
が値型でも @State
なら書き換えられる(ので例としてちょっとややこしいけど)という意でした。 (edited)func main() { let a: Result<Int, Never> = .success(42) print(a.get()) } main()
<stdin>:3:11: error: call can throw, but it is not marked with 'try' and the error is not handled print(a.get()) ^
extension Result where Failure == Never { func get() -> Success { switch self { case .success(let value): return value } } } func main() { let a: Result<Int, Never> = .success(42) print(a.get()) } main()
extension
標準でほしくないですか?get
が throws Failure
になれば解決するのかな。Foo|Never == Foo
が自動的に成り立つという意味で < untaggedthrows Never
は消える。Result
使ってるってのは本末転倒だと思うんですよね。async/await
入ったら Result
使いたいケースも減るし。throws
に複数の型を書けるのは止めた方が良いと思います。throws
に書けるのは一つの型のみcatch
でも union っぽい記法になってるのおもしろい。もうどうせなら untagged union 導入しちゃえばいいのに。any Error: Error
については、 throws Error
が throws
と等価であることを扱うためにも意味あるんじゃないでしょうか。any Error: Error
が実現されてなければ同じ話になったんじゃないでしょうか? Typed Throws は不要というより単に話が進んでないだけで。Result
入れないって言ってたのに急に入れる方向に傾いた背景に、 SwiftUI と Combine があった可能性があるかもしれない。 (edited)Result
を進めないといけなかったとか。一方で Typed Throws に急ぎの事情はない(し Result
と違って実装も大変だ)から話が進んでない。Result
は入れないという結論になったって言ってましたからね。Result
だとサクッと入れられるし、 async/await
や Typed Throws ですら Result
と比べると相当重いアップデートですし。Result
の Failure == Never
のときに keypath member lookup あると便利そうじゃないですか?@dynamicMemberLookup enum MyResult<Success, Failure: Error> { case success(Success) case failure(Failure) subscript<T>(dynamicMember keyPath: KeyPath<Success, T>) -> T where Failure == Never { switch self { case .success(let value): return value[keyPath: keyPath] } } } struct User { var name: String var age: Int } let user: MyResult<User, Never> = .success(User(name: "Name", age: 42)) print(user.name)
Result
はエラーハンドリングにおいてサブ的な位置づけだし。 keypath memberlookup はやりすぎとしても、最初に書いた try
なしの get
は標準でできて良いと思います。 (edited)struct MyClass<T, Failure: Error> { @_disfavoredOverload func getResponse() -> Result<T, Failure> { fatalError() } } extension MyClass where Failure == Never { func getResponse() -> T { fatalError() } } let t = MyClass<Int, Never>().getResponse()
func on(_ queue: DispatchQueue) async { await suspendAsync { continuation in queue.async { continuation(()) } } }
みたいな関数を作ったら、 func foo() { let x = await bar() await on(.main) self.label.text = x }
みたいなことできそう。DistatchQueue.async
が async
になりそう。 func foo() { let x = await bar() await DispatchQueue.main.async() self.label.text = x }
await on(.main)
の方が下記心地良さそう。await
相当のマークが要らないみたいだけど。flatten
で回避されたやつ。 https://developer.apple.com/documentation/combine/future/replacenil(with:)func replaceNil<T>(with output: T) -> Publishers.Map<Future<Output, Failure>, T> where Self.Output == T?
where Self.Output == T?
の部分。 Parameterized extensions https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#parameterized-extensions 代わりの。 (edited)flatten
使ってるけど禁断なんだろうかflatten
実装できるって話になったけど、もし Parameterized Extensions が実現されたら ABI 変わっちゃうから今は追加しない方が良いのでは?みたいな話が出て取り下げられてたはず。@builder
とかでもいい気も・・・。 (edited)if let
と switch
は使えるようになったんでしたっけ?@resultBuilder
になって再Reviewになりそうな気配がする。resultBuilder
でググって頑張ってって感じかな。builder
は紛らわしいという指摘はあった )if
式ってなんでそんなに人気あんのかよくわかんないんですよね。巨大な式は可読性良くないし、小さな式なら三項演算子でいいし。let a = { if ... { return 1 } else { return 2 } }()
↑これよく書くけどif式なら楽だなあとは思いますreturn 1
のところで 2文書きたかったりする。let a: Int if ... { a = 1 } else { a = 2 }
で書きます。 a
がここで初期化されるのが保証されないというのはあるけど、普通に読めば読めると思う。: Int
を書かなきゃいけないというのはあるけど、三項演算子で書けないくらい巨大になるなら型が書いてあった方が読みやすいと思うし。guard
については、 if
で書く場合と比べて早期脱出を意味するという意味で比較対象になりうるとは思う。if
式導入するのはわかるけど。 (edited)if
式まるごとコピペするようなときは関数にするのが正解な気も・・・。return
省略を前提に if
式はわかるけど、if
式というのは違うと思うから、「 Swift って if
式ないの?」っていうのは Swift 理解してないんだろうなという気持ち。if let ... else
は無理かも? < map
??
でいけるか。if
で解決できないのって、プロパティの初期化とかを想定してますか?let a: Int if ... { a = 1 } else { a = 2 }
[]
使ってflatMapするか
resultBuilder
が利いてきそうですね。struct Foo { var bar = if let x = productionVersion { ... } else { } }
= null
が必須なんだけど、Optional
も nil
初期化必須でいいよね。= nil
って大したタイプ数じゃないし。struct Container<Value> { var value: Value init(value: Value) { self.value = value } func withValue(action: (Value) -> ()) { action(value) } } var container = Container<Any>(value: "foo") container.withValue { value in print(value) container.value = "bar" print(value) }
swiftc -O a.swift && ./a
と swiftc a.swift && ./a
で結果が異なる (edited)init
は async
にできない想定なのかな?まだ実装されてないだけ? @swift-main -Xfrontend -enable-experimental-concurrency struct Foo { init() async throws { fatalError() } }
(edited)<stdin>:2:12: error: initializer cannot be marked 'async' init() async throws { ^~~~~~
(edited)struct Foo { func foo() async throws { fatalError() } }
(edited)-emit-sil
が含まれてただけだった・・・。 (edited)init
を非同期にできるのは( Promise
を使う JS とかではできない)おもしろい特徴だから是非入れてほしいところ。init
入ってなかったら意見を上げたいですね。 (edited)Int
https://developer.apple.com/documentation/swift/int からたどって Comparable
に適合しているパスを見つけられなかったんですが、 Int
ってどの経路で Comparable
に適合しているかわかりますか?Numeric
とかばっかり探してた・・・。stride
といえば、最近 AtCoder 用にパフォーマンス計測しながらコード書いてるんだけど、 for k in 2 ... number / m { sieve[k * m] = true }
っていうコードを for k in stride(from: 2, through: number / 2, by: 2) { sieve[k] = true }
に書き換えたら乗算を減らせて速くなるかと思ったら激遅になっておどろいたんだけど、 Range<Int>
や ClosedRange<Int>
の場合だけ特殊な最適化が行われてる? (edited)+ 2
と比べてインクリメントが速い? (edited)Range
の場合はイテレータとか消えて for (int i = 0; i < n; i++)
とか相当になってるんだろうと予測してるんだけどstride
の場合はイテレータで回してるのかなって。-Ounchecked
なのは間違いない。measure
した結果です。-Ounchecked
で。.lazy
使って無駄な Array
の生成を防ごうとしたら遅くなったとか、色々おもしろい。func primes(upTo number: Int) -> [Int] { precondition(number >= 0) if number < 2 { return [] } var sieve: [Bool] = .init(repeating: false, count: number + 1) for m in stride(from: 3, through: Int(Double(number).squareRoot() + 1.5), by: 2) { if sieve[m] { continue } let maxK = number / m if maxK < 2 { continue } for k in 2 ... maxK { sieve[k * m] = true } } var result: [Int] = [2] for m in stride(from: 3, through: number, by: 2) { if sieve[m] { continue } result.append(m) } return result } print(primes(upTo: 100))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
import Foundation
がないとコンパイルエラーになるんだけど、どういう状態だろう? Swift 5.2.1 だけど、 5.2.1 特有のバグ?そんなバグありましたか?それとも実行環境が特殊?swiftc -Ounchecked -o {dirname}/a.out {dirname}/{basename} {dirname}/a.out
https://atcoder.jp/contests/language-test-202001squareRoot
って標準ライブラリですけど libc 依存なんですっけ?for m in (3...number) where !m.isMultiple(of: 2)
(edited)squareRoot
については内部で libc の sqrt
に依存してて、環境に寄らないですよね?ということは、 import Glibc
付けとくのが良さそう。FROM ubuntu:20.04 RUN apt -y update # Install development utils RUN DEBIAN_FRONTEND=noninteractive \ apt install -y wget tar vim openssh-server sudo tmux lldb \ git cmake ninja-build \ clang python uuid-dev \ libicu-dev icu-devtools \ libedit-dev libxml2-dev \ libsqlite3-dev swig libtinfo5 \ libpython2-dev libncurses5-dev \ pkg-config libcurl4-openssl-dev \ systemtap-sdt-dev \ tzdata rsync python-six \ libncurses5 \ libz3-dev python3-distutils
import Foundation let processInfo: ProcessInfo = .processInfo print(processInfo.operatingSystemVersionString)
結果は↓でした。 Ubuntu 5.4.0-1025.25~18.04.1-aws 5.4.60
"適宜sudoをつけてください apt install clang libicu-dev libpython-all-dev wget https://swift.org/builds/swift-5.1.3-release/ubuntu1804/swift-5.1.3-RELEASE/swift-5.1.3-RELEASE-ubuntu18.04.tar.gz tar xvfz ./swift-5.1.3-RELEASE-ubuntu18.04.tar.gz mv ./swift-5.1.3-RELEASE-ubuntu18.04/usr {install_dir} export PATH={install_dir}/usr/bin:""${PATH}"" # パスを通す"
https://docs.google.com/spreadsheets/d/1PmsqufkF3wjKN6g1L0STS80yP4a6u-VdGiEv5uOHe0M/ (edited)swiftc
を使うとリンクエラーになりますね。ボットはswift
を使ってる。 $ pbpaste|docker-swift-run swiftc -Ounchecked - /tmp/--90f083.o:--90f083.o:function $s4main6primes4upToSaySiGSi_tF: error: undefined reference to 'sqrt' clang-10: error: linker command failed with exit code 1 (use -v to see invocation) <unknown>:0: error: link command failed with exit code 1 (use -v to see invocation) $ pbpaste|docker-swift-run swift -Ounchecked - [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
(edited)$s4main1aSivp ---> main.a : Swift.Int
swift
コマンドでimmediateモードで走らせるとLLVMのJITで実行されるからllvm.sqrtがlibmのシンボルに依存しない形で実行されるのか (edited)Int(S_IRUSR) | Int(S_IWUSR) | Int(S_IRGRP) | Int(S_IWGRP) | Int(S_IROTH) | Int(S_IWOTH)
を思い出したprotocol P {} struct S: P {} extension P { static var bar: S { S() } } func foo<T: P>(_: T) {} foo(.bar)
: P
の時に implicit member expression で参照できる (edited)protocol P { associatedtype Arg } struct S: P { typealias Arg = Int } extension P { static func bar(x: Arg) -> S { S() } } func foo<T: P>(_: T) {} foo(.bar(x: 12))
なので、たぶんこれは無理と思います。class B { static var c: C { C() } } class C: B {} func foo(_: B) {} foo(.c)
ルール的にはレシーバのサブタイプなので、矛盾はしてないかな?Equatable
とかのやつ、 AdditiveArithmetic
とか Numeric
もほしくなる。標準で付けるのは微妙だけど、コンパイラでやらないとできないし・・・。protocol P { associatedtype Foo func foo() -> Foo } extension Int: P { func foo() -> Int { self * self } } let a = (2, 3, 5) print(a.foo())
(edited)protocol P { func foo() -> Int } extension Int: P { func foo() -> Int { self * self } } let a = (2, 3, 5) print(a.foo())
(Int, Int, Int).foo()
が Int
を返さないので。AdditiveArithmetic
は横ベクトル同士の自然な加減算があるのでできなくはなさそうですよねStructure of encoded enums The following enum with associated values enum Command: Codable { case load(key: String) case store(key: String, value: Int) } would be encoded to { "load": { "key": "MyKey" } } and { "store": { "key": "MyKey", "value": 42 } }
{ "kind": "store", "key": "MyKey", "value": 42 }
{ "kind": "store", "value": { "key": "MyKey", "value": 42 } }
enum Command { struct JSON: Codable { var load: Load? var store: Store? } }
type
文で 相互に受け渡すのが楽だった。type JSON = { load: { key: string } } | { store: { key: string, value: number } }
こういうことですか?type JSON = { load?: { ... } store?: { ... } }
いやこっちURI
型とか TypeScript側でただの string になっちゃう。User.ID
型とかも。try
と整合性が取れなくなってしまうけど、わざわざ async let
で代入しなくても前者の書き方で並行になってくれたらうれしい?try
との整合性」というのは https://gist.github.com/koher/ca74ef2c882418f683d06092f4408c3c のような意味です。 (edited)async let
で 1 回代入挟むくらいがバランス良さそうですね。await hoge.foo().bar()
って呼べるので嬉しい、これがコンフリクトしそうだなとawait hoge.foo(bar())
みたいなの (edited)async let
は一見わかりにくいけど、文法として新しいコードの形になっている(これまでの文法として、letの左にasyncは書けない)ので、暴発することはない。 (edited)await
でまとめた式は並列になる」は、async/awaitで新しく導入されるといっても、「await
がついてない部分について実行順序の挙動が変わる」のは、 これまでと変化がある部分に対して見た目の変化がないのでわかりづらいと思う。fileprivate
書かなきゃいけないケースってあまりないなと思ってたんですが、↓のようなケースで fileprivate
でしか書けなさそうなのを発見しました。 struct S {} private struct T {} private protocol P { func foo() -> T } extension S: P { fileprivate func foo() -> T { T() } }
let s = readLine()! let a = (0..<2).map(s.dropFirst).count
を実行して 11111111111111111
など 15 or 16 文字以上の文字列を標準入力として与えると -O
または -Ounchecked
時に実行時エラーになるそうです。気持ち悪い・・・。 https://twitter.com/_kebo/status/1357011619440599044let s = readLine()! let a = (0..<2).map(s.dropFirst).count
を実行して 11111111111111111
など 15 or 16 文字以上の文字列を標準入力として与えると -O
または -Ounchecked
時に実行時エラーになるそうです。気持ち悪い・・・。 https://twitter.com/_kebo/status/1357011619440599044 s.dropFirst
を変数に束縛するのでも再現しなくなるみたいですし、{ s.dropFirst($0) }
のようにクロージャにしても再現しなくなりますし、あとは (ちゃんと記録してませんでしたが) 形を変えて行った時にどこかで二重解放の実行時エラーが出ていたので、s
が解放されちゃいけないタイミングで解放されているのかなぁという気がしてます。 かといって withExtendedLifetime
で s
のライフタイムを伸ばしても変わらなかったので、何処かの最適化フェーズに問題があったのではと睨んでます。concurrentPerform
の引数を1にするとわかりやすいんですが、最後に0のデータが来るかどうかがランダムなんですよね。// おまじない func print(_ item: String) { fputs(item + "\n", stderr) }
availableData
が空だったらEOFだからhandlerをnilにするってのを何件かみたんですが、 ドキュメントだと使い方が不明瞭ですね……低レイヤなれてる人なら常識なのかもしれませんが。 https://developer.apple.com/documentation/foundation/filehandle/1412413-readabilityhandler
https://developer.apple.com/documentation/foundation/filehandle/1411463-availabledata
availableData
が空ならEOFというのは合ってそうですが、readabilityHandlerのほうはEOFが来ることを保証していないような気がする。readToEnd
はmacOS10.15.4からなのにPackage.swiftでは.v10_15
しか指定できない……var s = 1 var a: Int = s var b: Int? = s
のaとbが異なるということですね。クラスサブクラス、あるいはExistentialと具体型の関係なら、これは同じものを指すわけです。(Existentialは厳密には異なるが省略) var s = Cat() var a: Animal = s var b: Cat = s
この結果発生する問題として、 実態が異なっているということを知っているユーザーと知らないユーザーとの間で、期待する挙動に差が出てきます。例えば.some(1)
と 1
は別でしょ」っていう理解のユーザーからは、AnyHashableの値が異なることこそが期待される挙動なはずです。var s = Cat() var a: Animal = s // CatとAnimalのサブタイプ関係が成り立つことを示す var b: Cat? = s var c: Animal? = b
U?はT?のサブタイプ、は具体のコードとしては、 UITableViewController?
な変数が UIViewController?
に渡せる、というのが頻出すると思います。 (edited)var s = Cat() var a: Animal = s // CatとAnimalのサブタイプ関係が成り立つことを示す var b: Cat? = .some(s) var c: Animal? = b.map { $0 as Animal }
mapのところは本当は違うと思うんですが、まあ雑にこんな感じ。 (edited)var d: Cat?? = b // b: Cat?
1. オプショナルのサブタイプなので.someでラップする 2. CatはCat?のサブタイプなのだから、.mapでOK
// 1. var d1 = .some(b) // 見たまま.some(.none)になります // 2. var d2 = b.map { $0 as Cat? } // これは.noneになります
じゃあこれがどういう問題を引き起こしたかというとWrapped
として扱えるように隠蔽していた部分でエッジケースで齟齬が出るのはしょうがないという感じもしますねvar s = Cat() var a: Animal = s // CatとAnimalのサブタイプ関係が成り立つことを示す var b: Cat? = s var c: Animal? = b
U?はT?のサブタイプ、は具体のコードとしては、 UITableViewController?
な変数が UIViewController?
に渡せる、というのが頻出すると思います。 (edited)UIView?
の間違い?protocol IntCollection: RangeReplaceableCollection where Self.Element == Int {}
こういうふうにassociatedtypeを埋めて再定義してやれば使えるけどany Collection<Element = Int>
みたいにパラメータを埋められる、OrderedSet/Dictionary
もいいけど SortedSet/Dictionary
も欲しくないですか?ツリーで実装されてて、各種操作が O(log N) で、イテレートしたらソート済で全要素 O(N) で取得できるやつ。let a: (foo: Int) a = (foo: 42) print(a)
<stdin>:1:9: error: cannot create a single-element tuple with an element label let a: (foo: Int) ^~~~~ <stdin>:2:5: error: cannot assign value of type '(foo: Int)' to type 'Int' a = (foo: 42) ^~~~~~~~~
(T)
と T
が同じものですよねlet a: (Int) = 42
let a: (Int) = 42 print(type(of: a)) // Int
let f: () -> (Int) = { 0 } print(type(of: f)) // () -> Int
T
と (T)
は同じものですべての値は single element なタプルだと思ってる。内部的な扱いは別として、概念的に。func foo() -> (isUpdated: Bool) { ... }
enumerated
(オフセットしか返してくれない)ではなく。見たところ見当たらない。 @swift-5.3.3
let a: ArraySlice<Int> = [0, 1, 2, 3].dropFirst(2) for (i, x) in a.enumerated() { print(i, x) } for i in a.indices { print(i) }
0 2
, 1 3
ではなく 2 2
, 3 3
を得たい。zip(x.indices, x)
ArraySlice
使ってるときとか使いづらくて・・・。でも zip(x.indices, x)
で良さそうですね。.indexed()
のほうがzipに同じx
が2回書かれている事を読み取らなくていいので、filterでかけるときはreduceを使わなくて良いのと同じで、 indexedのほうが良いと思います 名前も自明だし。indexed()
がいいのはもちろんとして、わざわざそのために Swift Algorithms を入れたくないようなケースでは、ですね。->
の前に async
が要りそう。 extension BankAccount { func meetsTransactionLimit(_ limit: Amount) -> Bool { return try! await self.lastTransaction.amount < limit // ^~~~~~~~~~~~~~~~ // this access is async & throws } }
unsafelyUnwrapped
の利用例こんなところで見つけた-O
でさえチェックコストが消えるらしい。
extension Sequence { func sorted<T>(by keyPath: KeyPath<Element, T>) -> [Element] where T: Comparable { sorted(by: { $0[keyPath: keyPath] < $1[keyPath: keyPath] }) } }
// ↓みたいにして使う users.sorted(by: \.age)
keyPaths: KeyPath<Element, T>...
を受け取るようにしようとしたけど、 Swift の Comparable
は同値判定がないから Equatable
もないとだめだな。a == b
は !(a < b) && !(b<a)
ですよComparable: Equatable
だった。これは知らなかった。extension Sequence { func sorted<T>(by keyPaths: KeyPath<Element, T>...) -> [Element] where T: Comparable { sorted(by: { for keyPath in keyPaths { let l = $0[keyPath: keyPath] let r = $1[keyPath: keyPath] if l < r { return true } else if l > r { return false } } return false }) } }
reversed
あるからなくてもいい気がする。array.sort(Comparators.key(\.a).key(\.b, .desc))
T...
と KeyPath<Element, T>...
の ...
が対応することを示さないといけなくて構文上きついなぁ。 extension Sequence { func sorted<T...>(by keyPaths: KeyPath<Element, T>...) -> [Element] where T: Comparable { // 省略 } }
private func _cmp<Value: Comparable>(_ lhs: Value, _ rhs: Value) -> Bool? { lhs == rhs ? nil : lhs < rhs } private func _cmp<Root, Value: Comparable>( _ lhs: Root, _ rhs: Root, on keyPath: KeyPath<Root, Value> ) -> Bool? { _cmp(lhs[keyPath: keyPath], rhs[keyPath: keyPath]) }
こういうの用意しておいて array.sorted { _cmp($0, $1, on: \.prop1) ?? _cmp($0, $1, on: \.prop2.other) ?? false }
でいいや、って最近なりました。func sorted<T>(by keyPath: KeyPath<Element, T>)
と、複数のキーの場合は都度クロージャ式を実装でもいい気も・・・。 3 キーくらいまで extension
用意しておけば 98% くらいカバーできそう。struct Foo<Bar> { struct Baz {} }
があるときに、 Foo<Bar>.Baz
であってほしいときと Foo.Baz
であってほしいときないですか?単に Foo
に関連する型をネームスペースとして Foo.
にまとめたいときに Foo<Bar>.Baz
になると鬱陶しいことが。Foos
を作って解消するのが良さそうFoo.Baz
を書けるようにする、みたいな議論あるのかな (edited)Foo.Baz
を書けるようにする、みたいな議論あるのかな (edited)enum
でネームスペースにしてるのとか微妙だし。struct Foo<Bar> { } struct Foo.Baz { }
こういうふうに書けたらいいんちゃうかと思ったことある。extension
はそうですしね。Foo<Bar>.Baz
と Foo.Baz
の共存とかあり得る?もっと言えば nested type と namespace で名前が被るとどうなる??struct Foo<Bar> { } struct Foo.Baz { }
こういうふうに書けたらいいんちゃうかと思ったことある。 async let
単体のピッチが立った。ちゃんとした提案書もついてる。 提案書の中で、 TaskGroup.spawn
が TaskGroup.async
に変更されていて、 spawn let
ではなく async let
なのはそれに揃ってるという事になっている。await
しなければ静的エラーだろうって話を以前したけど、 この文書だと、スコープ脱出時にキャンセルしてキャンセル完了を待機するって仕様になっている。 1つ目のタスクの結果を見て2つ目のタスクの結果を捨てるみたいなフローを考慮したっぽい。 (edited)spawn
の方がいいんじゃないかって意見を書き込んだら、 async let
になった経緯として、 複数のAPIを async
に統一するって方針の議論がある事を教えてもらった。let foo: Foo if let f = self.foo { foo = f } else { foo = Foo(...) self.foo = foo }
let foo: Foo = self.foo.value(default: Foo(...))
Dictionary
の dictionary[key, default: value] += 1
みたいなノリで。let foo = self.foo.ensure { Foo(...) }
@discardableResult public mutating func ensure(_ f: () throws -> Wrapped) rethrows -> Wrapped { if let x = self { return x } let x = try f() self = x return x }
@autoclosure
でいい気がする。 (edited)nil
のときだけ初期化だからやっぱクロージャがいいか。ensure
普通に標準ライブラリにほしいね。toggle
, subscript[_:default:]
, reduce(into:_)
とかの仲間としていけるんじゃないかなぁ。 public mutating func consume<R>(_ f: (Wrapped) throws -> R) rethrows -> R? { guard let x = self else { return nil } self = nil return try f(x) }
self.foo.consume { $0.shutdown() }
if let foo = self.foo { foo.shutdown() self.foo = nil }
Bool
の toggle
が通ったことを考えるとこれもいけるのでは? Dictionary
の subscript(_:default:)
は同じじゃないですか?foo
って4回書くのが1回で済むので結構使ってます。struct
で不必要に let
つかうのやめようぜっていったら猛攻をくらった...let
を使いたくなる気持ちはわからんでもないですけどね。原理的に変えられることと、変えるための手軽な手段が存在することは異なるので。ただ、境界があいまいになるので、突き詰めると↑になっちゃうんですよね・・・。let
にすれば十分なケースまでプロパティ側を全部 let
にするのは許容しづらいですが。let
で宣言してれば問題ないと思いますね。
[User]
がレスポンスで返ってくるようなケースを考えると、 Array
は mutaing func
を持つので、それは問題にならないの?って話になりますしね。それが許容できるなら(利用側で let users: [User]
で問題ないなら) struct
のプロパティも var
でいいでしょという。struct Point { let x: Int let y: Int init(x: Int, y: Int) { self.x = x; self.y = y } } extension Point { mutating func setX(_ x: Int) { self = .init(x: x, y: self.y) } }
という理解でいいですか?struct Point { let x: Int let y: Int init(x: Int, y: Int) { self.x = x; self.y = y } } extension Point { mutating func setX(_ x: Int) { self = .init(x: x, y: self.y) } }
という理解でいいですか? private(set) var
で同じ事が起きますよ (edited)Bool
の toggle()
で、 var
使ってる以上プロパティをどれだけ守っても可変だし、可変であっても共有されないから本質的にイミュータブルと同じなんだよなぁ。let
にして守った気になってるけど self
書き換えを extension
で生やせるから守れてなくて、一部どころか全体すら書き換えられるから、イミュータブルに見える Bool
ですらイミュータブルでないし、それで問題ないって意味。toggle
って参照型中心の世界の考え方に浸かってるとぶったまげると思うんよね。Bool
や String
がミュータブルってだけで泣いちゃいそう。extension Bool { var negate: Bool { !self } }
の方があり得る。けどこれは!
を使えで蹴散らされるtoggled()
は無いのか。struct A { let i: Int } struct B { let a: A? = nil } //let _: [Any] = [B()].compactMap { $0[keyPath: \B.a?.i] } // OK let _: [Any] = [B()].compactMap(\.a?.i)
import Foundation struct Foo: Codable { var value: Int?? } let foo: Foo = .init(value: .some(.none)) print(foo) let data = try! JSONEncoder().encode(foo) print(String(data: data, encoding: .utf8)!) let decoded = try! JSONDecoder().decode(Foo.self, from: data) print(decoded)
Foo(value: Optional(nil)) {"value":null} Foo(value: nil)
import Foundation struct Foo: Codable { var value: Int?? } let foo: Foo = .init(value: .some(.none)) print(foo) let data = try! JSONEncoder().encode(foo) print(String(data: data, encoding: .utf8)!) let decoded = try! JSONDecoder().decode(Foo.self, from: data) print(decoded)
enum
の Codable
の話の文脈で話されてるっぽいけど、そもそも単体でこれまでに起こってた話なんですよね。String?
とかが Codable
でないのはそれはそれで辛いですね。うーん。"some": { "_0": { "some": { "_0": {...} } } } }
とかで表現すればいいわけだ。"some": { "_0": { "some": { "_0": {...} } } } }
とかで表現すればいいわけだ。 Optional
って今どうなってるんだ?と思ってやってみたら↑みたいになってたという・・・。でも、 98% くらいの用途である Foo?
が自動 Codable
形式になるのは辛いですよね・・・。 (edited)T?
だけ特別扱いは出来なくはなさそうですよね。T?
の T
が Optional
でないときみたいな条件書けますっけ? Optional.init(from:)
/Optional.encode(to:)
のstdlib実装なら可能だと思います。という意図でした。 (edited)Wrapped
が Optional
なら分岐させるっていう意味です。もちろん evolution proposal 必要ですけど。actor Counter { private(set) var count: Int = 0 func countUp(_ amount: Int) -> Int { count += amound return count } func countUp() -> Int { countUp(1) } }
(edited)final class Counter { private let queue: DiqpatchQueue = ... // Serial Queue private var _count: Int = 0 var count: Int { get async { withCheckedContinuation { continuation in queue.async { continuation.resume(returning: _count) } } } } private func _countUp(_ amount: Int) -> Int { count += amound return count } func countUp(_ amount: Int) async -> Int { withCheckedContinuation { continuation in queue.async { continuation.returning(returning: _countUp(amount)) } } } private func _countUp() -> Int { _countUp(1) } func countUp() async -> Int { withCheckedContinuation { continuation in queue.async { continuation.resume(returning: _countUp()) } } } }
(edited)f (_ a: @escaping () async -> Void) { let s = DispatchSemaphore(value: 0) async { await a() s.signal() } s.wait() }
a
が元のスレッドに突入しようとしたらダメじゃないでしょうか?f
をメインスレッドで呼び出して a
に DispatchQueue.main.async
の async/await
版を渡すとか。f (_ a: @escaping () async -> Void) { let s = DispatchSemaphore(value: 0) async { await a() s.signal() } s.wait() }
asnyc
の中身が f
を実行してる スレッドに投入される可能性がありますね。 async/awaitではブロッキングが起きないことを前提にスケジューラが構成されているらしいので、 他のTaskの処理がそのwaitしてるスレッドに投入されて複雑に噛み合った結果止まる可能性もあります。LIBDISPATCH_COOPERATIVE_POOL_STRICT
で実行時に死ぬか確かめるしかないのか... わかるけど...@MainActor
以外に thread 特定されるような要素ってなにがあるかなasnyc
ではなく DispatchQueue.asnyc
で書いているはずで、Task
を作ったところで Task
オブジェクトを引き回しておかないとcurl
みたいな CLI を URLSession の async でつくるってできないのかも。async {}
aka Task.init {}
か何かが必要 (edited)semaphore.signal()
/semaphore.wait()
の みたいな安全ではないコードがどこかに必要になってしまう... (edited)Task.init
で起動するけど wait できないって状態か@MainActor
以外、cooperative thread pool の thread 以外は使われないから、さっきの f()
が Task から呼ばれないことが保証されていれば絶対安全なんじゃないかなあ。Task.unsafeCurrentTask
をチェックするassertionをいれとこう
notTask(_ t: @escaping () -> Void)
みたいな (edited)@main struct MyProgram { static func main() async { ... } }
https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md#launching-async-tasks@main struct MyProgram { static func main() async { ... } }
https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md#launching-async-tasks --parse-as-library
つけないと @main
つかえない… (別の話ですが) (edited)@main
必須かなあ @MainActor @Sendable func _doMain(_ asyncFun: @escaping () async throws -> ()) async { do { try await asyncFun() } catch { _errorInMain(error) } } Task.detached { await _doMain(asyncFun) exit(0) } _asyncMainDrainQueue()
これが実装で、Task.detached
して exit(0)
, main は CFRunLoopRun()
ということらしい。 (edited)actor A { var c = 0 func inc() -> Int { c += 1 return c } func f() { async { print(inc()) } } } @main struct Main { static func main() async { let a = A() await a.f() print(await a.c) } }
actor A { var c = 0 func inc() -> Int { c += 1 return c } func f() { async { print(inc()) } } } _runAsyncMain { let a = A() await a.f() print(await a.c) }
1 1
stderr:<stdin>:10:9: warning: 'async(priority:operation:)' is deprecated: `async` was replaced by `Task.init` and will be removed shortly. async { ^
func f() async
ですか?それともbodyの async { .. }
ですか?async { .. }
を外すと(今の所)必ず実行されてるように見える (edited)func f() async
ですか?それともbodyの async { .. }
ですか? func f() async
にするとデッドロックしなくなった。これでniwさんの環境を再現できた気がするawait a.f()
以下と fの async { .. }
ブロックの中が同じスレッドで実行されるようになるっぽい…import Foundation actor A { var c = 0 func inc() -> Int { c += 1 return c } func f() async { async { print("begin f") print(Thread.current) print(inc()) print("end f") } } } _runAsyncMain { print(Thread.current) let a = A() await a.f() print("begin main") print(Thread.current) print(await a.c) print("end main") }
<Thread: 0x00007fd110000c60> begin main <Thread: 0x00007fd114002dd0> 0 end main begin f <Thread: 0x00007fd114002dd0> 1 end f
stderr:<stdin>:13:9: warning: 'async(priority:operation:)' is deprecated: `async` was replaced by `Task.init` and will be removed shortly. async { ^
import Foundation actor A { var c = 0 func inc() -> Int { c += 1 return c } func f() { async { print("begin f") print(Thread.current) print(inc()) print("end f") } } } _runAsyncMain { print(Thread.current) let a = A() await a.f() print("begin main") print(Thread.current) print(await a.c) print("end main") }
<Thread: 0x00007f3114000c60> begin f <Thread: 0x00007f3118003360> 1 end f begin main <Thread: 0x00007f3114000c60> 1 end main
stderr:<stdin>:13:9: warning: 'async(priority:operation:)' is deprecated: `async` was replaced by `Task.init` and will be removed shortly. async { ^
func msg(f: String = #function, _ s: Any) { print("\(Thread.current): \(f): \(s)") } actor A { var c = 0 func inc() -> Int { c += 1 return c } func f() { msg("3") async { msg("4") print(inc()) } } } @main struct Main { static func main() async { msg("1") let a = A() await a.f() msg("2") print(await a.c) } }
(edited)<NSThread: 0x7fa109c0bfd0>{number = 1, name = main}: main(): 1 <NSThread: 0x7fa109d05540>{number = 2, name = (null)}: f(): 3 <NSThread: 0x7fa109d05540>{number = 2, name = (null)}: f(): 4 1 <NSThread: 0x7fa109c0bfd0>{number = 1, name = main}: main(): 2 ^Cmake: *** [run] Interrupt: 2
deadlockした場合<NSThread: 0x7f9250d0bfb0>{number = 1, name = main}: main(): 1 <NSThread: 0x7f9250e05b80>{number = 2, name = (null)}: f(): 3 <NSThread: 0x7f9250e05b80>{number = 2, name = (null)}: f(): 4 1 <NSThread: 0x7f9250d0bfb0>{number = 1, name = main}: main(): 2 1
deadlockしなかった場合actor A { func g() { } func f() { async { g() } } } @main struct Main { static func main() async { let a = A() await a.f() await a.f() // ここでdeadlock.. したりしなかった } }
(edited)async(priority: .background) {
などpriority変えても起きますか?AsyncSequence
に Publisher
の代わりをさせるとして、 combineLatest
や merge
相当のことを簡潔に実現するのは難しいでしょうか? AsyncSequence
に combineLatest
や merge
が生えれば解決?AsyncStream
が出てきたら割と何でも出来るようになると思います。combineLatest
作るとしたら↓みたいに並行でループ回してとかでしょうか? var values: (S1.Element?, S2.Element?) = (nil, nil) { didSet { switch values { case (let e1?, let e2?): // (e1, e2) をイテレータに流す case (.nil, nil): // nil をイテレータに流す default: break } } } async { for await e in s1 { values = (e, values.1) } } async { for await e in s2 { values = (values.0, e) } }
AsyncStream { (cont) in var a: A! var b: B! async { for await x in a_s { a = x cont.yield(a, b) } } async { for await x in b_s { b = x cont.yield(a, b) } } }
(edited)AsyncSequence
に combineLatest
はほしいな。combineLatest
とかはサードパーティ提供になりそうな気がするなぁ。Sequence
と同じのしかないですよねー。guard let
でシャドーイングすると、 { }
の中では(バインド先の変数にアクセスできないのは当たり前として)元の変数にもアクセスできなくなる。コードリーディング時の勘違いを減らすにはこれでいいのかもしれないけど・・・。 guard let response = response as? HTTPURLResponse else { assertionFailure("Never reaches here.") print(type(of: response)) return }
(edited)print(type(of: response))
の response
がシャドーイング前のものって読み取りづらい気もするから、まあ妥当な制約かもなぁと。これまで遭遇したことなかったからこんなケースあったのかと思って。↑のケースでは結局 guard let httpResponse = response as? ...
にした。do { defer { print(a) } // ←まだaが宣言されてないよ…? let a = 0 }
do { defer { print(a) } // ←まだaが宣言されてないよ…? let a = 0 }
do { defer { print(a) } // ←まだaが宣言されてないよ…? let a = 0 }
do { defer { print(a) } // ←まだaが宣言されてないよ…? let a = 0 }
... お、swiftbot-sandbox でさんざん実験済みだったのか笑 (edited)<stdin>:2:19: error: use of local variable 'a' before its declaration defer { print(a) } // ←まだaが宣言されてないよ…? <stdin>:3:9: note: 'a' declared here let a = 0 ^
(some P)?
みたいなのができるようになるっぽい?// ERROR: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type // func asHOFRetArg() -> (some P) -> () { return { (x: String) -> () in } } //
これなんでinferできないんだろう (edited)@_optimize([none|size|speed])
これおもしろい。 関数ごとに最適化モード変えられるんだ。@_optimize(none) func fib1() {...} @_optimize(speed) func fib2() {...}
こういうのを用意したら確かめられるかな?@_private(sourceFile: "FileName.swift")
URLSession
の新しい data(from:)
メソッドを Task
の cancel
メソッドでキャンセルしたら、 CancellationError
じゃなくて code == NSURLErrorCancelled
な NSError
が throw
されるVersion 13.0 beta 5 (13A5212g)
URLError
にas
でキャストできますよe.code == .cancelled
かなdata(from:)
メソッドの実装で code
をチェックして CancellationError
を投げるべきでは?Task
がキャンセルされているかをチェックして URLSessionDataTask
をキャンセルしてるコードがあるはずで、そこで管理すべき?checkCancellation
が投げるデフォルトのエラーとして CancellationError
は定義されてるけど、Concurrencyのキャンセル機構としてそこは実装依存かCancellationError
に寄せるのは難しいと思います (edited)CancellationError
が var source: Error?
を持つようにすれば解決できそうな?CancellationError
は isCancelled == true
のときに Task.checkCancellation
が投げるエラーというだけで、キャンセルされたときに throw
されるエラーは CancellationError
とは限らないという仕様なのかな? catch let error as CancellationError
でキャンセルをチェックするのではなく、 catch
節の中で Task.isCancelled
でチェックするのが正しい? (edited)URLSession
の data(from:)
がどこで実装されてるのか見つからない・・・。CancellationError()
CancellationError
を throw
しなくても良い?でも CancellationError
CancellationError
を throw
することが期待されている? URLSession.data(from:)
が cancel
時に CancellationError
を throw
しないのはお行儀が悪い? https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md#cancellation
(edited)CancellationError
, to communicate that the task was cancelled. CancellationError
として検出できるように、くらいの意味まで持つならキャンセル時に独自のエラーを throw
するのではなく CancellationError
を throw
すべきとなりそうだけど。
do { try await foo() } catch let error as CancellationError { // キャンセル時の処理 } catch { // エラーハンドリング }
とするよりも do { try await foo() } catch { if Task.isCancelled { // キャンセル時の処理 } else { // エラーハンドリング } }
がベストプラクティス? (edited)cancel
メソッドを呼んだレシーバのTaskに対してキャンセルフラグが立つ実装になっています。cancel
メソッドを呼んだレシーバのTaskに対してキャンセルフラグが立つ実装になっています。 Task.checkCancellation()
を呼ぶか、 Task.isCancelled
をチェックして適切な処理を行った後に CancellationError
を throw
するというルールなのかと思っていました。
catch
節で Task.isCancelled
でチェックするのが良さそうですね。 (edited)func download(...) async throws -> Data
を呼び出して、自分がキャンセルボタンを押した場合と何かしらエラーが発生した場合のどちらもエラーが throw
されるけど、前者では何もしないけど後者ではアラートを出したいみたいなケースを想定しています。 (edited)try { await download() } catch { if Task.isCancelled { throw error // or return } switch error { case let error as HogeError: // ... } }
みたいな感じになるんですかね? (edited)try { await download() } catch { if Task.isCancelled { throw error // or return } switch error { case let error as HogeError: // ... } }
みたいな感じになるんですかね? (edited)Task.isCancelled
だとTask
インスタンスとしてどこかに保持されていると思うので、 そのTaskに対して task.isCancelled
するのが良いと思います。Task
インスタンスとしてどこかに保持されていると思うので、 そのTaskに対して task.isCancelled
するのが良いと思います。 catch
でエラーが飛んできたところで分岐しないといけなくないですか?それとも Task { }
の中で catch
せずに task.result
で結果を受け取った方が良い?var downloadTask: Task<Data, Error>? func didTapDownload() { let task = Task { try await download(...) } self.downloadTask = task do { let result = try await task.value } catch { if task.isCancelled { ... } } } func didTapCancell() { self.downloadTask?.cancel() }
こういうのを想像してました。 (edited)var downloadTask: Task<Data, Error>? func didTapDownload() { let task = Task { try await download(...) } self.downloadTask = task do { let result = try await task.value } catch { if task.isCancelled { ... } } } func didTapCancell() { self.downloadTask?.cancel() }
こういうのを想像してました。 (edited)didTapXX
はasyncにできないからStructuredにしちゃいけないですねdownload
の戻り値をわざわざ Task
の外側でハンドリングするのも若干気持ち悪い気もするし、 task.value
を async
の中に入れるためにもう一つ Task
作るとかはもっと気持ち悪いので仕方なさそうですね。 (edited)func download(url: URL) async throws -> Data? { var urlSessionTask: URLSessionTask? return try withTaskCancellationHandler { return try await withUnsafeThrowingContinuation { continuation in urlSessionTask = URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // Ideally translate NSURLErrorCancelled to CancellationError here continuation.resume(throwing: error) } else { continuation.resume(returning: data) } } urlSessionTask?.resume() } } onCancel: { urlSessionTask?.cancel() // runs immediately when cancelled } }
withTaskCancellationHandler使って手動でチェックするのがいいんですかね?(結局呼び出し側でError Typeチェックしなければいけないので、あまり意味ない気もしますが...) (edited)task.cancel
が呼び出されたときに、 download
関数の実装者は URLSessionTask
を即時 cancel
したいけど、それを実現するために使えるという。CancellationError
が throw
されるようにしたいという意識なのかな。ただ、 100% 保証は無理だから、やっぱり呼び出し側では as CancellationError
で判別するのではなく Task.isCancelled
が良さそう。
onCancel
に Sendable
でない Box
を渡せてしまってるのはなぜ??? func download(url: URL) async throws -> Data? { let urlSessionTask: Box<URLSessionTask> = .init() return try await withTaskCancellationHandler { return try await withUnsafeThrowingContinuation { continuation in let task = URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { // Ideally translate NSURLErrorCancelled to CancellationError here continuation.resume(throwing: error) } else { continuation.resume(returning: data) } } task.resume() urlSessionTask.value = task } } onCancel: { urlSessionTask.value?.cancel() // runs immediately when cancelled } } final class Box<Value> { var value: Value? }
onCancel: { [urlSessionTask] in urlSessionTask?.cancel() // runs immediately when cancelled }
Sendableチェックはmainでも起きませんか?おそらくこのPRがrelease/5.5に入ってないからチェックされてないんじゃないかなーと思いました。 https://github.com/apple/swift/pull/38866 SwiftFiddleでやってみたらmainの場合warningが出ました。 /main.swift:21:7: warning: cannot use let 'urlSessionTask' with a non-sendable type 'Box<URLSessionTask>' from concurrently-executed code urlSessionTask.value?.cancel() // runs immediately when cancelled ^ /main.swift:25:13: note: generic class 'Box' does not conform to the `Sendable` protocol final class Box<Value> {
(edited)nil
の状態で capture されるだけで変更が反映されなくないでしょうか?
Sendable
のチェックはまだお預けなんですね・・・。
Sendable
の status も↓ですしね。 @MainActor
も付けてないのに↓のコンパイルエラーになるのなぜ??そして Task.detached
の代わりに Task.init
を使うと起こらない・・・。 Version 13.0 beta 5 (13A5212g)
final class UserViewController: UIViewController { let userID: User.ID var user: User? ... override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { [self] in do { let user = try await fetchUser(for: userID) self.user = user // ⛔ Property 'user' isolated to global actor 'MainActor' can not be mutated from a non-isolated context } catch { // エラーハンドリング } } } }
UIViewController
に付いてのか。 @MainActor class UIViewController : UIResponder
https://developer.apple.com/documentation/uikit/uiviewcontrollerTask.init
だと MainActor
上で実行されるけど、 detached
だとそうじゃないからエラーになってしまったと。 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { [self] in do { let user = try await fetchUser(for: userID) DispatchQueue.main.async { [self] in self.user = user } } catch { // エラーハンドリング } } }
MainActor
に戻したらいけたけど、 DispatchQueue.main
よりもいい方法ないんだっけ?DispatchQueue.main
って型じゃないけど、どうやって MainActor
と紐付いてるの?( DispatchQueue.global()
だとコンパイルエラー)extension UserViewController { func setUser(_ user: User?) { self.user = user } }
作っておいて await self.setUser(user)
とか? override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { @MainActor [self] in do { let user = try await fetchUser(for: userID) self.user = user } catch { // エラーハンドリング } } }
extension UserViewController { func setUser(_ user: User?) { self.user = user } }
作っておいて await self.setUser(user)
とか? setUser
も actor-isolated でダメじゃないですか?await
するのか。UserViewController
のメソッドは暗黙で MainActor
isolatedになるので、他の Actor コンテキストからは暗黙に async なので await で呼べると。await self.user = user
とかできるんだけど、そうなってないし、そうなっていない理由があるんじゃないかと。async
は getter only ですもんね、今のところ。Task.detached(priority: .background) { @MainActor [self] in
って Task.init
と実質かわらなくないです?Task.detached(priority: .background) { @MainActor [self] in
って Task.init
と実質かわらなくないです? MainActor
だけど Task
の priority
は .background
っていうのはあり得るのかと思いましたが、 priority
は queue ごとに決まっていてそんなことはない? DispatchQueue
の priority
って queue を作るときに決めますもんね。setUser
を作るのもなぁ・・・。 DispatchQueue.main.async { self.user = user }
か、そうでなければ↓? Task { @MainActor in self.user = user }
@MainActor in
はいらないと思います。await { @MainActor in self.user = user }()
@MainActor in
はいらないと思います。 @MainActor in
はいらない Task.init
ではなく Task.detached
を挟んで context を引き継がないときなので MainActor
に戻す必要があるかと。 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { [self] in do { let user = try await fetchUser(for: userID); Task { @MainActor in self.user = user } } catch { // エラーハンドリング } } }
@MainActor in
はいらない Task.init
ではなく Task.detached
を挟んで context を引き継がないときなので MainActor
に戻す必要があるかと。 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { [self] in do { let user = try await fetchUser(for: userID); Task { @MainActor in self.user = user } } catch { // エラーハンドリング } } }
await { @MainActor in self.user = user }()
await
付けないといけないことに気付いてなかったんですけど、そしたらコンパイラがクラッシュしました
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { [self] in do { let user = try await fetchUser(for: userID); await { @MainActor in self.user = user }() } catch { // エラーハンドリング } } }
await { @MainActor in self.user = user }()
await
付けないといけないことに気付いてなかったんですけど、そしたらコンパイラがクラッシュしました
func foo() async { { @MainActor in }() }
でクラッシュ再現しました。報告しておきますTask.detached
で作られた Task
上で実行されて、 Task { @MainActor in
だと新しい Task
上で実行されるのが違いか。ただし、どちらでも main スレッド( DispatchQueue.main
上)で実行されると。DispatchQueue
はキューが priority
を持ってるけど、 Swift Concurrency では actor
(キューの保持者)ではなく Task
が priority
を持っている。 Task
が suspend されたときに resume される優先度が Task
の priority
で決まると思ってたけど、 actor
は suspend した Task
の priority
に関わらずキューに突っ込んだ順に resume する?それとも MainActor
は DispatchQueue.main
を使っている関係で Task
の priority
を無視して前から処理する? (edited)Since actors are designed for reentrancy, the runtime may choose to move the higher-priority item to the front of the queue, ahead of the lower-priority items. This way, higher-priority work could be executed first, with lower-priority work following later. This directly addresses the problem of priority inversion, allowing for more effective scheduling and resource utilization.
https://developer.apple.com/videos/play/wwdc2021/10254/?time=2160
actor
は入れ替えてくれそうな感じがしますね。 DispatchQueue
も DispatchWorkItem
に qos
を指定できるみたいですね。使ったことなかった。 https://developer.apple.com/documentation/dispatch/dispatchworkitem
MainActor
でも priority
を考慮してくれそう。 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { @MainActor in do { let user = try await fetchUser(for: userID) self.user = user } catch { // エラーハンドリング } } }
@MainActor
のクロージャ式が丸ごと↓の block
に突っ込まれるイメージ? DispatchQueue.main.async(DispatchWorkItem(qos: .background, block: /* ここ */))
(edited)DispatchQueue.main
はSerial Queueなので、qos
が高いDispatchWorkItem
が追加されても、前のDispatchWorkItem
のqos
上げて先に実行してしまわないんでしょうか?(priority inversionが起きる?)DispatchQueue.main
はSerial Queueなので、qos
が高いDispatchWorkItem
が追加されても、前のDispatchWorkItem
のqos
上げて先に実行してしまわないんでしょうか?(priority inversionが起きる?) @expanded
を追加する提案 これがついている引数は、その型の init
の引数を、直接その関数の引数として渡せる。 (edited)protocol P { associatedtype Value } protocol Q: P where Value == Int {}
このQ
を作るのをサボりたいってことですか?protocol Base { func foo() func bar() } protocol Derived: Base { func foo() override func bar() }
protocol Base { func foo() func bar() } protocol Derived: Base { func foo() override func bar() }
protocol Base { func foo() func bar() } protocol Derived: Base { func foo() override func bar() }
protocol Base { func foo() func bar() } protocol Derived: Base { func foo() override func bar() }
<stdin>:7:10: warning: implicit override should be marked with 'override' or suppressed with '@_nonoverride' func foo() ^ override <stdin>:2:10: note: overridden declaration is here func foo() ^
-warn-implicit-overrides
flag when building the standard library and overlays, so that each protocol member that overrides a member of an inherited protocol will produce a warning unless ...public protocol BaseP { func foo() func bar() } public protocol DerivedP: BaseP { override func foo() @_nonoverride func bar() } public struct Concrete: DerivedP { public func foo() {} public func bar() {} }
のときの witness table が sil_witness_table Concrete: DerivedP module test { base_protocol BaseP: Concrete: BaseP module test method #DerivedP.bar: <Self where Self : DerivedP> (Self) -> () -> () : @$s4test8ConcreteVAA8DerivedPA2aDP3baryyFTW // protocol witness for DerivedP.bar() in conformance Concrete } sil_witness_table Concrete: BaseP module test { method #BaseP.foo: <Self where Self : BaseP> (Self) -> () -> () : @$s4test8ConcreteVAA5BasePA2aDP3fooyyFTW // protocol witness for BaseP.foo() in conformance Concrete method #BaseP.bar: <Self where Self : BaseP> (Self) -> () -> () : @$s4test8ConcreteVAA5BasePA2aDP3baryyFTW // protocol witness for BaseP.bar() in conformance Concrete }
で @_nonoverride
と明示された requirement は別の witness になる。BidirectionalCollection.index(_:offsetBy:)
is the most obvious example, because the BidirectionalCollection
’s version of index(_:offsetBy:)
allows negative indices. RandomAccessCollection
’s version is also marked @_nonoverride
because it is required to be asymptotically faster than the Collection
or BidirectionalCollection
versions.@_nonoverride
に関しては public protocol BaseP { func foo() func bar() } public extension BaseP { func foo() { print("BaseP.foo()") } func bar() { print("BaseP.bar()") } } public protocol DerivedP: BaseP { override func foo() @_nonoverride func bar() } public extension DerivedP { func foo() { print("DerivedP.foo()") } func bar() { print("DerivedP.bar()") } } public struct Concrete: DerivedP {}
sil_witness_table Concrete: DerivedP module test { base_protocol BaseP: Concrete: BaseP module test method #DerivedP.bar: <Self where Self : DerivedP> (Self) -> () -> () : @$s4test8ConcreteVAA8DerivedPA2aDP3baryyFTW // protocol witness for DerivedP.bar() in conformance Concrete } sil_witness_table Concrete: BaseP module test { method #BaseP.foo: <Self where Self : BaseP> (Self) -> () -> () : @$s4test8ConcreteVAA5BasePA2aDP3fooyyFTW // protocol witness for BaseP.foo() in conformance Concrete method #BaseP.bar: <Self where Self : BaseP> (Self) -> () -> () : @$s4test8ConcreteVAA5BasePA2aDP3baryyFTW // protocol witness for BaseP.bar() in conformance Concrete }
こうなるんだけどprotocol witness for BaseP.bar()
を呼び出す方法がわからないので、 public protocol BaseP { func foo() func bar() } public extension BaseP { func foo() { print("BaseP.foo()") } func bar() { print("BaseP.bar()") } } public protocol DerivedP: BaseP { override func foo() @_nonoverride func bar() } public extension DerivedP { func foo() { print("DerivedP.foo()") } func bar() { print("DerivedP.bar()") } } public struct Concrete: DerivedP { } func testBase<T: BaseP>(x: T) { x.foo() x.bar() } func testDerived<T: DerivedP>(x: T) { x.foo() x.bar() } testBase(x: Concrete()) testDerived(x: Concrete())
public protocol BaseP { func foo() func bar() } public extension BaseP { func foo() { print("BaseP.foo()") } func bar() { print("BaseP.bar()") } } public protocol DerivedP: BaseP { override func foo() @_nonoverride func bar() } public extension DerivedP { func foo() { print("DerivedP.foo()") } func bar() { print("DerivedP.bar()") } } public struct Concrete: DerivedP { } func testBase<T: BaseP>(x: T) { x.foo() x.bar() } func testDerived<T: DerivedP>(x: T) { x.foo() x.bar() } testBase(x: Concrete()) testDerived(x: Concrete())
DerivedP.foo() DerivedP.bar() DerivedP.foo() DerivedP.bar()
protocol Base { func foo() } protocol Derived: Base { @_nonoverride func foo() } struct Impl: Derived { @_implements(Base, foo()) func foo1() { print("Impl.foo1") } @_implements(Derived, foo()) func foo2() { print("Impl.foo2") } } func callBase<T: Base>(_ x: T) { x.foo() } func callDerived<T: Derived>(_ x: T) { x.foo() } callBase(Impl()) callDerived(Impl())
protocol Base { func foo() } protocol Derived: Base { @_nonoverride func foo() } struct Impl: Derived { @_implements(Base, foo()) func foo1() { print("Impl.foo1") } @_implements(Derived, foo()) func foo2() { print("Impl.foo2") } } func callBase<T: Base>(_ x: T) { x.foo() } func callDerived<T: Derived>(_ x: T) { x.foo() } callBase(Impl()) callDerived(Impl())
Impl.foo1 Impl.foo2
BidirectionalCollection.index(_:offsetBy:)
は、RandomAccessCollection.index(_:offetBy:)
は、一気にオフセットしたインデックスを作ってアクセスできるpublic protocol BaseP { func foo() func bar() } public extension BaseP { func foo() { print("BaseP.foo()") } func bar() { print("BaseP.bar()") } } public protocol DerivedP: BaseP { override func foo() @_nonoverride func bar() } public struct Concrete: DerivedP {}
でエラーにならないのでそういうわけでもなさそう@_nonoverride
相当だったのを override
相当にすることによって witness table が小さくなってサイズ削減できたって話っぽいですよね。override
も nonoverride
も付けない場合で、サブプロトコルで同じ名前をrequirementsに書くことができて、その場合テーブルエントリが別にできるわけですね。This makes restating protocol requirements in inheriting protocols ABI-neutral, so we can restate protocol requirements to influence associated type inference without baking those decisions into the ABI.
protocol Base { func foo() } protocol Derived: Base { func foo() } struct Impl: Derived { @_implements(Base, foo()) func foo1() { print("foo1") } @_implements(Derived, foo()) func foo2() { print("foo2") } } func callBase<T: Base>(_ x: T) { x.foo() } func callDerived<T: Derived>(_ x: T) { x.foo() } callBase(Impl()) callDerived(Impl())
protocol Base { func foo() } protocol Derived: Base { func foo() } struct Impl: Derived { @_implements(Base, foo()) func foo1() { print("foo1") } @_implements(Derived, foo()) func foo2() { print("foo2") } } func callBase<T: Base>(_ x: T) { x.foo() } func callDerived<T: Derived>(_ x: T) { x.foo() } callBase(Impl()) callDerived(Impl())
foo1 foo1
Derived.foo
に @_nonoverride
をつけると、2行目の出力が foo2 に変わる。AsyncSequence
でOptionalを扱いたい場合、nextの戻り値の型をOptionalのOptionalにしなければいけないのは、ループの終わり記号としてのnilと、要素としてのnilを区別するために仕方がないんですかね?(AsyncStream
との比較のためにあえてAsyncSequence
使ってます)IteratorProtocol
と同じですね(失礼しましたmm)。 mutating func next() async throws -> Self.Element?
struct IntGenerator: AsyncSequence { typealias Element = Int? var strings: [String] struct AsyncIterator: AsyncIteratorProtocol { var current = 1 var strings: [String] fileprivate var index = 0 mutating func next() async -> Int?? { // <- ここ guard index < strings.count else { return nil } let string = strings[index] index += 1 return Int(string) } } func makeAsyncIterator() -> AsyncIterator { AsyncIterator(strings: strings) } } Task { let strings: [String] = ["1", "a", "2"] for await i in IntGenerator(strings: strings) { print(i) } }
Optional(1) nil Optional(2)
(edited)AsyncSequence
でOptionalを扱いたい場合、nextの戻り値の型をOptionalのOptionalにしなければいけないのは、ループの終わり記号としてのnilと、要素としてのnilを区別するために仕方がないんですかね?(AsyncStream
との比較のためにあえてAsyncSequence
使ってます)IteratorProtocol
と同じですね(失礼しましたmm)。 mutating func next() async throws -> Self.Element?
struct IntGenerator: AsyncSequence { typealias Element = Int? var strings: [String] struct AsyncIterator: AsyncIteratorProtocol { var current = 1 var strings: [String] fileprivate var index = 0 mutating func next() async -> Int?? { // <- ここ guard index < strings.count else { return nil } let string = strings[index] index += 1 return Int(string) } } func makeAsyncIterator() -> AsyncIterator { AsyncIterator(strings: strings) } } Task { let strings: [String] = ["1", "a", "2"] for await i in IntGenerator(strings: strings) { print(i) } }
Optional(1) nil Optional(2)
(edited)Iterator
の API が hasNext
と next
に分かれていて、まず hasNext
で存在を確認してから next
を呼ぶという手順になっていますね。その点、 next
だけで済ませられる Swift の設計はシンプルだと思います。Iterator
の API が hasNext
と next
に分かれていて、まず hasNext
で存在を確認してから next
を呼ぶという手順になっていますね。その点、 next
だけで済ませられる Swift の設計はシンプルだと思います。 func graphemeBreakPropertyData( forLine line: String ) -> (scalars: ClosedRange<Unicode.Scalar>, property: Unicode.GraphemeBreakProperty)? { line .match(/([0-9A-F]+)(?:\.\.([0-9A-F]+))?\s*;\s(\w+).*/)? .captures.flatMap { (l, u, p) in guard let property = Unicode.GraphemeBreakProperty(p) else { return nil } let scalars = Unicode.Scalar(hex: l)! ... Unicode.Scalar(hex: u ?? l)! return (scalars, property) } }
l, u, p
の3要素タプルなのは正規表現パターンと対応しているのか。これはいいなあ。func graphemeBreakPropertyData( forLine line: String ) -> (scalars: ClosedRange<Unicode.Scalar>, property: Unicode.GraphemeBreakProperty)? { line.match { OneOrMore(.hexDigit).capture { Unicode.Scalar(hex: $0) } Optionally { ".." OneOrMore(.hexDigit).capture { Unicode.Scalar(hex: $0) } } OneOrMore(.whitespace) ";" OneOrMore(.whitespace) OneOrMore(.word).capture(GraphemeBreakProperty.init) Repeat(.anyCharacter) }?.captures.map { (lower, upper, property) in let scalars = lower ... (upper ?? lower) return (scalars, property) } }
captures.map {
が (lower, upper, property)
の3要素タプルになってるところで.match
の中に書いてある3つの .capture
があることと対応しているのか。*
がOneOrMore
って書かれてて心配になる。ZeroOrMore
AsyncStream
の挙動を調べていたのですが、(1)のループの後にfinish
を呼ぶとonTermination
のコールバックが呼ばれてcancel扱いになるのですが、これってこういうものなのでしょうか? なんとなく「値の出力が全部終わってfinishしたらなぜかcancel処理が走った」みたいな印象を受けたのでどうなのかなあと思いまして
func hoge() async { let digits = AsyncStream(Int.self) { continuation in Task.detached { for digit in 0..<10 { // (1) continuation.yield(digit) } continuation.finish() continuation.onTermination = { @Sendable finished in if case .cancelled = finished { print("cancel") // 呼ばれる } } } } for await digit in digits { print(digit) } }
https://developer.apple.com/documentation/swift/asyncstream (edited)func hoge() async { let digits = AsyncStream(Int.self) { continuation in Task.detached { for digit in 0..<10 { continuation.yield(digit) } continuation.onTermination = { @Sendable finished in switch finished { case .finished: print("finished") // タイミングがバラバラ case .cancelled: print("cancel") @unknown default: break } } continuation.finish() } } for await digit in digits { print(digit) } }
(edited)MainActor
)で final class UserViewController: UIViewController { private let state: UserViewState ... override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) state.loadUser() } }
としてるの、ダメでは?これだと loadUser
の非同期処理が終わるまで viewDidAppear
を抜けられない・・・。 https://zenn.dev/koher/articles/swift-concurrency-cheatsheet#%F0%9F%92%BC-case-20-(mainactor)%3A-%E5%85%B1%E6%9C%89%E3%81%95%E3%82%8C%E3%81%9F%E7%8A%B6%E6%85%8B%E3%81%AE%E5%A4%89%E6%9B%B4%EF%BC%88%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E4%B8%8A%E3%81%A7%E3%81%AE%E5%87%A6%E7%90%86%EF%BC%89 func loadUser() { Task { do { user = try await fetchUser(for: userID) } catch { // エラーハンドリング } } }
@MainActor
で意図せず async
であるべきメソッドが同期的に呼べてヤバいケースがあるんじゃないかと考えてたんだけど、 fetchUser
は @MainActor
とは関係のない async
関数だから、ここで await
が必須になって、 loadUser
に async
をつけなきゃとなった時点で気付くか。よかった。 (edited)protocol P {} struct S: P {} func makeS() -> S { S() } let f1 = makeS as () -> P print("f1 ok") if let f2 = makeS as? () -> P { print("f2 ok") } else { print("f2 ng") }
protocol P {} struct S: P {} func makeS() -> S { S() } let f1 = makeS as () -> P print("f1 ok") if let f2 = makeS as? () -> P { print("f2 ok") } else { print("f2 ng") }
f1 ok f2 ng
stderr:<stdin>:8:19: warning: runtime conversion from '() -> S' to '() -> P' is not supported; cast always fails if let f2 = makeS as? () -> P { ^ <stdin>:8:19: note: consider using 'as' coercion instead if let f2 = makeS as? () -> P { ^~~ as <stdin>:8:8: warning: value 'f2' was defined but never used; consider replacing with boolean test if let f2 = makeS as? () -> P { ~~~~^~~~~ ~~~ is
as
は通るけど as?
は失敗する、という事があるらしい。protocol P {} struct S: P {} func makeS() -> S { S() } let f1 = makeS as () -> P print("f1 ok") if let f2 = makeS as Any as? () -> P { print("f2 ok") } else { print("f2 ng") }
protocol P {} struct S: P {} func makeS() -> S { S() } let f1 = makeS as () -> P print("f1 ok") if let f2 = makeS as Any as? () -> P { print("f2 ok") } else { print("f2 ng") }
f1 ok f2 ng
stderr:<stdin>:8:8: warning: value 'f2' was defined but never used; consider replacing with boolean test if let f2 = makeS as Any as? () -> P { ~~~~^~~~~ ~~~ is
Swift
パッケージに、 CodableConfiguration
という新要素が勝手に追加されてる。 https://developer.apple.com/documentation/swift/keyeddecodingcontainer/3766918-decodeCodableConfiguration
は Foundation
だからセーフかSwift.KeyedDecodingContainer.func decode<T, C>(_: CodableConfiguration<T?, C>.Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> CodableConfiguration<T?, C> where T : DecodableWithConfiguration, T : EncodableWithConfiguration, C : DecodingConfigurationProviding, C : EncodingConfigurationProviding, T.DecodingConfiguration == C.DecodingConfiguration, T.EncodingConfiguration == C.EncodingConfiguration
KeyedDecodingContainer
は struct
だから、Foundation
から extension
で生やす分にはSwift
モジュールに影響はないのか。#if
書ける案#if
より if
書きたいなと思った。Optional
な値で、存在するときだけ Array
や Dictionary
に入れたいとか。const args = [ 'xcresulttool', 'get', '--path', this.bundlePath, '--format', 'json' ] if (reference) { args.push('--id') args.push(reference) }
こういう感じだな。+
と三項演算子でそれっぽく書けるか。。。?.compact()
で取り除いてるprotocol P {} extension P { // static funcが書けるようになった static func foo() -> Int { 0 } /* インナータイプは作れないけど struct Inner {} */ } /* typealiasできるので実質可能? */ struct _P_Inner {} extension P { typealias Inner = _P_Inner } print(P.Inner())
struct InvalidP: P {} extension P where Self == InvalidP {}
とすることで foo
を呼べますP.foo()
じゃなくて func f(_ p: P)
に対して f(.foo())
という形になるので多少ややこしいextension Never: P {} extension P where Self == Never {}
のほうが安全だということに気づいた@Bar var bar = 1
コレは通るんじゃないかしら@propertyWrapper struct StringDictionary { var wrappedValue: [String: String] } @StringDictionary var d1. // infers Dictionary<String, String> @StringDictionary var d2: Dictionary // infers <String, String>
↑なんかいろいろ怪しいサンプルコードはある (edited)var x: Int?
は右辺値要らないけど・・・@Bar var b
が駄目なのが話と違うような気もしてきたなvar a: Void
var a: Void var b: Int
func foo() { let a: Void let b: Int }
func foo() { let a: Void let b: Int }
<stdin>:2:7: warning: immutable value 'a' was never used; consider replacing with '_' or removing it let a: Void ^ _ <stdin>:3:7: warning: immutable value 'b' was never used; consider replacing with '_' or removing it let b: Int ^ _
func foo() { let a: Void let b: Int }
func foo() { let a: Void let b: Int }
<stdin>:2:7: warning: immutable value 'a' was never used; consider replacing with '_' or removing it let a: Void ^ _ <stdin>:3:7: warning: immutable value 'b' was never used; consider replacing with '_' or removing it let b: Int ^ _
func foo() { let a: Void let b: Int }
<stdin>:2:7: warning: immutable value 'a' was never used; consider replacing with '_' or removing it let a: Void ^ _ <stdin>:3:7: warning: immutable value 'b' was never used; consider replacing with '_' or removing it let b: Int ^ _
func foo() { let a: Void let b: Int }
<stdin>:2:7: warning: immutable value 'a' was never used; consider replacing with '_' or removing it let a: Void ^ _ <stdin>:3:7: warning: immutable value 'b' was never used; consider replacing with '_' or removing it let b: Int ^ _
let a: Void print(a)
func foo() { let a: Void let b: Int print(a) print(b) }
(edited)func foo() { let a: Void let b: Int print(a) print(b) }
(edited)<stdin>:5:9: error: constant 'b' used before being initialized print(b) ^ <stdin>:3:7: note: constant defined here let b: Int ^
@Foo var a = ()
って書くのは変だな= ()
は何もない時にそう書いてる扱いがされるって感じか@propertyWrapper struct Bar { var wrappedValue: Int init(wrappedValue: Int = 0) { self.wrappedValue = wrappedValue } } struct S { @Bar var b }
init(wrappedValue: T? = nil)
がいけるのか@Bar<Int> var b
これはいけるでしょ (edited)@Environment(\.colorScheme) var colorScheme
みたいなonAppear
の中はコンパイラ的にはMainActorっぽい(viewModel2のメソッド呼び出しにawaitがいらないので)けど明示的に @MainActor
がついてないほうはコンテキストの引き継ぎが行われなくて、メインスレッド警告がでている?-warn-concurrency
ついてなかったから気づかなかったけどちゃんと警告でてた・・( Cannot use parameter 'self' with a non-sendable type 'ViewModel1' from concurrently-executed code
) Taskの中でawaitが起こるときにselfが明示的なactor contextを持っているかを確認してあればそのactor contextにスイッチするという感じなのかなself
が Sendable
じゃないからそもそも Task.init
のクロージャに渡せないのか。func
ごとに静的に決定されるもので、クロージャの場合、条件を満たせば( non-@Sendable
または Task.init
)引き継がれる?await
の箇所ごとにコンパイラが見える範囲まで最寄りのActor Contextを探して、見つからなかったらそのままasync側のコンテキストに着地してそうasync
側のコンテクストも存在しないのかな。結果的に async
API の呼び出し先依存のスレッドで実行されるにしても、そこでさらにクロージャを書いたからといってそのコンテクストが引き継がれるとかはないと。actor Foo { func bar() -> Int { // Thread 2 & Foo の Actor Context 42 } }
let foo = Foo() Task { // Thread 1 let x = await foo.bar() // Thread 2 (ただし Foo の Actor Context ではない) print(x) Task { // Thread 3 ( Foo の Actor Context は引き継がない) print(x) } }
(edited)func global() async -> Int { return await withCheckedContinuation { c in DispatchQueue.global().async { c.resume(returning: 42) } } } @MainActor final class Foo { func start() { Task { let x = await global() print(x, Thread.isMainThread) // 42 true Task { let x = await global() print(x, Thread.isMainThread) // 42 true } } } }
これは引き継がれてそうですstart
が Main Actor の Actor Context を持つので、その中に書かれたクロージャには(条件を満たせば) Actor Context が引き継がれますね。bar
を呼び出すのに foo
を書き忘れてたので・・・。await bar()
→ await foo.bar()
の修正と、 foo
の宣言を最初に追加しました。actor Bar { func bar() -> Int { DispatchQueue.main.sync { 42 } } } final class Foo { func start() { let bar = Bar() Task { let x = await bar.bar() print(x, Thread.isMainThread) // 42 false Task { let x = await bar.bar() print(x, Thread.isMainThread) // 42 false } } } }
これはfalseだ。async元のコンテキストを引き継いでるのではなく無のコンテキストにスイッチしてるのかな@MainActor final class Bar { func bar() -> Int { 42 } } final class Foo { func start(bar: Bar) { Task { print(Thread.isMainThread) // 42 false let x = await bar.bar() print(x, Thread.isMainThread) // 42 true Task { print(Thread.isMainThread) // 42 false let x = await bar.bar() print(x, Thread.isMainThread) // 42 true } } } }
(edited)Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
Bar
がinit()できないですね・・Foo
は actor
になってないですよね?Task.init
自体がコンテクストを持つのかな?import SwiftUI @MainActor struct ContentView: View { let bar = Bar() var body: some View { Text("Hello, world!") .onAppear { Foo().start(bar: bar) } } } @MainActor final class Bar { func bar() -> Int { print("bar", Thread.isMainThread) return 42 } } final class Foo { func start(bar: Bar) { Task { print("1", Thread.isMainThread, ObjectIdentifier(Thread.current)) let x = await bar.bar() print("2", Thread.isMainThread, ObjectIdentifier(Thread.current), x) Task { print("3", Thread.isMainThread, ObjectIdentifier(Thread.current)) let x = await bar.bar() print("4", Thread.isMainThread, ObjectIdentifier(Thread.current), x) Task.detached { print("5", Thread.isMainThread, ObjectIdentifier(Thread.current)) let x = await bar.bar() print("6", Thread.isMainThread, ObjectIdentifier(Thread.current), x) } } } } }
1 false ObjectIdentifier(0x0000600001348780) bar true 2 false ObjectIdentifier(0x0000600001319580) 42 3 false ObjectIdentifier(0x0000600001319580) bar true 4 false ObjectIdentifier(0x0000600001348780) 42 5 false ObjectIdentifier(0x0000600001319580) bar true 6 false ObjectIdentifier(0x0000600001348780) 42
bar
はたしかにメインスレッドで実行されているのとTask
は Actor Context を持つわけではなさそう。 Actor Context がない場合も await
先が結果を返したスレッドでそのまま実行されるわけではなく、また別のスレッドに投入される?await
の後は同期的だと思ってましたけど、それは違うと(どのスレッドで実行されるかわからない)。 Actor Context は静的に解決されると考えて、それ以外に想定外なところはなさそう。@usableFromInline internal struct S { @usableFromInline internal var a: Int @inlinable internal init() { self.a = 0 } }
a.swift:8:16: error: 'self' used before 'self.init' call or assignment to 'self' self.a = 0 ^ a.swift:9:5: error: 'self.init' isn't called on all paths before returning from initializer } ^
struct S
を @frozen
にするとビルドできるようになるんだけどinit
が @inlinable
だと、レイアウト変化後に、埋め込まれた古い init
が正しく動作しないからおかしい」 というのが根底の話でTask.yield()
使ってみたらうまくいけたんですけれど、合法的な使い方なのか怪しい… async let result: () = viewModel.fetchRepository() await Task.yield() XCTAssertEqual(viewModel.repositories, []) stubApiClient.continue() // resumeで値を返却させる await result XCTAssertEqual(viewModel.repositories.count, 1)
func fetchRepositories(userName: String) async throws -> [FooItem] { // ここで値を返すとawait中の検証ができない return try await withCheckedThrowingContinuation { continuation in self.continuation = continuation } } func `continue`() { self.continuation.resume(returning: expectResult) }
Task.yield()
がない場合、await内の処理がまったく進まないので、上のコードだと支障ないんですけれど、内部で値を変えているものが未反映になる感じですねXCTAssertFalse(viewModel.showProgress) async let result: () = viewModel.fetchRepository() await Task.yield() XCTAssertTrue(viewModel.showProgress) // ここを検証したい stubApiClient.continue() // resumeで値を返却させる await result XCTAssertFalse(viewModel.showProgress)
viewModel.fetchRepository
の処理が動きはじめて viewModel.showPregress
になったが、stubAPIClient.continue
の呼び出しがまだされていない 時刻で、状態をassertしたいが、async let
の後に Task.yield
が無い場合、 viewModel.fetchRepository
の処理がまだ開始していない時刻に assert が処理してしまうってことですよね?viewModel.fetchRepository
の処理が動き始めるのを待機する」ロジックを明確に書くのが良いと思います。viewModel.fetchRepository
の中で、 StubAPIClient.fetchRepository
を呼ぶようになってますよね、なので、StubAPIClient
に、 fetchRepository
が呼ばれたら返ってくる await property を生やすのが良いんじゃないでしょうかXCTAssertFalse(viewModel.showProgress) async let result: () = viewModel.fetchRepository() await stubAPIClient.willFetchRepository XCTAssertTrue(viewModel.showProgress) stubApiClient.continue() await result XCTAssertFalse(viewModel.showProgress)
Task.yield
1回だと期待した状態まで進行しない懸念も消せます (edited)withCString(:_)
とか withUnsafeBytes(:_)
をリストで扱いたいと思って、つまり let array: [String] = [...] array.with(each: String.withCString) { cStrings/*: [UnsafePointer<CChar>] */ in cFunctionReceivingCStringArray(cStrings) }
こういうのがやりたくて、邪悪なものを生み出してしまった。 extension BidirectionalCollection { func with<U, R>( each mapFn: (Element) -> ((U) throws -> R) throws -> R, _ fn: ([U]) throws -> R ) rethrows -> R { try withoutActuallyEscaping(fn) { fn in try withoutActuallyEscaping(mapFn) { mapFn in var stash: [U] = [] stash.reserveCapacity(count) let closure = reversed().reduce({ try fn(stash) }) { closure, elem in { try mapFn(elem)() { val in stash.append(val) return try closure() } } } return try closure() } } } }
(edited)@available(macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4, *) public protocol CodingKeyRepresentable
となっているものの適合時に@available
付けなくても何も言われず、下位バージョンだと以前のままArray扱いになってしまうみたいですね…main
に取り込まれたんじゃなかったっけ?マニフェストが出ただけだっけ?main
に取り込まれたんじゃなかったっけ?マニフェストが出ただけだっけ? import _Differentiation
で使える状態っぽい。 プロポーザルは下書きしか存在しないはずget/set
の組で操作せずに _modify
に出来ると回避できるんですよねimport Foundation let arraySize = 1000000 let loopCount = 1000 class Values { var values = [Int](repeating: 0, count: arraySize) func foo1() { values[0] += values[0] } func foo2() { let v = values[0] values[0] += v } } func measure(_ body: () -> Void) { let time1 = Date.timeIntervalSinceReferenceDate for _ in 0 ..< loopCount { body() } let time2 = Date.timeIntervalSinceReferenceDate print(time2 - time1) } let values = Values() measure { values.foo1() } measure { values.foo2() }
import Foundation let arraySize = 1000000 let loopCount = 1000 class Values { var values = [Int](repeating: 0, count: arraySize) func foo1() { values[0] += values[0] } func foo2() { let v = values[0] values[0] += v } } func measure(_ body: () -> Void) { let time1 = Date.timeIntervalSinceReferenceDate for _ in 0 ..< loopCount { body() } let time2 = Date.timeIntervalSinceReferenceDate print(time2 - time1) } let values = Values() measure { values.foo1() } measure { values.foo2() }
0.9675040245056152 0.00011301040649414062
Values
をstructにしても起こりません。@escaping
な non-@Sendable
クロージャは actor context を引き継ぐという理解なんですけど、↓の foo
が返すクロージャは常にメインスレッドで実行される( true が表示される)ということでしょうか? @MainActor func foo() -> () -> Void { // main actor context { // actor context を引き継ぐ print(Thread.isMainThread) } }
この foo
で返されたクロージャを Task.detached
から呼び出すと false
が表示されたんですが、これは僕の仕様理解が間違っているか、バグか、未実装か、実行方法に問題がある(何かオプションを付けないといけないなど)か、どう考えれば良いでしょうか? (edited)async
でないからメインスレッドで同期実行というのが破綻してるか。DispatchQueue.global().async
みたいな API に渡すケースは今は穴が空いているけど、将来的にはそういう穴は塞がれる( DispatchQueue.global().async
のような API が廃止される?)みたいな話をしたと思うんですが、↑のようなケースはどうなるんでしょう? foo
がコンパイルエラー? func foo() -> @MainActor () -> Void
じゃないといけない?func bar() { // StrictConcurrencyChecking: Minimal DispatchQueue.global().async { await foo()() // ❌ Cannot pass function of type '() async -> Void' to parameter expecting synchronous function type } // StrictConcurrencyChecking: Complete DispatchQueue.global().async { await foo()() // ❌ Cannot pass function of type '@Sendable () async -> Void' to parameter expecting synchronous function type } }
(edited)@escaping
クロージャの actor context 引き継ぎとは、何(誰)の context を引き継ぐのでしょうか?
@Sendable
cannot escape the concurrency domain in which it was formed. @MainActor
で escape できるのがおかしい? @swift-5.6.3
func run(_ operation: () -> Void) { operation() } func runEscaping(_ operation: @escaping () -> Void) { operation() } actor Foo { var count: Int = 0 func countUp() { run { count += 1 } runEscaping { [weak self] in guard let self = self else { return } print(self.count) } } }
@MainActor
で escape できるのがおかしい? @swift-5.6.3
func run(_ operation: () -> Void) { operation() } func runEscaping(_ operation: @escaping () -> Void) { operation() } actor Foo { var count: Int = 0 func countUp() { run { count += 1 } runEscaping { [weak self] in guard let self = self else { return } print(self.count) } } }
<stdin>:18:24: error: actor-isolated property 'count' can not be referenced from a non-isolated context print(self.count) ^ <stdin>:10:9: note: property declared here var count: Int = 0 ^
func run(_ operation: () -> Void) { operation() } func runEscaping(_ operation: @escaping () -> Void) { operation() } @MainActor final class Bar { var count: Int = 0 func countUp() { run { count += 1 } runEscaping { [weak self] in guard let self = self else { return } print(self.count) } } }
func run(_ operation: () -> Void) { operation() } func runEscaping(_ operation: @escaping () -> Void) { operation() } @MainActor final class Bar { var count: Int = 0 func countUp() { run { count += 1 } runEscaping { [weak self] in guard let self = self else { return } print(self.count) } } }
func runEscaping(_ operation: @escaping () -> Void) { Task.detached { operation() } }
うろ覚えなんですが、Concurrencyの機能が使われていない場合、Sendableを削除してませんでしたっけ?なのでMainActorの方のcountはエラーにならない? // Strip off Sendable and (possibly) the global actor.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4546 asyncとSendableを使っている場合はConcurrencyを使っているとみなされる。 // Async and @Sendable closures use concurrency features.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4001 actorの内部にある場合もConcurrencyを使っているとみなされる。 // If we're in an actor, we're using concurrency features.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4027 @Sendable付けると@MainActor Barの方もエラーになるんですよねえ func runEscaping(_ operation: @Sendable @escaping () -> Void) { operation() }
(edited)func runEscaping(_ operation: @escaping () -> Void) { Task.detached { operation() } }
うろ覚えなんですが、Concurrencyの機能が使われていない場合、Sendableを削除してませんでしたっけ?なのでMainActorの方のcountはエラーにならない? // Strip off Sendable and (possibly) the global actor.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4546 asyncとSendableを使っている場合はConcurrencyを使っているとみなされる。 // Async and @Sendable closures use concurrency features.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4001 actorの内部にある場合もConcurrencyを使っているとみなされる。 // If we're in an actor, we're using concurrency features.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4027 @Sendable付けると@MainActor Barの方もエラーになるんですよねえ func runEscaping(_ operation: @Sendable @escaping () -> Void) { operation() }
(edited)runEscaping
がどのように実装されているかわからないと思うんですが、 runEscaping
がブラックボックスだと考えたときに @MainActor func foo() { // main actor context runEscaping { // ここに main actor context が引き継がれることが保証されていないと何も信じられない } }
となりませんか?だから、 actor context の引き継ぎはコールスタックがどのようになっていてもクロージャの宣言箇所によって決められる(たとえ Task.detached
を挟もうが、 main actor context を引き継ぐ箇所で宣言されたクロージャは実行時に再度 main queue に投入される)のだと思っていました。 ただ、普通の actor
だとコンパイルエラーになっていることから、 MainActor
(というか global actor ?)の挙動がおかしいんでしょうか?
(edited)// ここに main actor context が引き継がれることが保証されていないと何も信じられない
runEscaping
は func runEscaping(_ operation: @escaping () -> Void) { Task.detached { // actorのcontextは引き継がないのでmain actor contextではない operation() } }
とコンパイラが判定していると思ってました。 あと、今再確認したら↓はSwift5.7だとエラーになりますね(昨日はなんかキャッシュが残っていたようです) func run(_ operation: () -> Void) { operation() } func runEscaping(_ operation: @escaping () -> Void) { operation() } @MainActor final class Bar { var count: Int = 0 func countUp() { run { count += 1 } runEscaping { [weak self] in guard let self = self else { return } print(self.count) // :x: Property 'count' isolated to global actor 'MainActor' can not be referenced from a non-isolated synchronous context } } }
(edited)@escaping
な non-@Sendable
クロージャは actor context を引き継ぐという理解なんですけど、↓の foo
が返すクロージャは常にメインスレッドで実行される( true が表示される)ということでしょうか? @MainActor func foo() -> () -> Void { // main actor context { // actor context を引き継ぐ print(Thread.isMainThread) } }
この foo
で返されたクロージャを Task.detached
から呼び出すと false
が表示されたんですが、これは僕の仕様理解が間違っているか、バグか、未実装か、実行方法に問題がある(何かオプションを付けないといけないなど)か、どう考えれば良いでしょうか? (edited)import Foundation @MainActor func foo() -> () -> Void { { print(Thread.isMainThread) } } @main struct Main { @MainActor static func main() async { let f = foo() let t = Task.detached { f() } _ = await t.value } }
(edited)import Foundation @MainActor func foo() -> () -> Void { { print(Thread.isMainThread) } } @main struct Main { @MainActor static func main() async { let f = foo() let t = Task.detached { f() } _ = await t.value } }
(edited)false
stderr:<stdin>:16:7: warning: capture of 'f' with non-sendable type '() -> Void' in a `@Sendable` closure f() ^ <stdin>:16:7: note: a function type must be marked '@Sendable' to conform to 'Sendable' f() ^
(edited)func runEscaping(_ operation: @escaping () -> Void) { Task.detached { // Taskも同じ結果 print("3", Thread.isMainThread) // false operation() } } @MainActor final class Bar { func countUp() { print("1", Thread.isMainThread) // true runEscaping { print("2", Thread.isMainThread) // false } } }
↓はWWDCのセッションで言われていた通り、nonisolatedなsync関数は呼び出し側のcontextに依る(そもそもescapingしないのか) func runEscaping(_ operation: @escaping () -> Void) { print("3", Thread.isMainThread) // true operation() } @MainActor final class Bar { func countUp() { print("1", Thread.isMainThread) // true runEscaping { print("2", Thread.isMainThread) // true } } }
(edited)func runEscaping(_ operation: @escaping () -> Void) { Task.detached { print("3", Thread.isMainThread) // false operation() } } @MainActor final class Bar { func countUp() { print("1", Thread.isMainThread) // true runEscaping { print("2", Thread.isMainThread) // false Task { // ここはmain actor contextが引き継がれる print("4", Thread.isMainThread) // true👀 } } } }
asyncを足すとtrueになる(MainActorにhopできる) func runEscaping(operation: @escaping () async -> Void) { Task.detached { print("3", Thread.isMainThread) // false await operation() } } @MainActor final class Bar { func countUp() { runEscaping { print("2", Thread.isMainThread) // trueになる👀 } } }
(edited)public struct _NSRange { public var location: Int public var length: Int public init() { location = 0 length = 0 } public init(location: Int, length: Int) { self.location = location self.length = length } }
https://github.com/apple/swift-corelibs-foundation/blob/ee856f110177289af602c4040a996507f7d1b3ce/Sources/Foundation/NSRange.swiftextension _NSRange: @unchecked Sendable {}
はどうですか?@unchecked
つけると黙らせられるな。なんか気持ち悪いけど、まぁ今回のケースは実際安全なことがわかってるからとりあえずはこれでもいいか。ありがとう。@unchecked
を使いなさい」って感じですけど、 この理由はおそらく、別のファイルで Sendable
準拠すると、定義元のライブラリがソースアップデートする場合には(本当にスレッドアンセーフなデグレ改修がされて)ソース互換性が壊れる可能性があるし、バイナリアップデートする場合には嘘のSendableになってしまうからですね。 @unchecked
はそのように後に壊れる可能性がある事を許容する宣言でもある。 まさに「実際安全な事がわかってる間」にしのぐための言語仕様です。 (edited)@unchecked
をつけることになるのでその場しのぎじゃなくても普通に使いませんか?class
の実装で内部は DispatchQueue
とかで自力で安全を保障する場合はそうですね。@unchecked
が気持ち悪いとか実装で保証してることとの区別がつかなくて嫌なときは @preconcurrency import struct Foundation.NSRange
と書くのもいいかもしれないimport Foundation @preconcurrency import struct Foundation.NSRange func f() { let range = NSRange.init() let string = NSString.init() Task { print(range, string) } }
import Foundation @preconcurrency import struct Foundation.NSRange func f() { let range = NSRange.init() let string = NSString.init() Task { print(range, string) } }
<stdin>:8:15: warning: capture of 'range' with non-sendable type 'NSRange' (aka '_NSRange') in a `@Sendable` closure print(range, string) ^ Foundation._NSRange:1:15: note: struct '_NSRange' does not conform to the 'Sendable' protocol public struct _NSRange { ^ <stdin>:1:1: remark: add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'Foundation' import Foundation ^ @preconcurrency <stdin>:8:22: warning: capture of 'string' with non-sendable type 'NSString' in a `@Sendable` closure print(range, string) ^ Foundation.NSString:1:12: note: class 'NSString' does not conform to the 'Sendable' protocol open class NSString : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCoding { ^ <stdin>:2:17: remark: '@preconcurrency' attribute on module 'Foundation' is unused @preconcurrency import struct Foundation.NSRange ~~~~~~~~~~~~~~~~^
@preconcurrency import struct Foundation.NSRange import Foundation func f() { let range = NSRange.init() let string = NSString.init() Task { print(range, string) } }
@preconcurrency import struct Foundation.NSRange import Foundation func f() { let range = NSRange.init() let string = NSString.init() Task { print(range, string) } }
@rethrows
って、いつ正式に Swift に入ったんですっけ? @rethrows protocol AsyncSequence
https://developer.apple.com/documentation/Swift/AsyncSequence ↓にも追加されてなさそうだし、 https://docs.swift.org/swift-book/ReferenceManual/Attributes.html ↓ AsyncSequence
の Proposal にも書かれてなさそう。 https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md@_
じゃない? _
なくてびっくりした。@rethrows
が付くのか_
が無いんだろうか?AsyncSequenceProtocol
にも @rethrows
付いてるな。 https://developer.apple.com/documentation/swift/asynciteratorprotocol@rethrows
が付くのか @rethrows
と rethrows
は直接的にキーワードとして結びつくのかな? @Sendable
と Sendable
みたいな意味合いでってこと? @Sendable
は Proposal 通ってるしなぁ。 Marker Protocol と @
の関係が先送りされただけで。func next() rethrows
によって機能するらしいけど@rethrows
が何をやりたいのかはわかるにしても、どう働く仕様なのか何の資料もなくてわからない・・・。@rethrows
の影響をうけてどう変化するのかがわからんAsyncIteratorProtocol
の方は唯一の throws
を持つメソッドがあるからわからんでもないけど、 AsyncSequence
はさらに謎じゃない?Rethrows protocol
って表現されてるプロトコルに @rethrows
が付いてそう@available(SwiftStdlib 5.1, *) @rethrows public protocol AsyncSequence { /// The type of asynchronous iterator that produces elements of this /// asynchronous sequence. associatedtype AsyncIterator: AsyncIteratorProtocol where AsyncIterator.Element == Element /// The type of element produced by this asynchronous sequence. associatedtype Element /// Creates the asynchronous iterator that produces elements of this /// asynchronous sequence. /// /// - Returns: An instance of the `AsyncIterator` type used to produce /// elements of the asynchronous sequence. __consuming func makeAsyncIterator() -> AsyncIterator }
@rethrows
だから、throws性が伝搬するって決まるの?@available(SwiftStdlib 5.1, *) @rethrows public protocol AsyncIteratorProtocol { associatedtype Element /// Asynchronously advances to the next element and returns it, or ends the /// sequence if there is no next element. /// /// - Returns: The next element, if it exists, or `nil` to signal the end of /// the sequence. mutating func next() async throws -> Element? }
↑さらに、rethrowsじゃなくてthrowsって書いてあるんだけど・・・ (edited)@rethrows protocol
における requirements に書かれた throws
に影響があるとしたらAsyncIteratorProtocol
は throws
を持つ唯一の required なメソッドを持つからそれに @rethrows
が働いていると考えられるけど、AsyncSequence
への伝播が謎。norethrows
とかで抑制するとか?@rethrows
付与されてるだけで解釈されてなくて、 AsyncSequence
と AsyncIteratorProtocol
以外では働かない可能性ありそう。for try await in
が for await in
にできるだけみたいな。@rethrows protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) async throws -> Int? { try iter.next() }
@rethrows protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) async throws -> Int? { try iter.next() }
protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) async throws -> Int? { try iter.next() }
protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) async throws -> Int? { try iter.next() }
@rethrows
関係ない気が。@rethrows protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } struct FailableIter: RethrowsIterator { func next() throws -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) rethrows -> Int? { try iter.next() } var it = NonfailableIter() _ = callNextRethrows(&it) var it2 = FailableIter() _ = try callNextRethrows(&it2)
(edited)@rethrows protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } struct FailableIter: RethrowsIterator { func next() throws -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) rethrows -> Int? { try iter.next() } var it = NonfailableIter() _ = callNextRethrows(&it) var it2 = FailableIter() _ = try callNextRethrows(&it2)
(edited)Result<T, Never>
にするのと同じことが throws
に対してできるprotocol RethrowsIterator { associatedtype Failure: Error func next() -> Result<Int?, Failure> } struct NonfailableIter: RethrowsIterator { func next() -> Result<Int?, Never> { return .success(1) } } struct FailableIter: RethrowsIterator { func next() -> Result<Int?, Error> { return .success(1) } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) -> Result<Int?, T.Failure> { iter.next() } var it = NonfailableIter() let _: Result<Int?, Never> = callNextRethrows(&it) var it2 = FailableIter() _ = try callNextRethrows(&it2).get()
(edited)protocol RethrowsIterator { associatedtype Failure: Error func next() -> Result<Int?, Failure> } struct NonfailableIter: RethrowsIterator { func next() -> Result<Int?, Never> { return .success(1) } } struct FailableIter: RethrowsIterator { func next() -> Result<Int?, Error> { return .success(1) } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) -> Result<Int?, T.Failure> { iter.next() } var it = NonfailableIter() let _: Result<Int?, Never> = callNextRethrows(&it) var it2 = FailableIter() _ = try callNextRethrows(&it2).get()
(edited)inout
とか除いて単純にするとこうか。 @swift-5.7.3
@rethrows protocol P { func foo() throws } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } func callFoo<T: P>(of p: T) rethrows { try p.foo() } callFoo(of: S1()) do { try callFoo(of: S2()) } catch { print(error) }
inout
とか除いて単純にするとこうか。 @swift-5.7.3
@rethrows protocol P { func foo() throws } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } func callFoo<T: P>(of p: T) rethrows { try p.foo() } callFoo(of: S1()) do { try callFoo(of: S2()) } catch { print(error) }
S1 S2
@rethrows protocol P { func foo() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) }
@rethrows protocol P { func foo() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) }
S1 S2
throws
を持つメソッドが複数あった場合、両方に働きそう。 @swift-5.7.3
@rethrows protocol P { func foo() throws func bar() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1.foo") } func bar() { print("S1.bar") } } struct S2: P { func foo() throws { print("S2.foo") } func bar() throws { print("S2.bar") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) }
throws
を持つメソッドが複数あった場合、両方に働きそう。 @swift-5.7.3
@rethrows protocol P { func foo() throws func bar() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1.foo") } func bar() { print("S1.bar") } } struct S2: P { func foo() throws { print("S2.foo") } func bar() throws { print("S2.bar") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) }
S1.foo S2.foo
throws
を互い違いに付けた場合。 @swift-5.7.3
@rethrows protocol P { func foo() throws func bar() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1.foo") } func bar() throws { print("S1.bar") } } struct S2: P { func foo() throws { print("S2.foo") } func bar() { print("S2.bar") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) }
throws
を互い違いに付けた場合。 @swift-5.7.3
@rethrows protocol P { func foo() throws func bar() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1.foo") } func bar() throws { print("S1.bar") } } struct S2: P { func foo() throws { print("S2.foo") } func bar() { print("S2.bar") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) }
<stdin>:49:1: error: call can throw but is not marked with 'try' callFoo(from: T1()) ^~~~~~~~~~~~~~~~~~~ <stdin>:49:1: note: call is to 'rethrows' function, but a conformance has a throwing witness callFoo(from: T1()) ^
throws
を互い違いに付けた場合。 @swift-5.7.3
@rethrows protocol P { func foo() throws func bar() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1.foo") } func bar() throws { print("S1.bar") } } struct S2: P { func foo() throws { print("S2.foo") } func bar() { print("S2.bar") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) }
callFoo
の宣言がrethrowsになってて、普通なら一緒に渡してる引数のクロージャがthrowsがどうかが伝搬するけど、ここでは引数のrethrowsプロトコルがどうか、になるんですね (edited)@rethrows protocol P { func foo() throws } @rethrows protocol Q { associatedtype PType1: P associatedtype PType2: P func makeP1() -> PType1 func makeP2() -> PType2 } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T1: Q { func makeP1() -> S1 { S1() } func makeP2() -> S2 { S2() } } struct T2: Q { func makeP1() -> S2 { S2() } func makeP2() -> S1 { S1() } } func callFooOfP1<T: Q>(from q: T) rethrows { try q.makeP1().foo() } callFooOfP1(from: T1()) do { try callFooOfP1(from: T2()) } catch { print(error) }
@rethrows protocol P { func foo() throws } @rethrows protocol Q { associatedtype PType1: P associatedtype PType2: P func makeP1() -> PType1 func makeP2() -> PType2 } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T1: Q { func makeP1() -> S1 { S1() } func makeP2() -> S2 { S2() } } struct T2: Q { func makeP1() -> S2 { S2() } func makeP2() -> S1 { S1() } } func callFooOfP1<T: Q>(from q: T) rethrows { try q.makeP1().foo() } callFooOfP1(from: T1()) do { try callFooOfP1(from: T2()) } catch { print(error) }
<stdin>:48:1: error: call can throw but is not marked with 'try' callFooOfP1(from: T1()) ^~~~~~~~~~~~~~~~~~~~~~~ <stdin>:48:1: note: call is to 'rethrows' function, but a conformance has a throwing witness callFooOfP1(from: T1()) ^
@rethrows
が付与されたプロトコルを @rethrows
プロトコルと呼ぶことにする。また、 P
が @rethrows
プロトコルのとき 、 func foo<T: P>(_ x: T) rethrows
が throws
になる場合、 T
が rethrowing であると呼ぶことにする。 このとき、 T
が non-rethrowing である条件は、下記の二つが同時に満たされる場合に限る。 P
のメソッドの内、 throws
が付与されたものをすべて non-throws
なメソッドとして実装するP
の associatedtype
の内、 @rethrows
プロトコルを制約として持つものをすべて non-rethrowing な型で実装する@rethrows protocol P { func foo() throws } @rethrows protocol Q { func bar() throws } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T<PType: P>: Q { let p: PType func bar() rethrows { try p.foo() } } T(p: S1()).bar() do { try T(p: S2()).bar() } catch { print(error) }
@rethrows protocol P { func foo() throws } @rethrows protocol Q { func bar() throws } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T<PType: P>: Q { let p: PType func bar() rethrows { try p.foo() } } T(p: S1()).bar() do { try T(p: S2()).bar() } catch { print(error) }
S1 S2
@rethrows
使って rethrows
できる。@rethrows protocol Error { func throw() throws -> Never }
(edited)@rethrows protocol FailureProtocol: Error { func throwSelf() throws -> Never } extension FailureProtocol { func throwSelf() throws -> Never { throw self } } extension Never: FailureProtocol { func throwSelf() -> Never { fatalError() } } struct FooError: FailureProtocol {} @rethrows protocol ResultProtocol { associatedtype Value associatedtype Failure: FailureProtocol func get() throws -> Value } enum Result<Value, Failure: FailureProtocol>: ResultProtocol { case success(Value) case failure(Failure) func get() rethrows -> Value { switch self { case .success(let value): return value case .failure(let error): try error.throwSelf() } } } func useResult<R: ResultProtocol>(_ result: R) rethrows { try print(result.get()) } useResult(Result<Int, Never>.success(42)) try useResult(Result<Int, FooError>.success(42))
Publisher
が AsyncSequence
でないのはなんでだろう?と考えていて、 values
を挟んで AsyncPublisher
と AsyncThrowingPublisher
が必要になるのは↑が欠けているのが問題なんじゃないかなと。@reasync
があれば AsyncSequnce
も Sequence
に統合可能になる?async
な関数を async
な関数として渡すときは大丈夫なの?暗黙の変換がされている?async
な関数を async
な関数として渡すときは大丈夫なの?暗黙の変換がされている? func asyncF(_ item: Int, transform: (Int) async -> String)) async -> String { let out = await transform(item) print(out) return out }
が、気持ち的にはこんな感じなんですよね↓ func asyncF(_ item: Int, transform: (Int) async -> String), nextContinuation: () -> Void) async { return transform(item, { out in print(out) return nextContinuation(out) }) }
(edited)func asyncF(_ item: Int, transform: (Int) async -> String), nextContinuation: () -> Void) async -> String [sync] func asyncF(_ item: Int, transform: (Int) -> String) -> String { var out: String! asyncF(item, sync_to_async(transform), { out = $0 }) return out }
(edited)Dictionary.Keys
に Set
的なメソッド( intersection
, subtracting
等)ほしくないですか?一度 Set
に包み直すのはオーバーヘッドがあるし、 Dictionary
も Set
もハッシュテーブルなので原理的には同等のパフォーマンスで実現できるかと。keys
が OrderedSet
なのでそういうことできそうなOrderedDictionary
の keys
が O(1) じゃないんじゃないかという心配が・・・OrderedDictionary
の keys
が O(1) じゃないんじゃないかという心配が・・・ OrderedSet
で持っとくのか。NSDictionary
のときに O(n) になってたんですが、それも O(1) になる PR を昔出しました。Clock
ってmacOS Venturaにならないと無いんでしたっけCannot find type 'Clock' in scope
言われちゃうんですよね@available(SwiftStdlib 5.7, *) public protocol Clock: Sendable { associatedtype Instant: InstantProtocol var now: Instant { get } var minimumResolution: Instant.Duration { get } func sleep(until deadline: Instant, tolerance: Instant.Duration?) async throws }
Xxx is available from mac OS 13
みたいな言い方されてた気がしますが、それすら出ない'Clock' is only available in iOS 16.0 or newer
になった[omochi@omochi-iMacPro Contents]$ find . | grep libswiftCore.dylib ./Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/appletvsimulator/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/watchos/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/iphoneos/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/appletvos/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/watchsimulator/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/iphonesimulator/libswiftCore.dylib [omochi@omochi-iMacPro Contents]$ pwd /Applications/Xcode14.app/Contents
swift-5.0/macos/libswiftCore.dylib
はあった。(この -5.0
ってなんだ?-5.0
ちゃんと知らないThat is to say, instead of # # @available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) # # we can write # # @available(SwiftStdlib 5.2, *) SwiftStdlib 5.7:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0
https://github.com/apple/swift/blob/475ed80c3eedb060d617fceed74686a4c6c86990/utils/availability-macros.deffunc get(id: Int)->String { "" } var myID: String { get(id: 1) // error: expected '{' to start getter definition }
(edited)func get(id: Int)->String { "" } var myID: String { get(id: 1) // error: expected '{' to start getter definition }
(edited)<stdin>:6:8: error: expected '{' to start getter definition get(id: 1) // error: expected '{' to start getter definition ^
(edited)func get(id: Int)->String { "" } var myID: String { `get`(id: 1) // error: expected '{' to start getter definition }
func get(id: Int)->String { "" } var myID: String { `get`(id: 1) // error: expected '{' to start getter definition }
protocol P {} struct S: P {} extension CustomStringConvertible { func f() -> some P { S() } } var foo: some P { "".f() } var bar: some P { ("" as any CustomStringConvertible).f() }
protocol P {} struct S: P {} extension CustomStringConvertible { func f() -> some P { S() } } var foo: some P { "".f() } var bar: some P { ("" as any CustomStringConvertible).f() }
<stdin>:15:39: error: type 'any P' cannot conform to 'P' ("" as any CustomStringConvertible).f() ^ <stdin>:15:39: note: only concrete types such as structs, enums and classes can conform to protocols ("" as any CustomStringConvertible).f() ^ <stdin>:14:10: note: required by opaque return type of var 'bar' var bar: some P { ^~~~~~
Error
にしても変わりませんでしたprotocol P {} struct S: P {} extension Error { func f() -> some P { S() } } extension String: Error {} var foo: some P { "".f() } var bar: some P { ("" as any Error).f() }
(edited)protocol P {} struct S: P {} extension Error { func f() -> some P { S() } } extension String: Error {} var foo: some P { "".f() } var bar: some P { ("" as any Error).f() }
(edited)<stdin>:17:21: error: type 'any P' cannot conform to 'P' ("" as any Error).f() ^ <stdin>:17:21: note: only concrete types such as structs, enums and classes can conform to protocols ("" as any Error).f() ^ <stdin>:16:10: note: required by opaque return type of var 'bar' var bar: some P { ^~~~~~
(edited)protocol P {} struct S: P {} extension Error { func f() -> some P { S() } } extension String: Error {} var foo: some P { "".f() } var bar: some P { ("" as any Error).f() }
(edited)<stdin>:17:7: error: member 'f' cannot be used on value of protocol type 'Error'; use a generic constraint instead ("" as any Error).f() ~~~~^~~~~~~~~~~~~ ~
Self
に依存しうるから、一律で禁止されているという感じでしょうかね?struct S<T>: P {} extension CustomErrorConvertible { func foo() -> some P { return S<Self>() } }
var bar: some P
の真の型がどうなるかが CustomStringConvertible
を open した self によって動的に変わるから事前に決定できない (edited)P
に生えてるものは全部 any P
に生えてる扱いなんですよprotocol P {} struct S: P {} extension Error { func f() -> some P { S() } }
protocol P {} struct S: P {} extension Error { func f() -> some P { S() } }
protocol P {} struct S: P {} extension Error { func f() -> some P { S() } }
protocol P {} struct S: P {} extension Error { func f() -> some P { S() } }
extension P
は常に <Self where Self. P>
に対するextensionなんですよねany P
が implicit openによって <T: P>
も使えてるってのが真実 (edited)extension any P
だったら固定で真の型が返せるね。 (edited)extension P { ... } // ↓第一の心の目: ジェネリックなセマンティクスを正しくみつめる extension <Self: P> {} // ↓第二の心の目: self引数を opaque parameter type的に捉える事ができる extension some P { } // ↓第三の心の目: Swift 7 で `P` を `any P` から `some P` に切り替えるので extension P { ... } // もどった!
(edited)protocol P {} protocol Q { func p() -> some P } func foo(q: Q) -> some P { return q.p() }
protocol P {} protocol Q { func p() -> some P } func foo(q: Q) -> some P { return q.p() }
<stdin>:3:15: error: 'some' type cannot be the return type of a protocol requirement; did you mean to add an associated type? func p() -> some P ^~~~~~ <#AssocType#>
protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> some P { return q.p() }
protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> some P { return q.p() }
<stdin>:8:10: error: member 'p' cannot be used on value of protocol type 'Q'; use a generic constraint instead return q.p() ^ ~
any Q
の some P を返すエントリは呼び出せなかったのか〜protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> some P { return q.p() }
protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> some P { return q.p() }
<stdin>:8:10: error: member 'p' cannot be used on value of protocol type 'Q'; use a generic constraint instead return q.p() ^ ~
protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> some P { return q.p() }
<stdin>:8:12: error: type 'any P' cannot conform to 'P' return q.p() ^ <stdin>:8:12: note: only concrete types such as structs, enums and classes can conform to protocols return q.p() ^ <stdin>:7:19: note: required by opaque return type of global function 'foo(q:)' func foo(q: Q) -> some P { ^~~~~~
protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> any P { return q.p() // erase to any P }
(edited)protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> any P { return q.p() // erase to any P }
(edited)any P
のanyキーワードのプロポーザルが出たのが去年の12月、引数の some P
が2月で、 自分の場合1年間ぐらい予習期間があったっぽいな (edited)some
と any
中心の話というより、 Primary Associated Type と some
, any
込みで Sequence
や Collection
を題材にもう一度 POP を考えてみましょうって話でした。some
と any
中心の話というより、 Primary Associated Type と some
, any
込みで Sequence
や Collection
を題材にもう一度 POP を考えてみましょうって話でした。 final
つければObjCから見えなくなるって書いてあるので、Swiftの機能が使えたりしそう?a non-category @objcImplementation extension
ってそういうことか@objc
を付けるなどのオプトインの作業が必要だったけど、SwiftをC++から呼び出す機能の設計にあたっては、 C++コンパイラが Swift Calling Conventionをサポートする事によって、オプトインの作業無しで、全てのSwift moduleを直接C++から使えるようにするらしい。すごい。
__stdcall
みたいな、関数にattributeを付ける事によって、呼び出し規約を指定する機能があるんだけど、これに __swiftcc
を追加するって事だと思う。 で、 __swiftcc
が付いたC++で書かれたブリッジヘッダーをSwiftコンパイラ側が生成するんだろう。 (edited)swiftcc
追加されたりしてるから、 clang にそれを追加するのもいけるって感じなんじゃないかなあ。SwiftコアチームにはC++コンパイラの人も居るし。 あと、家庭くんがSwiftをWASMにコンパイルするために、WASMのバイナリフォーマットにRelative Pointerを追加してもらった件とかはちょっと似てると思う。 ただ、Visual Studioに搭載されてるMS製のC++コンパイラはどうするんだろうね。c++ CLANG_MACRO("SWIFT_CALL", , "__attribute__((swiftcall))")
https://github.com/apple/swift/blob/8d548efbb981666297077658a745db3bffb35e2d/include/swift/PrintAsClang/ClangMacros.defc++ CLANG_MACRO("SWIFT_INDIRECT_RESULT", , "__attribute__((swift_indirect_result))") CLANG_MACRO("SWIFT_CONTEXT", , "__attribute__((swift_context))") CLANG_MACRO("SWIFT_ERROR_RESULT", , "__attribute__((swift_error_result))")
↑このへんもあった。indirect_result
とかもポートしてるの面白いねSIMD4(repeating:)
を多用してるのは気になる。SIMD3
にして質量を別で扱うと速くなるかも。 SIMD4
にしてる利点がない気がする。-Ounchecked
でコンパイルされてるみたいだから、普通に考えたら Specialize はされそう。swiftc nbody.swift-9.swift -Ounchecked -wmo -o nbody.swift-9.swift_run
dot
を自前なのやめて import simd
したら倍くらいの速度になりました ❯ time ./test 20000000 -0.169075164 -0.169031665 ./test 20000000 2.19s user 0.00s system 99% cpu 2.200 total ❯ time ./test2 20000000 -0.169075164 -0.169031665 ./test2 20000000 1.20s user 0.00s system 99% cpu 1.207 total
❯ diff test.swift test2.swift 1a2 > import simd 8,14c9,15 < func dot<V : SIMD>(_ a: V, _ b: V) -> V.Scalar where V.Scalar : FloatingPoint { < var total = V.Scalar(0) < for i in a.indices { < total = total.addingProduct(a[i], b[i]) < } < return total < } --- > // func dot<V : SIMD>(_ a: V, _ b: V) -> V.Scalar where V.Scalar : FloatingPoint { > // var total = V.Scalar(0) > // for i in a.indices { > // total = total.addingProduct(a[i], b[i]) > // } > // return total > // }
@_transparent public static func *(a: Self, b: Self) -> Self { var result = Self() for i in result.indices { result[i] = a[i] * b[i] } return result }
public func SHOW_ME_MULPD(lhs: SIMD2<Double>, rhs: SIMD2<Double>) -> SIMD2<Double> { lhs * rhs }
(edited)public func SHOW_ME_MULPD(lhs: SIMD2<Double>, rhs: SIMD2<Double>) -> SIMD2<Double> { lhs * rhs }
(edited) .text .file "<swift-imported-modules>" .protected $s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF .globl $s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF .p2align 4, 0x90 .type $s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF,@function $s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF: mulpd %xmm1, %xmm0 retq .Lfunc_end0: .size $s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF, .Lfunc_end0-($s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF) .hidden __swift_reflection_version .type __swift_reflection_version,@object .section .rodata,"a",@progbits .weak __swift_reflection_version .p2align 1 __swift_reflection_version: .short 3 .size __swift_reflection_version, 2 .type .L_swift1_autolink_entries,@object .section .swift1_autolink_entries,"e",@progbits .p2align 3 .L_swift1_autolink_entries: .asciz "-lswift_Concurrency\000-lswiftCore\000-lswift_StringProcessing" .size .L_swift1_autolink_entries, 57 .section ".linker-options","e",@llvm_linker_options .section ".note.GNU-stack","",@progbits
(edited)mulpd %xmm1, %xmm0
func dot<V : SIMD>(_ a: V, _ b: V) -> V.Scalar where V.Scalar : FloatingPoint { var total = V.Scalar(0) for i in a.indices { total = total.addingProduct(a[i], b[i]) } return total }
このdotの実装はLLVMがうまくパターン見つけられなくて最適化されなかったのかもなぁ-O
はレベル2なのでそもそも比較の土台が全然違った-O3
が必要じゃん。for i in 0 ..< 100
みたいなのでもアンロールされない?perf-stat
の結果 $ perf stat -d -d -- ./target.rust-run 5000000 Performance counter stats for './target.rust-run 5000000': 187.63 msec task-clock # 0.994 CPUs utilized 20 context-switches # 0.107 K/sec 0 cpu-migrations # 0.000 K/sec 85 page-faults # 0.453 K/sec 864088142 cycles # 4.605 GHz (32.07%) 385088 stalled-cycles-frontend # 0.04% frontend cycles idle (34.18%) 421948 stalled-cycles-backend # 0.05% backend cycles idle (36.31%) 1486120161 instructions # 1.72 insn per cycle # 0.00 stalled cycles per insn (38.44%) 18768109 branches # 100.027 M/sec (40.55%) 29184 branch-misses # 0.16% of all branches (42.36%) 543690464 L1-dcache-loads # 2897.670 M/sec (40.26%) 19859 L1-dcache-load-misses # 0.00% of all L1-dcache hits (38.12%) <not supported> LLC-loads <not supported> LLC-load-misses 141810 L1-icache-loads # 0.756 M/sec (35.99%) 2004 L1-icache-load-misses # 1.41% of all L1-icache hits (33.87%) 78 dTLB-loads # 0.416 K/sec (31.97%) 21 dTLB-load-misses # 26.92% of all dTLB cache hits (31.96%) 0 iTLB-loads # 0.000 K/sec (31.97%) 18 iTLB-load-misses # 0.00% of all iTLB cache hits (31.96%) 0.188802738 seconds time elapsed 0.188263000 seconds user 0.000000000 seconds sys
$ perf stat -d -d -- ./head.swift-run 5000000 Performance counter stats for './head.swift-run 5000000': 260.12 msec task-clock # 0.996 CPUs utilized 26 context-switches # 0.100 K/sec 0 cpu-migrations # 0.000 K/sec 71 page-faults # 0.273 K/sec 1200493971 cycles # 4.615 GHz (30.86%) 451884 stalled-cycles-frontend # 0.04% frontend cycles idle (32.41%) 132 stalled-cycles-backend # 0.00% backend cycles idle (33.94%) 2904262641 instructions # 2.42 insn per cycle # 0.00 stalled cycles per insn (35.46%) 217096810 branches # 834.616 M/sec (37.00%) 10523 branch-misses # 0.00% of all branches (38.42%) 675633481 L1-dcache-loads # 2597.435 M/sec (38.42%) 10807 L1-dcache-load-misses # 0.00% of all L1-dcache hits (38.42%) <not supported> LLC-loads <not supported> LLC-load-misses 119631 L1-icache-loads # 0.460 M/sec (38.43%) 3361 L1-icache-load-misses # 2.81% of all L1-icache hits (38.41%) 6382 dTLB-loads # 0.025 M/sec (36.86%) 116 dTLB-load-misses # 1.82% of all dTLB cache hits (35.33%) 417 iTLB-loads # 0.002 M/sec (33.80%) 176 iTLB-load-misses # 42.21% of all iTLB cache hits (32.25%) 0.261201137 seconds time elapsed 0.260805000 seconds user 0.000000000 seconds sys
withTaskGroup
で解決したとツイートしてたのを見てから(探したけどツイートを発見できず)、 withGroupTask
はちょっと大掛かりな気がして考えてたんだけど、一番シンプルな方法って↓かな? async let x = { print("foo") // 重めの同期処理 return 42 }() print("bar") print(await x)
↓の順で表示されるから、同期関数でもちゃんと child task として実行されている。 bar foo 42
前はよく let x = await Task.detached { print("foo") // 重めの同期処理 return 42 }.value print(x)
としてたけど、これだと unstructured concurrency になってしまってキャンセル伝播しないから、 async let
の方が良い気がする。 (edited)Task.yield()
を呼び出すことでTask.yield
が存在するのもこのような状況に対応するためという認識です。 (edited)Data.write
とかは存在するわけで、メインスレッドで Data.write
を待ちたくないし、別スレッドに逃したいケースはあるのでは?async
に write する API を作るべきってこと?DispatchIO
などの非同期IO を asyncでラップすることだと思います。async let x = { // (1) print("foo") // 重めの同期処理 return 42 }()
↑この書き方なんですけど、(1)が「メインスレッドに投入されない」保証はあるんですかね?async
で細切れにして提供されるようになった上で、自前のループの中では適切に Task.yield
を挟むというのをみんなが意識しないとですね。
withTaskGroup
は動的なN個のためのAPIで、async let
は定数個の場合のAPIなので、今回1件なのであれば withTaskGroup
を async let
に書き換える事自体は全く正しい改善だと思うのですが。async
で細切れにして提供されるようになった上で、自前のループの中では適切に Task.yield
を挟むというのをみんなが意識しないとですね。
async let x = { // (1) print("foo") // 重めの同期処理 return 42 }()
↑この書き方なんですけど、(1)が「メインスレッドに投入されない」保証はあるんですかね? import Foundation @MainActor func main() async { print(Thread.current.isMainThread) async let x = { print("foo", Thread.current.isMainThread) // 重めの同期処理 return 42 }() print("bar") print(await x) } await main()
true bar foo false 42
(edited)import Foundation @MainActor func main() async { print(Thread.current.isMainThread) async let x = { print("foo", Thread.current.isMainThread) // 重めの同期処理 return 42 }() print("bar") print(await x) } await main()
true bar foo false 42
(edited)DispatchQueue.global().async
とかだとその保証はあったのかな?DispatchQueue.global().async
とかだとその保証はあったのかな? async
を呼び出して処理を投入した時、その前後順も無視されるんですよね 同時に走るDispatchQueue.global().async
もメインスレッドで実行されるのでは?DispatchQueue.global().async
もメインスレッドで実行されるのでは? DispatchQueue
の場合と同じくらいにならないかなぁ。明確にドキュメントされてないのと、スレッド新しく作らない点が違い?DispatchQueue
の場合と同じくらいにならないかなぁ。明確にドキュメントされてないのと、スレッド新しく作らない点が違い? sort
とかでも n が増えたら詰まるよ?async
版の sort
があったとしても、どっち使うべきか判断できない。詰まらないときは async
版使うの効率悪いし。MainActor
context でない限りメインスレッドに投入されないのであれば、 iOS アプリでは重めの同期処理を投げても現実的にはあまり問題にはならなさそう。ThreadPool
があって 後者のために EventLoop
があって分けられているのでプログラマがコントロール可能。ThreadPool
に明示的に逃せばいいっちゃいいが、swift concurrencyで完結できてないのはダサいよな。@globalActor
で作れたと思うexecutor
をオーバライドできるんじゃなかったかな。MainActor
context でない限りメインスレッドに投入されないのであれば、 iOS アプリでは重めの同期処理を投げても現実的にはあまり問題にはならなさそう。 actor
を使って ViewModel を書いたりするアーキテクチャでやってたりする場合ですかね。ObservableObject
の objectWillChange
がメインスレッド以外でトリガーされるのを禁止しているはずなので大丈夫そう。ObservableObject
の objectWillChange
がメインスレッド以外でトリガーされるのを禁止しているはずなので大丈夫そう。 @StateObject
が必要になって、メインスレッド必須だから Main Actor にするしかなさそう。ViewModel.run1()
はMainActorでは実行されないから、処理系が混んでたら止まる。 具体的にどうやってUIKitやSwiftUIに接続するか、とか、実際やってる人がいるかはわかんないです。ViewModel.run1()
はMainActorでは実行されないから、処理系が混んでたら止まる。 具体的にどうやってUIKitやSwiftUIに接続するか、とか、実際やってる人がいるかはわかんないです。 actor
を使うこともあるけど、最終的に View に接続するときに @MainActor
な ObservableObject
を挟むから大丈夫だと思う。直接は接続できないけど。async
を避けるために Task.init
にしたことで何が解決されてるのかはよくわからない。async
にした方がいいと思う。 (edited)actor
を使うこともあるけど、最終的に View に接続するときに @MainActor
な ObservableObject
を挟むから大丈夫だと思う。直接は接続できないけど。 @MainActor func updateUI(_ diffs: [Difference]) { /* ... */ } public extension SingleUpdate { func apply() { generateDiffs(from: rawContent, on: DispatchQueue.main) { diffs in unsafeAssumeOnMainActor { updateUI(diffs) } } } // ... }
https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074 実行時に本当にMainActorかのチェックもしてくれるみたいです。 The unsafeAssumeOnMainActor function is a nonisolated, non-async function that accepts a @MainActor closure. First, unsafeAssumeOnMainActor performs a runtime test to see if it has been called on the MainActor. If the assumption was wrong, the program will emit a diagnostic message and can either continue executing the closure or abort execution.
(edited)HogeProtocol?
は (any HogeProtocol)?
になってくれるのに HogeProtocol!
は any HogeProtocol!
になっちゃって意味が変わるので結局手作業になっちゃうany
付与は完遂したのですが、以下の事象に出くわしました(メモ書き形式で雑ですが……) # Xcode 14.3 ## 正しい提案 - `HogeProtocol` → `any HogeProtocol` - `HogeProtocol?` → `(any HogeProtocol)?` - `as HogeProtocol` → `as (any HogeProtocol)`(`as?`・`as!` も同様) - `HogeProtocol.self` → `(any HogeProtocol).self` - `HogeProtocol.Type` → `any HogeProtocol.Type` ## ビルドできなくなる提案(100% 再現) - `HogeProtocol!` を `any HogeProtocol!` にしてしまう - `HogeProtocol?` は `(any HogeProtocol)?` になってくれるのに…… - `HogeProtocol & FugaProtocol` を `any HogeProtocol & any FugaProtocol` にしてしまう - `any HogeProtocol & FugaProtocol` にしてほしい ## ビルドできなくなる提案(ときどき再現) - `HogeProtocol.self` を `(any HogeProtocol).self` にしてくれないときがあった - `any HogeProtocol.self` にしたときがあった - Array でビルドできなくなるときがある - `let arr = [HogeProtocol]()` が `let arr = [any HogeProtocol]()` に置換されたが、「file contains invalid or unrecognized Swift syntax.」と言われてビルドに失敗することがあった - 一度この状況になると clean したりしてもダメだった - 別なプロジェクト等では発生しない - `let dict = [Key: HogeProtocol]()` が `let dict = [Key: any HogeProtocol]()` に置換されたが、「file contains invalid or unrecognized Swift syntax.」と言われてビルドに失敗することがあった - 一度この状況になると clean したりしてもダメだった - 別なプロジェクト等では発生しない
any
付与は完遂したのですが、以下の事象に出くわしました(メモ書き形式で雑ですが……) # Xcode 14.3 ## 正しい提案 - `HogeProtocol` → `any HogeProtocol` - `HogeProtocol?` → `(any HogeProtocol)?` - `as HogeProtocol` → `as (any HogeProtocol)`(`as?`・`as!` も同様) - `HogeProtocol.self` → `(any HogeProtocol).self` - `HogeProtocol.Type` → `any HogeProtocol.Type` ## ビルドできなくなる提案(100% 再現) - `HogeProtocol!` を `any HogeProtocol!` にしてしまう - `HogeProtocol?` は `(any HogeProtocol)?` になってくれるのに…… - `HogeProtocol & FugaProtocol` を `any HogeProtocol & any FugaProtocol` にしてしまう - `any HogeProtocol & FugaProtocol` にしてほしい ## ビルドできなくなる提案(ときどき再現) - `HogeProtocol.self` を `(any HogeProtocol).self` にしてくれないときがあった - `any HogeProtocol.self` にしたときがあった - Array でビルドできなくなるときがある - `let arr = [HogeProtocol]()` が `let arr = [any HogeProtocol]()` に置換されたが、「file contains invalid or unrecognized Swift syntax.」と言われてビルドに失敗することがあった - 一度この状況になると clean したりしてもダメだった - 別なプロジェクト等では発生しない - `let dict = [Key: HogeProtocol]()` が `let dict = [Key: any HogeProtocol]()` に置換されたが、「file contains invalid or unrecognized Swift syntax.」と言われてビルドに失敗することがあった - 一度この状況になると clean したりしてもダメだった - 別なプロジェクト等では発生しない
any
を自動で付けるのってXcodeにリファクタリングメニュー(?)があるのでしょうか? それとも、コンパイルした後個別のエラーとして表示されて、FixItボタンを一個ずつ(?)押していますか?any
付けるのは機械的だからマイグレーターで自動でできるよって仕様書には書いてある。 https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md
(edited)any
を自動で付けるのってXcodeにリファクタリングメニュー(?)があるのでしょうか? それとも、コンパイルした後個別のエラーとして表示されて、FixItボタンを一個ずつ(?)押していますか? c++ ERROR(existential_requires_any,none, "use of %select{protocol |}2%0 as a type must be written %1", (Type, Type, bool))
https://github.com/apple/swift/blob/main/include/swift/AST/DiagnosticsSema.def#L5159-L5161c++ std::string fix; llvm::raw_string_ostream OS(fix); if (needsParens) OS << "("; ExistentialTypeRepr existential(SourceLoc(), replaceRepr); existential.print(OS); if (needsParens) OS << ")"; if (auto *proto = dyn_cast_or_null<ProtocolDecl>(T->getBoundDecl())) { if (proto->existentialRequiresAny() && !Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) { Ctx.Diags.diagnose(T->getNameLoc(), diag::existential_requires_any, proto->getDeclaredInterfaceType(), proto->getDeclaredExistentialType(), /*isAlias=*/false) .fixItReplace(replaceRepr->getSourceRange(), fix); } }
https://github.com/apple/swift/blob/1b6d160c5ce9b55cf82bd4ee0df51774d0410e03/lib/Sema/TypeCheckType.cpp#L5201 (edited)c++ auto parameterized = ParameterizedProtocolType::get(ctx, protoType, argTys); diags.diagnose(loc, diag::existential_requires_any, parameterized, ExistentialType::get(parameterized), /*isAlias=*/isa<TypeAliasType>(type.getPointer()));
https://github.com/apple/swift/blob/1b6d160c5ce9b55cf82bd4ee0df51774d0410e03/lib/Sema/TypeCheckType.cpp#L778-L785c++ needsParens = existentialNeedsParens(*parentIt);
c++ bool existentialNeedsParens(TypeRepr *parent) { switch (parent->getKind()) { case TypeReprKind::Optional: case TypeReprKind::Protocol: return true; case TypeReprKind::Metatype: case TypeReprKind::Attributed: case TypeReprKind::Error: case TypeReprKind::Function: case TypeReprKind::Ownership: case TypeReprKind::Composition: case TypeReprKind::OpaqueReturn: case TypeReprKind::NamedOpaqueReturn: case TypeReprKind::Existential: case TypeReprKind::SimpleIdent: case TypeReprKind::GenericIdent: case TypeReprKind::Member: case TypeReprKind::Dictionary: case TypeReprKind::ImplicitlyUnwrappedOptional: case TypeReprKind::Tuple: case TypeReprKind::Fixed: case TypeReprKind::Array: case TypeReprKind::SILBox: case TypeReprKind::Isolated: case TypeReprKind::Placeholder: case TypeReprKind::CompileTimeConst: case TypeReprKind::Vararg: case TypeReprKind::Pack: case TypeReprKind::PackExpansion: case TypeReprKind::PackElement: return false; } }
case TypeReprKind::ImplicitlyUnwrappedOptional:
を上にもってくるだけでなんかうまくいったりしねえかなw (edited)case TypeReprKind::Composition:
のパターンもあるけどこれはそう簡単な話かどうか微妙だなfile contains invalid unrecognized Swift syntax
は swift-format のメッセージ(だった)っぽいです[any HogeProtocol]()
をパースできなかった、とかですかねany P?
を (any P)?
にする対応はここの分岐で書かれているので,ここに any P!
を (any P)!
にする対応を追加する必要があるのかもしれないですね. https://github.com/apple/swift/blob/c7766763e8ef7b0425c95c8c688f34fcaa884f26/lib/Sema/TypeCheckType.cpp#L4845-L4857- `HogeProtocol!` を `any HogeProtocol!` にしてしまう - `HogeProtocol?` は `(any HogeProtocol)?` になってくれるのに…… - `HogeProtocol & FugaProtocol` を `any HogeProtocol & any FugaProtocol` にしてしまう - `any HogeProtocol & FugaProtocol` にしてほしい
について 2つ issue を書いてみようと思います。any P?
を (any P)?
にする対応はここの分岐で書かれているので,ここに any P!
を (any P)!
にする対応を追加する必要があるのかもしれないですね. https://github.com/apple/swift/blob/c7766763e8ef7b0425c95c8c688f34fcaa884f26/lib/Sema/TypeCheckType.cpp#L4845-L4857 P?
を (any P)?
にしてるこちらを改良して P!
を (any P)!
にする機能も追加したほうが一手短くて良いと思います https://discord.com/channels/291054398077927425/306995750418513920/1094085905586855937 (edited)- `HogeProtocol!` を `any HogeProtocol!` にしてしまう - `HogeProtocol?` は `(any HogeProtocol)?` になってくれるのに…… - `HogeProtocol & FugaProtocol` を `any HogeProtocol & any FugaProtocol` にしてしまう - `any HogeProtocol & FugaProtocol` にしてほしい
について 2つ issue を書いてみようと思います。 ExistentialAny
· Issue #65026 · apple/swift https://github.com/apple/swift/issues/65026- `HogeProtocol!` を `any HogeProtocol!` にしてしまう - `HogeProtocol?` は `(any HogeProtocol)?` になってくれるのに…… - `HogeProtocol & FugaProtocol` を `any HogeProtocol & any FugaProtocol` にしてしまう - `any HogeProtocol & FugaProtocol` にしてほしい
について 2つ issue を書いてみようと思います。 &
で繋ぐことを "Protocol Composition Type" というんですね… 知りませんでした) The suggested fix for the protocol composition type is incorrect when enable upcoming featureExistentialAny
· Issue #65027 · apple/swift https://github.com/apple/swift/issues/65027ObservedChanges emits values when a change is made and an available suspension point has been met by the isolation. This means that changes can be grouped up transactionally to the boundary of when a given actor is available to run.
The ObservedValues asynchronous sequence on the other hand will await a change to values end emit the latest value that is available between the last iteration (or starting point of iteration) and the suspension.
changesのグルーピングのタイミングがアイソレーションの中断ごとというところがまだイメージできてないんですよね...public struct ObservedChange<Subject: Observable>: @unchecked Sendable { public func contains(_ member: PartialKeyPath<Subject>) -> Bool } extension ObservedChange where Subject: Sendable { public var subject: Subject { get } } /// An asynchronous sequence of observed changes. public struct ObservedChanges<Subject: Observable, Delivery: Actor>: AsyncSequence { public typealias Element = ObservedChange<Subject> public struct Iterator: AsyncIteratorProtocol { public mutating func next() async -> Element? } public func makeAsyncIterator() -> Iterator } extension ObservedChanges: @unchecked Sendable where Subject: Sendable { } @available(*, unavailable) extension ObservedChanges.Iterator: Sendable { }
subject
から取り出せて、「何が変わったか」が contains(PartialKeyPath)
で調べられる?ObservedChanges emits values when a change is made and an available suspension point has been met by the isolation. This means that changes can be grouped up transactionally to the boundary of when a given actor is available to run.
The ObservedValues asynchronous sequence on the other hand will await a change to values end emit the latest value that is available between the last iteration (or starting point of iteration) and the suspension.
changesのグルーピングのタイミングがアイソレーションの中断ごとというところがまだイメージできてないんですよね... ObservedChanges emits values when a change is made and an available suspension point has been met by the isolation. This means that changes can be grouped up transactionally to the boundary of when a given actor is available to run.
The ObservedValues asynchronous sequence on the other hand will await a change to values end emit the latest value that is available between the last iteration (or starting point of iteration) and the suspension.
changesのグルーピングのタイミングがアイソレーションの中断ごとというところがまだイメージできてないんですよね... access
とか withMutation
のスコープ関数で囲って実現してるような気がしたけど、 これらはプロパティの内側で呼ばれるから無理そうなんですよね どうやってこの「ひとまとめにするグループ」を制御しているんだろう。nextChange
が呼ばれていて、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservedChanges.swift#L93 その中でinsertNextChange
が呼ばれて、さらにその中で各Stateによって処理が分かれるのですが、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L312 Pending中は値をinsertしていて、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrarStateMachine.swift#L54 来たるべきタイミングの時にcontinuation.resumeで変更した値を全部emitしているのではないか思っています。 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L323@StateObject
や @ObservedObject
なくなると Binding
が簡単に取れなくなって困らないのかなぁ。 @Observable final class Model { var order: Order? var account: Account? var hasAccount: Bool { return userCredential != nil && account != nil } var favoriteSmoothieIDs: Set<Smoothie.ID> = [] var selectedSmoothieID: Smoothie.ID? var searchString: String = "" var isApplePayEnabled: Bool = true var allRecipesUnlocked: Bool = false var unlockAllRecipesProduct: Product? } struct SmoothieList: View { var smoothies: [Smoothie] var model: Model var listedSmoothies: [Smoothie] { smoothies .filter { $0.matches(model.searchString) } .sorted(by: { $0.title.localizedCompare($1.title) == .orderedAscending }) } var body: some View { List(listedSmoothies) { smoothie in ... } } }
@Observable
マクロががんばってくれたりはしない? extension MyObject { var someComputedProperty: Int { somePrivateProperty + someOtherProperty } nonisolated static func dependencies( of keyPath: PartialKeyPath<Self> ) -> TrackedProperties<Self> { switch keyPath { case \.someComputedProperty: return [\.somePrivateProperty, \.someOtherProperty] default: return [keyPath] } } }
nextChange
が呼ばれていて、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservedChanges.swift#L93 その中でinsertNextChange
が呼ばれて、さらにその中で各Stateによって処理が分かれるのですが、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L312 Pending中は値をinsertしていて、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrarStateMachine.swift#L54 来たるべきタイミングの時にcontinuation.resumeで変更した値を全部emitしているのではないか思っています。 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L323 @Observable
マクロががんばってくれたりはしない? extension MyObject { var someComputedProperty: Int { somePrivateProperty + someOtherProperty } nonisolated static func dependencies( of keyPath: PartialKeyPath<Self> ) -> TrackedProperties<Self> { switch keyPath { case \.someComputedProperty: return [\.somePrivateProperty, \.someOtherProperty] default: return [keyPath] } } }
foo.a = 3
) その単位で didSet が起きて発行して終わるような気がする・・・dependencies
は、全てのプロパティに依存してる設定でマクロ生成されるようですよremoveDuplicates
しておけばマシになるかな。dependencies
は、全てのプロパティに依存してる設定でマクロ生成されるようですよ dependencies
独自実装しようとすると、それ以外のプロパティも実装しないといけないことになって、自動実装が消えてミス多発しそう・・・。insertNextChange
が idle中に来るとactiveになる pending中に来ると、そこまで集めた変更(があれば)をresume送信してidleになるnext()
が呼ばれるからnext()
が呼ばれるまでの間」に、発生したdidSetがpending stateとして累積されているんだわ (edited)@MainActor func handleChanges(_ example: ChangesExample) { Task { @MainActor in for await change in example.changes(for: [\.someField, \.someOtherField]) { switch (change.contains(\.someField), change.contains(\.someOtherField)) case (true, true): print("Changed both properties") case (true, false): print("changed integer field") case (false, true): print("changed string field") default: fatalError("this case will never happen") } } } }
example.changes().next
が for-await によって呼ばれて・・・.next
の結果を送信するための Continuationが、MainActorコンテキストなTaskから呼びされた場合はwithCheckedContinuation
系のみなはずなんですが、例外が増えるということなんでしょうかねwithCheckedContinuation
系のみなはずなんですが、例外が増えるということなんでしょうかね withUnsafeContinuation
なんですよ。 送信してるところ: https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L250 Continuationを作ったところ: https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L432 (edited)withUnsafeContinuation
なんですよ。 送信してるところ: https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L250 Continuationを作ったところ: https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L432 (edited)scheduleNextChange
自体がasync関数なので1フレ遅れ得ませんかね?ObservedChanges.next
→ ObservationRegistrar.nextChange
(isolated) → ObservationRegistrar.scheduleNextChange
(isolated) → withUnsafeContinuation
ObservedChanges
は init
で isolation: Isolation
を受け取る。func viewDidLoad(…) { … handleChanges(example) example.someField = 10 // ここ }
の「ここ」の変更は通知されないと思っているのですが、合ってますでしょうか?
func viewDidLoad(…) { … handleChanges(example) example.someField = 10 // ここ }
の「ここ」の変更は通知されないと思っているのですが、合ってますでしょうか?
Task { for await value in chain([object.someProperty].async, object.values(for: \.someProperty)) { // Really important processing of the value } }
https://forums.swift.org/t/se-0395-observability/64342/24 (edited)if a property will be observed and the change in that property only happens once (and never happens again) and the confluence of events is such that the property observation via values(for:) is called asynchronously (e.g. in a Task) the first value could get lost.
ただし、 初期値の取得はイテレーションと同じ分離領域で取得する必要がある 初期値が全部のケースで必要というわけではない 初期値を保持することでどこかのライフサイクルに影響を与えるかもしれない といったことを考慮しなければならない、と書かれてました。 https://forums.swift.org/t/se-0395-observability/64342/89 /// Returns a URL constructed by appending the given path to self. /// - Parameters: /// - path: The path to add /// - directoryHint: A hint to whether this URL will point to a directory @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) public func appending<S>(path: S, directoryHint: URL.DirectoryHint = .inferFromPath) -> URL where S : StringProtocol
https://forums.swift.org/t/back-from-revision-foundation-url-improvements/54605/28URL.appending()
、 Linux版 Foundation に全然追加されないのなんなんだろ? ソース(なさそう): https://github.com/apple/swift-corelibs-foundation/blob/2d23cf3dc07951ed2b988608d08d7a54cc53b26e/Sources/Foundation/URL.swift#L199 実行結果(なさそう): https://discord.com/channels/291054398077927425/430242233468452865/1101699773775499265 (edited)URL
に限らず Foundation の API 変更への追従は基本遅いですよね.Date.now
も iOS 15 で使えるようになって swift-corelibs-foundation にも乗って Swift のリリースに乗ったのが Swift 5.8 なので,1 年半遅れて Linux で使えるようになりましたし. https://github.com/apple/swift-corelibs-foundation/pull/4609 (edited)URL
に限らず Foundation の API 変更への追従は基本遅いですよね.Date.now
も iOS 15 で使えるようになって swift-corelibs-foundation にも乗って Swift のリリースに乗ったのが Swift 5.8 なので,1 年半遅れて Linux で使えるようになりましたし. https://github.com/apple/swift-corelibs-foundation/pull/4609 (edited)Date.now
の事、知りませんでした。 こんな簡単なやつも実装されないのか。 dannflorさんはAppleの人ではないみたいだし、野良のコントリビューションで対応されたのか・・・ あまりにもやる気ないな・・・withObservationTracking
を使ってsyncのfor loopでtrackingできるようになったり、個々のプロパティをTrackingから除外する@ObservationIgnored
が追加になったり、色々変更になるようです。 ObservationTracking
という型に@_spi(SwiftUI)
がついているのがよくわかってないです
https://github.com/apple/swift/pull/65528@_spi
はモジュールプライベートみたいなアクセス制御で普通にImportしても見えなくてImportしたい場合はImportする側で@_spi(SwiftUI) import
って書くと見えるやつです。@_spi(SwiftUI)
ついてると明確にSwiftUI意識されてることわかって面白いですね@_spi
つけてimportされた型って再exportされるんでしたっけ?それとも@_implementationOnly
相当?/tmp/tmp.7NtbKdeUsR/Sources/Bar/Bar.swift:3:29: error: cannot use struct 'BarPrivate' here; it is an SPI imported from 'Foo' public func reExport(_: Foo.BarPrivate) { ^ Foo.BarPrivate:1:15: note: type declared here public struct BarPrivate { ^ /tmp/tmp.7NtbKdeUsR/Sources/Bar/Bar.swift:3:29: error: cannot use struct 'BarPrivate' here; it is an SPI imported from 'Foo' public func reExport(_: Foo.BarPrivate) { ^ Foo.BarPrivate:1:15: note: type declared here public struct BarPrivate { ^
@_spi(ForBar)
を再エクスポートしてる宣言にもつけるとできる (edited)ObservationTracking
は完全にユーザが意識しない型なんですね.target(name: "MyTarget", dependencies:[.fancyLibrary], swiftSettings: [.enableUpcomingFeature(name: “ConciseMagicFile”), .enableUpcomingFeature(name: “BareSlashRegexLiterals”), .enableUpcomingFeature(name: “ExistentialAny”)])
(edited)struct UpcomingFeatureName: ExpressibleByStringLiteral { static let conciseMagicFile = UpcomingFeatureName("ConciseMagicFile")
(edited)-warn-concurrency
以降の変更が反映されていないですが確かにそれが一番近そうですね.Rust の https://doc.rust-lang.org/unstable-book/ みたいに別ドキュメントが必要になっちゃうのも管理が面倒そうなので Swift Evolution だけで済むようになっていけば確かにその方が良いなとは思います.-strict-concurrency
は書いてないですよね。Array
や Dictionary
に入れることを考えると、これまで要素の一つが変更されても全体の変更として検出されてしまっていたのが、 @Observable
なクラスを使えば要素単体の変更として検出できて効率的。 これまでも全部 ObservableObject
にして Array
や Dictionary
の要素の変更を直接検出する作戦はあったかもしれないけど、全部 ObservableObject
にしちゃうと ObservableObject
が ObservableObject
を保持してるときに、前者(保持してる側)を @StateObject
等で保持しても後者(保持されてる側)の変更を自動的に検出できるわけでなかったのであまり現実的でなかったけど、 Observation を使えば @StateObject
とかなくても後者の変更を直接検出できる。@Observable struct
は作れなくなったようです。 https://discord.com/channels/291054398077927425/499393715140558881/1132302591146078299 あと、今見つけられないのですが、前に shiz さんが値型の @Observable
には問題があるという意見をコアチームの誰かが発言してるフォーラムの投稿を挙げてくれてた気が・・・。Timeline
が var posts: [Post]
を持つようなケースで、タイムラインのページから投稿の一つをタップして投稿詳細ページに遷移、遷移先で post.isLiked.toggle()
したら遷移元のタイムラインにも反映させたいようなケースで、これまでは Post
を struct
にして @Binding
で渡せばよかったですが、 Observation の恩恵を受けるには Post
を @Observable class
にしないといけないですよね。@Binding
で 渡すのではなく、遷移元と遷移先で共有する ObservableObject
が Timeline
を保持していて、その ObservableObject
を参照するとかかもですが、いずれにせよ本題は Observation の恩恵を受けるために Post
が @Observable class
でないといけなくならないかという点で、その点には影響ないかと思います。 (edited)Binding
だと、 post.isLiked,toggle()
でタイムラインの View
の body
も走っちゃいませんか? (edited)posts
自体は変更されてるから( 1 要素の isLiked
だけとはいえ)、タイムライン側で ==
しても変更があるとしか言えなくないですか?View
のレンダリングは行われなくても body
は実行されないですか?posts
自体が変わっても各要素で差分ある値のみレンダリングされる認識です。View
の body
は勝手に再実行されませんでしたっけ?これは気のせいかも。 (edited)_printChanges()
でひたすらログを眺めてたらそんな感じだったようなArray
や Dictionary
でエンティティを保持しており、その 1 要素の 1 プロパティが変更されただけで、それを参照しているすべての body
の再実行がされ、その中にはループして O(N) のものもあり、実 View のレンダリングは行われないにしても、仮想 View の再生成すら辛い状況が、特にハイパフォーマンスが求められるアプリでは起こっているのかなと。State
型を作るという話ではなく、 struct
を Array
や Dictionary
に入れてしまった時点で、そのコレクション全体の変更として検出されてしまうという問題についてですね(SSOTの話を出したのは、そのコレクションが色々なところから参照された場合に影響度が大きいという話です)。 これまでも各要素を ObservableObject
で表現すれば要素の変更を検出できましたが、ネストした ObservableObject
の変更を(自動的には)検出できなかったのでいまいちでした。 Observation によって変更箇所がピンポイントで検出できるようになったけれども、それに乗っかるには @Observable class
にしないといけない( struct
にできない)というのが悩んでいるポイントです。 (edited)T
のコピーをしている可能性がある。@main struct MyApp: App { var body: some Scene { WindowGroup { Text("Hello, world!") } } }
SwiftUI.App
に渡されるところから始まるので。var a: Foo = .init() var b: Foo = a withObservationTraking { print(a.x) } onChange: { print("onChange") } b.x = 42
で onChange
が呼ばれるのか。呼ばれるなら、異なるインスタンスである a
と b
の observation が混ざってて変だし(このあと、 a.x = -1
を実行するとどうなるのか)、呼ばれないならコピーされたら(たとえば computed property を介して Foo
が return
されたら)観測できなくなる。 (edited)ObservableObject
とほぼ同じ使い方になる。struct S<X> { var x: X } extension S: Equatable where X: Equatable {} extension S: Hashable where X: Hashable {}
struct S<X> { var x: X } extension S: Equatable where X: Equatable {} extension S: Hashable where X: Hashable {}
struct S<X> { var x: X } extension S: Hashable where X: Equatable { var hashValue: Int { 1 } } extension S: Equatable where X: Hashable {}
struct S<X> { var x: X } extension S: Hashable where X: Equatable { var hashValue: Int { 1 } } extension S: Equatable where X: Hashable {}
<stdin>:2:1: error: type 'X' does not conform to protocol 'Hashable' extension S: Hashable where X: Equatable { ^ <stdin>:2:1: error: 'Hashable' requires that 'X' conform to 'Hashable' extension S: Hashable where X: Equatable { ^ <stdin>:2:1: note: requirement specified as 'X' : 'Hashable' extension S: Hashable where X: Equatable { ^ <stdin>:2:1: note: requirement from conditional conformance of 'S<X>' to 'Equatable' extension S: Hashable where X: Equatable { ^ <stdin>:3:7: warning: 'Hashable.hashValue' is deprecated as a protocol requirement; conform type 'S' to 'Hashable' by implementing 'hash(into:)' instead var hashValue: Int { 1 } ^
struct S<X> { var x: X } extension S: Hashable where X: Equatable { var hashValue: Int { 1 } } extension S: Equatable where X: Hashable {}
S: Hashable
の条件は、 S: Equatable
の条件を満たしていないといけない、って制約がかかってる!賢い。protocol ExpressibleByStringLiteral : ExpressibleByExtendedGraphemeClusterLiteral {} protocol ExpressibleByExtendedGraphemeClusterLiteral : ExpressibleByUnicodeScalarLiteral {}
すごい、孫プロトコルだ 知らなかったstruct S<X> {} extension S: Error where X: Error {}
struct S<X> {} extension S: Error where X: Error {}
S: Error
は S: Sendable
も付いてきてるな・・・S
が Sendable になる条件が複数あっても大丈夫・・・?X: Sendable
を2つ書いたら怒られた。extension S: Error where X: Error {}
って書くと自動的に extension S: Sendable where X: Error {}
も生成されている(条件部分はコピー) ような挙動に見えますね。。 (edited)extension S: Sendable where X: Sendable {}
を書けば、自然な定義にはなるから、困ることは無さそうだけど、ルールが違うのが気になるな: Error
がソース互換性を壊すから、仕方なく特別処理してるのか・・・? (edited)Error
)に対してcond confの条件にできない、だから、逆にいえば必須にもできないのかなとextension S: Error where X: Sendable
が記載できないから、エラーにできないのかなとextension S: Error where X: Error
から、 暗黙に extension S: Sendable where X: Error
が生成されているよね、というのはextension S: Error where X: Sendable
←禁止されているのはこっちですね これは実行時に X: Sendable
がテストできないから駄目(情報自体が実行時には消えている) (edited)extension S: Sendable where X: Error
←こっちは実行時に条件部をテストする事自体はできる。 できるけど、 Sendableへの動的キャストが禁止されているから意味が無い。extension S: Error where X: Error
に対して extension S: Sendable where X: Error {}
が自動で定義されるのはちょっと不思議ですねS: Hashable
の場合と同様に、 S: Sendable
も明記しなさい、で良いと思うんですけど・・・Error
に対して親プロトコルの Sendable
が後付けされたので、 既存の S: Error where
がコンパイル不可能になるのを避けたのかなあ? (edited)c++ if (conformance->getSourceKind() == ConformanceEntryKind::Implied && !Proto->isMarkerProtocol()) {
ERROR(conditional_conformances_cannot_imply_conformances,none, "conditional conformance of type %0 to protocol %1 does not imply conformance to " "inherited protocol %2", (Type, Type, Type))
のエラーが出るようになってる。Error
), and is safe because there's no runtime component to marker protocols. func transaction<T>(_ closure: @Sendable @escaping (Database) async throws -> T) async throws -> T { try await self.transaction { db -> EventLoopFuture<T> in let promise = self.eventLoop.makePromise(of: T.self) promise.completeWithTask{ try await closure(db) } return promise.futureResult }.get() }
(edited)transaction
関数の引数の closure
って、 @Sendable
も @escaping
も外していいと思うんですけど、正しい考えですかね?self.transaction
が返す EventLoopFuture
を await future.get()
して待機しているから、 この transaction
メソッドが終了する時点では、必ず closure
の呼び出しも終了しているからです。closure
はEventLoopスレッドで実行されるのでconcurrency boundaryは跨いでるけど、 この transaction
メソッド自体が async
で、呼び出し側は必ず await
で待たないといけないし。let closure = { (db) in ... } Task { closure() } try await db.transaction(closure)
↑呼び出し側で勝手にこういうふうにする事ができるか・・・? でもこの場合は、let closure
の時点で @Sendable
にしないと Task
に渡せないから、この心配もないか@escaping
はなくても良さそうですね。@Sendable
に関してもclosure
は一瞬EventLoopと別Taskのスレッドに移るけど、reentrantを考えてもclosure
と同じActor contextの処理が並列実行されることはないような気がする?ので安全なようなtransaction
function に対して no って言ってるな・・・class NotSendableString { /* ... */ } final class Address: Sendable { /* ... */ } func greetCharlie(_ charlie: Charlie) {} actor Charlie { var score: Int let fixedNonSendable: NotSendableString let fixedSendable: Address var me: Charlie? = nil func incrementScore() { self.score += 1 } nonisolated func nonisolatedMethod() {} init(_ initialScore: Int) { self.score = initialScore // ✅ self.fixedNonSendable = NotSendableString("Charlie") // ✅ self.fixedSendable = NotSendableString("123 Main St.") // ✅ if score > 50 { nonisolatedMethod() // ✅ selfのnonisolatedな使用 greetCharlie(self) // ✅ selfのnonisolatedな使用 self.me = self // ✅ selfのnonisolatedな使用 } else if score < 50 { score = 50 } assert(score >= 50) // ❌ selfのnonisolatedな使用後に、可変のisolatedなプロパティへアクセスできなくなる _ = self.fixedNonSendable // ❌ selfのnonisolatedな使用後に、non-Sendableなプロパティへアクセスできなくなる _ = self.fixedSendable Task { await self.incrementScore() } // ✅ selfのnonisolatedな使用は引き続きOK(awaitしているのでactor isolated) } }
(edited)nonisolated
は isolated
の間違いですかね? (edited)Task { await self.incrementScore() } // ✅ selfのnonisolatedな使用は引き続きOK
そうですね。これはawaitしてactorのboudary内の処理になるのでコメントが変だと思いました。 (edited)nonisolated self
として扱うようにすることをdecayと呼んでいます。 the isolation of self decays (or changes) to nonisolated during any use of self that is not a direct stored-property access
(edited)nonisolated self
になっているってことなのか、なるほど。var a0 = [0] a0 += [a] a0 += [b] let a = consume a0
Task { await self.incrementScore() } // ✅ selfのnonisolatedな使用は引き続きOK
そうですね。これはawaitしてactorのboudary内の処理になるのでコメントが変だと思いました。 (edited)Task.init
に self
を capture させるところが nonisolated use of self だと言っているのかもしれません。 nonisolatedなので actor context の引継ぎもおきなくて、 中で await が必要ですTask.init
に self
を capture させるところが nonisolated use of self だと言っているのかもしれません。 nonisolatedなので actor context の引継ぎもおきなくて、 中で await が必要です throws CatError ↓ throws(CatError)
rethrowsにも型を指定できるようになった func foo<E: Error>(closure: () throws(E) -> Void) rethrows(E)
Resultはtyped errorを返すように変える extension Result { init(catching body: throws(Failure) -> Success) { ... } }
当時よりも型推論がパワーアップしてもっと簡単に書けるようになった。 enum CatError: Error { case sleeps case sitsAtATree } do { try callCat() // throws CatError if something { throw CatError.asleep // throws CatError } } catch .asleep { openFoodCan() } // note: CatError can be thrown out of this do...catch block when the cat isn't asleep**Rationale**:
#if FOUNDATION_FRAMEWORK @_implementationOnly import _ForSwiftFoundation /// Wraps an `NSCalendar` with more Swift-like `Calendar` API. See also: `_NSSwiftCalendar`. /// This is only used in the case where we have custom Objective-C subclasses of `NSCalendar`. It is assumed that the subclass is Sendable. internal final class _CalendarBridged: _CalendarProtocol, @unchecked Sendable { let _calendar: NSCalendar
#if
があって、#if FOUNDATION_FRAMEWORK @_implementationOnly import _ForSwiftFoundation import CoreFoundation #else #endif
こういうのもあるからFoundation.framework
がインポートできる環境において機能していて、// MARK: - Bridging #if FOUNDATION_FRAMEWORK @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Calendar : ReferenceConvertible { public typealias ReferenceType = NSCalendar } @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Calendar : _ObjectiveCBridgeable { @_semantics("convertToObjectiveC") public func _bridgeToObjectiveC() -> NSCalendar { _calendar.bridgeToNSCalendar() } public static func _forceBridgeFromObjectiveC(_ input: NSCalendar, result: inout Calendar?) { if !_conditionallyBridgeFromObjectiveC(input, result: &result) { fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") } } public static func _conditionallyBridgeFromObjectiveC(_ input: NSCalendar, result: inout Calendar?) -> Bool { result = Calendar(reference: input) return true } @_effects(readonly) public static func _unconditionallyBridgeFromObjectiveC(_ source: NSCalendar?) -> Calendar { var result: Calendar? _forceBridgeFromObjectiveC(source!, result: &result) return result! } } @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension NSCalendar : _HasCustomAnyHashableRepresentation { // Must be @nonobjc to avoid infinite recursion during bridging. @nonobjc public func _toCustomAnyHashable() -> AnyHashable? { return AnyHashable(self as Calendar) } } #endif // FOUNDATION_FRAMEWORK
@_implementationOnly import _ForSwiftFoundation
Foundation.framework
の中に _ForSwiftFoundation
というモジュールがあるはずで、 // _CalendarICU, if present static var calendarICUClass: _CalendarProtocol.Type = { #if FOUNDATION_FRAMEWORK _CalendarICU.self #else if let name = _typeByName("FoundationInternationalization._CalendarICU"), let t = name as? _CalendarProtocol.Type { return t } else { // Use the default gregorian class return _CalendarGregorian.self } #endif }()
_CalendarICU
って型も corelibs-foundation の中から見つけられない。謎が深まる。SendNonSendable
で、トゲナシトゲトゲみたいでわかりづらいが#isolation
、 @inheritsActorIsolation
のアンスコ卒業$ file ./usr/bin/swift ./usr/bin/swift: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit x86_64 executable, flags:<NOUNDEFS|DYLDLINK|TWOLEVEL|WEAK_DEFINES|BINDS_TO_WEAK|PIE|HAS_TLV_DESCRI PTORS>] [arm64]
do throws(CatError) { if isDaylight && foodBowl.isEmpty { throw .sleeps // equivalent to CatError.sleeps } try callCat() } catch let myError { // myError is of type CatError }
(edited)Task(on: executor) { // starts and runs on the 'executor' await nonisolatedAsyncFunc() } Task.detached(on: executor) { // starts and runs on the 'executor' await nonisolatedAsyncFunc() } await withDiscardingTaskGroup { group in group.addTask(on: executor) { // starts and runs on the 'executor' await nonisolatedAsyncFunc() } } func nonisolatedAsyncFunc() async -> Int { // if the Task has a specific executor preference, // runs on that 'executor' rather than on the default global concurrent executor return 42 }
https://forums.swift.org/t/se-0417-task-executor-preference/68958 (edited)FullTypedThrows
なので正式にはSwift6からかもしれないですね。 今のところ、experimental feature flagに TypedThrows
付けるとmainブランチでは動くみたいです。release/5.10
には入れてはいなさそうに見えますね。
(edited)Limitations of preamble and body macros: While the proposed design for preamble macros makes it possible to inject both prologue and epilogue code using defer, the inability to examine a thrown error or the function's return value were cited as difficult limitations.
Lack of closure support: While the Language Steering Group does agree that it would be useful to have this capability, applying function body macros to closures presents a number of unique challenges that do not apply to regular function declarations. For this reason, we are comfortable slicing closures off as a future direction for their own proposal.
https://forums.swift.org/t/returned-for-revision-se-0415-function-body-macros/69114
(edited)Limitations of preamble and body macros: While the proposed design for preamble macros makes it possible to inject both prologue and epilogue code using defer, the inability to examine a thrown error or the function's return value were cited as difficult limitations.
Lack of closure support: While the Language Steering Group does agree that it would be useful to have this capability, applying function body macros to closures presents a number of unique challenges that do not apply to regular function declarations. For this reason, we are comfortable slicing closures off as a future direction for their own proposal.
https://forums.swift.org/t/returned-for-revision-se-0415-function-body-macros/69114
(edited)@OptionSet
がstdlibに追加されて、その後しれっと削除された様なのですが、SwiftMacro内には存在し続けて配布が続いている様です。これってどういう扱いになってるのでしょう? @swift-5.9.2 @swift-5.10 -Xfrontend -print-ast-decl @attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) @attached(extension, conformances: OptionSet) public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") @OptionSet<UInt8> struct ShippingOptions { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] } struct ShippingOptionsWithNoMacro { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] }
@OptionSet
がstdlibに追加されて、その後しれっと削除された様なのですが、SwiftMacro内には存在し続けて配布が続いている様です。これってどういう扱いになってるのでしょう? @swift-5.9.2 @swift-5.10 -Xfrontend -print-ast-decl @attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) @attached(extension, conformances: OptionSet) public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") @OptionSet<UInt8> struct ShippingOptions { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] } struct ShippingOptionsWithNoMacro { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] }
@attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) @attached(extension, conformances: OptionSet) public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") internal struct ShippingOptions {
@OptionSet
がstdlibに追加されて、その後しれっと削除された様なのですが、SwiftMacro内には存在し続けて配布が続いている様です。これってどういう扱いになってるのでしょう? @swift-5.9.2 @swift-5.10 -Xfrontend -print-ast-decl @attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) @attached(extension, conformances: OptionSet) public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") @OptionSet<UInt8> struct ShippingOptions { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] } struct ShippingOptionsWithNoMacro { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] }
@attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) @attached(extension, conformances: OptionSet) public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") internal struct ShippingOptions {
If the feature is merged but doesn’t seem to be making progress, it may be removed. For what it’s worth, that has happened to work by Apple engineers.
https://forums.swift.org/t/pitch-optionset-macro/63547/167 Embedded buildには残っているですかね? https://github.com/apple/swift/pull/69197 (edited)If the feature is merged but doesn’t seem to be making progress, it may be removed. For what it’s worth, that has happened to work by Apple engineers.
https://forums.swift.org/t/pitch-optionset-macro/63547/167 Embedded buildには残っているですかね? https://github.com/apple/swift/pull/69197 (edited)SwiftMacro
ライブラリに実装は残っててstdlibへの登録が削除されてるみたいですね。
(edited)@OptionSet
少し使ってみましたが、enum Options
のメンバーにつけたDoc commentが生成されたメンバに引き継がれないので、使わない方が便利という結論になりました。Fermyon
ってフェルミ粒子のことか spinは素粒子のスピンに掛けてるのかpublic extension Database { func transaction<T>(_ closure: @Sendable @escaping (Database) async throws -> T) async throws -> T { try await self.transaction { db -> EventLoopFuture<T> in let promise = self.eventLoop.makePromise(of: T.self) promise.completeWithTask{ try await closure(db) } return promise.futureResult }.get() }
この closure
から @Sendable
は外せない。 理由は、 closure
が EventLoopFuture
の中で実行されている間に、 この executor は await
しているだけでブロックはされていないから。 例えば closure
が Actor
の中にあった場合、 Actor
のメソッド自体は interleave するから、同じisolationからこの closure
を呼び出される可能性があって、 EventLoop threadとの並行実行になりうる。transferring
を使えば closure
が呼び出し元でコピーが生き残ってない事を要請できて @Sendable
が外せるんだろうか?When a call passes an argument to a transferring parameter, the caller cannot use the argument value again after the callee returns.
https://github.com/apple/swift-evolution/blob/main/proposals/0430-transferring-parameters-and-results.md#ownership-convention-for-transferring-parameters (edited)XCTestCase.setup
とかが同期を要求してて困ったりするんだよなXCTestCase.setUp
は async throws
版が使えませんか? https://developer.apple.com/documentation/xctest/xctest/3856481-setupXCTestCase.setUp
は async throws
版が使えませんか? https://developer.apple.com/documentation/xctest/xctest/3856481-setup Expansion of Swift Macros in Visual Studio Code by @lokeshtr and mentor @ahoppen and @adam-fowler Swift-etcd: A Native Client for Seamless Integration with etcd Servers by @ayushi2103 and mentor: @FranzBusch Lexical scopes for swift-syntax by @MAJKFL and mentor: @Douglas_Gregor Add a deploy SwiftPM plugin and a Swift-based DSL to the Swift runtime for AWS Lambda by @esraaeiid and mentor: @sebsto
(edited)