Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Brandon Kase: Grokking Lazy Sequences and Colle...
Search
Realm
July 07, 2016
Programming
1
9k
Brandon Kase: Grokking Lazy Sequences and Collections
Realm
July 07, 2016
Tweet
Share
More Decks by Realm
See All by Realm
WWDC 2017 Review
realm
0
2.2k
Xcode shortcuts
realm
0
4.7k
Self Branding with GitHub
realm
0
4.3k
Realm Mobile Platform overview and demo
realm
0
2.1k
Realm advanced topics and demo
realm
0
2k
Realm introduction Seoul meetup 10
realm
0
2.2k
Stuart Hall: How I got 2.3 Million App Downloads
realm
0
2k
James Majors: What the Swiftly Func?
realm
1
4.3k
Simina Pasat: Continuous everything for iOS apps
realm
0
650
Other Decks in Programming
See All in Programming
エラーログのマスキングの仕組みづくりに役立ったASTの話
kumoichi
0
230
ベクトル検索のフィルタを用いた機械学習モデルとの統合 / python-meetup-fukuoka-06-vector-attr
monochromegane
2
470
What Spring Developers Should Know About Jakarta EE
ivargrimstad
0
560
AWS Infrastructure as Code の新機能 2025 総まとめ 〜SA 4人による怒涛のデモ祭り〜
konokenj
10
3.4k
S3ストレージクラスの「見える」「ある」「使える」は全部違う ─ 体験から見た、仕様の深淵を覗く
ya_ma23
0
630
クライアントワークでSREをするということ。あるいは事業会社におけるSREと同じこと・違うこと
nnaka2992
1
340
文字コードの話
qnighy
44
17k
モックわからないマン卒業記 ~振る舞いを起点に見直した、フロントエンドテストにおけるモックの使いどころ~
tasukuwatanabe
2
390
Everything Claude Code OSS詳細 — 5層構造の中身と導入方法
targe
0
120
「やめとこ」がなくなった — 1月にZennを始めて22本書いた AI共創開発のリアル
atani14
0
390
new(1.26) ← これすき / kamakura.go #8
utgwkk
0
2.4k
モダンOBSプラグイン開発
umireon
0
140
Featured
See All Featured
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
770
The SEO identity crisis: Don't let AI make you average
varn
0
420
Raft: Consensus for Rubyists
vanstee
141
7.4k
Marketing to machines
jonoalderson
1
5k
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
400
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
200
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
240
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
1
160
Un-Boring Meetings
codingconduct
0
230
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
220
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
250
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
440
Transcript
GROKKING LAZY SEQUENCES AND COLLECTIONS By / Brandon Kase @bkase_
PHOTOS http://friendlycomputersspokane.com/wp-content/uploads/2014/06/photo-stack.jpg
PHOTOS
PROBLEMS There are a lot of photos Loading a photo's
data is expensive
PROBLEMS There are a lot of things Loading a thing
is expensive
HOLDING THINGS http://odditymall.com/includes/content/wall-mounted-hands-for-holding-stuff-0.jpg
HOLDING THINGS Arrays?
HOLDING THINGS let fetchresult = PHAsset.fetchAllAssets/*...*/()
HOLDING THINGS let fetchresult = PHAsset.fetchAllAssets/*...*/() var arr = [PHAsset]()
HOLDING THINGS let fetchresult = PHAsset.fetchAllAssets/*...*/() var arr = [PHAsset]()
fetchresult.enumerateAssetsUsingBlock { obj, /*...*/ arr.append(obj as! PHAsset) }
HOLDING THINGS let fetchresult = PHAsset.fetchAllAssets/*...*/() var arr = [PHAsset]()
fetchresult.enumerateAssetsUsingBlock { obj, /*...*/ arr.append(obj as! PHAsset) } // slowwwwww
HOLDING THINGS We need a Collection
GROUPS OF THINGS A Collection is an Indexable Sequence A
Sequence is an Iterator maker An Iterator can produce one or more Elements
ITERATORPROTOCOL protocol IteratorProtocol { associatedtype Element mutating func next() ->
Element? }
ITERATORPROTOCOL Let's say we want to iterate over the even
numbers https://i.ytimg.com/vi/A9NfHIIwNN4/maxresdefault.jpg
ITERATORPROTOCOL struct Evens: IteratorProtocol { var state: Int = 0
mutating func next() -> Int? { let curr = state state += 2 return .some(curr) } }
ITERATORPROTOCOL let s = Evens() for _ in 1...5 {
print(s.next()!) // 0, 2, 4, 6, 8 }
SEQUENCE Recall: A Sequence is an Iterator maker http://ichef.bbci.co.uk/news/660/media/images/80295000/jpg/_80295405_thinking522738989.jpg
ITERATORPROTOCOL struct Evens: IteratorProtocol, Sequence
SEQUENCE for x in Evens().prefix(5) { print(x) // 0, 2,
4, 6, 8 }
SEQUENCE http://customer-blog-images.s3.amazonaws.com/questions.jpg
SEQUENCE extension Sequence where Iterator == Self, Self: IteratorProtocol {
func makeIterator() -> Iterator { return self } }
SEQUENCE http://www.thedirtondusters.com/wp-content/uploads/2012/08/WHAT-ELSE-NO-TEXT.jpg
SEQUENCE for x in someSequence { print(x) }
SEQUENCE map, filter, reduce prefix, dropFirst much, much more
SEQUENCE must be able to produce an iterator could be
one-pass or multipass could be finite or infinite
COLLECTION http://ichef.bbci.co.uk/news/660/media/images/80295000/jpg/_80295405_thinking522738989.jpg
COLLECTION protocol Collection: Sequence, Indexable
COLLECTION http://friendlycomputersspokane.com/wp-content/uploads/2014/06/photo-stack.jpg
COLLECTION struct PhotosMetadata: Collection { /*...*/ var startIndex: Int {
/*...*/ } var endIndex: Int { /*...*/ } func index(after i: Int) -> Int { /*...*/ } subscript(i: Int) -> PHAsset { /*...*/ } }
COLLECTION Why do we need to deal with the index
crap? struct PhotosMetadata: Collection { /*...*/ var startIndex: Int { /*...*/ } var endIndex: Int { /*...*/ } func index(after i: Int) -> Int { /*...*/ } subscript(i: Int) -> PHAsset { /*...*/ } }
COLLECTION RULES has addressable positions (index) must support multipass iteration
exists a one-to-one mapping of indices to elements No infinite structures! expect O(1) subscript access
COLLECTION Non-integer indices are useful: https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/CPT-LinkedLists-deletingnode.svg/380px-CPT- LinkedLists-deletingnode.svg.png
COLLECTION struct PhotosMetadata: Collection {
COLLECTION private let base: PHFetchResult init(_ base: PHFetchResult) { self.base
= base }
COLLECTION var startIndex: Int { return 0 }
COLLECTION var endIndex: Int { return base.count }
COLLECTION func index(after i: Int) -> Int { return i
+ 1 }
COLLECTION subscript(i: Int) -> PHAsset { return base[i] as! PHAsset
}
PHOTOS let a = PhotosMetadata(PHAsset.fetchAllAssets/*...*/()) // not slow!
PHOTOS Now we want to turn our PHAssets into Photos
PHOTOS struct Photo { let url: NSURL /*...*/ }
PHOTOS let metadata: PhotosMetadata let photos = metadata.map{ Photo(url: $0.url)
} // slowwww
PHOTOS Delay metadata load
PHOTOS Delay operations on collections or sequences
LAZY GROUPS OF THINGS Transformations computed when the information is
forced out https://media.giphy.com/media/AyXMnDH4nA7jW/giphy.gif
LAZYSEQUENCE protocol LazySequenceProtocol: Sequence
LAZYCOLLECTION protocol LazyCollectionProtocol: LazySequenceProtocol, Collection
NORMAL MAP // eager sequence/collection [1,2,3].map{ $0 + 1 }
// [2, 3, 4]
LAZYSEQUENCE MAP Evens().lazy.map{ $0 + 1 } // LazyMapSequence<Int, Int>
// nothing is computed yet!
LAZYCOLLECTION MAP [1,2,3].lazy.map{ $0 + 1 } // LazyMapRandomAccessCollection<Int, Int>
// nothing is computed yet!
WAIT A SECOND protocol LazyCollectionProtocol: LazySequenceProtocol, Collection
H O W C A N A N O V
E R R I D E N M E T H O D R E T U R N D I F F E R E N T T Y P E S ?
PROTOCOL DEFAULT IMPLEMENTATIONS https://pptcrafter.files.wordpress.com/2013/01/gears-0.png
DEFAULT IMPLEMENTATIONS protocol A { } extension A { func
woo() -> Self { return self } func hi() -> String { return "A" } }
DEFAULT IMPLEMENTATIONS struct ConcA: A {} print(ConcA().woo().hi()) // "A"
DEFAULT IMPLEMENTATIONS protocol B { } extension B { func
hi() -> Int { return 0 } }
DEFAULT IMPLEMENTATIONS struct Mystery: A, B { } print(Mystery().woo().hi()) //
compile error!
DEFAULT IMPLEMENTATIONS // now B subsumes A protocol B: A
{ } extension B { func hi() -> Int { return 0 } }
DEFAULT IMPLEMENTATIONS struct Mystery: B { } print(Mystery().woo().hi()) // 0
// NOT a compile error!
MAPS Evens().lazy.map{ $0 + 1 } // LazyMapSequence<Int, Int> //
nothing is computed yet!
LAZYMAPSEQUENCE? struct LazyMapSequence<S: Sequence>: LazySequenceProtocol
LAZYMAPSEQUENCE struct LazyMapSequence<S: Sequence>: LazySequenceProtocol { func makeIterator() -> LazyMapIterator</*...*/>
{ /*...*/ } }
LAZYMAPITERATOR struct LazyMapIterator<Base: Iterator, Element>: IteratorProtocol, Sequence { }
LAZYMAPITERATOR struct LazyMapIterator<Base: Iterator, Element>: IteratorProtocol, Sequence { var _base:
Base let _transform: (Base.Element) -> Element }
LAZYMAPITERATOR struct LazyMapIterator<Base: Iterator, Element>: IteratorProtocol, Sequence { var _base:
Base let _transform: (Base.Element) -> Element mutating func next() -> Element? { return _base.next().map(_transform) } }
LAZYMAPSEQUENCE struct LazyMapSequence<S: Sequence>: LazySequenceProtocol http://www.tacobueno.com/media/1381/beefbob.png?quality=65
BURRITOS let a = [1,2,3].lazy.map{ $0 + 1 }.filter{ $0
!= 3 } // a: LazyFilterBidirectionalCollection< // LazyMapRandomAccessCollection< // Array<Int>, Int // > // >
BURRITOS http://images.wisegeek.com/flour-tortilla.jpg
PHOTOS let metadata: PhotosMetadata let photos = metadata.lazy.map{ Photo(url: $0.url)
} // not slow!
PHOTOS func prepareData(d: PhotosMetadata) -> ? { return d.lazy .filter{
$0.isPhoto() } // skip videos .map{ Photo(url: $0.url) } // make photos .map{ PhotoView(photo: $0) } // make view }
PHOTOS LazyFilterBidirectionalCollection< LazyMapRandomAccessCollection< LazyMapRandomAccessCollection< PHAsset, Photo >, PhotoView > >
TORTILLA INFERENCE http://m.jumpstart.com/JumpstartNew/uploadedFiles/sne/small-screenshots/the-magic-tortilla.jpg
TYPE INFERENCE let m = d.lazy.map/*...*/ return m.first
TORTILLA ERASURE https://2e0a24317f4a9294563f-26c3b154822345d9dde0204930c49e9c.ssl.cf1.rackcdn.com/9218426_the-tortilla- pencil-case-deliciously-wraps_11d463da_m.jpg?bg=DFC1B7
TYPE ERASURE let m = AnyCollection( d.lazy.filter{}.map{}/*...*/ ) // m:
AnyCollection<PhotoView>
TYPE ERASURE AnySequence AnyCollection ... see appendix for more
PHOTOS func prepareData(d: PhotosMetadata) -> AnyCollection<PhotoView>
PHOTOS Product wants everyOther photo http://www.giftofcuriosity.com/wp-content/uploads/2016/04/Letter-hopscotch-4.jpg
NEW TORTILLAS let a = [1,2,3,4] for x in a.lazy.everyOther()
{ print(x) // 2,4 }
NEW TORTILLAS // the tortilla sequence struct LazyEveryOtherSequence <S: Sequence>:
LazySequenceProtocol { var base: S func makeIterator() -> LazyEveryOtherIterator<S.Iterator> { return LazyEveryOtherIterator(base: base.makeIterator()) } }
NEW TORTILLAS struct LazyEveryOtherIterator <I: IteratorProtocol>: IteratorProtocol { var base:
I mutating func next() -> I.Element? { if let _ = base.next() { return base.next() } else { return nil } } }
NEW TORTILLAS extension LazySequenceProtocol { func everyOther() -> LazyEveryOtherSequence<Self> {
return LazyEveryOtherSequence(base: self) } }
PHOTOS let skipped = photos.everyOther()
PHOTOS Product wants every 4th photo http://www.giftofcuriosity.com/wp-content/uploads/2016/04/Letter-hopscotch-4.jpg
PHOTOS let skipped = photos.everyOther().everyOther()
RECAP
RECAP Collections hold our photo metadata and photos
RECAP Collections hold our photo metadata and photos LazyCollection's map
transforms our data
RECAP Collections hold our photo metadata and photos LazyCollection's map
transforms our data AnyCollection gives maintainable type signatures
RECAP Collections hold our photo metadata and photos LazyCollection's map
transforms our data AnyCollection gives maintainable type signatures We can create new operators that compose
FINALLY Our app works!
P.S. IBM Swift Sandbox is legit Check out Unary indexed
collections in appendix
GROK IT? By / Slide Deck: Brandon Kase @bkase_ https://is.gd/mEvKbt
APPENDIX
EAGER Eager (or strict) is the opposite of lazy Computed
always
EAGER class A { var eagerField: Int = 1 +
1 /* ... */ }
LAZY class A { lazy var lazyField: Int = 1
+ 1 /* ... */ }
EAGER GROUPS OF THINGS
EAGER GROUPS OF THINGS Eager means operations on the groups
of things are eager The groups of things themselves can be strict or lazy
UNARY
UNARY let u: Unary</*...*/> u[""] // 1st element u["x"] //
2st element u["xx"] // 3st element u["xxx"] // 4st element
UNARY Given any input collection addressable via Ints, Create a
collection addressable via unary numbers!
UNARY More powerful than overriding the subscript operator!
UNARY print(u.lazy.map{ x in x + 1 }["xxx"])
UNARY Let's build it
UNARY struct Unary<C: Collection>: Collection where C.Index == Int, C.IndexDistance
== Int {
UNARY { var startIndex: String { /*...*/ } var endIndex:
String { /*...*/ } func index(after i: String) -> String { /*...*/ } subscript(i: String) -> C.Iterator.Element { /*...*/ } }
UNARY private let xs: C init(_ xs: C) { self.xs
= xs }
UNARY var startIndex: String { return "" }
UNARY /// O(n) endIndex var endIndex: String { return String((0...xs.count).map{
_ in "x"}) }
UNARY func index(after i: String) -> String { return i
+ "x" }
UNARY subscript(i: String) -> C.Iterator.Element { return xs[i.characters.count] }
UNARY let u: Unary<Array<Int>> = Unary([1, 2, 3, 4, 5,
6]) print(u.lazy.map{ x in x + 1 }["xxx"])
COLLECTION-INDEX VARIANTS How can you move to the next index?
Collection (ForwardDirection) Go forward in O(1) BidirectionalCollection Go forward or backward in O(1) RandomAccessCollection Get the difference between two indices in O(1)
TYPE ERASURE
TYPE ERASURE Type erasure works by moving the type info
to the constructor
TYPE ERASURE // one of the inits for AnySequence init<S
: Sequence where S.Iterator.Element == Element, S.SubSequence : Sequence, S.SubSequence.Iterator.Element == Element, S.SubSequence.SubSequence == S.SubSequence>(_ base: S)