Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Brandon Kase: Grokking Lazy Sequences and Collections

1fa9cb8c7997c8c4d3d251fb5e41f749?s=47 Realm
July 07, 2016

Brandon Kase: Grokking Lazy Sequences and Collections

1fa9cb8c7997c8c4d3d251fb5e41f749?s=128

Realm

July 07, 2016
Tweet

Transcript

  1. GROKKING LAZY SEQUENCES AND COLLECTIONS By / Brandon Kase @bkase_

  2. PHOTOS http://friendlycomputersspokane.com/wp-content/uploads/2014/06/photo-stack.jpg

  3. PHOTOS

  4. PROBLEMS There are a lot of photos Loading a photo's

    data is expensive
  5. PROBLEMS There are a lot of things Loading a thing

    is expensive
  6. HOLDING THINGS http://odditymall.com/includes/content/wall-mounted-hands-for-holding-stuff-0.jpg

  7. HOLDING THINGS Arrays?

  8. HOLDING THINGS let fetchresult = PHAsset.fetchAllAssets/*...*/()

  9. HOLDING THINGS let fetchresult = PHAsset.fetchAllAssets/*...*/() var arr = [PHAsset]()

  10. HOLDING THINGS let fetchresult = PHAsset.fetchAllAssets/*...*/() var arr = [PHAsset]()

    fetchresult.enumerateAssetsUsingBlock { obj, /*...*/ arr.append(obj as! PHAsset) }
  11. HOLDING THINGS let fetchresult = PHAsset.fetchAllAssets/*...*/() var arr = [PHAsset]()

    fetchresult.enumerateAssetsUsingBlock { obj, /*...*/ arr.append(obj as! PHAsset) } // slowwwwww
  12. HOLDING THINGS We need a Collection

  13. GROUPS OF THINGS A Collection is an Indexable Sequence A

    Sequence is an Iterator maker An Iterator can produce one or more Elements
  14. ITERATORPROTOCOL protocol IteratorProtocol { associatedtype Element mutating func next() ->

    Element? }
  15. ITERATORPROTOCOL Let's say we want to iterate over the even

    numbers https://i.ytimg.com/vi/A9NfHIIwNN4/maxresdefault.jpg
  16. ITERATORPROTOCOL struct Evens: IteratorProtocol { var state: Int = 0

    mutating func next() -> Int? { let curr = state state += 2 return .some(curr) } }
  17. ITERATORPROTOCOL let s = Evens() for _ in 1...5 {

    print(s.next()!) // 0, 2, 4, 6, 8 }
  18. SEQUENCE Recall: A Sequence is an Iterator maker http://ichef.bbci.co.uk/news/660/media/images/80295000/jpg/_80295405_thinking522738989.jpg

  19. ITERATORPROTOCOL struct Evens: IteratorProtocol, Sequence

  20. SEQUENCE for x in Evens().prefix(5) { print(x) // 0, 2,

    4, 6, 8 }
  21. SEQUENCE http://customer-blog-images.s3.amazonaws.com/questions.jpg

  22. SEQUENCE extension Sequence where Iterator == Self, Self: IteratorProtocol {

    func makeIterator() -> Iterator { return self } }
  23. SEQUENCE http://www.thedirtondusters.com/wp-content/uploads/2012/08/WHAT-ELSE-NO-TEXT.jpg

  24. SEQUENCE for x in someSequence { print(x) }

  25. SEQUENCE map, filter, reduce prefix, dropFirst much, much more

  26. SEQUENCE must be able to produce an iterator could be

    one-pass or multipass could be finite or infinite
  27. COLLECTION http://ichef.bbci.co.uk/news/660/media/images/80295000/jpg/_80295405_thinking522738989.jpg

  28. COLLECTION protocol Collection: Sequence, Indexable

  29. COLLECTION http://friendlycomputersspokane.com/wp-content/uploads/2014/06/photo-stack.jpg

  30. COLLECTION struct PhotosMetadata: Collection { /*...*/ var startIndex: Int {

    /*...*/ } var endIndex: Int { /*...*/ } func index(after i: Int) -> Int { /*...*/ } subscript(i: Int) -> PHAsset { /*...*/ } }
  31. 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 { /*...*/ } }
  32. 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
  33. 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

  34. COLLECTION struct PhotosMetadata: Collection {

  35. COLLECTION private let base: PHFetchResult init(_ base: PHFetchResult) { self.base

    = base }
  36. COLLECTION var startIndex: Int { return 0 }

  37. COLLECTION var endIndex: Int { return base.count }

  38. COLLECTION func index(after i: Int) -> Int { return i

    + 1 }
  39. COLLECTION subscript(i: Int) -> PHAsset { return base[i] as! PHAsset

    }
  40. PHOTOS let a = PhotosMetadata(PHAsset.fetchAllAssets/*...*/()) // not slow!

  41. PHOTOS Now we want to turn our PHAssets into Photos

  42. PHOTOS struct Photo { let url: NSURL /*...*/ }

  43. PHOTOS let metadata: PhotosMetadata let photos = metadata.map{ Photo(url: $0.url)

    } // slowwww
  44. PHOTOS Delay metadata load

  45. PHOTOS Delay operations on collections or sequences

  46. LAZY GROUPS OF THINGS Transformations computed when the information is

    forced out https://media.giphy.com/media/AyXMnDH4nA7jW/giphy.gif
  47. LAZYSEQUENCE protocol LazySequenceProtocol: Sequence

  48. LAZYCOLLECTION protocol LazyCollectionProtocol: LazySequenceProtocol, Collection

  49. NORMAL MAP // eager sequence/collection [1,2,3].map{ $0 + 1 }

    // [2, 3, 4]
  50. LAZYSEQUENCE MAP Evens().lazy.map{ $0 + 1 } // LazyMapSequence<Int, Int>

    // nothing is computed yet!
  51. LAZYCOLLECTION MAP [1,2,3].lazy.map{ $0 + 1 } // LazyMapRandomAccessCollection<Int, Int>

    // nothing is computed yet!
  52. WAIT A SECOND protocol LazyCollectionProtocol: LazySequenceProtocol, Collection

  53. 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 ?
  54. PROTOCOL DEFAULT IMPLEMENTATIONS https://pptcrafter.files.wordpress.com/2013/01/gears-0.png

  55. DEFAULT IMPLEMENTATIONS protocol A { } extension A { func

    woo() -> Self { return self } func hi() -> String { return "A" } }
  56. DEFAULT IMPLEMENTATIONS struct ConcA: A {} print(ConcA().woo().hi()) // "A"

  57. DEFAULT IMPLEMENTATIONS protocol B { } extension B { func

    hi() -> Int { return 0 } }
  58. DEFAULT IMPLEMENTATIONS struct Mystery: A, B { } print(Mystery().woo().hi()) //

    compile error!
  59. DEFAULT IMPLEMENTATIONS // now B subsumes A protocol B: A

    { } extension B { func hi() -> Int { return 0 } }
  60. DEFAULT IMPLEMENTATIONS struct Mystery: B { } print(Mystery().woo().hi()) // 0

    // NOT a compile error!
  61. MAPS Evens().lazy.map{ $0 + 1 } // LazyMapSequence<Int, Int> //

    nothing is computed yet!
  62. LAZYMAPSEQUENCE? struct LazyMapSequence<S: Sequence>: LazySequenceProtocol

  63. LAZYMAPSEQUENCE struct LazyMapSequence<S: Sequence>: LazySequenceProtocol { func makeIterator() -> LazyMapIterator</*...*/>

    { /*...*/ } }
  64. LAZYMAPITERATOR struct LazyMapIterator<Base: Iterator, Element>: IteratorProtocol, Sequence { }

  65. LAZYMAPITERATOR struct LazyMapIterator<Base: Iterator, Element>: IteratorProtocol, Sequence { var _base:

    Base let _transform: (Base.Element) -> Element }
  66. 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) } }
  67. LAZYMAPSEQUENCE struct LazyMapSequence<S: Sequence>: LazySequenceProtocol http://www.tacobueno.com/media/1381/beefbob.png?quality=65

  68. BURRITOS let a = [1,2,3].lazy.map{ $0 + 1 }.filter{ $0

    != 3 } // a: LazyFilterBidirectionalCollection< // LazyMapRandomAccessCollection< // Array<Int>, Int // > // >
  69. BURRITOS http://images.wisegeek.com/flour-tortilla.jpg

  70. PHOTOS let metadata: PhotosMetadata let photos = metadata.lazy.map{ Photo(url: $0.url)

    } // not slow!
  71. 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 }
  72. PHOTOS LazyFilterBidirectionalCollection< LazyMapRandomAccessCollection< LazyMapRandomAccessCollection< PHAsset, Photo >, PhotoView > >

  73. TORTILLA INFERENCE http://m.jumpstart.com/JumpstartNew/uploadedFiles/sne/small-screenshots/the-magic-tortilla.jpg

  74. TYPE INFERENCE let m = d.lazy.map/*...*/ return m.first

  75. TORTILLA ERASURE https://2e0a24317f4a9294563f-26c3b154822345d9dde0204930c49e9c.ssl.cf1.rackcdn.com/9218426_the-tortilla- pencil-case-deliciously-wraps_11d463da_m.jpg?bg=DFC1B7

  76. TYPE ERASURE let m = AnyCollection( d.lazy.filter{}.map{}/*...*/ ) // m:

    AnyCollection<PhotoView>
  77. TYPE ERASURE AnySequence AnyCollection ... see appendix for more

  78. PHOTOS func prepareData(d: PhotosMetadata) -> AnyCollection<PhotoView>

  79. PHOTOS Product wants everyOther photo http://www.giftofcuriosity.com/wp-content/uploads/2016/04/Letter-hopscotch-4.jpg

  80. NEW TORTILLAS let a = [1,2,3,4] for x in a.lazy.everyOther()

    { print(x) // 2,4 }
  81. NEW TORTILLAS // the tortilla sequence struct LazyEveryOtherSequence <S: Sequence>:

    LazySequenceProtocol { var base: S func makeIterator() -> LazyEveryOtherIterator<S.Iterator> { return LazyEveryOtherIterator(base: base.makeIterator()) } }
  82. 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 } } }
  83. NEW TORTILLAS extension LazySequenceProtocol { func everyOther() -> LazyEveryOtherSequence<Self> {

    return LazyEveryOtherSequence(base: self) } }
  84. PHOTOS let skipped = photos.everyOther()

  85. PHOTOS Product wants every 4th photo http://www.giftofcuriosity.com/wp-content/uploads/2016/04/Letter-hopscotch-4.jpg

  86. PHOTOS let skipped = photos.everyOther().everyOther()

  87. RECAP

  88. RECAP Collections hold our photo metadata and photos

  89. RECAP Collections hold our photo metadata and photos LazyCollection's map

    transforms our data
  90. RECAP Collections hold our photo metadata and photos LazyCollection's map

    transforms our data AnyCollection gives maintainable type signatures
  91. 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
  92. FINALLY Our app works!

  93. P.S. IBM Swift Sandbox is legit Check out Unary indexed

    collections in appendix
  94. GROK IT? By / Slide Deck: Brandon Kase @bkase_ https://is.gd/mEvKbt

  95. APPENDIX

  96. EAGER Eager (or strict) is the opposite of lazy Computed

    always
  97. EAGER class A { var eagerField: Int = 1 +

    1 /* ... */ }
  98. LAZY class A { lazy var lazyField: Int = 1

    + 1 /* ... */ }
  99. EAGER GROUPS OF THINGS

  100. EAGER GROUPS OF THINGS Eager means operations on the groups

    of things are eager The groups of things themselves can be strict or lazy
  101. UNARY

  102. UNARY let u: Unary</*...*/> u[""] // 1st element u["x"] //

    2st element u["xx"] // 3st element u["xxx"] // 4st element
  103. UNARY Given any input collection addressable via Ints, Create a

    collection addressable via unary numbers!
  104. UNARY More powerful than overriding the subscript operator!

  105. UNARY print(u.lazy.map{ x in x + 1 }["xxx"])

  106. UNARY Let's build it

  107. UNARY struct Unary<C: Collection>: Collection where C.Index == Int, C.IndexDistance

    == Int {
  108. UNARY { var startIndex: String { /*...*/ } var endIndex:

    String { /*...*/ } func index(after i: String) -> String { /*...*/ } subscript(i: String) -> C.Iterator.Element { /*...*/ } }
  109. UNARY private let xs: C init(_ xs: C) { self.xs

    = xs }
  110. UNARY var startIndex: String { return "" }

  111. UNARY /// O(n) endIndex var endIndex: String { return String((0...xs.count).map{

    _ in "x"}) }
  112. UNARY func index(after i: String) -> String { return i

    + "x" }
  113. UNARY subscript(i: String) -> C.Iterator.Element { return xs[i.characters.count] }

  114. UNARY let u: Unary<Array<Int>> = Unary([1, 2, 3, 4, 5,

    6]) print(u.lazy.map{ x in x + 1 }["xxx"])
  115. 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)
  116. TYPE ERASURE

  117. TYPE ERASURE Type erasure works by moving the type info

    to the constructor
  118. 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)