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.1k
Xcode shortcuts
realm
0
4.7k
Self Branding with GitHub
realm
0
4.3k
Realm Mobile Platform overview and demo
realm
0
2k
Realm advanced topics and demo
realm
0
2k
Realm introduction Seoul meetup 10
realm
0
2.1k
Stuart Hall: How I got 2.3 Million App Downloads
realm
0
1.9k
James Majors: What the Swiftly Func?
realm
1
4.2k
Simina Pasat: Continuous everything for iOS apps
realm
0
600
Other Decks in Programming
See All in Programming
cmp.Or に感動した
otakakot
3
200
Figma Dev Modeで変わる!Flutterの開発体験
watanave
0
140
受け取る人から提供する人になるということ
little_rubyist
0
230
TypeScriptでライブラリとの依存を限定的にする方法
tutinoko
3
690
「今のプロジェクトいろいろ大変なんですよ、app/services とかもあって……」/After Kaigi on Rails 2024 LT Night
junk0612
5
2.2k
Creating a Free Video Ad Network on the Edge
mizoguchicoji
0
120
광고 소재 심사 과정에 AI를 도입하여 광고 서비스 생산성 향상시키기
kakao
PRO
0
170
Jakarta EE meets AI
ivargrimstad
0
110
シェーダーで魅せるMapLibreの動的ラスタータイル
satoshi7190
1
480
ふかぼれ!CSSセレクターモジュール / Fukabore! CSS Selectors Module
petamoriken
0
150
Realtime API 入門
riofujimon
0
150
Generative AI Use Cases JP (略称:GenU)奮闘記
hideg
1
300
Featured
See All Featured
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
47
2.1k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
6.8k
Ruby is Unlike a Banana
tanoku
97
11k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
109
49k
Typedesign – Prime Four
hannesfritz
40
2.4k
Java REST API Framework Comparison - PWX 2021
mraible
PRO
28
8.2k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
27
840
The Cult of Friendly URLs
andyhume
78
6k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.4k
Practical Orchestrator
shlominoach
186
10k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
4
370
Thoughts on Productivity
jonyablonski
67
4.3k
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)