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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Realm
July 07, 2016
Programming
9k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Brandon Kase: Grokking Lazy Sequences and Collections
Realm
July 07, 2016
More Decks by Realm
See All by Realm
WWDC 2017 Review
realm
0
2.2k
Xcode shortcuts
realm
0
4.8k
Self Branding with GitHub
realm
0
4.4k
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
670
Other Decks in Programming
See All in Programming
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
1.8k
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
180
Foundation Models frameworkで画像分析
ryodeveloper
1
620
AI時代に設計が 最大の生産性レバーになる 意図駆動開発とデータを消さない設計|Don't Delete Your Data or Your Intent — Design as the Deepest Lever in the AI Era
tomohisa
1
910
AIを紡ぐPMのお話
swdtkuy
0
110
リアルな遅延を測る仕様
kota_yata
1
110
今さら聞けない .NET CLI
htkym
0
190
Japan Community Day at Kubecon + CloudNativeCon Japan 2026: Learning Container Privilege Control by Building My Own Low-Level Container Runtime
ternbusty
1
150
the container ship “Apple Silicon”@WWDC26 Recap -Japan-\(region).swift
shingangan
0
130
torikago - Ruby::Boxで照らすモジュラモノリスの実行境界
se4weed
1
380
生成AI導入の「期待外れ」を乗り越える ー 開発フロー改革が目指す、真の組織変革
starfish719
0
4.5k
そこに3びきプロダクトがいるじゃろう——生成AI時代における“価値が届かない理由”の構造
kosuket
0
490
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
410
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.5k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
890
How to train your dragon (web standard)
notwaldorf
97
6.8k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
470
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.3k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
240
Unsuck your backbone
ammeep
672
58k
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)